From e77ce94e8e15c53d627cc76475903d69dfe11e62 Mon Sep 17 00:00:00 2001 From: Andreas Dyck Date: Mon, 13 Jul 2020 17:29:22 +0200 Subject: [PATCH] adjustments to the general design --- .../managedObjectCollectionFilter_test.go | 1 - .../managedObjectCollection_filter.go | 1 - .../managedObjects/managedObjectFilter.go | 47 ++- .../managedObjectFilter_test.go | 4 +- .../managedObjectReference_Api.go | 226 ++++++++++++ inventory/managedObjects/managedObject_Api.go | 342 +++++++++--------- .../managedObject_collection_test.go | 6 +- .../managedObject_create_test.go | 10 +- .../managedObject_update_test.go | 10 + inventory/managedObjects/model.go | 117 +++--- inventory/managedObjects/test_responses.go | 2 +- 11 files changed, 498 insertions(+), 268 deletions(-) delete mode 100644 inventory/managedObjects/managedObjectCollectionFilter_test.go delete mode 100644 inventory/managedObjects/managedObjectCollection_filter.go create mode 100644 inventory/managedObjects/managedObjectReference_Api.go diff --git a/inventory/managedObjects/managedObjectCollectionFilter_test.go b/inventory/managedObjects/managedObjectCollectionFilter_test.go deleted file mode 100644 index e545dce..0000000 --- a/inventory/managedObjects/managedObjectCollectionFilter_test.go +++ /dev/null @@ -1 +0,0 @@ -package managedObjects diff --git a/inventory/managedObjects/managedObjectCollection_filter.go b/inventory/managedObjects/managedObjectCollection_filter.go deleted file mode 100644 index e545dce..0000000 --- a/inventory/managedObjects/managedObjectCollection_filter.go +++ /dev/null @@ -1 +0,0 @@ -package managedObjects diff --git a/inventory/managedObjects/managedObjectFilter.go b/inventory/managedObjects/managedObjectFilter.go index 6294f39..784d5e5 100644 --- a/inventory/managedObjects/managedObjectFilter.go +++ b/inventory/managedObjects/managedObjectFilter.go @@ -1,32 +1,45 @@ package managedObjects import ( + "fmt" "net/url" + "strconv" + "strings" ) -type ManagedObjectCollectionFilter struct { - Type string - Owner string - FragmentType string - QueryLanguage string //please note: If the 'QueryLanguage' has been set, other parameters like 'DeviceID' and 'Type' will be ignored. - +type ManagedObjectFilter struct { + Type string + FragmentType string + Ids []int + Text string } -func (m ManagedObjectCollectionFilter) QueryParams() string { - params := url.Values{} +// Appends the filter query parameters to the provided parameter values for a request. +// When provided values is nil an error will be created +func (managedObjectFilter ManagedObjectFilter) QueryParams(params *url.Values) error { + if params == nil { + return fmt.Errorf("The provided parameter values must not be nil!") + } - if len(m.QueryLanguage) > 0 { - return m.QueryLanguage + if len(managedObjectFilter.Type) > 0 { + params.Add("type", managedObjectFilter.Type) } - if len(m.Type) > 0 { - params.Add("type", m.Type) + if len(managedObjectFilter.FragmentType) > 0 { + params.Add("fragmentType", managedObjectFilter.FragmentType) } - if len(m.Owner) > 0 { - params.Add("owner", m.Type) + + if len(managedObjectFilter.Ids) > 0 { + var idsAsString []string + for _, id := range managedObjectFilter.Ids { + idsAsString = append(idsAsString, strconv.Itoa(id)) + } + params.Add("ids", strings.Join(idsAsString, ",")) } - if len(m.FragmentType) > 0 { - params.Add("fragmentType", m.FragmentType) + + if len(managedObjectFilter.Text) > 0 { + params.Add("text", managedObjectFilter.Text) } - return params.Encode() + + return nil } diff --git a/inventory/managedObjects/managedObjectFilter_test.go b/inventory/managedObjects/managedObjectFilter_test.go index 8af027c..6bfe505 100644 --- a/inventory/managedObjects/managedObjectFilter_test.go +++ b/inventory/managedObjects/managedObjectFilter_test.go @@ -7,7 +7,7 @@ import ( func TestManagedObjectCollectionFilter_QueryParams_WithoutQueryLanguage(t *testing.T) { // given - collectionFilter := ManagedObjectCollectionFilter{ + collectionFilter := ManagedObjectFilter{ Type: "Type", Owner: "Owner", FragmentType: "FragmentType", @@ -35,7 +35,7 @@ func TestManagedObjectCollectionFilter_QueryParams_WithoutQueryLanguage(t *testi func TestManagedObjectCollectionFilter_QueryParams_WithQueryLanguage(t *testing.T) { // given - collectionFilter := ManagedObjectCollectionFilter{ + collectionFilter := ManagedObjectFilter{ Type: "Type", Owner: "Owner", FragmentType: "FragmentType", diff --git a/inventory/managedObjects/managedObjectReference_Api.go b/inventory/managedObjects/managedObjectReference_Api.go new file mode 100644 index 0000000..f7acab8 --- /dev/null +++ b/inventory/managedObjects/managedObjectReference_Api.go @@ -0,0 +1,226 @@ +package managedObjects + +import ( + "encoding/json" + "fmt" + "github.com/tarent/gomulocity/generic" + "log" + "net/http" + "net/url" +) + +const ( + MANAGED_OBJECT_REFERENCE_TYPE = "application/vnd.com.nsn.cumulocity.managedObjectReference+json" + MANAGED_OBJECT_REFERENCE_COLLECTION_TYPE = "application/vnd.com.nsn.cumulocity.managedObjectReferenceCollection+json" + + MANAGED_OBJECT_REFERENCE_API_PATH = "/inventory/managedObjects" +) + +type ManagedObjectReferenceApi interface { + // Create a new managed object reference and returns the created entity with id, creation time and other properties + Create(managedObjectId string, referenceType ReferenceType) (*ManagedObjectReference, *generic.Error) + + // Gets an exiting managed object reference by its id. If the id does not exists, nil is returned. + Get(managedObjectId string, referenceType ReferenceType, referenceID string) (*ManagedObjectReference, *generic.Error) + + GetMany(managedObjectId string, referenceType ReferenceType, pageSize int) (*ManagedObjectReferenceCollection, *generic.Error) + + // Deletion by managedObjectReference id. If error is nil, managed object reference was deleted successfully. + Delete(managedObjectId string, referenceType ReferenceType, referenceID string) *generic.Error + + // Gets the next page from an existing managed object reference collection. + // If there is no next page, nil is returned. + NextPage(c *ManagedObjectReferenceCollection) (*ManagedObjectReferenceCollection, *generic.Error) + + // Gets the previous page from an existing managed object reference collection. + // If there is no previous page, nil is returned. + PreviousPage(c *ManagedObjectReferenceCollection) (*ManagedObjectReferenceCollection, *generic.Error) +} + +type managedObjectReferenceApi struct { + client *generic.Client + basePath string +} + +// Creates a new managed object reference api object +// +// client - Must be a gomulocity client. +// returns - The `ManagedObjectReferenceApi` object +func NewManagedObjectReferenceApi(client *generic.Client) ManagedObjectReferenceApi { + return &managedObjectReferenceApi{client, MANAGED_OBJECT_REFERENCE_API_PATH} +} + +/* +Creates a new managed object reference based on the given variables. + +See: https://cumulocity.com/guides/reference/inventory/#post-create-a-new-managedobject +*/ +func (managedObjectReferenceApi *managedObjectReferenceApi) Create(managedObjectId string, referenceType ReferenceType) (*ManagedObjectReference, *generic.Error) { + if len(managedObjectId) == 0 { + return nil, generic.ClientError("managedObjectId must not be empty", "GetManagedObjectReference") + } + + newManagedObjectReference := NewManagedObjectReference{Source{Id: managedObjectId}} + bytes, err := json.Marshal(newManagedObjectReference) + if err != nil { + return nil, generic.ClientError(fmt.Sprintf("Error while marshalling the managedObjectReference: %s", err.Error()), "CreateManagedObjectReference") + } + headers := generic.AcceptAndContentTypeHeader(MANAGED_OBJECT_REFERENCE_TYPE, MANAGED_OBJECT_REFERENCE_TYPE) + + path := fmt.Sprintf("%s/%s/%s", managedObjectReferenceApi.basePath, url.QueryEscape(managedObjectId), url.QueryEscape(string(referenceType))) + body, status, err := managedObjectReferenceApi.client.Post(path, bytes, headers) + if err != nil { + return nil, generic.ClientError(fmt.Sprintf("Error while posting a new managedObjectReference: %s", err.Error()), "CreateManagedObjectReference") + } + if status != http.StatusCreated { + return nil, generic.CreateErrorFromResponse(body, status) + } + + return parseManagedObjectReferenceResponse(body) +} + +/* +Gets a managedObjectReference for a given Id. + +Returns 'ManagedObjectReference' on success or nil if the id does not exist. +*/ +func (managedObjectReferenceApi *managedObjectReferenceApi) Get(managedObjectId string, referenceType ReferenceType, referenceID string) (*ManagedObjectReference, *generic.Error) { + if len(managedObjectId) == 0 { + return nil, generic.ClientError("managedObjectId must not be empty", "GetManagedObjectReference") + } + if len(referenceID) == 0 { + return nil, generic.ClientError("referenceID must not be empty", "GetManagedObjectReference") + } + + path := fmt.Sprintf("%s/%s/%s/%s", managedObjectReferenceApi.basePath, url.QueryEscape(managedObjectId), url.QueryEscape(string(referenceType)), url.QueryEscape(referenceID)) + body, status, err := managedObjectReferenceApi.client.Get(path, generic.AcceptHeader(MANAGED_OBJECT_REFERENCE_TYPE)) + + if err != nil { + return nil, generic.ClientError(fmt.Sprintf("Error while getting a managedObjectReference: %s", err.Error()), "GetManagedObjectReference") + } + if status == http.StatusNotFound { + return nil, nil + } + if status != http.StatusOK { + return nil, generic.CreateErrorFromResponse(body, status) + } + + return parseManagedObjectReferenceResponse(body) +} + +/* + Returns a collection of managed object references on success or nil if the id does not exist. +*/ +func (managedObjectReferenceApi *managedObjectReferenceApi) GetMany(managedObjectId string, referenceType ReferenceType, pageSize int) (*ManagedObjectReferenceCollection, *generic.Error) { + if len(managedObjectId) == 0 { + return nil, generic.ClientError("managedObjectId must not be empty", "GetManagedObjectReference") + } + queryParamsValues := &url.Values{} + err := generic.PageSizeParameter(pageSize, queryParamsValues) + if err != nil { + return nil, generic.ClientError(fmt.Sprintf("Error while building pageSize parameter to fetch managedObjectReferences: %s", err.Error()), "FindManagedObjectReferences") + } + + path := fmt.Sprintf("%s/%s/%s?%s", managedObjectReferenceApi.basePath, url.QueryEscape(managedObjectId), url.QueryEscape(string(referenceType)), queryParamsValues.Encode()) + + return managedObjectReferenceApi.getCommon(path) +} + +/* +Deletes managedObjectReference by id. +*/ +func (managedObjectReferenceApi *managedObjectReferenceApi) Delete(managedObjectId string, referenceType ReferenceType, referenceID string) *generic.Error { + if len(managedObjectId) == 0 { + return generic.ClientError("Deleting managedObjectReference without an id is not allowed", "DeleteManagedObjectReference") + } + if len(referenceID) == 0 { + return generic.ClientError("referenceID must not be empty", "DeleteManagedObjectReference") + } + + path := fmt.Sprintf("%s/%s/%s/%s", managedObjectReferenceApi.basePath, url.QueryEscape(managedObjectId), url.QueryEscape(string(referenceType)), url.QueryEscape(referenceID)) + + body, status, err := managedObjectReferenceApi.client.Delete(path, generic.EmptyHeader()) + if err != nil { + return generic.ClientError(fmt.Sprintf("Error while deleting managedObjectReference with id [%s]: %s", managedObjectId, err.Error()), "DeleteManagedObjectReference") + } + + if status != http.StatusNoContent { + return generic.CreateErrorFromResponse(body, status) + } + + return nil +} + +func (managedObjectReferenceApi *managedObjectReferenceApi) NextPage(c *ManagedObjectReferenceCollection) (*ManagedObjectReferenceCollection, *generic.Error) { + return managedObjectReferenceApi.getPage(c.Next) +} + +func (managedObjectReferenceApi *managedObjectReferenceApi) PreviousPage(c *ManagedObjectReferenceCollection) (*ManagedObjectReferenceCollection, *generic.Error) { + return managedObjectReferenceApi.getPage(c.Prev) +} + +// -- internal + +func (managedObjectReferenceApi *managedObjectReferenceApi) getPage(reference string) (*ManagedObjectReferenceCollection, *generic.Error) { + if reference == "" { + log.Print("No page reference given. Returning nil.") + return nil, nil + } + + nextUrl, err := url.Parse(reference) + if err != nil { + return nil, generic.ClientError(fmt.Sprintf("Unparsable URL given for page reference: '%s'", reference), "GetPage") + } + + collection, genErr := managedObjectReferenceApi.getCommon(fmt.Sprintf("%s?%s", nextUrl.Path, nextUrl.RawQuery)) + if genErr != nil { + return nil, genErr + } + + if len(collection.References) == 0 { + log.Print("Returned collection is empty. Returning nil.") + return nil, nil + } + + return collection, nil +} + +func (managedObjectReferenceApi *managedObjectReferenceApi) getCommon(path string) (*ManagedObjectReferenceCollection, *generic.Error) { + body, status, err := managedObjectReferenceApi.client.Get(path, generic.AcceptHeader(MANAGED_OBJECT_REFERENCE_COLLECTION_TYPE)) + if err != nil { + return nil, generic.ClientError(fmt.Sprintf("Error while getting managedObjectReferences: %s", err.Error()), "GetManagedObjectReferenceCollection") + } + + if status == http.StatusNotFound { + return nil, nil + } + if status != http.StatusOK { + return nil, generic.CreateErrorFromResponse(body, status) + } + + var result ManagedObjectReferenceCollection + if len(body) > 0 { + err = json.Unmarshal(body, &result) + if err != nil { + return nil, generic.ClientError(fmt.Sprintf("Error while parsing response JSON: %s", err.Error()), "GetManagedObjectReferenceCollection") + } + } else { + return nil, generic.ClientError("Response body was empty", "GetManagedObjectReferenceCollection") + } + + return &result, nil +} + +func parseManagedObjectReferenceResponse(body []byte) (*ManagedObjectReference, *generic.Error) { + var result ManagedObjectReference + if len(body) > 0 { + err := json.Unmarshal(body, &result) + if err != nil { + return nil, generic.ClientError(fmt.Sprintf("Error while parsing response JSON: %s", err.Error()), "ResponseParser") + } + } else { + return nil, generic.ClientError("Response body was empty", "GetManagedObjectReference") + } + + return &result, nil +} \ No newline at end of file diff --git a/inventory/managedObjects/managedObject_Api.go b/inventory/managedObjects/managedObject_Api.go index b102d41..eb8ba19 100644 --- a/inventory/managedObjects/managedObject_Api.go +++ b/inventory/managedObjects/managedObject_Api.go @@ -1,273 +1,265 @@ package managedObjects import ( - "bytes" "encoding/json" "fmt" "github.com/tarent/gomulocity/generic" + "log" "net/http" "net/url" ) +const ( + MANAGED_OBJECT_TYPE = "application/vnd.com.nsn.cumulocity.managedObject+json" + MANAGED_OBJECT_COLLECTION_TYPE = "application/vnd.com.nsn.cumulocity.managedObjectCollection+json" + + MANAGED_OBJECT_API_PATH = "/inventory/managedObjects" +) + type ManagedObjectApi interface { - CreateManagedObject(object *CreateManagedObject) (*NewManagedObject, *generic.Error) - ManagedObjectCollection(filter ManagedObjectCollectionFilter) (ManagedObjectCollection, *generic.Error) - ManagedObjectByID(deviceID string) (ManagedObject, *generic.Error) - ReferenceByID(deviceID, reference, referenceID string) (Reference, *generic.Error) - ReferenceCollection(deviceID, reference string) (ReferenceCollection, *generic.Error) - DeleteReference(deviceID, reference, referenceID string) *generic.Error - AddReferenceToCollection(deviceID, reference string) (interface{}, *generic.Error) - UpdateManagedObject(deviceID string, model *Update) (UpdateResponse, *generic.Error) - DeleteManagedObject(deviceID string) *generic.Error + // Create a new managed object and returns the created entity with id, creation time and other properties + Create(newManagedObject *NewManagedObject) (*ManagedObject, *generic.Error) + + // Gets an exiting managed object by its id. If the id does not exists, nil is returned. + Get(managedObjectId string) (*ManagedObject, *generic.Error) + + Update(managedObjectId string, managedObject *ManagedObjectUpdate) (*ManagedObject, *generic.Error) + + // Deletion by managedObject id. If error is nil, managed object was deleted successfully. + Delete(managedObjectId string) *generic.Error + + // Returns a managed object collection, found by the given managed object filter parameters. + // All query parameters are AND concatenated. + Find(managedObjectFilter *ManagedObjectFilter, pageSize int) (*ManagedObjectCollection, *generic.Error) + + // Returns a managed object collection, found by the given managed object query. + // See the query language: https://cumulocity.com/guides/reference/inventory/#query-language + FindByQuery(query string, pageSize int) (*ManagedObjectCollection, *generic.Error) + + // Gets the next page from an existing managed object collection. + // If there is no next page, nil is returned. + NextPage(c *ManagedObjectCollection) (*ManagedObjectCollection, *generic.Error) + + // Gets the previous page from an existing managed object collection. + // If there is no previous page, nil is returned. + PreviousPage(c *ManagedObjectCollection) (*ManagedObjectCollection, *generic.Error) } type managedObjectApi struct { - Client *generic.Client - ManagedObjectsPath string + client *generic.Client + basePath string } +// Creates a new managed object api object +// +// client - Must be a gomulocity client. +// returns - The `managed object`-api object func NewManagedObjectApi(client *generic.Client) ManagedObjectApi { - return managedObjectApi{ - Client: client, - ManagedObjectsPath: managedObjectPath, - } + return &managedObjectApi{client, MANAGED_OBJECT_API_PATH} } /* Creates a new managed object based on the given variables. -See: https://cumulocity.com/guides/reference/inventory/#managed-object-collection +See: https://cumulocity.com/guides/reference/inventory/#post-create-a-new-managedobject */ -func (m managedObjectApi) CreateManagedObject(model *CreateManagedObject) (*NewManagedObject, *generic.Error) { - bytes, err := json.Marshal(model) +func (managedObjectApi *managedObjectApi) Create(newManagedObject *NewManagedObject) (*ManagedObject, *generic.Error) { + bytes, err := json.Marshal(newManagedObject) if err != nil { - return nil, clientError(fmt.Sprintf("Error while marshalling managed object: %s", err), "CreateManagedObject") + return nil, generic.ClientError(fmt.Sprintf("Error while marshalling the managedObject: %s", err.Error()), "CreateManagedObject") } - result, statusCode, err := m.Client.Post(fmt.Sprintf("%v", managedObjectPath), bytes, generic.AcceptAndContentTypeHeader(MANAGED_OBJECT_ACCEPT, MANAGED_OBJECT_CONTENT_TYPE)) + headers := generic.AcceptAndContentTypeHeader(MANAGED_OBJECT_TYPE, MANAGED_OBJECT_TYPE) + + body, status, err := managedObjectApi.client.Post(managedObjectApi.basePath, bytes, headers) if err != nil { - return nil, clientError(fmt.Sprintf("Error while creating a new managed object: %s", err), "CreateManagedObject") + return nil, generic.ClientError(fmt.Sprintf("Error while posting a new managedObject: %s", err.Error()), "CreateManagedObject") } - - if statusCode != http.StatusCreated { - return nil, createErrorFromResponse(result) + if status != http.StatusCreated { + return nil, generic.CreateErrorFromResponse(body, status) } - newManagedObject := &NewManagedObject{} - if err = json.Unmarshal(result, newManagedObject); err != nil { - return nil, clientError(fmt.Sprintf("Error while unmarshalling response: %s", err), "CreateManagedObject") - } - return newManagedObject, nil + return parseManagedObjectResponse(body) } /* -Returns a collection of managed objects. +Gets a managedObject for a given Id. -See: https://cumulocity.com/guides/reference/inventory/#managed-object-collection +Returns 'ManagedObject' on success or nil if the id does not exist. */ -func (m managedObjectApi) ManagedObjectCollection(filter ManagedObjectCollectionFilter) (ManagedObjectCollection, *generic.Error) { - var tempCollection ManagedObjectCollection - - url := fmt.Sprintf("%v?%v", m.ManagedObjectsPath, filter.QueryParams()) - for { - result, statusCode, err := m.Client.Get(url, generic.EmptyHeader()) - if err != nil { - return ManagedObjectCollection{}, clientError(fmt.Sprintf("failed to execute rest request: %s", err), "ManagedObjectCollection") - } - if statusCode != http.StatusOK { - return ManagedObjectCollection{}, createErrorFromResponse(result) - } +func (managedObjectApi *managedObjectApi) Get(managedObjectId string) (*ManagedObject, *generic.Error) { + if len(managedObjectId) == 0 { + return nil, generic.ClientError("managedObjectId must not be empty", "GetManagedObject") + } - objectCollection := ManagedObjectCollection{} - if err := json.NewDecoder(bytes.NewReader(result)).Decode(&objectCollection); err != nil { - return ManagedObjectCollection{}, clientError(fmt.Sprintf("failed to unmarshal response body: %s", err), "ManagedObjectCollection") - } - - for _, collection := range objectCollection.ManagedObjects { - tempCollection.ManagedObjects = append(tempCollection.ManagedObjects, collection) - } - - if objectCollection.hasNextPage() { - var genericErr *generic.Error - url, genericErr = objectCollection.NextPage() - if genericErr != nil { - return ManagedObjectCollection{}, genericErr - } - } else { - break - } + path := fmt.Sprintf("%s/%s", managedObjectApi.basePath, url.QueryEscape(managedObjectId)) + body, status, err := managedObjectApi.client.Get(path, generic.AcceptHeader(MANAGED_OBJECT_TYPE)) + if err != nil { + return nil, generic.ClientError(fmt.Sprintf("Error while getting a managedObject: %s", err.Error()), "GetManagedObject") + } + if status == http.StatusNotFound { + return nil, nil + } + if status != http.StatusOK { + return nil, generic.CreateErrorFromResponse(body, status) } - return tempCollection, nil + + return parseManagedObjectResponse(body) } + /* -Returns a single managed object. +Updates the managedObject with given Id. + +See: https://cumulocity.com/guides/reference/managedObjects/#update-an-managedObject */ -func (m managedObjectApi) ManagedObjectByID(deviceID string) (ManagedObject, *generic.Error) { - if len(deviceID) == 0 { - return ManagedObject{}, clientError("given deviceID is empty", "ManagedObjectByID") +func (managedObjectApi *managedObjectApi) Update(managedObjectId string, managedObject *ManagedObjectUpdate) (*ManagedObject, *generic.Error) { + if len(managedObjectId) == 0 { + return nil, generic.ClientError("Updating managedObject without an id is not allowed", "UpdateManagedObject") } - - result, code, err := m.Client.Get(fmt.Sprintf("%v/%v", m.ManagedObjectsPath, url.QueryEscape(deviceID)), generic.EmptyHeader()) + bytes, err := json.Marshal(managedObject) if err != nil { - return ManagedObject{}, clientError(fmt.Sprintf("error while getting managedObject: %v", err), "ManagedObjectByID") + return nil, generic.ClientError(fmt.Sprintf("Error while marshalling the update managedObject: %s", err.Error()), "UpdateManagedObject") } - if code != http.StatusOK { - return ManagedObject{}, createErrorFromResponse(result) - } + path := fmt.Sprintf("%s/%s", managedObjectApi.basePath, url.QueryEscape(managedObjectId)) + headers := generic.AcceptAndContentTypeHeader(MANAGED_OBJECT_TYPE, MANAGED_OBJECT_TYPE) - managedObject := ManagedObject{} - if err := json.Unmarshal(result, &managedObject); err != nil { - return ManagedObject{}, clientError(fmt.Sprintf("error while unmarshalling managedObject: %v", err), "ManagedObjectByID") + body, status, err := managedObjectApi.client.Put(path, bytes, headers) + if err != nil { + return nil, generic.ClientError(fmt.Sprintf("Error while updating an managedObject: %s", err.Error()), "UpdateManagedObject") + } + if status != http.StatusOK { + return nil, generic.CreateErrorFromResponse(body, status) } - return managedObject, nil + + return parseManagedObjectResponse(body) } /* -See: https://cumulocity.com/guides/reference/inventory/#managed-object-reference +Deletes managedObject by id. */ -func (m managedObjectApi) ReferenceByID(deviceID, reference, referenceID string) (Reference, *generic.Error) { - if len(deviceID) == 0 || len(reference) == 0 || len(referenceID) == 0 { - return Reference{}, clientError("given deviceID, reference or referenceID is empty", "ReferenceByID") +func (managedObjectApi *managedObjectApi) Delete(managedObjectId string) *generic.Error { + if len(managedObjectId) == 0 { + return generic.ClientError("Deleting managedObject without an id is not allowed", "DeleteManagedObject") } - result, code, err := m.Client.Get(fmt.Sprintf("%v/%v/%v/%v", m.ManagedObjectsPath, url.QueryEscape(deviceID), url.QueryEscape(reference), url.QueryEscape(referenceID)), generic.EmptyHeader()) + body, status, err := managedObjectApi.client.Delete(fmt.Sprintf("%s/%s", managedObjectApi.basePath, url.QueryEscape(managedObjectId)), generic.EmptyHeader()) if err != nil { - return Reference{}, clientError(fmt.Sprintf("error while getting reference: %v with referenceID: %v for device: %v, %s", reference, referenceID, deviceID, err), "ReferenceByID") + return generic.ClientError(fmt.Sprintf("Error while deleting managedObject with id [%s]: %s", managedObjectId, err.Error()), "DeleteManagedObject") } - if code != http.StatusOK { - return Reference{}, createErrorFromResponse(result) + if status != http.StatusNoContent { + return generic.CreateErrorFromResponse(body, status) } - var referenceModel Reference - if err := json.Unmarshal(result, &referenceModel); err != nil { - return Reference{}, clientError(fmt.Sprintf("received an error while unmarshalling response: %s", err), "ReferenceByID") - } - return referenceModel, nil + return nil } /* -Returns a reference collection for a device and reference. + Returns a collection of managed objects. -See: https://cumulocity.com/guides/reference/inventory/#managed-object-reference + See: https://cumulocity.com/guides/reference/inventory/#managed-object-collection */ -func (m managedObjectApi) ReferenceCollection(deviceID, reference string) (ReferenceCollection, *generic.Error) { - if len(deviceID) == 0 || len(reference) == 0 { - return ReferenceCollection{}, clientError("given deviceID or reference is empty", "ReferenceCollection") +func (managedObjectApi *managedObjectApi) Find(managedObjectFilter *ManagedObjectFilter, pageSize int) (*ManagedObjectCollection, *generic.Error) { + queryParamsValues := &url.Values{} + err := managedObjectFilter.QueryParams(queryParamsValues) + if err != nil { + return nil, generic.ClientError(fmt.Sprintf("Error while building query parameters to search for managedObjects: %s", err.Error()), "FindManagedObjects") } - result, code, err := m.Client.Get(fmt.Sprintf("%v/%v/%v", m.ManagedObjectsPath, url.QueryEscape(deviceID), url.QueryEscape(reference)), generic.EmptyHeader()) + err = generic.PageSizeParameter(pageSize, queryParamsValues) if err != nil { - return ReferenceCollection{}, clientError(fmt.Sprintf("error while getting reference collections: %s", err), "ReferenceCollection") + return nil, generic.ClientError(fmt.Sprintf("Error while building pageSize parameter to fetch managedObjects: %s", err.Error()), "FindManagedObjects") } - if code != http.StatusOK { - if code == http.StatusNotFound { - return ReferenceCollection{}, clientError(fmt.Sprintf("no reference collection found for reference: %v", reference), "ReferenceCollection") - } - return ReferenceCollection{}, createErrorFromResponse(result) + return managedObjectApi.getCommon(fmt.Sprintf("%s?%s", managedObjectApi.basePath, queryParamsValues.Encode())) +} + +func (managedObjectApi *managedObjectApi) FindByQuery(query string, pageSize int) (*ManagedObjectCollection, *generic.Error) { + queryParamsValues := &url.Values{} + if len(query) > 0 { + queryParamsValues.Add("query", query) } - referenceCollection := ReferenceCollection{} - if err := json.Unmarshal(result, &referenceCollection); err != nil { - return ReferenceCollection{}, clientError(fmt.Sprintf("received an error while unmarshalling response: %s", err), "ReferenceCollection") + err := generic.PageSizeParameter(pageSize, queryParamsValues) + if err != nil { + return nil, generic.ClientError(fmt.Sprintf("Error while building pageSize parameter to fetch managedObjects: %s", err.Error()), "FindManagedObjectsByQuery") } - return referenceCollection, nil -} -func (m managedObjectApi) AddReferenceToCollection(deviceID, reference string) (interface{}, *generic.Error) { - return nil, nil + return managedObjectApi.getCommon(fmt.Sprintf("%s?%s", managedObjectApi.basePath, queryParamsValues.Encode())) } -func (m managedObjectApi) DeleteReference(deviceID, reference, referenceID string) *generic.Error { - if len(deviceID) == 0 || len(reference) == 0 || len(referenceID) == 0 { - return clientError("given deviceID, reference or referenceID is empty", "DeleteReference") - } - result, code, err := m.Client.Delete(fmt.Sprintf("%v/%v/%v/%v", m.ManagedObjectsPath, url.QueryEscape(deviceID), url.QueryEscape(reference), url.QueryEscape(referenceID)), generic.EmptyHeader()) - if err != nil { - return clientError(fmt.Sprintf("received an error while deleting reference: %s", err), "DeleteReference") - } +func (managedObjectApi *managedObjectApi) NextPage(c *ManagedObjectCollection) (*ManagedObjectCollection, *generic.Error) { + return managedObjectApi.getPage(c.Next) +} - if code != http.StatusNoContent { - return createErrorFromResponse(result) - } - return nil +func (managedObjectApi *managedObjectApi) PreviousPage(c *ManagedObjectCollection) (*ManagedObjectCollection, *generic.Error) { + return managedObjectApi.getPage(c.Prev) } -func (m managedObjectApi) UpdateManagedObject(deviceID string, model *Update) (UpdateResponse, *generic.Error) { - if len(deviceID) == 0 { - return UpdateResponse{}, clientError("given deviceID is empty", "UpdateManagedObject") - } - bytes, err := json.Marshal(model) - if err != nil { - return UpdateResponse{}, clientError(fmt.Sprintf("received an error while marshalling managedObject: %s", err), "UpdateManagedObject") + + +// -- internal + +func (managedObjectApi *managedObjectApi) getPage(reference string) (*ManagedObjectCollection, *generic.Error) { + if reference == "" { + log.Print("No page reference given. Returning nil.") + return nil, nil } - result, code, err := m.Client.Put(fmt.Sprintf("%v/%v", m.ManagedObjectsPath, url.QueryEscape(deviceID)), bytes, generic.ContentTypeHeader(MANAGED_OBJECT_CONTENT_TYPE)) + nextUrl, err := url.Parse(reference) if err != nil { - return UpdateResponse{}, clientError(fmt.Sprintf("received an error while updating managedObject: %s", err), "UpdateManagedObject") + return nil, generic.ClientError(fmt.Sprintf("Unparsable URL given for page reference: '%s'", reference), "GetPage") } - if code != http.StatusOK { - return UpdateResponse{}, createErrorFromResponse(result) + + collection, genErr := managedObjectApi.getCommon(fmt.Sprintf("%s?%s", nextUrl.Path, nextUrl.RawQuery)) + if genErr != nil { + return nil, genErr } - updateResponse := UpdateResponse{} - if err = json.Unmarshal(result, &updateResponse); err != nil { - return UpdateResponse{}, clientError(fmt.Sprintf("received an error while unmarshalling response: %s", err), "UpdateManagedObject") + if len(collection.ManagedObjects) == 0 { + log.Print("Returned collection is empty. Returning nil.") + return nil, nil } - return updateResponse, nil + + return collection, nil } -func (m managedObjectApi) DeleteManagedObject(deviceID string) *generic.Error { - if len(deviceID) == 0 { - return clientError("given deviceID is empty", "DeleteManagedObject") - } - result, code, err := m.Client.Delete(fmt.Sprintf("%v/%v", m.ManagedObjectsPath, url.QueryEscape(deviceID)), generic.EmptyHeader()) +func (managedObjectApi *managedObjectApi) getCommon(path string) (*ManagedObjectCollection, *generic.Error) { + body, status, err := managedObjectApi.client.Get(path, generic.AcceptHeader(MANAGED_OBJECT_COLLECTION_TYPE)) if err != nil { - return clientError(fmt.Sprintf("received an error while deleting managed object: %s", err), "DeleteManagedObject") + return nil, generic.ClientError(fmt.Sprintf("Error while getting managedObjects: %s", err.Error()), "GetManagedObjectCollection") } - if code != http.StatusNoContent { - return createErrorFromResponse(result) - } - return nil -} -func (d ManagedObjectCollection) PrintToConsole() { - for _, managedObject := range d.ManagedObjects { - fmt.Println(fmt.Sprintf("Device ID: %v Device name: %v", managedObject.ID, managedObject.Name)) + if status != http.StatusOK { + return nil, generic.CreateErrorFromResponse(body, status) } - fmt.Printf("Amount of devices: %v", len(d.ManagedObjects)) -} - -func (d ManagedObjectCollection) NextPage() (string, *generic.Error) { - return buildURL(d.Next) -} -func (d ManagedObjectCollection) hasNextPage() bool { - return len(d.Next) > 0 -} - -func buildURL(next string) (string, *generic.Error) { - url, err := url.Parse(next) - if err != nil { - return "", clientError(fmt.Sprintf("failed to parse url of the next managedObject page: %s", err), "buildURL") + var result ManagedObjectCollection + if len(body) > 0 { + err = json.Unmarshal(body, &result) + if err != nil { + return nil, generic.ClientError(fmt.Sprintf("Error while parsing response JSON: %s", err.Error()), "GetManagedObjectCollection") + } + } else { + return nil, generic.ClientError("Response body was empty", "GetManagedObjectCollection") } - return fmt.Sprintf("%v?%v", url.Path, url.RawQuery), nil + + return &result, nil } -func clientError(message string, info string) *generic.Error { - return &generic.Error{ - ErrorType: "ClientError", - Message: message, - Info: info, +func parseManagedObjectResponse(body []byte) (*ManagedObject, *generic.Error) { + var result ManagedObject + if len(body) > 0 { + err := json.Unmarshal(body, &result) + if err != nil { + return nil, generic.ClientError(fmt.Sprintf("Error while parsing response JSON: %s", err.Error()), "ResponseParser") + } + } else { + return nil, generic.ClientError("Response body was empty", "GetManagedObject") } -} -func createErrorFromResponse(responseBody []byte) *generic.Error { - var err generic.Error - _ = json.Unmarshal(responseBody, &err) - return &err + return &result, nil } diff --git a/inventory/managedObjects/managedObject_collection_test.go b/inventory/managedObjects/managedObject_collection_test.go index dd36ce6..b8c867e 100644 --- a/inventory/managedObjects/managedObject_collection_test.go +++ b/inventory/managedObjects/managedObject_collection_test.go @@ -15,7 +15,7 @@ const ( func TestManagedObjectApi_ManagedObjectCollection(t *testing.T) { tests := []struct { name string - filter ManagedObjectCollectionFilter + filter ManagedObjectFilter respCode int respBody string expectedRespBody string @@ -23,7 +23,7 @@ func TestManagedObjectApi_ManagedObjectCollection(t *testing.T) { }{ { name: "Happy - no QueryLanguage given", - filter: ManagedObjectCollectionFilter{ + filter: ManagedObjectFilter{ Type: "c8y_SensorPhone", Owner: "device_4D8AFED3", FragmentType: "fragmentType", @@ -34,7 +34,7 @@ func TestManagedObjectApi_ManagedObjectCollection(t *testing.T) { }, { name: "Happy - QueryLanguage given", - filter: ManagedObjectCollectionFilter{ + filter: ManagedObjectFilter{ //Filter like Type and Owner should be ignored Type: "c8y_SensorPhone", Owner: "device_4D8AFED3", diff --git a/inventory/managedObjects/managedObject_create_test.go b/inventory/managedObjects/managedObject_create_test.go index 4642ab8..f5e1b86 100644 --- a/inventory/managedObjects/managedObject_create_test.go +++ b/inventory/managedObjects/managedObject_create_test.go @@ -19,13 +19,13 @@ func TestManagedObjectApi_CreateManagedObject(t *testing.T) { c8yRespBody string expectedErr error - requestData *CreateManagedObject + requestData *NewManagedObject responseData NewManagedObject }{ { name: "create managed object - happy", - requestData: &CreateManagedObject{ + requestData: &NewManagedObject{ Type: "", Name: "", CreationDate: creationDate, @@ -50,7 +50,7 @@ func TestManagedObjectApi_CreateManagedObject(t *testing.T) { }, { name: "create managed object - status is not StatusCreated", - requestData: &CreateManagedObject{ + requestData: &NewManagedObject{ Type: "type", Name: "name", CreationDate: time.Time{}, @@ -65,14 +65,14 @@ func TestManagedObjectApi_CreateManagedObject(t *testing.T) { }, { name: "create managed object - failed to unmarshal result", - requestData: &CreateManagedObject{ + requestData: &NewManagedObject{ Type: "type", Name: "name", CreationDate: time.Time{}, }, c8yRespCode: http.StatusCreated, c8yRespBody: ``, - expectedErr: clientError("Error while unmarshalling response: invalid character '<' looking for beginning of value", "CreateManagedObject"), + expectedErr: clientError("Error while unmarshalling response: invalid character '<' looking for beginning of value", "NewManagedObject"), }, } diff --git a/inventory/managedObjects/managedObject_update_test.go b/inventory/managedObjects/managedObject_update_test.go index 6996c4b..d34759b 100644 --- a/inventory/managedObjects/managedObject_update_test.go +++ b/inventory/managedObjects/managedObject_update_test.go @@ -9,6 +9,16 @@ import ( "time" ) +type UpdateResponse struct { + ID string `json:"id"` + Name string `json:"name"` + Self string `json:"self"` + Type string `json:"type"` + LastUpdated time.Time `json:"lastUpdated"` + StrongTypedClass struct{} `json:"com_othercompany_StrongTypedClass"` + ChildDevices ChildDevices `json:"childDevices"` +} + func TestManagedObjectApi_UpdateManagedObject(t *testing.T) { lastUpdated, _ := time.Parse(time.RFC3339, "2019-08-23T15:10:00.653Z") diff --git a/inventory/managedObjects/model.go b/inventory/managedObjects/model.go index 7942fb2..29153ce 100644 --- a/inventory/managedObjects/model.go +++ b/inventory/managedObjects/model.go @@ -5,69 +5,43 @@ import ( "time" ) -const ( - MANAGED_OBJECT_ACCEPT = "application/vnd.com.nsn.cumulocity.managedObject+json" - MANAGED_OBJECT_CONTENT_TYPE = "application/vnd.com.nsn.cumulocity.managedObject+json" - - managedObjectPath = "/inventory/managedObjects" -) - -type Update struct { - Type string `json:"type"` - Name string `json:"name"` -} - -type UpdateResponse struct { - ID string `json:"id"` - Name string `json:"name"` - Self string `json:"self"` - Type string `json:"type"` - LastUpdated time.Time `json:"lastUpdated"` - StrongTypedClass struct{} `json:"com_othercompany_StrongTypedClass"` - ChildDevices ChildDevices `json:"childDevices"` -} - -type Reference struct { - ManagedObject ManagedObject `json:"managedObject"` - Self string `json:"self"` -} - -type ReferenceCollection struct { - Next string `json:"next"` - Self string `json:"self"` - References []Reference `json:"references"` -} - type NewManagedObject struct { - ID string `json:"id"` - Self string `json:"self"` - Type string `json:"type"` - Name string `json:"name"` + Type string `json:"type,omitempty"` + Name string `json:"name,omitempty"` CreationDate time.Time `json:"creationDate"` - LastUpdated time.Time `json:"lastUpdated"` - BinarySwitch struct { - State string `json:"state"` - } `json:"com_cumulocity_model_BinarySwitch"` } -type CreateManagedObject struct { - Type string `json:"type"` - Name string `json:"name"` - CreationDate time.Time `json:"creationDate"` +type ManagedObjectUpdate struct { + Type string `json:"type,omitempty"` + Name string `json:"name,omitempty"` } type ( ManagedObjectCollection struct { - Next string `json:"next"` - Self string `json:"self"` - ManagedObjects []ManagedObject `json:"managedObjects"` - Prev string `json:"prev"` - Statistics generic.PagingStatistics `json:"statistics"` + Self string `json:"self"` + ManagedObjects []ManagedObject `json:"managedObjects"` + Statistics *generic.PagingStatistics `json:"statistics,omitempty"` + Prev string `json:"prev,omitempty"` + Next string `json:"next,omitempty"` } ManagedObject struct { - AdditionParents AdditionParents `json:"additionParents"` - AssetParents AdditionParents `json:"assetParents"` + ID string `json:"id"` + Type string `json:"type"` + Name string `json:"name"` + CreationTime time.Time `json:"creationTime"` + LastUpdated time.Time `json:"lastUpdated"` + Self string `json:"self"` + Owner string `json:"owner"` + + AdditionParents AdditionParents `json:"additionParents"` + AssetParents AdditionParents `json:"assetParents"` + DeviceParents DeviceParents `json:"deviceParents"` + + ChildAdditions ChildAdditions `json:"childAdditions"` + ChildAssets ChildAssets `json:"childAssets"` + ChildDevices ChildDevices `json:"childDevices"` + C8YActiveAlarmsStatus C8YActiveAlarmsStatus `json:"c8y_ActiveAlarmsStatus"` C8YAvailability C8YAvailability `json:"c8y_Availability"` C8YConnection C8YConnection `json:"c8y_Connection"` @@ -78,17 +52,6 @@ type ( C8YIsSensorPhone interface{} `json:"c8y_IsSensorPhone"` C8YRequiredAvailability C8YRequiredAvailability `json:"c8y_RequiredAvailability"` C8YSupportedOperations []string `json:"c8y_SupportedOperations"` - ChildAdditions ChildAdditions `json:"childAdditions"` - ChildAssets ChildAssets `json:"childAssets"` - ChildDevices ChildDevices `json:"childDevices"` - ID string `json:"id"` - LastUpdated time.Time `json:"lastUpdated"` - Name string `json:"name"` - Owner string `json:"owner"` - Self string `json:"self"` - Type string `json:"type"` - CreationTime time.Time `json:"creationTime"` - DeviceParents DeviceParents `json:"deviceParents"` C8YStatus C8YStatus `json:"c8y_Status"` } @@ -179,3 +142,31 @@ type ( Status string `json:"status"` } ) + +type ReferenceType string + +const ( + CHILD_DEVICES ReferenceType = "childDevices" + CHILD_ASSETS ReferenceType = "childAssets" +) + +type Source struct { + Id string `json:"id"` +} + +type NewManagedObjectReference struct { + ManagedObject Source `json:"managedObject"` +} + +type ManagedObjectReference struct { + ManagedObject ManagedObject `json:"managedObject"` + Self string `json:"self"` +} + +type ManagedObjectReferenceCollection struct { + Self string `json:"self"` + References []ManagedObjectReference `json:"references"` + Statistics *generic.PagingStatistics `json:"statistics,omitempty"` + Prev string `json:"prev,omitempty"` + Next string `json:"next,omitempty"` +} diff --git a/inventory/managedObjects/test_responses.go b/inventory/managedObjects/test_responses.go index 3ce972e..081ddc1 100644 --- a/inventory/managedObjects/test_responses.go +++ b/inventory/managedObjects/test_responses.go @@ -471,7 +471,7 @@ var NewManagedObjectCollection_ResponseBody = func(next string) string { }` } -var UpdateManagedObject = func(name string) string { +var UpdatedManagedObject = func(name string) string { return `{ "id" : "104940", "name" : "` + name + `",