diff --git a/.github/workflows/api-tests.yml b/.github/workflows/api-tests.yml index d380cf9e9c..93befd0f4b 100644 --- a/.github/workflows/api-tests.yml +++ b/.github/workflows/api-tests.yml @@ -4,12 +4,23 @@ name: Property Based Tests on: - push: - branches: - - main pull_request: branches: - main + paths: + - ".github/workflows/api-tests.yml" + - "api/**" + - "auth/api/http/**" + - "bootstrap/api**" + - "certs/api/**" + - "consumers/notifiers/api/**" + - "http/api/**" + - "invitations/api/**" + - "provision/api/**" + - "readers/api/**" + - "things/api/**" + - "twins/api/**" + - "users/api/**" env: TOKENS_URL: http://localhost:9002/users/tokens/issue @@ -17,6 +28,8 @@ env: USER_IDENTITY: admin@example.com USER_SECRET: 12345678 DOMAIN_NAME: demo-test + USERS_URL: http://localhost:9002 + THINGS_URL: http://localhost:9000 jobs: api-test: @@ -44,11 +57,82 @@ jobs: export USER_TOKEN=$(curl -sSX POST $TOKENS_URL -H "Content-Type: application/json" -d "{\"identity\": \"$USER_IDENTITY\",\"secret\": \"$USER_SECRET\",\"domain_id\": \"$DOMAIN_ID\"}" | jq -r .access_token) echo "USER_TOKEN=$USER_TOKEN" >> $GITHUB_ENV + - name: Check for changes in specific paths + uses: dorny/paths-filter@v2 + id: changes + with: + filters: | + auth: + - ".github/workflows/api-tests.yml" + - "api/openapi/auth.yml" + - "auth/api/http/**" + + bootstrap: + - ".github/workflows/api-tests.yml" + - "api/openapi/bootstrap.yml" + - "bootstrap/api/**" + + certs: + - ".github/workflows/api-tests.yml" + - "api/openapi/certs.yml" + - "certs/api/**" + + notifiers: + - ".github/workflows/api-tests.yml" + - "api/openapi/notifiers.yml" + - "consumers/notifiers/api/**" + + http: + - ".github/workflows/api-tests.yml" + - "api/openapi/http.yml" + - "http/api/**" + + invitations: + - ".github/workflows/api-tests.yml" + - "api/openapi/invitations.yml" + - "invitations/api/**" + + provision: + - ".github/workflows/api-tests.yml" + - "api/openapi/provision.yml" + - "provision/api/**" + + readers: + - ".github/workflows/api-tests.yml" + - "api/openapi/readers.yml" + - "readers/api/**" + + things: + - ".github/workflows/api-tests.yml" + - "api/openapi/things.yml" + - "things/api/**" + + twins: + - ".github/workflows/api-tests.yml" + - "api/openapi/twins.yml" + - "twins/api/**" + + users: + - ".github/workflows/api-tests.yml" + - "api/openapi/users.yml" + - "users/api/**" + - name: Run Users API tests + if: steps.changes.outputs.users == 'true' uses: schemathesis/action@v1 with: schema: api/openapi/users.yml - base-url: http://localhost:9002 + base-url: ${{ env.USERS_URL }} + checks: all + report: false + args: '--header "Authorization: Bearer ${{ env.USER_TOKEN }}" --contrib-openapi-formats-uuid --hypothesis-suppress-health-check=filter_too_much --stateful=links' + + - name: Run Things API tests + if: steps.changes.outputs.things == 'true' + uses: schemathesis/action@v1 + with: + schema: api/openapi/things.yml + base-url: ${{ env.THINGS_URL }} checks: all report: false args: '--header "Authorization: Bearer ${{ env.USER_TOKEN }}" --contrib-openapi-formats-uuid --hypothesis-suppress-health-check=filter_too_much --stateful=links' diff --git a/Makefile b/Makefile index 58eb2cf3bb..0c2ef0fe14 100644 --- a/Makefile +++ b/Makefile @@ -6,6 +6,8 @@ BUILD_DIR = build SERVICES = auth users things http coap ws lora influxdb-writer influxdb-reader mongodb-writer \ mongodb-reader cassandra-writer cassandra-reader postgres-writer postgres-reader timescale-writer timescale-reader cli \ bootstrap opcua twins mqtt provision certs smtp-notifier smpp-notifier invitations +TEST_API_SERVICES = auth bootstrap certs http invitations notifiers provision readers things twins users +TEST_API = $(addprefix test_api_,$(TEST_API_SERVICES)) DOCKERS = $(addprefix docker_,$(SERVICES)) DOCKERS_DEV = $(addprefix docker_dev_,$(SERVICES)) CGO_ENABLED ?= 0 @@ -129,19 +131,30 @@ test: mocks done go test -v --race -count 1 -tags test -coverprofile=coverage/coverage.out $$(go list ./... | grep -v 'consumers\|readers\|postgres\|internal\|opcua\|cmd') -test_api: -ifeq ($(USER_TOKEN),) - @echo "env variable USER_TOKEN is empty" - exit 1 -endif +define test_api_service + $(eval svc=$(subst test_api_,,$(1))) @which st > /dev/null || (echo "schemathesis not found, please install it from https://github.com/schemathesis/schemathesis#getting-started" && exit 1) - st run api/openapi/users.yml \ + + @if [ -z "$(USER_TOKEN)" ]; then \ + echo "USER_TOKEN is not set"; \ + echo "Please set it to a valid token"; \ + exit 1; \ + fi + + st run api/openapi/$(svc).yml \ --checks all \ - --base-url http://localhost:9002 \ + --base-url $(2) \ --header "Authorization: Bearer $(USER_TOKEN)" \ --contrib-openapi-formats-uuid \ --hypothesis-suppress-health-check=filter_too_much \ --stateful=links +endef + +test_api_users: TEST_API_URL := http://localhost:9002 +test_api_things: TEST_API_URL := http://localhost:9000 + +$(TEST_API): + $(call test_api_service,$(@),$(TEST_API_URL)) proto: protoc -I. --go_out=. --go_opt=paths=source_relative pkg/messaging/*.proto diff --git a/api/openapi/auth.yml b/api/openapi/auth.yml index 15dffb967a..432c6ae4b8 100644 --- a/api/openapi/auth.yml +++ b/api/openapi/auth.yml @@ -16,8 +16,8 @@ info: version: 0.14.0 servers: - - url: http://localhost:8180 - - url: https://localhost:8180 + - url: http://localhost:8189 + - url: https://localhost:8189 tags: - name: Auth diff --git a/api/openapi/consumers-notifiers.yml b/api/openapi/notifiers.yml similarity index 100% rename from api/openapi/consumers-notifiers.yml rename to api/openapi/notifiers.yml diff --git a/api/openapi/things.yml b/api/openapi/things.yml index 74843303cb..2ff2dff78c 100644 --- a/api/openapi/things.yml +++ b/api/openapi/things.yml @@ -39,6 +39,7 @@ tags: paths: /things: post: + operationId: createThing tags: - Things summary: Adds new thing @@ -54,6 +55,8 @@ paths: description: Failed due to malformed JSON. "401": description: Missing or invalid access token provided. + "403": + description: Failed to perform authorization over the entity. "409": description: Failed due to using an existing identity. "415": @@ -64,6 +67,7 @@ paths: $ref: "#/components/responses/ServiceError" get: + operationId: listThings tags: - Things summary: Retrieves things @@ -89,6 +93,8 @@ paths: "401": description: | Missing or invalid access token provided. + "403": + description: Failed to perform authorization over the entity. "404": description: A non-existent entity request. "422": @@ -98,6 +104,7 @@ paths: /things/bulk: post: + operationId: bulkCreateThings summary: Bulk provisions new things description: | Adds new things to the list of things owned by user identified using @@ -107,19 +114,24 @@ paths: requestBody: $ref: "#/components/requestBodies/ThingsCreateReq" responses: - "201": + "200": $ref: "#/components/responses/ThingPageRes" "400": description: Failed due to malformed JSON. "401": description: Missing or invalid access token provided. + "403": + description: Failed to perform authorization over the entity. "415": description: Missing or invalid content type. + "422": + description: Database can't process request. "500": $ref: "#/components/responses/ServiceError" /things/{thingID}: get: + operationId: getThing summary: Retrieves thing info description: | Retrieves a specific thing that is identifier by the thing ID. @@ -134,6 +146,8 @@ paths: $ref: "#/components/responses/ThingRes" "401": description: Missing or invalid access token provided. + "403": + description: Failed to perform authorization over the entity. "404": description: A non-existent entity request. "422": @@ -142,6 +156,7 @@ paths: $ref: "#/components/responses/ServiceError" patch: + operationId: updateThing summary: Updates name and metadata of the thing. description: | Update is performed by replacing the current resource data with values @@ -162,10 +177,16 @@ paths: description: Failed due to malformed JSON. "401": description: Missing or invalid access token provided. + "403": + description: Failed to perform authorization over the entity. "404": description: Failed due to non existing thing. + "409": + description: Failed due to using an existing identity. "415": description: Missing or invalid content type. + "422": + description: Database can't process request. "500": $ref: "#/components/responses/ServiceError" delete: @@ -190,6 +211,7 @@ paths: $ref: "#/components/responses/ServiceError" /things/{thingID}/tags: patch: + operationId: updateThingTags summary: Updates tags the thing. description: | Updates tags of the thing with provided ID. Tags is updated using @@ -207,15 +229,20 @@ paths: $ref: "#/components/responses/ThingRes" "400": description: Failed due to malformed JSON. + "403": + description: Failed to perform authorization over the entity. "404": description: Failed due to non existing thing. "401": description: Missing or invalid access token provided. + "422": + description: Database can't process request. "500": $ref: "#/components/responses/ServiceError" /things/{thingID}/secret: patch: + operationId: updateThingSecret summary: Updates Secret of the identified thing. description: | Updates secret of the identified in thing. Secret is updated using @@ -235,17 +262,22 @@ paths: description: Failed due to malformed JSON. "401": description: Missing or invalid access token provided. + "403": + description: Failed to perform authorization over the entity. "404": description: Failed due to non existing thing. "409": description: Specified key already exists. "415": description: Missing or invalid content type. + "422": + description: Database can't process request. "500": $ref: "#/components/responses/ServiceError" /things/{thingID}/disable: post: + operationId: disableThing summary: Disables a thing description: | Disables a specific thing that is identifier by the thing ID. @@ -262,8 +294,12 @@ paths: description: Failed due to malformed thing's ID. "401": description: Missing or invalid access token provided. + "403": + description: Failed to perform authorization over the entity. "404": description: A non-existent entity request. + "409": + description: Failed due to already disabled thing. "422": description: Database can't process request. "500": @@ -271,6 +307,7 @@ paths: /things/{thingID}/enable: post: + operationId: enableThing summary: Enables a thing description: | Enables a specific thing that is identifier by the thing ID. @@ -287,8 +324,12 @@ paths: description: Failed due to malformed thing's ID. "401": description: Missing or invalid access token provided. + "403": + description: Failed to perform authorization over the entity. "404": description: A non-existent entity request. + "409": + description: Failed due to already enabled thing. "422": description: Database can't process request. "500": @@ -296,6 +337,7 @@ paths: /things/{thingID}/share: post: + operationId: shareThing summary: Shares a thing description: | Shares a specific thing that is identifier by the thing ID. @@ -314,6 +356,8 @@ paths: description: Failed due to malformed thing's ID. "401": description: Missing or invalid access token provided. + "403": + description: Failed to perform authorization over the entity. "404": description: A non-existent entity request. "422": @@ -323,6 +367,7 @@ paths: /things/{thingID}/unshare: post: + operationId: unshareThing summary: Unshares a thing description: | Unshares a specific thing that is identifier by the thing ID. @@ -341,6 +386,8 @@ paths: description: Failed due to malformed thing's ID. "401": description: Missing or invalid access token provided. + "403": + description: Failed to perform authorization over the entity. "404": description: A non-existent entity request. "422": @@ -350,6 +397,7 @@ paths: /channels/{chanID}/things: get: + operationId: listThingsInaChannel summary: List of things connected to specified channel description: | Retrieves list of things connected to specified channel with pagination @@ -368,6 +416,8 @@ paths: description: Failed due to malformed query parameters. "401": description: Missing or invalid access token provided. + "403": + description: Failed to perform authorization over the entity. "404": description: A non-existent entity request. "422": @@ -377,6 +427,7 @@ paths: /channels: post: + operationId: createChannel tags: - Channels summary: Creates new channel @@ -393,14 +444,19 @@ paths: description: Failed due to malformed JSON. "401": description: Missing or invalid access token provided. + "403": + description: Failed to perform authorization over the entity. "409": description: Failed due to using an existing identity. "415": description: Missing or invalid content type. + "422": + description: Database can't process request. "500": $ref: "#/components/responses/ServiceError" get: + operationId: listChannels summary: Lists channels. description: | Retrieves a list of channels. Due to performance concerns, data @@ -423,6 +479,8 @@ paths: description: Failed due to malformed query parameters. "401": description: Missing or invalid access token provided. + "403": + description: Failed to perform authorization over the entity. "404": description: Channel does not exist. "422": @@ -432,6 +490,7 @@ paths: /channels/{chanID}: get: + operationId: getChannel summary: Retrieves channel info. description: | Gets info on a channel specified by id. @@ -448,6 +507,8 @@ paths: description: Failed due to malformed channel's ID. "401": description: Missing or invalid access token provided. + "403": + description: Failed to perform authorization over the entity. "404": description: Channel does not exist. "422": @@ -456,6 +517,7 @@ paths: $ref: "#/components/responses/ServiceError" put: + operationId: updateChannel summary: Updates channel data. description: | Update is performed by replacing the current resource data with values @@ -476,10 +538,16 @@ paths: description: Failed due to malformed query parameters. "401": description: Missing or invalid access token provided. + "403": + description: Failed to perform authorization over the entity. "404": description: Channel does not exist. + "409": + description: Failed due to using an existing identity. "415": description: Missing or invalid content type. + "422": + description: Database can't process request. "500": $ref: "#/components/responses/ServiceError" delete: @@ -505,6 +573,7 @@ paths: /channels/{chanID}/enable: post: + operationId: enableChannel summary: Enables a channel description: | Enables a specific channel that is identifier by the channel ID. @@ -521,8 +590,12 @@ paths: description: Failed due to malformed query parameters. "401": description: Missing or invalid access token provided. + "403": + description: Failed to perform authorization over the entity. "404": description: A non-existent entity request. + "409": + description: Failed due to already enabled channel. "422": description: Database can't process request. "500": @@ -530,6 +603,7 @@ paths: /channels/{chanID}/disable: post: + operationId: disableChannel summary: Disables a channel description: | Disables a specific channel that is identifier by the channel ID. @@ -546,8 +620,12 @@ paths: description: Failed due to malformed channel's ID. "401": description: Missing or invalid access token provided. + "403": + description: Failed to perform authorization over the entity. "404": description: A non-existent entity request. + "409": + description: Failed due to already disabled channel. "422": description: Database can't process request. "500": @@ -555,6 +633,7 @@ paths: /channels/{chanID}/users/assign: post: + operationId: assignUsersToChannel summary: Assigns a member to a channel description: | Assigns a specific member to a channel that is identifier by the channel ID. @@ -573,6 +652,8 @@ paths: description: Failed due to malformed thing's ID. "401": description: Missing or invalid access token provided. + "403": + description: Failed to perform authorization over the entity. "404": description: A non-existent entity request. "422": @@ -582,6 +663,7 @@ paths: /channels/{chanID}/users/unassign: post: + operationId: unassignUsersFromChannel summary: Unassigns a member from a channel description: | Unassigns a specific member from a channel that is identifier by the channel ID. @@ -594,12 +676,14 @@ paths: security: - bearerAuth: [] responses: - "200": + "204": description: Thing unshared. "400": description: Failed due to malformed thing's ID. "401": description: Missing or invalid access token provided. + "403": + description: Failed to perform authorization over the entity. "404": description: A non-existent entity request. "422": @@ -609,6 +693,7 @@ paths: /channels/{chanID}/groups/assign: post: + operationId: assignGroupsToChannel summary: Assigns a member to a channel description: | Assigns a specific member to a channel that is identifier by the channel ID. @@ -627,6 +712,8 @@ paths: description: Failed due to malformed thing's ID. "401": description: Missing or invalid access token provided. + "403": + description: Failed to perform authorization over the entity. "404": description: A non-existent entity request. "422": @@ -636,6 +723,7 @@ paths: /channels/{chanID}/groups/unassign: post: + operationId: unassignGroupsFromChannel summary: Unassigns a member from a channel description: | Unassigns a specific member from a channel that is identifier by the channel ID. @@ -648,12 +736,14 @@ paths: security: - bearerAuth: [] responses: - "200": + "204": description: Thing unshared. "400": description: Failed due to malformed thing's ID. "401": description: Missing or invalid access token provided. + "403": + description: Failed to perform authorization over the entity. "404": description: A non-existent entity request. "422": @@ -663,6 +753,7 @@ paths: /things/{thingID}/channels: get: + operationId: listChannelsConnectedToThing summary: List of channels connected to specified thing description: | Retrieves list of channels connected to specified thing with pagination @@ -680,6 +771,8 @@ paths: description: Failed due to malformed query parameters. "401": description: Missing or invalid access token provided. + "403": + description: Failed to perform authorization over the entity. "404": description: Thing does not exist. "422": @@ -689,6 +782,7 @@ paths: /users/{memberID}/channels: get: + operationId: listChannelsConnectedToUser summary: List of channels connected to specified user description: | Retrieves list of channels connected to specified user with pagination @@ -706,6 +800,8 @@ paths: description: Failed due to malformed query parameters. "401": description: Missing or invalid access token provided. + "403": + description: Failed to perform authorization over the entity. "404": description: Thing does not exist. "422": @@ -715,6 +811,7 @@ paths: /groups/{memberID}/channels: get: + operationId: listChannelsConnectedToGroup summary: List of channels connected to specified group description: | Retrieves list of channels connected to specified group with pagination @@ -732,6 +829,8 @@ paths: description: Failed due to malformed query parameters. "401": description: Missing or invalid access token provided. + "403": + description: Failed to perform authorization over the entity. "404": description: Thing does not exist. "422": @@ -741,6 +840,7 @@ paths: /connect: post: + operationId: connectThingsAndChannels summary: Connects thing and channel. description: | Connect things specified by IDs to channels specified by IDs. @@ -756,17 +856,22 @@ paths: description: Failed due to malformed JSON. "401": description: Missing or invalid access token provided. + "403": + description: Failed to perform authorization over the entity. "404": description: A non-existent entity request. "409": description: Entity already exist. "415": description: Missing or invalid content type. + "422": + description: Database can't process request. "500": $ref: "#/components/responses/ServiceError" /disconnect: post: + operationId: disconnectThingsAndChannels summary: Disconnect things and channels using lists of IDs. description: | Disconnect things from channels specified by lists of IDs. @@ -782,15 +887,20 @@ paths: description: Failed due to malformed JSON. "401": description: Missing or invalid access token provided. + "403": + description: Failed to perform authorization over the entity. "404": description: A non-existent entity request. "415": description: Missing or invalid content type. + "422": + description: Database can't process request. "500": $ref: "#/components/responses/ServiceError" /channels/{chanID}/things/{thingID}/connect: post: + operationId: connectThingToChannel summary: Connects a thing to a channel description: | Connects a specific thing to a channel that is identifier by the channel ID. @@ -806,6 +916,8 @@ paths: description: Failed due to malformed thing's ID. "401": description: Missing or invalid access token provided. + "403": + description: Failed to perform authorization over the entity. "404": description: A non-existent entity request. "422": @@ -815,6 +927,7 @@ paths: /channels/{chanID}/things/{thingID}/disconnect: post: + operationId: disconnectThingFromChannel summary: Disconnects a thing to a channel description: | Disconnects a specific thing to a channel that is identifier by the channel ID. @@ -830,6 +943,8 @@ paths: description: Failed due to malformed thing's ID. "401": description: Missing or invalid access token provided. + "403": + description: Failed to perform authorization over the entity. "404": description: A non-existent entity request. "422": @@ -1245,7 +1360,7 @@ components: ChannelsPage: type: object properties: - channels: + groups: type: array minItems: 0 uniqueItems: true @@ -1263,9 +1378,9 @@ components: example: 10 description: Maximum number of items to return in one page. required: - - channels + - groups - total - - level + - offset PoliciesPage: type: object @@ -1423,6 +1538,9 @@ components: schema: type: string format: uuid + minLength: 36 + maxLength: 36 + pattern: "^[a-f0-9]{8}-[a-f0-9]{4}-[1-5][a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$" required: true example: bb7edb32-2eac-4aad-aebe-ed96fe073879 @@ -1433,6 +1551,9 @@ components: schema: type: string format: uuid + minLength: 36 + maxLength: 36 + pattern: "^[a-f0-9]{8}-[a-f0-9]{4}-[1-5][a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$" required: true example: bb7edb32-2eac-4aad-aebe-ed96fe073879 @@ -1686,6 +1807,43 @@ components: application/json: schema: $ref: "#/components/schemas/Thing" + links: + get: + operationId: getThing + parameters: + thingID: $response.body#/id + get_channels: + operationId: listChannelsConnectedToThing + parameters: + thingID: $response.body#/id + update: + operationId: updateThing + parameters: + thingID: $response.body#/id + update_tags: + operationId: updateThingTags + parameters: + thingID: $response.body#/id + update_secret: + operationId: updateThingSecret + parameters: + thingID: $response.body#/id + share: + operationId: shareThing + parameters: + thingID: $response.body#/id + unsahre: + operationId: unshareThing + parameters: + thingID: $response.body#/id + disable: + operationId: disableThing + parameters: + thingID: $response.body#/id + enable: + operationId: enableThing + parameters: + thingID: $response.body#/id ThingRes: description: Data retrieved. @@ -1693,6 +1851,19 @@ components: application/json: schema: $ref: "#/components/schemas/Thing" + links: + get_channels: + operationId: listChannelsConnectedToThing + parameters: + thingID: $response.body#/id + share: + operationId: shareThing + parameters: + thingID: $response.body#/id + unsahre: + operationId: unshareThing + parameters: + thingID: $response.body#/id ThingPageRes: description: Data retrieved. @@ -1720,6 +1891,51 @@ components: application/json: schema: $ref: "#/components/schemas/Channel" + links: + get: + operationId: getChannel + parameters: + chanID: $response.body#/id + get_things: + operationId: listThingsInaChannel + parameters: + chanID: $response.body#/id + get_users: + operationId: listChannelsConnectedToUser + parameters: + memberID: $response.body#/id + get_groups: + operationId: listChannelsConnectedToGroup + parameters: + memberID: $response.body#/id + update: + operationId: updateChannel + parameters: + chanID: $response.body#/id + disable: + operationId: disableChannel + parameters: + chanID: $response.body#/id + enable: + operationId: enableChannel + parameters: + chanID: $response.body#/id + assign_users: + operationId: assignUsersToChannel + parameters: + chanID: $response.body#/id + unassign_users: + operationId: unassignUsersFromChannel + parameters: + chanID: $response.body#/id + assign_groups: + operationId: assignGroupsToChannel + parameters: + chanID: $response.body#/id + unassign_groups: + operationId: unassignGroupsFromChannel + parameters: + chanID: $response.body#/id ChannelRes: description: Data retrieved. @@ -1727,6 +1943,35 @@ components: application/json: schema: $ref: "#/components/schemas/Channel" + links: + get_things: + operationId: listThingsInaChannel + parameters: + chanID: $response.body#/id + get_users: + operationId: listChannelsConnectedToUser + parameters: + memberID: $response.body#/id + get_groups: + operationId: listChannelsConnectedToGroup + parameters: + memberID: $response.body#/id + assign_users: + operationId: assignUsersToChannel + parameters: + chanID: $response.body#/id + unassign_users: + operationId: unassignUsersFromChannel + parameters: + chanID: $response.body#/id + assign_groups: + operationId: assignGroupsToChannel + parameters: + chanID: $response.body#/id + unassign_groups: + operationId: unassignGroupsFromChannel + parameters: + chanID: $response.body#/id ChannelPageRes: description: Data retrieved. diff --git a/auth/service.go b/auth/service.go index fd9fa96085..9e77e25b60 100644 --- a/auth/service.go +++ b/auth/service.go @@ -130,7 +130,7 @@ func (svc service) RetrieveKey(ctx context.Context, token, id string) (Key, erro key, err := svc.keys.Retrieve(ctx, issuerID, id) if err != nil { - return Key{}, errors.Wrap(errRetrieve, err) + return Key{}, errors.Wrap(svcerr.ErrViewEntity, err) } return key, nil } @@ -206,7 +206,7 @@ func (svc service) checkPolicy(ctx context.Context, pr PolicyReq) error { func (svc service) checkDomain(ctx context.Context, subjectType, subject, domainID string) error { d, err := svc.domains.RetrieveByID(ctx, domainID) if err != nil { - return errors.Wrap(errors.ErrUnidentified, err) + return errors.Wrap(svcerr.ErrViewEntity, err) } switch d.Status { @@ -572,7 +572,7 @@ func (svc service) RetrieveDomain(ctx context.Context, token, id string) (Domain } dom, err := svc.domains.RetrieveByID(ctx, id) if err != nil { - return Domain{}, errors.Wrap(svcerr.ErrNotFound, err) + return Domain{}, errors.Wrap(svcerr.ErrViewEntity, err) } return dom, nil } @@ -601,7 +601,7 @@ func (svc service) RetrieveDomainPermissions(ctx context.Context, token, id stri ObjectType: DomainType, }, []string{AdminPermission, EditPermission, ViewPermission, MembershipPermission}) if err != nil { - return []string{}, err + return []string{}, errors.Wrap(svcerr.ErrViewEntity, err) } return lp, nil } diff --git a/cmd/auth/main.go b/cmd/auth/main.go index d0924a5941..f7efc0a974 100644 --- a/cmd/auth/main.go +++ b/cmd/auth/main.go @@ -49,7 +49,7 @@ const ( envPrefixGrpc = "MG_AUTH_GRPC_" envPrefixDB = "MG_AUTH_DB_" defDB = "auth" - defSvcHTTPPort = "8180" + defSvcHTTPPort = "8189" defSvcGRPCPort = "8181" ) diff --git a/internal/api/common.go b/internal/api/common.go index e4b23396a2..ce7b5e3649 100644 --- a/internal/api/common.go +++ b/internal/api/common.go @@ -117,6 +117,7 @@ func EncodeError(_ context.Context, err error, w http.ResponseWriter) { errors.Contains(err, apiutil.ErrInvalidIDFormat), errors.Contains(err, apiutil.ErrInvalidStatus), errors.Contains(err, svcerr.ErrInvalidStatus), + errors.Contains(err, apiutil.ErrValidation), errors.Contains(err, apiutil.ErrInvitationState), errors.Contains(err, apiutil.ErrInvalidRole), errors.Contains(err, apiutil.ErrMissingEmail), @@ -127,10 +128,9 @@ func EncodeError(_ context.Context, err error, w http.ResponseWriter) { errors.Contains(err, apiutil.ErrMissingConfPass), errors.Contains(err, apiutil.ErrInvalidResetPass), errors.Contains(err, apiutil.ErrMissingRelation), - errors.Contains(err, svcerr.ErrPasswordFormat), + errors.Contains(err, apiutil.ErrPasswordFormat), errors.Contains(err, apiutil.ErrInvalidLevel), - errors.Contains(err, apiutil.ErrInvalidQueryParams), - errors.Contains(err, apiutil.ErrValidation): + errors.Contains(err, apiutil.ErrInvalidQueryParams): w.WriteHeader(http.StatusBadRequest) case errors.Contains(err, svcerr.ErrAuthentication), errors.Contains(err, svcerr.ErrLogin), diff --git a/internal/groups/service.go b/internal/groups/service.go index e3e3133cc4..17acc0d2e5 100644 --- a/internal/groups/service.go +++ b/internal/groups/service.go @@ -115,7 +115,12 @@ func (svc service) ViewGroup(ctx context.Context, token, id string) (groups.Grou return groups.Group{}, err } - return svc.groups.RetrieveByID(ctx, id) + group, err := svc.groups.RetrieveByID(ctx, id) + if err != nil { + return groups.Group{}, errors.Wrap(repoerr.ErrViewEntity, err) + } + + return group, nil } func (svc service) ViewGroupPerms(ctx context.Context, token, id string) ([]string, error) { diff --git a/pkg/sdk/go/channels_test.go b/pkg/sdk/go/channels_test.go index f0742b3964..bbdab6c754 100644 --- a/pkg/sdk/go/channels_test.go +++ b/pkg/sdk/go/channels_test.go @@ -324,14 +324,14 @@ func TestViewChannel(t *testing.T) { token: "wrongtoken", channelID: channel.ID, response: sdk.Channel{Children: []*sdk.Channel{}}, - err: errors.NewSDKErrorWithStatus(svcerr.ErrAuthorization, http.StatusForbidden), + err: errors.NewSDKErrorWithStatus(errors.Wrap(svcerr.ErrViewEntity, svcerr.ErrViewEntity), http.StatusUnprocessableEntity), }, { desc: "view channel for wrong id", token: validToken, channelID: wrongID, response: sdk.Channel{Children: []*sdk.Channel{}}, - err: errors.NewSDKErrorWithStatus(svcerr.ErrNotFound, http.StatusNotFound), + err: errors.NewSDKErrorWithStatus(errors.Wrap(svcerr.ErrViewEntity, svcerr.ErrViewEntity), http.StatusUnprocessableEntity), }, } diff --git a/pkg/sdk/go/groups_test.go b/pkg/sdk/go/groups_test.go index a07993a1c8..90a63616eb 100644 --- a/pkg/sdk/go/groups_test.go +++ b/pkg/sdk/go/groups_test.go @@ -581,14 +581,14 @@ func TestViewGroup(t *testing.T) { token: "wrongtoken", groupID: group.ID, response: sdk.Group{Children: []*sdk.Group{}}, - err: errors.NewSDKErrorWithStatus(svcerr.ErrAuthorization, http.StatusForbidden), + err: errors.NewSDKErrorWithStatus(errors.Wrap(svcerr.ErrViewEntity, svcerr.ErrViewEntity), http.StatusUnprocessableEntity), }, { desc: "view group for wrong id", token: validToken, groupID: wrongID, response: sdk.Group{Children: []*sdk.Group{}}, - err: errors.NewSDKErrorWithStatus(svcerr.ErrNotFound, http.StatusNotFound), + err: errors.NewSDKErrorWithStatus(errors.Wrap(svcerr.ErrViewEntity, svcerr.ErrViewEntity), http.StatusUnprocessableEntity), }, } diff --git a/pkg/sdk/go/things_test.go b/pkg/sdk/go/things_test.go index ac463662ac..a1cf67ad9c 100644 --- a/pkg/sdk/go/things_test.go +++ b/pkg/sdk/go/things_test.go @@ -646,7 +646,7 @@ func TestThing(t *testing.T) { response: sdk.Thing{}, token: validToken, thingID: wrongID, - err: errors.NewSDKErrorWithStatus(errors.Wrap(repoerr.ErrNotFound, repoerr.ErrNotFound), http.StatusNotFound), + err: errors.NewSDKErrorWithStatus(errors.Wrap(svcerr.ErrViewEntity, svcerr.ErrViewEntity), http.StatusUnprocessableEntity), }, { desc: "view thing with an invalid token and invalid thing id", @@ -956,7 +956,7 @@ func TestEnableThing(t *testing.T) { thing: enabledThing1, response: sdk.Thing{}, repoErr: sdk.ErrFailedEnable, - err: errors.NewSDKErrorWithStatus(errors.Wrap(sdk.ErrFailedEnable, svcerr.ErrNotFound), http.StatusNotFound), + err: errors.NewSDKErrorWithStatus(errors.Wrap(sdk.ErrFailedEnable, svcerr.ErrViewEntity), http.StatusUnprocessableEntity), }, { desc: "enable non-existing thing", @@ -965,7 +965,7 @@ func TestEnableThing(t *testing.T) { thing: sdk.Thing{}, response: sdk.Thing{}, repoErr: sdk.ErrFailedEnable, - err: errors.NewSDKErrorWithStatus(errors.Wrap(sdk.ErrFailedEnable, repoerr.ErrNotFound), http.StatusNotFound), + err: errors.NewSDKErrorWithStatus(errors.Wrap(sdk.ErrFailedEnable, svcerr.ErrViewEntity), http.StatusUnprocessableEntity), }, } @@ -1091,7 +1091,7 @@ func TestDisableThing(t *testing.T) { thing: disabledThing1, response: sdk.Thing{}, repoErr: sdk.ErrFailedDisable, - err: errors.NewSDKErrorWithStatus(errors.Wrap(sdk.ErrFailedDisable, svcerr.ErrNotFound), http.StatusNotFound), + err: errors.NewSDKErrorWithStatus(errors.Wrap(sdk.ErrFailedDisable, svcerr.ErrViewEntity), http.StatusUnprocessableEntity), }, { desc: "disable non-existing thing", @@ -1100,7 +1100,7 @@ func TestDisableThing(t *testing.T) { token: validToken, response: sdk.Thing{}, repoErr: sdk.ErrFailedDisable, - err: errors.NewSDKErrorWithStatus(errors.Wrap(sdk.ErrFailedDisable, repoerr.ErrNotFound), http.StatusNotFound), + err: errors.NewSDKErrorWithStatus(errors.Wrap(sdk.ErrFailedDisable, svcerr.ErrViewEntity), http.StatusUnprocessableEntity), }, } diff --git a/pkg/sdk/go/users_test.go b/pkg/sdk/go/users_test.go index 0ced676c82..0012de581e 100644 --- a/pkg/sdk/go/users_test.go +++ b/pkg/sdk/go/users_test.go @@ -985,7 +985,7 @@ func TestEnableClient(t *testing.T) { client: enabledClient1, response: sdk.User{}, repoErr: sdk.ErrFailedEnable, - err: errors.NewSDKErrorWithStatus(errors.Wrap(sdk.ErrFailedEnable, repoerr.ErrNotFound), http.StatusNotFound), + err: errors.NewSDKErrorWithStatus(errors.Wrap(sdk.ErrFailedEnable, svcerr.ErrViewEntity), http.StatusUnprocessableEntity), }, { desc: "enable non-existing client", @@ -994,7 +994,7 @@ func TestEnableClient(t *testing.T) { client: sdk.User{}, response: sdk.User{}, repoErr: sdk.ErrFailedEnable, - err: errors.NewSDKErrorWithStatus(errors.Wrap(sdk.ErrFailedEnable, repoerr.ErrNotFound), http.StatusNotFound), + err: errors.NewSDKErrorWithStatus(errors.Wrap(sdk.ErrFailedEnable, svcerr.ErrViewEntity), http.StatusUnprocessableEntity), }, } @@ -1114,7 +1114,7 @@ func TestDisableClient(t *testing.T) { client: disabledClient1, response: sdk.User{}, repoErr: sdk.ErrFailedDisable, - err: errors.NewSDKErrorWithStatus(errors.Wrap(sdk.ErrFailedDisable, repoerr.ErrNotFound), http.StatusNotFound), + err: errors.NewSDKErrorWithStatus(errors.Wrap(sdk.ErrFailedDisable, svcerr.ErrViewEntity), http.StatusUnprocessableEntity), }, { desc: "disable non-existing client", @@ -1123,7 +1123,7 @@ func TestDisableClient(t *testing.T) { token: validToken, response: sdk.User{}, repoErr: sdk.ErrFailedDisable, - err: errors.NewSDKErrorWithStatus(errors.Wrap(sdk.ErrFailedDisable, repoerr.ErrNotFound), http.StatusNotFound), + err: errors.NewSDKErrorWithStatus(errors.Wrap(sdk.ErrFailedDisable, svcerr.ErrViewEntity), http.StatusUnprocessableEntity), }, } diff --git a/things/api/http/endpoints_test.go b/things/api/http/endpoints_test.go index 15eef27566..4762505d63 100644 --- a/things/api/http/endpoints_test.go +++ b/things/api/http/endpoints_test.go @@ -1080,7 +1080,7 @@ func TestUpdateClientSecret(t *testing.T) { contentType: contentType, token: validToken, status: http.StatusBadRequest, - err: apiutil.ErrBearerKey, + err: apiutil.ErrMissingSecret, }, { desc: "update thing secret with invalid contentype", diff --git a/things/api/http/requests.go b/things/api/http/requests.go index 814ce167b9..36d6663fce 100644 --- a/things/api/http/requests.go +++ b/things/api/http/requests.go @@ -187,7 +187,7 @@ func (req updateClientCredentialsReq) validate() error { return apiutil.ErrMissingID } if req.Secret == "" { - return apiutil.ErrBearerKey + return apiutil.ErrMissingSecret } return nil diff --git a/things/api/http/requests_test.go b/things/api/http/requests_test.go index 368e7508cc..5622f74def 100644 --- a/things/api/http/requests_test.go +++ b/things/api/http/requests_test.go @@ -448,7 +448,7 @@ func TestUpdateClientCredentialsReqValidate(t *testing.T) { id: validID, Secret: "", }, - err: apiutil.ErrBearerKey, + err: apiutil.ErrMissingSecret, }, } for _, c := range cases { diff --git a/things/api/http/responses.go b/things/api/http/responses.go index 3987f182fd..de7d8cb81e 100644 --- a/things/api/http/responses.go +++ b/things/api/http/responses.go @@ -27,8 +27,8 @@ var ( type pageRes struct { Limit uint64 `json:"limit,omitempty"` - Offset uint64 `json:"offset,omitempty"` - Total uint64 `json:"total,omitempty"` + Offset uint64 `json:"offset"` + Total uint64 `json:"total"` } type createClientRes struct { diff --git a/things/service.go b/things/service.go index e94a7b876a..31845407b9 100644 --- a/things/service.go +++ b/things/service.go @@ -138,7 +138,7 @@ func (svc service) ViewClient(ctx context.Context, token, id string) (mgclients. } client, err := svc.clients.RetrieveByID(ctx, id) if err != nil { - return mgclients.Client{}, errors.Wrap(svcerr.ErrNotFound, err) + return mgclients.Client{}, errors.Wrap(svcerr.ErrViewEntity, err) } return client, nil } @@ -241,7 +241,7 @@ func (svc service) listUserThingPermission(ctx context.Context, userID, thingID ObjectType: auth.ThingType, }) if err != nil { - return []string{}, err + return []string{}, errors.Wrap(svcerr.ErrAuthorization, err) } return lp.GetPermissions(), nil } @@ -504,7 +504,7 @@ func (svc service) changeClientStatus(ctx context.Context, token string, client } dbClient, err := svc.clients.RetrieveByID(ctx, client.ID) if err != nil { - return mgclients.Client{}, errors.Wrap(repoerr.ErrNotFound, err) + return mgclients.Client{}, errors.Wrap(repoerr.ErrViewEntity, err) } if dbClient.Status == client.Status { return mgclients.Client{}, errors.ErrStatusAlreadyAssigned @@ -607,7 +607,7 @@ func (svc *service) authorize(ctx context.Context, domainID, subjType, subjKind, } res, err := svc.auth.Authorize(ctx, req) if err != nil { - return "", err + return "", errors.Wrap(svcerr.ErrAuthorization, err) } if !res.GetAuthorized() { return "", errors.Wrap(svcerr.ErrAuthorization, err) diff --git a/users/service.go b/users/service.go index a64d931ae5..0b2c394121 100644 --- a/users/service.go +++ b/users/service.go @@ -406,7 +406,7 @@ func (svc service) changeClientStatus(ctx context.Context, token string, client } dbClient, err := svc.clients.RetrieveByID(ctx, client.ID) if err != nil { - return mgclients.Client{}, errors.Wrap(svcerr.ErrNotFound, err) + return mgclients.Client{}, errors.Wrap(svcerr.ErrViewEntity, err) } if dbClient.Status == client.Status { return mgclients.Client{}, errors.ErrStatusAlreadyAssigned