diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml new file mode 100644 index 000000000..6e8b52f58 --- /dev/null +++ b/.github/workflows/ci.yaml @@ -0,0 +1,34 @@ +# Copyright (c) Abstract Machines +# SPDX-License-Identifier: Apache-2.0 + +name: Continuous Integration + +on: + push: + branches: [main] + pull_request: + branches: [main] + +jobs: + ci: + name: Continuous Integration + runs-on: ubuntu-latest + steps: + - name: Install Go + uses: actions/setup-go@v4 + with: + go-version: 1.21.x + cache-dependency-path: "go.sum" + + - name: Checkout code + uses: actions/checkout@v4 + + - name: golangci-lint + uses: golangci/golangci-lint-action@v3 + with: + version: latest + + - name: Build Binaries + run: | + go mod vendor + make all -j $(nproc) diff --git a/.golangci.yml b/.golangci.yml index 94c8d0572..90702458b 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -9,6 +9,9 @@ run: issues: max-issues-per-linter: 10 max-same-issues: 10 + exclude: + - "string `Usage:\n` has (\\d+) occurrences, make it a constant" + - "string `For example:\n` has (\\d+) occurrences, make it a constant" linters-settings: gocritic: diff --git a/auth/api/http/domains/decode.go b/auth/api/http/domains/decode.go index 9ca8d7ce2..85ff75b44 100644 --- a/auth/api/http/domains/decode.go +++ b/auth/api/http/domains/decode.go @@ -66,7 +66,6 @@ func decodeListDomainRequest(ctx context.Context, r *http.Request) (interface{}, } return req, nil - } func decodeEnableDomainRequest(_ context.Context, r *http.Request) (interface{}, error) { diff --git a/auth/api/http/domains/endpoint.go b/auth/api/http/domains/endpoint.go index 7a408c05c..2419b533c 100644 --- a/auth/api/http/domains/endpoint.go +++ b/auth/api/http/domains/endpoint.go @@ -45,7 +45,6 @@ func retrieveDomainEndpoint(svc auth.Service) endpoint.Endpoint { return nil, err } return retrieveDomainRes{Data: domain}, nil - } } @@ -98,7 +97,6 @@ func listDomainsEndpoint(svc auth.Service) endpoint.Endpoint { return nil, err } return listDomainsRes{Data: dp}, nil - } } diff --git a/auth/api/http/domains/responses.go b/auth/api/http/domains/responses.go index 6342f8ca6..500eab6b7 100644 --- a/auth/api/http/domains/responses.go +++ b/auth/api/http/domains/responses.go @@ -25,12 +25,15 @@ type createDomainRes struct { func (res createDomainRes) Code() int { return http.StatusOK } + func (res createDomainRes) Headers() map[string]string { return map[string]string{} } + func (res createDomainRes) Empty() bool { return false } + func (res createDomainRes) MarshalJSON() ([]byte, error) { return json.Marshal(res.Data) } @@ -42,12 +45,15 @@ type retrieveDomainRes struct { func (res retrieveDomainRes) Code() int { return http.StatusOK } + func (res retrieveDomainRes) Headers() map[string]string { return map[string]string{} } + func (res retrieveDomainRes) Empty() bool { return false } + func (res retrieveDomainRes) MarshalJSON() ([]byte, error) { return json.Marshal(res.Data) } @@ -59,12 +65,15 @@ type updateDomainRes struct { func (res updateDomainRes) Code() int { return http.StatusOK } + func (res updateDomainRes) Headers() map[string]string { return map[string]string{} } + func (res updateDomainRes) Empty() bool { return false } + func (res updateDomainRes) MarshalJSON() ([]byte, error) { return json.Marshal(res.Data) } @@ -76,38 +85,43 @@ type listDomainsRes struct { func (res listDomainsRes) Code() int { return http.StatusOK } + func (res listDomainsRes) Headers() map[string]string { return map[string]string{} } + func (res listDomainsRes) Empty() bool { return false } + func (res listDomainsRes) MarshalJSON() ([]byte, error) { return json.Marshal(res.Data) } -type enableDomainRes struct { -} +type enableDomainRes struct{} func (res enableDomainRes) Code() int { return http.StatusOK } + func (res enableDomainRes) Headers() map[string]string { return map[string]string{} } + func (res enableDomainRes) Empty() bool { return true } -type disableDomainRes struct { -} +type disableDomainRes struct{} func (res disableDomainRes) Code() int { return http.StatusOK } + func (res disableDomainRes) Headers() map[string]string { return map[string]string{} } + func (res disableDomainRes) Empty() bool { return true } @@ -117,9 +131,11 @@ type assignUsersRes struct{} func (res assignUsersRes) Code() int { return http.StatusCreated } + func (res assignUsersRes) Headers() map[string]string { return map[string]string{} } + func (res assignUsersRes) Empty() bool { return true } @@ -129,9 +145,11 @@ type unassignUsersRes struct{} func (res unassignUsersRes) Code() int { return http.StatusNoContent } + func (res unassignUsersRes) Headers() map[string]string { return map[string]string{} } + func (res unassignUsersRes) Empty() bool { return true } @@ -143,12 +161,15 @@ type listUserDomainsRes struct { func (res listUserDomainsRes) Code() int { return http.StatusOK } + func (res listUserDomainsRes) Headers() map[string]string { return map[string]string{} } + func (res listUserDomainsRes) Empty() bool { return false } + func (res listUserDomainsRes) MarshalJSON() ([]byte, error) { return json.Marshal(res.Data) } diff --git a/auth/mocks/domains.go b/auth/mocks/domains.go index 7969c26a0..37eb522b6 100644 --- a/auth/mocks/domains.go +++ b/auth/mocks/domains.go @@ -21,35 +21,42 @@ func (m *DomainsRepo) Save(ctx context.Context, d auth.Domain) (auth.Domain, err return ret.Get(0).(auth.Domain), ret.Error(1) } + func (m *DomainsRepo) RetrieveByID(ctx context.Context, id string) (auth.Domain, error) { ret := m.Called(ctx, id) return ret.Get(0).(auth.Domain), ret.Error(1) } + func (m *DomainsRepo) RetrieveAllByIDs(ctx context.Context, pm auth.Page) (auth.DomainsPage, error) { ret := m.Called(ctx, pm) return ret.Get(0).(auth.DomainsPage), ret.Error(1) } + func (m *DomainsRepo) ListDomains(ctx context.Context, pm auth.Page) (auth.DomainsPage, error) { ret := m.Called(ctx, pm) return ret.Get(0).(auth.DomainsPage), ret.Error(1) } + func (m *DomainsRepo) Update(ctx context.Context, id string, userID string, d auth.DomainReq) (auth.Domain, error) { ret := m.Called(ctx, d, id, userID) return ret.Get(0).(auth.Domain), ret.Error(1) } + func (m *DomainsRepo) Delete(ctx context.Context, id string) error { ret := m.Called(ctx, id) return ret.Error(0) } + func (m *DomainsRepo) SavePolicies(ctx context.Context, pcs ...auth.Policy) error { ret := m.Called(ctx, pcs) return ret.Error(0) } + func (m *DomainsRepo) DeletePolicies(ctx context.Context, pcs ...auth.Policy) error { ret := m.Called(ctx, pcs) diff --git a/auth/postgres/domains.go b/auth/postgres/domains.go index 96a63d558..8a1b5c8d6 100644 --- a/auth/postgres/domains.go +++ b/auth/postgres/domains.go @@ -22,8 +22,6 @@ import ( var _ auth.DomainsRepository = (*domainRepo)(nil) -var errRollbackTx = errors.New("failed to rollback transaction") - type domainRepo struct { db postgres.Database } @@ -36,7 +34,6 @@ func NewDomainRepository(db postgres.Database) auth.DomainsRepository { } } -// Save the domain to database func (repo domainRepo) Save(ctx context.Context, d auth.Domain) (ad auth.Domain, err error) { q := `INSERT INTO domains (id, name, tags, alias, metadata, created_at, updated_at, updated_by, created_by, status) VALUES (:id, :name, :tags, :alias, :metadata, :created_at, :updated_at, :updated_by, :created_by, :status) diff --git a/auth/service.go b/auth/service.go index 722616978..0ddc4a4fd 100644 --- a/auth/service.go +++ b/auth/service.go @@ -119,7 +119,7 @@ func (svc service) Issue(ctx context.Context, token string, key Key) (Token, err case InvitationKey: return svc.tmpKey(invitationDuration, key) default: - return svc.accessKey(key) + return svc.accessKey(ctx, key) } } @@ -300,12 +300,12 @@ func (svc service) tmpKey(duration time.Duration, key Key) (Token, error) { return Token{AccessToken: value}, nil } -func (svc service) accessKey(key Key) (Token, error) { +func (svc service) accessKey(ctx context.Context, key Key) (Token, error) { var err error key.Type = AccessKey key.ExpiresAt = time.Now().Add(svc.loginDuration) - key.Subject, err = svc.checkUserDomain(key) + key.Subject, err = svc.checkUserDomain(ctx, key) if err != nil { return Token{}, err } @@ -339,7 +339,7 @@ func (svc service) refreshKey(ctx context.Context, token string, key Key) (Token key.User = k.User key.Type = AccessKey - key.Subject, err = svc.checkUserDomain(key) + key.Subject, err = svc.checkUserDomain(ctx, key) if err != nil { return Token{}, err } @@ -359,10 +359,10 @@ func (svc service) refreshKey(ctx context.Context, token string, key Key) (Token return Token{AccessToken: access, RefreshToken: refresh}, nil } -func (svc service) checkUserDomain(key Key) (subject string, err error) { +func (svc service) checkUserDomain(ctx context.Context, key Key) (subject string, err error) { if key.Domain != "" { - // Check user is platform admin - if err = svc.Authorize(context.Background(), PolicyReq{ + // Check user is platform admin. + if err = svc.Authorize(ctx, PolicyReq{ Subject: key.User, SubjectType: UserType, Permission: AdminPermission, @@ -371,9 +371,9 @@ func (svc service) checkUserDomain(key Key) (subject string, err error) { }); err == nil { return key.User, nil } - //Check user is domain member + // Check user is domain member. domainUserSubject := EncodeDomainUserID(key.Domain, key.User) - if err = svc.Authorize(context.Background(), PolicyReq{ + if err = svc.Authorize(ctx, PolicyReq{ Subject: domainUserSubject, SubjectType: UserType, Permission: MembershipPermission, @@ -611,7 +611,7 @@ func (svc service) UnassignUsers(ctx context.Context, token string, id string, u return svc.removeDomainPolicies(ctx, id, relation, userIds...) } -// IMPROVEMENT NOTE: Take decision: Only Patform admin or both Patform and domain admins can see others users domain +// IMPROVEMENT NOTE: Take decision: Only Patform admin or both Patform and domain admins can see others users domain. func (svc service) ListUserDomains(ctx context.Context, token string, userID string, p Page) (DomainsPage, error) { res, err := svc.Identify(ctx, token) if err != nil { diff --git a/auth/service_test.go b/auth/service_test.go index 9cfbf40a2..87a3c482d 100644 --- a/auth/service_test.go +++ b/auth/service_test.go @@ -19,8 +19,6 @@ import ( "github.com/stretchr/testify/require" ) -var idProvider = uuid.New() - const ( secret = "secret" email = "test@example.com" @@ -33,10 +31,6 @@ const ( loginDuration = 30 * time.Minute refreshDuration = 24 * time.Hour accessToken = "access" - - readPolicy = "read" - writePolicy = "write" - deletePolicy = "delete" ) func newService() (auth.Service, *mocks.Keys) { diff --git a/internal/apiutil/errors.go b/internal/apiutil/errors.go index 9110291a7..25eb3fad5 100644 --- a/internal/apiutil/errors.go +++ b/internal/apiutil/errors.go @@ -159,6 +159,6 @@ var ( // ErrUnsupportedContentType indicates unacceptable or lack of Content-Type. ErrUnsupportedContentType = errors.New("unsupported content type") - // ErrRollbackTx indicates failed to rollback transaction + // ErrRollbackTx indicates failed to rollback transaction. ErrRollbackTx = errors.New("failed to rollback transaction") ) diff --git a/internal/groups/service.go b/internal/groups/service.go index 911d7755d..89e6a94b8 100644 --- a/internal/groups/service.go +++ b/internal/groups/service.go @@ -210,7 +210,7 @@ func (svc service) ListGroups(ctx context.Context, token, memberKind, memberID s return svc.groups.RetrieveByIDs(ctx, gm, ids...) } -// IMPROVEMENT NOTE: remove this function and all its related auxillary function, ListMembers are moved to respective service +// IMPROVEMENT NOTE: remove this function and all its related auxiliary function, ListMembers are moved to respective service. func (svc service) ListMembers(ctx context.Context, token, groupID, permission, memberKind string) (groups.MembersPage, error) { _, err := svc.authorize(ctx, auth.UserType, token, auth.ViewPermission, auth.GroupType, groupID) if err != nil { diff --git a/things/api/http/clients.go b/things/api/http/clients.go index fcee4f495..8695a7569 100644 --- a/things/api/http/clients.go +++ b/things/api/http/clients.go @@ -238,22 +238,6 @@ func decodeUpdateClientCredentials(_ context.Context, r *http.Request) (interfac return req, nil } -func decodeUpdateClientOwner(_ context.Context, r *http.Request) (interface{}, error) { - if !strings.Contains(r.Header.Get("Content-Type"), api.ContentType) { - return nil, errors.Wrap(apiutil.ErrValidation, apiutil.ErrUnsupportedContentType) - } - - req := updateClientOwnerReq{ - token: apiutil.ExtractBearerToken(r), - id: chi.URLParam(r, "thingID"), - } - if err := json.NewDecoder(r.Body).Decode(&req); err != nil { - return nil, errors.Wrap(apiutil.ErrValidation, errors.Wrap(errors.ErrMalformedEntity, err)) - } - - return req, nil -} - func decodeCreateClientReq(_ context.Context, r *http.Request) (interface{}, error) { if !strings.Contains(r.Header.Get("Content-Type"), api.ContentType) { return nil, errors.Wrap(apiutil.ErrValidation, apiutil.ErrUnsupportedContentType) diff --git a/things/api/http/endpoints.go b/things/api/http/endpoints.go index 7f932816f..e3f15508d 100644 --- a/things/api/http/endpoints.go +++ b/things/api/http/endpoints.go @@ -120,7 +120,7 @@ func listMembersEndpoint(svc things.Service) endpoint.Endpoint { if err := req.validate(); err != nil { return nil, errors.Wrap(apiutil.ErrValidation, err) } - // TODO : remove svc.ListMembers and all functions to svc.ListClients https://github.com/absmach/magistrala/issues/5 + page, err := svc.ListClientsByGroup(ctx, req.token, req.groupID, req.Page) if err != nil { return nil, err diff --git a/things/api/http/requests.go b/things/api/http/requests.go index b3c29fb9a..ae4faf4fb 100644 --- a/things/api/http/requests.go +++ b/things/api/http/requests.go @@ -154,26 +154,6 @@ func (req updateClientTagsReq) validate() error { return nil } -type updateClientOwnerReq struct { - id string - token string - Owner string `json:"owner,omitempty"` -} - -func (req updateClientOwnerReq) validate() error { - if req.token == "" { - return apiutil.ErrBearerToken - } - if req.id == "" { - return apiutil.ErrMissingID - } - if req.Owner == "" { - return apiutil.ErrMissingOwner - } - - return nil -} - type updateClientCredentialsReq struct { token string id string diff --git a/things/service.go b/things/service.go index 80df4e3d8..e93d5b20e 100644 --- a/things/service.go +++ b/things/service.go @@ -90,7 +90,6 @@ func (svc service) CreateThings(ctx context.Context, token string, cls ...mgclie if err != nil { return nil, err } - // ToDo: Add defer function, on error delete all clients policies := magistrala.AddPoliciesReq{} for _, c := range saved { diff --git a/things/standalone/standalone.go b/things/standalone/standalone.go index fa82a1325..9aa11549a 100644 --- a/things/standalone/standalone.go +++ b/things/standalone/standalone.go @@ -69,6 +69,7 @@ func (repo singleUserRepo) DeletePolicy(ctx context.Context, in *magistrala.Dele func (repo singleUserRepo) DeletePolicies(ctx context.Context, in *magistrala.DeletePoliciesReq, opts ...grpc.CallOption) (*magistrala.DeletePoliciesRes, error) { return nil, nil } + func (repo singleUserRepo) ListObjects(ctx context.Context, in *magistrala.ListObjectsReq, opts ...grpc.CallOption) (*magistrala.ListObjectsRes, error) { return nil, nil } diff --git a/users/api/endpoints.go b/users/api/endpoints.go index 8580dfb8d..169892a48 100644 --- a/users/api/endpoints.go +++ b/users/api/endpoints.go @@ -110,7 +110,6 @@ func listMembersByGroupEndpoint(svc users.Service) endpoint.Endpoint { return nil, errors.Wrap(apiutil.ErrValidation, err) } - // TODO : remove svc.ListMembers and all functions to svc.ListClients https://github.com/absmach/magistrala/issues/5 page, err := svc.ListMembers(ctx, req.token, req.objectKind, req.objectID, req.Page) if err != nil { return nil, err @@ -129,7 +128,6 @@ func listMembersByChannelEndpoint(svc users.Service) endpoint.Endpoint { return nil, errors.Wrap(apiutil.ErrValidation, err) } - // TODO : remove svc.ListMembers and all functions to svc.ListClients https://github.com/absmach/magistrala/issues/5 page, err := svc.ListMembers(ctx, req.token, req.objectKind, req.objectID, req.Page) if err != nil { return nil, err @@ -147,7 +145,6 @@ func listMembersByThingEndpoint(svc users.Service) endpoint.Endpoint { return nil, errors.Wrap(apiutil.ErrValidation, err) } - // TODO : remove svc.ListMembers and all functions to svc.ListClients https://github.com/absmach/magistrala/issues/5 page, err := svc.ListMembers(ctx, req.token, req.objectKind, req.objectID, req.Page) if err != nil { return nil, err @@ -165,7 +162,6 @@ func listMembersByDomainEndpoint(svc users.Service) endpoint.Endpoint { return nil, errors.Wrap(apiutil.ErrValidation, err) } - // TODO : remove svc.ListMembers and all functions to svc.ListClients https://github.com/absmach/magistrala/issues/5 page, err := svc.ListMembers(ctx, req.token, req.objectKind, req.objectID, req.Page) if err != nil { return nil, err diff --git a/users/service.go b/users/service.go index 69b44dfe6..3cb834410 100644 --- a/users/service.go +++ b/users/service.go @@ -508,7 +508,6 @@ func (svc service) Identify(ctx context.Context, token string) (string, error) { return user.GetUserId(), nil } -// ToDo: change the role of clients clients.Role == admin / user func (svc service) updateClientPolicy(ctx context.Context, userID string, role mgclients.Role) error { switch role { case mgclients.AdminRole: