NOISSUE - Add API layer for Invitation service (#125)

* feat(api): Implement endpoints for invitations

Implemented endpoints for sending, viewing, accepting, and deleting invitations in the API. Added validation, error handling, logging, and metrics functionality. Utilized various packages, routers, and frameworks to handle HTTP requests and perform invitation operations. Also included functions for decoding request payloads and implementing tracing functionality.

Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com>

* feat(invitations): add service mocks

* feat(endpoint): test API layer

"endpoint_test.go" is introduced, which contains test cases for API endpoints. These test functions cover various features related to invitations, including different input parameters and expected outcomes. Furthermore, the code includes modifications to a function in the transport file, where a parameter is removed and a route is modified for better performance and efficiency.

Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com>

* refactor(invitations): accept invitation to take domain

Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com>

* refactor(invitations): rename domain to domainID

Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com>

---------

Signed-off-by: Rodney Osodo <28790446+rodneyosodo@users.noreply.github.com>
This commit is contained in:
b1ackd0t
2023-12-08 15:11:31 +03:00
committed by GitHub
parent 75a2e8d4f3
commit c3882675f9
20 changed files with 1626 additions and 117 deletions
+4
View File
@@ -0,0 +1,4 @@
// Copyright (c) Abstract Machines
// SPDX-License-Identifier: Apache-2.0
package api
+106
View File
@@ -0,0 +1,106 @@
// Copyright (c) Abstract Machines
// SPDX-License-Identifier: Apache-2.0
package api
import (
"context"
"github.com/absmach/magistrala/internal/apiutil"
"github.com/absmach/magistrala/invitations"
"github.com/absmach/magistrala/pkg/errors"
"github.com/go-kit/kit/endpoint"
)
// InvitationSent is the message returned when an invitation is sent.
const InvitationSent = "invitation sent"
func sendInvitationEndpoint(svc invitations.Service) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
req := request.(sendInvitationReq)
if err := req.validate(); err != nil {
return nil, errors.Wrap(apiutil.ErrValidation, err)
}
invitation := invitations.Invitation{
UserID: req.UserID,
DomainID: req.DomainID,
Relation: req.Relation,
Resend: req.Resend,
}
if err := svc.SendInvitation(ctx, req.token, invitation); err != nil {
return nil, err
}
return sendInvitationRes{
Message: InvitationSent,
}, nil
}
}
func viewInvitationEndpoint(svc invitations.Service) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
req := request.(invitationReq)
if err := req.validate(); err != nil {
return nil, errors.Wrap(apiutil.ErrValidation, err)
}
invitation, err := svc.ViewInvitation(ctx, req.token, req.userID, req.domainID)
if err != nil {
return nil, err
}
return viewInvitationRes{
Invitation: invitation,
}, nil
}
}
func listInvitationsEndpoint(svc invitations.Service) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
req := request.(listInvitationsReq)
if err := req.validate(); err != nil {
return nil, errors.Wrap(apiutil.ErrValidation, err)
}
page, err := svc.ListInvitations(ctx, req.token, req.Page)
if err != nil {
return nil, err
}
return listInvitationsRes{
page,
}, nil
}
}
func acceptInvitationEndpoint(svc invitations.Service) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
req := request.(acceptInvitationReq)
if err := req.validate(); err != nil {
return nil, errors.Wrap(apiutil.ErrValidation, err)
}
if err := svc.AcceptInvitation(ctx, req.token, req.DomainID); err != nil {
return nil, err
}
return acceptInvitationRes{}, nil
}
}
func deleteInvitationEndpoint(svc invitations.Service) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
req := request.(invitationReq)
if err := req.validate(); err != nil {
return nil, errors.Wrap(apiutil.ErrValidation, err)
}
if err := svc.DeleteInvitation(ctx, req.token, req.userID, req.domainID); err != nil {
return nil, err
}
return deleteInvitationRes{}, nil
}
}
+522
View File
@@ -0,0 +1,522 @@
// Copyright (c) Abstract Machines
// SPDX-License-Identifier: Apache-2.0
package api_test
import (
"fmt"
"io"
"net/http"
"net/http/httptest"
"strings"
"testing"
"github.com/absmach/magistrala/internal/apiutil"
"github.com/absmach/magistrala/internal/testsutil"
"github.com/absmach/magistrala/invitations"
"github.com/absmach/magistrala/invitations/api"
"github.com/absmach/magistrala/invitations/mocks"
mglog "github.com/absmach/magistrala/logger"
"github.com/absmach/magistrala/pkg/errors"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
)
var (
validToken = "valid"
validContenType = "application/json"
validID = testsutil.GenerateUUID(&testing.T{})
)
type testRequest struct {
client *http.Client
method string
url string
token string
contentType string
body io.Reader
}
func (tr testRequest) make() (*http.Response, error) {
req, err := http.NewRequest(tr.method, tr.url, tr.body)
if err != nil {
return nil, err
}
if tr.token != "" {
req.Header.Set("Authorization", apiutil.BearerPrefix+tr.token)
}
if tr.contentType != "" {
req.Header.Set("Content-Type", tr.contentType)
}
return tr.client.Do(req)
}
func newIvitationsServer() (*httptest.Server, *mocks.Service) {
svc := new(mocks.Service)
logger := mglog.NewMock()
mux := api.MakeHandler(svc, logger, "test")
return httptest.NewServer(mux), svc
}
func TestSendInvitation(t *testing.T) {
is, svc := newIvitationsServer()
cases := []struct {
desc string
token string
data string
contentType string
status int
svcErr error
}{
{
desc: "valid request",
token: validToken,
data: fmt.Sprintf(`{"user_id": "%s", "domain_id": "%s", "relation": "%s"}`, validID, validID, "domain"),
status: http.StatusCreated,
contentType: validContenType,
svcErr: nil,
},
{
desc: "invalid token",
token: "",
data: fmt.Sprintf(`{"user_id": "%s", "domain_id": "%s", "relation": "%s"}`, validID, validID, "domain"),
status: http.StatusUnauthorized,
contentType: validContenType,
svcErr: nil,
},
{
desc: "invalid content type",
token: validToken,
data: fmt.Sprintf(`{"user_id": "%s", "domain_id": "%s", "relation": "%s"}`, validID, validID, "domain"),
status: http.StatusUnsupportedMediaType,
contentType: "text/plain",
svcErr: nil,
},
{
desc: "invalid data",
token: validToken,
data: `data`,
status: http.StatusBadRequest,
contentType: validContenType,
svcErr: nil,
},
{
desc: "with service error",
token: validToken,
data: fmt.Sprintf(`{"user_id": "%s", "domain_id": "%s", "relation": "%s"}`, validID, validID, "domain"),
status: http.StatusForbidden,
contentType: validContenType,
svcErr: errors.ErrAuthorization,
},
}
for _, tc := range cases {
repoCall := svc.On("SendInvitation", mock.Anything, tc.token, mock.Anything).Return(tc.svcErr)
req := testRequest{
client: is.Client(),
method: http.MethodPost,
url: is.URL + "/invitations",
token: tc.token,
contentType: tc.contentType,
body: strings.NewReader(tc.data),
}
res, err := req.make()
assert.Nil(t, err, tc.desc)
assert.Equal(t, tc.status, res.StatusCode, tc.desc)
repoCall.Unset()
}
}
func TestListInvitation(t *testing.T) {
is, svc := newIvitationsServer()
cases := []struct {
desc string
token string
query string
contentType string
status int
svcErr error
}{
{
desc: "valid request",
token: validToken,
status: http.StatusOK,
contentType: validContenType,
svcErr: nil,
},
{
desc: "invalid token",
token: "",
status: http.StatusUnauthorized,
contentType: validContenType,
svcErr: nil,
},
{
desc: "with offset",
token: validToken,
query: "offset=1",
status: http.StatusOK,
contentType: validContenType,
svcErr: nil,
},
{
desc: "with invalid offset",
token: validToken,
query: "offset=invalid",
status: http.StatusInternalServerError,
contentType: validContenType,
svcErr: nil,
},
{
desc: "with limit",
token: validToken,
query: "limit=1",
status: http.StatusOK,
contentType: validContenType,
svcErr: nil,
},
{
desc: "with invalid limit",
token: validToken,
query: "limit=invalid",
status: http.StatusInternalServerError,
contentType: validContenType,
svcErr: nil,
},
{
desc: "with user_id",
token: validToken,
query: fmt.Sprintf("user_id=%s", validID),
status: http.StatusOK,
contentType: validContenType,
svcErr: nil,
},
{
desc: "with duplicate user_id",
token: validToken,
query: "user_id=1&user_id=2",
status: http.StatusInternalServerError,
contentType: validContenType,
svcErr: nil,
},
{
desc: "with invited_by",
token: validToken,
query: fmt.Sprintf("invited_by=%s", validID),
status: http.StatusOK,
contentType: validContenType,
svcErr: nil,
},
{
desc: "with duplicate invited_by",
token: validToken,
query: "invited_by=1&invited_by=2",
status: http.StatusInternalServerError,
contentType: validContenType,
svcErr: nil,
},
{
desc: "with relation",
token: validToken,
query: fmt.Sprintf("relation=%s", "relation"),
status: http.StatusOK,
contentType: validContenType,
svcErr: nil,
},
{
desc: "with duplicate relation",
token: validToken,
query: "relation=1&relation=2",
status: http.StatusInternalServerError,
contentType: validContenType,
svcErr: nil,
},
{
desc: "with domain_id",
token: validToken,
query: fmt.Sprintf("domain_id=%s", validID),
status: http.StatusOK,
contentType: validContenType,
svcErr: nil,
},
{
desc: "with duplicate domain_id",
token: validToken,
query: "domain_id=1&domain_id=2",
status: http.StatusInternalServerError,
contentType: validContenType,
svcErr: nil,
},
{
desc: "with service error",
token: validToken,
status: http.StatusForbidden,
contentType: validContenType,
svcErr: errors.ErrAuthorization,
},
}
for _, tc := range cases {
repoCall := svc.On("ListInvitations", mock.Anything, tc.token, mock.Anything).Return(invitations.InvitationPage{}, tc.svcErr)
req := testRequest{
client: is.Client(),
method: http.MethodGet,
url: is.URL + "/invitations?" + tc.query,
token: tc.token,
contentType: tc.contentType,
}
res, err := req.make()
assert.Nil(t, err, tc.desc)
assert.Equal(t, tc.status, res.StatusCode, tc.desc)
repoCall.Unset()
}
}
func TestViewInvitation(t *testing.T) {
is, svc := newIvitationsServer()
cases := []struct {
desc string
token string
domainID string
userID string
contentType string
status int
svcErr error
}{
{
desc: "valid request",
token: validToken,
userID: validID,
domainID: validID,
status: http.StatusOK,
contentType: validContenType,
svcErr: nil,
},
{
desc: "invalid token",
token: "",
userID: validID,
domainID: validID,
status: http.StatusUnauthorized,
contentType: validContenType,
svcErr: nil,
},
{
desc: "with service error",
token: validToken,
userID: validID,
domainID: validID,
status: http.StatusForbidden,
contentType: validContenType,
svcErr: errors.ErrAuthorization,
},
{
desc: "with empty user_id",
token: validToken,
userID: "",
domainID: validID,
status: http.StatusBadRequest,
contentType: validContenType,
svcErr: nil,
},
{
desc: "with empty domain",
token: validToken,
userID: validID,
domainID: "",
status: http.StatusNotFound,
contentType: validContenType,
svcErr: nil,
},
{
desc: "with empty user_id and domain_id",
token: validToken,
userID: "",
domainID: "",
status: http.StatusNotFound,
contentType: validContenType,
svcErr: nil,
},
}
for _, tc := range cases {
repoCall := svc.On("ViewInvitation", mock.Anything, tc.token, tc.userID, tc.domainID).Return(invitations.Invitation{}, tc.svcErr)
req := testRequest{
client: is.Client(),
method: http.MethodGet,
url: is.URL + "/invitations/" + tc.userID + "/" + tc.domainID,
token: tc.token,
contentType: tc.contentType,
}
res, err := req.make()
assert.Nil(t, err, tc.desc)
assert.Equal(t, tc.status, res.StatusCode, tc.desc)
repoCall.Unset()
}
}
func TestDeleteInvitation(t *testing.T) {
is, svc := newIvitationsServer()
cases := []struct {
desc string
token string
domainID string
userID string
contentType string
status int
svcErr error
}{
{
desc: "valid request",
token: validToken,
userID: validID,
domainID: validID,
status: http.StatusNoContent,
contentType: validContenType,
svcErr: nil,
},
{
desc: "invalid token",
token: "",
userID: validID,
domainID: validID,
status: http.StatusUnauthorized,
contentType: validContenType,
svcErr: nil,
},
{
desc: "with service error",
token: validToken,
userID: validID,
domainID: validID,
status: http.StatusForbidden,
contentType: validContenType,
svcErr: errors.ErrAuthorization,
},
{
desc: "with empty user_id",
token: validToken,
userID: "",
domainID: validID,
status: http.StatusBadRequest,
contentType: validContenType,
svcErr: nil,
},
{
desc: "with empty domain_id",
token: validToken,
userID: validID,
domainID: "",
status: http.StatusNotFound,
contentType: validContenType,
svcErr: nil,
},
{
desc: "with empty user_id and domain_id",
token: validToken,
userID: "",
domainID: "",
status: http.StatusNotFound,
contentType: validContenType,
svcErr: nil,
},
}
for _, tc := range cases {
repoCall := svc.On("DeleteInvitation", mock.Anything, tc.token, tc.userID, tc.domainID).Return(tc.svcErr)
req := testRequest{
client: is.Client(),
method: http.MethodDelete,
url: is.URL + "/invitations/" + tc.userID + "/" + tc.domainID,
token: tc.token,
contentType: tc.contentType,
}
res, err := req.make()
assert.Nil(t, err, tc.desc)
assert.Equal(t, tc.status, res.StatusCode, tc.desc)
repoCall.Unset()
}
}
func TestAcceptInvitation(t *testing.T) {
is, svc := newIvitationsServer()
cases := []struct {
desc string
token string
data string
contentType string
status int
svcErr error
}{
{
desc: "valid request",
token: validToken,
data: fmt.Sprintf(`{"domain_id": "%s"}`, validID),
status: http.StatusOK,
contentType: validContenType,
svcErr: nil,
},
{
desc: "invalid token",
token: "",
data: fmt.Sprintf(`{"domain_id": "%s"}`, validID),
status: http.StatusUnauthorized,
contentType: validContenType,
svcErr: nil,
},
{
desc: "with service error",
token: validToken,
data: fmt.Sprintf(`{"domain_id": "%s"}`, validID),
status: http.StatusForbidden,
contentType: validContenType,
svcErr: errors.ErrAuthorization,
},
{
desc: "invalid content type",
token: validToken,
data: fmt.Sprintf(`{"domain_id": "%s"}`, validID),
status: http.StatusUnsupportedMediaType,
contentType: "text/plain",
svcErr: nil,
},
{
desc: "invalid data",
token: validToken,
data: `data`,
status: http.StatusBadRequest,
contentType: validContenType,
svcErr: nil,
},
}
for _, tc := range cases {
repoCall := svc.On("AcceptInvitation", mock.Anything, tc.token, mock.Anything).Return(tc.svcErr)
req := testRequest{
client: is.Client(),
method: http.MethodPost,
url: is.URL + "/invitations/accept",
token: tc.token,
contentType: tc.contentType,
body: strings.NewReader(tc.data),
}
res, err := req.make()
assert.Nil(t, err, tc.desc)
assert.Equal(t, tc.status, res.StatusCode, tc.desc)
repoCall.Unset()
}
}
+92
View File
@@ -0,0 +1,92 @@
// Copyright (c) Abstract Machines
// SPDX-License-Identifier: Apache-2.0
package api
import (
"errors"
"github.com/absmach/magistrala/internal/apiutil"
"github.com/absmach/magistrala/invitations"
)
const maxLimitSize = 100
var errMissingDomain = errors.New("missing domain")
type sendInvitationReq struct {
token string
UserID string `json:"user_id,omitempty"`
DomainID string `json:"domain_id,omitempty"`
Relation string `json:"relation,omitempty"`
Resend bool `json:"resend,omitempty"`
}
func (req *sendInvitationReq) validate() error {
if req.token == "" {
return apiutil.ErrBearerToken
}
if req.UserID == "" {
return apiutil.ErrMissingID
}
if req.DomainID == "" {
return errMissingDomain
}
if err := invitations.CheckRelation(req.Relation); err != nil {
return err
}
return nil
}
type listInvitationsReq struct {
token string
invitations.Page
}
func (req *listInvitationsReq) validate() error {
if req.token == "" {
return apiutil.ErrBearerToken
}
if req.Page.Limit > maxLimitSize || req.Page.Limit < 1 {
return apiutil.ErrLimitSize
}
return nil
}
type acceptInvitationReq struct {
token string
DomainID string `json:"domain_id,omitempty"`
}
func (req *acceptInvitationReq) validate() error {
if req.token == "" {
return apiutil.ErrBearerToken
}
if req.DomainID == "" {
return errMissingDomain
}
return nil
}
type invitationReq struct {
token string
userID string
domainID string
}
func (req *invitationReq) validate() error {
if req.token == "" {
return apiutil.ErrBearerToken
}
if req.userID == "" {
return apiutil.ErrMissingID
}
if req.domainID == "" {
return errMissingDomain
}
return nil
}
+226
View File
@@ -0,0 +1,226 @@
// Copyright (c) Abstract Machines
// SPDX-License-Identifier: Apache-2.0
package api
import (
"errors"
"fmt"
"testing"
"github.com/absmach/magistrala/auth"
"github.com/absmach/magistrala/internal/apiutil"
"github.com/absmach/magistrala/invitations"
"github.com/stretchr/testify/assert"
)
var (
errMissingRelation = errors.New("missing relation")
errInvalidRelation = errors.New("invalid relation")
valid = "valid"
)
func TestSendInvitationReqValidation(t *testing.T) {
cases := []struct {
desc string
req sendInvitationReq
err error
}{
{
desc: "valid request",
req: sendInvitationReq{
token: valid,
UserID: valid,
DomainID: valid,
Relation: auth.DomainRelation,
Resend: true,
},
err: nil,
},
{
desc: "empty token",
req: sendInvitationReq{
token: "",
UserID: valid,
DomainID: valid,
Relation: auth.DomainRelation,
Resend: true,
},
err: apiutil.ErrBearerToken,
},
{
desc: "empty user ID",
req: sendInvitationReq{
token: valid,
UserID: "",
DomainID: valid,
Relation: auth.DomainRelation,
Resend: true,
},
err: apiutil.ErrMissingID,
},
{
desc: "empty domain_id",
req: sendInvitationReq{
token: valid,
UserID: valid,
DomainID: "",
Relation: auth.DomainRelation,
Resend: true,
},
err: errMissingDomain,
},
{
desc: "missing relation",
req: sendInvitationReq{
token: valid,
UserID: valid,
DomainID: valid,
Relation: "",
Resend: true,
},
err: errMissingRelation,
},
{
desc: "invalid relation",
req: sendInvitationReq{
token: valid,
UserID: valid,
DomainID: valid,
Relation: "invalid",
Resend: true,
},
err: errInvalidRelation,
},
}
for _, tc := range cases {
err := tc.req.validate()
assert.Equal(t, tc.err, err, fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.err, err))
}
}
func TestListInvitationsReq(t *testing.T) {
cases := []struct {
desc string
req listInvitationsReq
err error
}{
{
desc: "valid request",
req: listInvitationsReq{
token: valid,
Page: invitations.Page{Limit: 1},
},
err: nil,
},
{
desc: "empty token",
req: listInvitationsReq{
token: "",
Page: invitations.Page{Limit: 1},
},
err: apiutil.ErrBearerToken,
},
{
desc: "invalid limit",
req: listInvitationsReq{
token: valid,
Page: invitations.Page{Limit: 1000},
},
err: apiutil.ErrLimitSize,
},
}
for _, tc := range cases {
err := tc.req.validate()
assert.Equal(t, tc.err, err, fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.err, err))
}
}
func TestAcceptInvitationReq(t *testing.T) {
cases := []struct {
desc string
req acceptInvitationReq
err error
}{
{
desc: "valid request",
req: acceptInvitationReq{
token: valid,
DomainID: valid,
},
err: nil,
},
{
desc: "empty token",
req: acceptInvitationReq{
token: "",
},
err: apiutil.ErrBearerToken,
},
{
desc: "empty domain_id",
req: acceptInvitationReq{
token: valid,
DomainID: "",
},
err: errMissingDomain,
},
}
for _, tc := range cases {
err := tc.req.validate()
assert.Equal(t, tc.err, err, fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.err, err))
}
}
func TestInvitationReqValidation(t *testing.T) {
cases := []struct {
desc string
req invitationReq
err error
}{
{
desc: "valid request",
req: invitationReq{
token: valid,
userID: valid,
domainID: valid,
},
err: nil,
},
{
desc: "empty token",
req: invitationReq{
token: "",
userID: valid,
domainID: valid,
},
err: apiutil.ErrBearerToken,
},
{
desc: "empty user ID",
req: invitationReq{
token: valid,
userID: "",
domainID: valid,
},
err: apiutil.ErrMissingID,
},
{
desc: "empty domain",
req: invitationReq{
token: valid,
userID: valid,
domainID: "",
},
err: errMissingDomain,
},
}
for _, tc := range cases {
err := tc.req.validate()
assert.Equal(t, tc.err, err, fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.err, err))
}
}
+95
View File
@@ -0,0 +1,95 @@
// Copyright (c) Abstract Machines
// SPDX-License-Identifier: Apache-2.0
package api
import (
"net/http"
"github.com/absmach/magistrala"
"github.com/absmach/magistrala/invitations"
)
var (
_ magistrala.Response = (*sendInvitationRes)(nil)
_ magistrala.Response = (*viewInvitationRes)(nil)
_ magistrala.Response = (*listInvitationsRes)(nil)
_ magistrala.Response = (*acceptInvitationRes)(nil)
_ magistrala.Response = (*deleteInvitationRes)(nil)
)
type sendInvitationRes struct {
Message string `json:"message"`
}
func (res sendInvitationRes) Code() int {
return http.StatusCreated
}
func (res sendInvitationRes) Headers() map[string]string {
return map[string]string{}
}
func (res sendInvitationRes) Empty() bool {
return true
}
type viewInvitationRes struct {
invitations.Invitation `json:",inline"`
}
func (res viewInvitationRes) Code() int {
return http.StatusOK
}
func (res viewInvitationRes) Headers() map[string]string {
return map[string]string{}
}
func (res viewInvitationRes) Empty() bool {
return false
}
type listInvitationsRes struct {
invitations.InvitationPage `json:",inline"`
}
func (res listInvitationsRes) Code() int {
return http.StatusOK
}
func (res listInvitationsRes) Headers() map[string]string {
return map[string]string{}
}
func (res listInvitationsRes) Empty() bool {
return false
}
type acceptInvitationRes struct{}
func (res acceptInvitationRes) Code() int {
return http.StatusOK
}
func (res acceptInvitationRes) Headers() map[string]string {
return map[string]string{}
}
func (res acceptInvitationRes) Empty() bool {
return true
}
type deleteInvitationRes struct{}
func (res deleteInvitationRes) Code() int {
return http.StatusNoContent
}
func (res deleteInvitationRes) Headers() map[string]string {
return map[string]string{}
}
func (res deleteInvitationRes) Empty() bool {
return true
}
+156
View File
@@ -0,0 +1,156 @@
// Copyright (c) Abstract Machines
// SPDX-License-Identifier: Apache-2.0
package api
import (
"context"
"encoding/json"
"net/http"
"strings"
"github.com/absmach/magistrala"
"github.com/absmach/magistrala/internal/api"
"github.com/absmach/magistrala/internal/apiutil"
"github.com/absmach/magistrala/invitations"
mglog "github.com/absmach/magistrala/logger"
"github.com/absmach/magistrala/pkg/errors"
"github.com/go-chi/chi/v5"
kithttp "github.com/go-kit/kit/transport/http"
"github.com/prometheus/client_golang/prometheus/promhttp"
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
)
const (
userIDKey = "user_id"
domainIDKey = "domain_id"
invitedByKey = "invited_by"
relationKey = "relation"
)
func MakeHandler(svc invitations.Service, logger mglog.Logger, instanceID string) http.Handler {
opts := []kithttp.ServerOption{
kithttp.ServerErrorEncoder(apiutil.LoggingErrorEncoder(logger, api.EncodeError)),
}
mux := chi.NewRouter()
mux.Route("/invitations", func(r chi.Router) {
r.Post("/", otelhttp.NewHandler(kithttp.NewServer(
sendInvitationEndpoint(svc),
decodeSendInvitationReq,
api.EncodeResponse,
opts...,
), "send_invitation").ServeHTTP)
r.Get("/", otelhttp.NewHandler(kithttp.NewServer(
listInvitationsEndpoint(svc),
decodeListInvitationsReq,
api.EncodeResponse,
opts...,
), "list_invitations").ServeHTTP)
r.Route("/{user_id}/{domain_id}", func(r chi.Router) {
r.Get("/", otelhttp.NewHandler(kithttp.NewServer(
viewInvitationEndpoint(svc),
decodeInvitationReq,
api.EncodeResponse,
opts...,
), "view_invitations").ServeHTTP)
r.Delete("/", otelhttp.NewHandler(kithttp.NewServer(
deleteInvitationEndpoint(svc),
decodeInvitationReq,
api.EncodeResponse,
opts...,
), "delete_invitation").ServeHTTP)
})
r.Post("/accept", otelhttp.NewHandler(kithttp.NewServer(
acceptInvitationEndpoint(svc),
decodeAcceptInvitationReq,
api.EncodeResponse,
opts...,
), "accept_invitation").ServeHTTP)
})
mux.Get("/health", magistrala.Health("invitations", instanceID))
mux.Handle("/metrics", promhttp.Handler())
return mux
}
func decodeSendInvitationReq(_ 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)
}
var req sendInvitationReq
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
return nil, errors.Wrap(apiutil.ErrValidation, errors.Wrap(err, errors.ErrMalformedEntity))
}
req.token = apiutil.ExtractBearerToken(r)
return req, nil
}
func decodeListInvitationsReq(_ context.Context, r *http.Request) (interface{}, error) {
offset, err := apiutil.ReadNumQuery[uint64](r, api.OffsetKey, api.DefOffset)
if err != nil {
return nil, errors.Wrap(apiutil.ErrValidation, err)
}
limit, err := apiutil.ReadNumQuery[uint64](r, api.LimitKey, api.DefLimit)
if err != nil {
return nil, errors.Wrap(apiutil.ErrValidation, err)
}
userID, err := apiutil.ReadStringQuery(r, userIDKey, "")
if err != nil {
return nil, errors.Wrap(apiutil.ErrValidation, err)
}
invitedBy, err := apiutil.ReadStringQuery(r, invitedByKey, "")
if err != nil {
return nil, errors.Wrap(apiutil.ErrValidation, err)
}
relation, err := apiutil.ReadStringQuery(r, relationKey, "")
if err != nil {
return nil, errors.Wrap(apiutil.ErrValidation, err)
}
domainID, err := apiutil.ReadStringQuery(r, domainIDKey, "")
if err != nil {
return nil, errors.Wrap(apiutil.ErrValidation, err)
}
req := listInvitationsReq{
token: apiutil.ExtractBearerToken(r),
Page: invitations.Page{
Offset: offset,
Limit: limit,
InvitedBy: invitedBy,
UserID: userID,
Relation: relation,
DomainID: domainID,
},
}
return req, nil
}
func decodeAcceptInvitationReq(_ 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)
}
var req acceptInvitationReq
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
return nil, errors.Wrap(apiutil.ErrValidation, errors.Wrap(err, errors.ErrMalformedEntity))
}
req.token = apiutil.ExtractBearerToken(r)
return req, nil
}
func decodeInvitationReq(_ context.Context, r *http.Request) (interface{}, error) {
req := invitationReq{
token: apiutil.ExtractBearerToken(r),
userID: chi.URLParam(r, "user_id"),
domainID: chi.URLParam(r, "domain_id"),
}
return req, nil
}
+5 -5
View File
@@ -21,7 +21,7 @@ var (
type Invitation struct {
InvitedBy string `json:"invited_by" db:"invited_by"`
UserID string `json:"user_id" db:"user_id"`
Domain string `json:"domain" db:"domain"`
DomainID string `json:"domain_id" db:"domain_id"`
Token string `json:"token,omitempty" db:"token"`
Relation string `json:"relation,omitempty" db:"relation"`
CreatedAt time.Time `json:"created_at" db:"created_at"`
@@ -36,7 +36,7 @@ type Page struct {
Limit uint64 `json:"limit" db:"limit"`
InvitedBy string `json:"invited_by,omitempty" db:"invited_by,omitempty"`
UserID string `json:"user_id,omitempty" db:"user_id,omitempty"`
Domain string `json:"domain,omitempty" db:"domain,omitempty"`
DomainID string `json:"domain_id,omitempty" db:"domain_id,omitempty"`
Relation string `json:"relation,omitempty" db:"relation,omitempty"`
InvitedByOrUserID string `db:"invited_by_or_user_id,omitempty"`
}
@@ -76,7 +76,7 @@ type Service interface {
// - the user who sent the invitation
// - domain administrators
// - platform administrators
ViewInvitation(ctx context.Context, token, userID, domain string) (invitation Invitation, err error)
ViewInvitation(ctx context.Context, token, userID, domainID string) (invitation Invitation, err error)
// ListInvitations returns a list of invitations.
// People who can list invitations are:
@@ -86,7 +86,7 @@ type Service interface {
ListInvitations(ctx context.Context, token string, page Page) (invitations InvitationPage, err error)
// AcceptInvitation accepts an invitation by adding the user to the domain.
AcceptInvitation(ctx context.Context, token, domain string) (err error)
AcceptInvitation(ctx context.Context, token, domainID string) (err error)
// DeleteInvitation deletes an invitation.
// People who can delete invitations are:
@@ -94,7 +94,7 @@ type Service interface {
// - the user who sent the invitation
// - domain administrators
// - platform administrators
DeleteInvitation(ctx context.Context, token, userID, domain string) (err error)
DeleteInvitation(ctx context.Context, token, userID, domainID string) (err error)
}
type Repository interface {
+2 -2
View File
@@ -40,11 +40,11 @@ func TestInvitation_MarshalJSON(t *testing.T) {
{
InvitedBy: "John",
UserID: "123",
Domain: "123",
DomainID: "123",
},
},
},
res: `{"total":1,"offset":0,"limit":0,"invitations":[{"invited_by":"John","user_id":"123","domain":"123","created_at":"0001-01-01T00:00:00Z","updated_at":"0001-01-01T00:00:00Z","confirmed_at":"0001-01-01T00:00:00Z"}]}`,
res: `{"total":1,"offset":0,"limit":0,"invitations":[{"invited_by":"John","user_id":"123","domain_id":"123","created_at":"0001-01-01T00:00:00Z","updated_at":"0001-01-01T00:00:00Z","confirmed_at":"0001-01-01T00:00:00Z"}]}`,
},
}
+9
View File
@@ -0,0 +1,9 @@
// Copyright (c) Abstract Machines
// SPDX-License-Identifier: Apache-2.0
// Package middleware contains the middleware for the invitations service.
// It is responsible for the following:
// - Logging
// - Metrics
// - Tracing
package middleware
+84
View File
@@ -0,0 +1,84 @@
// Copyright (c) Abstract Machines
// SPDX-License-Identifier: Apache-2.0
package middleware
import (
"context"
"fmt"
"time"
"github.com/absmach/magistrala/invitations"
mglog "github.com/absmach/magistrala/logger"
)
var _ invitations.Service = (*logging)(nil)
type logging struct {
logger mglog.Logger
svc invitations.Service
}
func Logging(logger mglog.Logger, svc invitations.Service) invitations.Service {
return &logging{logger, svc}
}
func (lm *logging) SendInvitation(ctx context.Context, token string, invitation invitations.Invitation) (err error) {
defer func(begin time.Time) {
message := fmt.Sprintf("Method send_invitation to user_id %s from domain_id %s took %s to complete", invitation.UserID, invitation.DomainID, time.Since(begin))
if err != nil {
lm.logger.Warn(fmt.Sprintf("%s with error: %s.", message, err))
return
}
lm.logger.Info(fmt.Sprintf("%s without errors.", message))
}(time.Now())
return lm.svc.SendInvitation(ctx, token, invitation)
}
func (lm *logging) ViewInvitation(ctx context.Context, token, userID, domainID string) (invitation invitations.Invitation, err error) {
defer func(begin time.Time) {
message := fmt.Sprintf("Method view_invitation took %s to complete", time.Since(begin))
if err != nil {
lm.logger.Warn(fmt.Sprintf("%s with error: %s.", message, err))
return
}
lm.logger.Info(fmt.Sprintf("%s without errors.", message))
}(time.Now())
return lm.svc.ViewInvitation(ctx, token, userID, domainID)
}
func (lm *logging) ListInvitations(ctx context.Context, token string, page invitations.Page) (invs invitations.InvitationPage, err error) {
defer func(begin time.Time) {
message := fmt.Sprintf("Method list_invitations took %s to complete", time.Since(begin))
if err != nil {
lm.logger.Warn(fmt.Sprintf("%s with error: %s.", message, err))
return
}
lm.logger.Info(fmt.Sprintf("%s without errors.", message))
}(time.Now())
return lm.svc.ListInvitations(ctx, token, page)
}
func (lm *logging) AcceptInvitation(ctx context.Context, token, domainID string) (err error) {
defer func(begin time.Time) {
message := fmt.Sprintf("Method accept_invitation took %s to complete", time.Since(begin))
if err != nil {
lm.logger.Warn(fmt.Sprintf("%s with error: %s.", message, err))
return
}
lm.logger.Info(fmt.Sprintf("%s without errors.", message))
}(time.Now())
return lm.svc.AcceptInvitation(ctx, token, domainID)
}
func (lm *logging) DeleteInvitation(ctx context.Context, token, userID, domainID string) (err error) {
defer func(begin time.Time) {
message := fmt.Sprintf("Method delete_invitation took %s to complete", time.Since(begin))
if err != nil {
lm.logger.Warn(fmt.Sprintf("%s with error: %s.", message, err))
return
}
lm.logger.Info(fmt.Sprintf("%s without errors.", message))
}(time.Now())
return lm.svc.DeleteInvitation(ctx, token, userID, domainID)
}
+68
View File
@@ -0,0 +1,68 @@
// Copyright (c) Abstract Machines
// SPDX-License-Identifier: Apache-2.0
package middleware
import (
"context"
"time"
"github.com/absmach/magistrala/invitations"
"github.com/go-kit/kit/metrics"
)
var _ invitations.Service = (*metricsmw)(nil)
type metricsmw struct {
counter metrics.Counter
latency metrics.Histogram
svc invitations.Service
}
func Metrics(counter metrics.Counter, latency metrics.Histogram, svc invitations.Service) invitations.Service {
return &metricsmw{
counter: counter,
latency: latency,
svc: svc,
}
}
func (mm *metricsmw) SendInvitation(ctx context.Context, token string, invitation invitations.Invitation) (err error) {
defer func(begin time.Time) {
mm.counter.With("method", "send_invitation").Add(1)
mm.latency.With("method", "send_invitation").Observe(time.Since(begin).Seconds())
}(time.Now())
return mm.svc.SendInvitation(ctx, token, invitation)
}
func (mm *metricsmw) ViewInvitation(ctx context.Context, token, userID, domainID string) (invitation invitations.Invitation, err error) {
defer func(begin time.Time) {
mm.counter.With("method", "view_invitation").Add(1)
mm.latency.With("method", "view_invitation").Observe(time.Since(begin).Seconds())
}(time.Now())
return mm.svc.ViewInvitation(ctx, token, userID, domainID)
}
func (mm *metricsmw) ListInvitations(ctx context.Context, token string, page invitations.Page) (invs invitations.InvitationPage, err error) {
defer func(begin time.Time) {
mm.counter.With("method", "list_invitations").Add(1)
mm.latency.With("method", "list_invitations").Observe(time.Since(begin).Seconds())
}(time.Now())
return mm.svc.ListInvitations(ctx, token, page)
}
func (mm *metricsmw) AcceptInvitation(ctx context.Context, token, domainID string) (err error) {
defer func(begin time.Time) {
mm.counter.With("method", "accept_invitation").Add(1)
mm.latency.With("method", "accept_invitation").Observe(time.Since(begin).Seconds())
}(time.Now())
return mm.svc.AcceptInvitation(ctx, token, domainID)
}
func (mm *metricsmw) DeleteInvitation(ctx context.Context, token, userID, domainID string) (err error) {
defer func(begin time.Time) {
mm.counter.With("method", "delete_invitation").Add(1)
mm.latency.With("method", "delete_invitation").Observe(time.Since(begin).Seconds())
}(time.Now())
return mm.svc.DeleteInvitation(ctx, token, userID, domainID)
}
+75
View File
@@ -0,0 +1,75 @@
// Copyright (c) Abstract Machines
// SPDX-License-Identifier: Apache-2.0
package middleware
import (
"context"
"github.com/absmach/magistrala/invitations"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/trace"
)
var _ invitations.Service = (*tracing)(nil)
type tracing struct {
tracer trace.Tracer
svc invitations.Service
}
func Tracing(svc invitations.Service, tracer trace.Tracer) invitations.Service {
return &tracing{tracer, svc}
}
func (tm *tracing) SendInvitation(ctx context.Context, token string, invitation invitations.Invitation) (err error) {
ctx, span := tm.tracer.Start(ctx, "send_invitation", trace.WithAttributes(
attribute.String("domain_id", invitation.DomainID),
attribute.String("user_id", invitation.UserID),
))
defer span.End()
return tm.svc.SendInvitation(ctx, token, invitation)
}
func (tm *tracing) ViewInvitation(ctx context.Context, token, userID, domain string) (invitation invitations.Invitation, err error) {
ctx, span := tm.tracer.Start(ctx, "view_invitation", trace.WithAttributes(
attribute.String("user_id", userID),
attribute.String("domain_id", domain),
))
defer span.End()
return tm.svc.ViewInvitation(ctx, token, userID, domain)
}
func (tm *tracing) ListInvitations(ctx context.Context, token string, page invitations.Page) (invs invitations.InvitationPage, err error) {
ctx, span := tm.tracer.Start(ctx, "list_invitations", trace.WithAttributes(
attribute.Int("limit", int(page.Limit)),
attribute.Int("offset", int(page.Offset)),
attribute.String("user_id", page.UserID),
attribute.String("domain_id", page.DomainID),
attribute.String("invited_by", page.InvitedBy),
))
defer span.End()
return tm.svc.ListInvitations(ctx, token, page)
}
func (tm *tracing) AcceptInvitation(ctx context.Context, token, domainID string) (err error) {
ctx, span := tm.tracer.Start(ctx, "accept_invitation", trace.WithAttributes(
attribute.String("domain_id", domainID),
))
defer span.End()
return tm.svc.AcceptInvitation(ctx, token, domainID)
}
func (tm *tracing) DeleteInvitation(ctx context.Context, token, userID, domainID string) (err error) {
ctx, span := tm.tracer.Start(ctx, "delete_invitation", trace.WithAttributes(
attribute.String("user_id", userID),
attribute.String("domain_id", domainID),
))
defer span.End()
return tm.svc.DeleteInvitation(ctx, token, userID, domainID)
}
+7 -7
View File
@@ -22,7 +22,7 @@ type Repository struct {
func (m *Repository) Create(ctx context.Context, invitation invitations.Invitation) error {
ret := m.Called(ctx, invitation)
if invitation.UserID == Invalid || invitation.Domain == Invalid || invitation.InvitedBy == Invalid {
if invitation.UserID == Invalid || invitation.DomainID == Invalid || invitation.InvitedBy == Invalid {
return errors.ErrNotFound
}
@@ -42,7 +42,7 @@ func (m *Repository) Retrieve(ctx context.Context, userID, domainID string) (inv
func (m *Repository) RetrieveAll(ctx context.Context, page invitations.Page) (invitations.InvitationPage, error) {
ret := m.Called(ctx, page)
if page.UserID == Invalid || page.Domain == Invalid {
if page.UserID == Invalid || page.DomainID == Invalid {
return invitations.InvitationPage{}, errors.ErrNotFound
}
@@ -52,7 +52,7 @@ func (m *Repository) RetrieveAll(ctx context.Context, page invitations.Page) (in
func (m *Repository) UpdateToken(ctx context.Context, invitation invitations.Invitation) error {
ret := m.Called(ctx, invitation)
if invitation.UserID == Invalid || invitation.Domain == Invalid {
if invitation.UserID == Invalid || invitation.DomainID == Invalid {
return errors.ErrNotFound
}
@@ -62,17 +62,17 @@ func (m *Repository) UpdateToken(ctx context.Context, invitation invitations.Inv
func (m *Repository) UpdateConfirmation(ctx context.Context, invitation invitations.Invitation) error {
ret := m.Called(ctx, invitation)
if invitation.UserID == Invalid || invitation.Domain == Invalid {
if invitation.UserID == Invalid || invitation.DomainID == Invalid {
return errors.ErrNotFound
}
return ret.Error(0)
}
func (m *Repository) Delete(ctx context.Context, userID, domain string) error {
ret := m.Called(ctx, userID, domain)
func (m *Repository) Delete(ctx context.Context, userID, domainID string) error {
ret := m.Called(ctx, userID, domainID)
if userID == Invalid || domain == Invalid {
if userID == Invalid || domainID == Invalid {
return errors.ErrNotFound
}
+72
View File
@@ -0,0 +1,72 @@
// Copyright (c) Abstract Machines
// SPDX-License-Identifier: Apache-2.0
package mocks
import (
"context"
"github.com/absmach/magistrala/invitations"
"github.com/absmach/magistrala/pkg/errors"
"github.com/stretchr/testify/mock"
)
var _ invitations.Service = (*Service)(nil)
type Service struct {
mock.Mock
}
func (svc *Service) SendInvitation(ctx context.Context, token string, invitation invitations.Invitation) (err error) {
ret := svc.Called(ctx, token, invitation)
if token == Invalid || invitation.UserID == Invalid || invitation.DomainID == Invalid || invitation.InvitedBy == Invalid {
return errors.ErrNotFound
}
return ret.Error(0)
}
func (svc *Service) ViewInvitation(ctx context.Context, token, userID, domainID string) (invitation invitations.Invitation, err error) {
ret := svc.Called(ctx, token, userID, domainID)
if token == Invalid || userID == Invalid || domainID == Invalid {
return invitations.Invitation{}, errors.ErrNotFound
}
return ret.Get(0).(invitations.Invitation), ret.Error(1)
}
func (svc *Service) ListInvitations(ctx context.Context, token string, page invitations.Page) (invitations.InvitationPage, error) {
ret := svc.Called(ctx, token, page)
if token == Invalid {
return invitations.InvitationPage{}, errors.ErrAuthentication
}
return ret.Get(0).(invitations.InvitationPage), ret.Error(1)
}
func (svc *Service) AcceptInvitation(ctx context.Context, token, domainID string) (err error) {
ret := svc.Called(ctx, token, domainID)
if token == Invalid {
return errors.ErrAuthentication
}
return ret.Error(0)
}
func (svc *Service) DeleteInvitation(ctx context.Context, token, userID, domainID string) (err error) {
ret := svc.Called(ctx, token, userID, domainID)
if token == Invalid {
return errors.ErrAuthentication
}
if userID == Invalid || domainID == Invalid {
return errors.ErrNotFound
}
return ret.Error(0)
}
+3 -3
View File
@@ -18,14 +18,14 @@ func Migration() *migrate.MemoryMigrationSource {
`CREATE TABLE IF NOT EXISTS invitations (
invited_by VARCHAR(36) NOT NULL,
user_id VARCHAR(36) NOT NULL,
domain VARCHAR(36) NOT NULL,
domain_id VARCHAR(36) NOT NULL,
token TEXT NOT NULL,
relation VARCHAR(254) NOT NULL,
created_at TIMESTAMP NOT NULL,
updated_at TIMESTAMP,
confirmed_at TIMESTAMP,
UNIQUE (user_id, domain),
PRIMARY KEY (user_id, domain)
UNIQUE (user_id, domain_id),
PRIMARY KEY (user_id, domain_id)
)`,
},
Down: []string{
+11 -11
View File
@@ -22,8 +22,8 @@ func NewRepository(db postgres.Database) invitations.Repository {
}
func (repo *repository) Create(ctx context.Context, invitation invitations.Invitation) (err error) {
q := `INSERT INTO invitations (invited_by, user_id, domain, token, relation, created_at, updated_at, confirmed_at)
VALUES (:invited_by, :user_id, :domain, :token, :relation, :created_at, :updated_at, :confirmed_at)`
q := `INSERT INTO invitations (invited_by, user_id, domain_id, token, relation, created_at, updated_at, confirmed_at)
VALUES (:invited_by, :user_id, :domain_id, :token, :relation, :created_at, :updated_at, :confirmed_at)`
if _, err = repo.db.NamedExecContext(ctx, q, invitation); err != nil {
return postgres.HandleError(repoerr.ErrCreateEntity, err)
@@ -33,11 +33,11 @@ func (repo *repository) Create(ctx context.Context, invitation invitations.Invit
}
func (repo *repository) Retrieve(ctx context.Context, userID, domainID string) (invitations.Invitation, error) {
q := `SELECT invited_by, user_id, domain, token, relation, created_at, updated_at, confirmed_at FROM invitations WHERE user_id = :user_id AND domain = :domain;`
q := `SELECT invited_by, user_id, domain_id, token, relation, created_at, updated_at, confirmed_at FROM invitations WHERE user_id = :user_id AND domain_id = :domain_id;`
inv := invitations.Invitation{
UserID: userID,
Domain: domainID,
UserID: userID,
DomainID: domainID,
}
rows, err := repo.db.NamedQueryContext(ctx, q, inv)
@@ -61,7 +61,7 @@ func (repo *repository) Retrieve(ctx context.Context, userID, domainID string) (
func (repo *repository) RetrieveAll(ctx context.Context, page invitations.Page) (invitations.InvitationPage, error) {
query := pageQuery(page)
q := fmt.Sprintf("SELECT invited_by, user_id, domain, relation, created_at, updated_at, confirmed_at FROM invitations %s LIMIT :limit OFFSET :offset;", query)
q := fmt.Sprintf("SELECT invited_by, user_id, domain_id, relation, created_at, updated_at, confirmed_at FROM invitations %s LIMIT :limit OFFSET :offset;", query)
rows, err := repo.db.NamedQueryContext(ctx, q, page)
if err != nil {
@@ -96,7 +96,7 @@ func (repo *repository) RetrieveAll(ctx context.Context, page invitations.Page)
}
func (repo *repository) UpdateToken(ctx context.Context, invitation invitations.Invitation) (err error) {
q := `UPDATE invitations SET token = :token, updated_at = :updated_at WHERE user_id = :user_id AND domain = :domain`
q := `UPDATE invitations SET token = :token, updated_at = :updated_at WHERE user_id = :user_id AND domain_id = :domain_id`
result, err := repo.db.NamedExecContext(ctx, q, invitation)
if err != nil {
@@ -110,7 +110,7 @@ func (repo *repository) UpdateToken(ctx context.Context, invitation invitations.
}
func (repo *repository) UpdateConfirmation(ctx context.Context, invitation invitations.Invitation) (err error) {
q := `UPDATE invitations SET confirmed_at = :confirmed_at, updated_at = :updated_at WHERE user_id = :user_id AND domain = :domain`
q := `UPDATE invitations SET confirmed_at = :confirmed_at, updated_at = :updated_at WHERE user_id = :user_id AND domain_id = :domain_id`
result, err := repo.db.NamedExecContext(ctx, q, invitation)
if err != nil {
@@ -124,7 +124,7 @@ func (repo *repository) UpdateConfirmation(ctx context.Context, invitation invit
}
func (repo *repository) Delete(ctx context.Context, userID, domain string) (err error) {
q := `DELETE FROM invitations WHERE user_id = $1 AND domain = $2`
q := `DELETE FROM invitations WHERE user_id = $1 AND domain_id = $2`
result, err := repo.db.ExecContext(ctx, q, userID, domain)
if err != nil {
@@ -140,8 +140,8 @@ func (repo *repository) Delete(ctx context.Context, userID, domain string) (err
func pageQuery(pm invitations.Page) string {
var query []string
var emq string
if pm.Domain != "" {
query = append(query, "domain = :domain")
if pm.DomainID != "" {
query = append(query, "domain_id = :domain_id")
}
if pm.UserID != "" {
query = append(query, "user_id = :user_id")
+52 -52
View File
@@ -31,7 +31,7 @@ func TestInvitationCreate(t *testing.T) {
})
repo := postgres.NewRepository(database)
domain := testsutil.GenerateUUID(t)
domainID := testsutil.GenerateUUID(t)
userID := testsutil.GenerateUUID(t)
cases := []struct {
@@ -44,7 +44,7 @@ func TestInvitationCreate(t *testing.T) {
invitation: invitations.Invitation{
InvitedBy: testsutil.GenerateUUID(t),
UserID: userID,
Domain: domain,
DomainID: domainID,
Token: validToken,
Relation: relation,
CreatedAt: time.Now(),
@@ -56,7 +56,7 @@ func TestInvitationCreate(t *testing.T) {
invitation: invitations.Invitation{
InvitedBy: testsutil.GenerateUUID(t),
UserID: testsutil.GenerateUUID(t),
Domain: testsutil.GenerateUUID(t),
DomainID: testsutil.GenerateUUID(t),
Token: validToken,
Relation: relation,
CreatedAt: time.Now(),
@@ -69,7 +69,7 @@ func TestInvitationCreate(t *testing.T) {
invitation: invitations.Invitation{
InvitedBy: testsutil.GenerateUUID(t),
UserID: userID,
Domain: domain,
DomainID: domainID,
Token: validToken,
Relation: relation,
CreatedAt: time.Now(),
@@ -81,7 +81,7 @@ func TestInvitationCreate(t *testing.T) {
invitation: invitations.Invitation{
InvitedBy: invalidUUID,
UserID: testsutil.GenerateUUID(t),
Domain: testsutil.GenerateUUID(t),
DomainID: testsutil.GenerateUUID(t),
Token: validToken,
Relation: relation,
CreatedAt: time.Now(),
@@ -93,7 +93,7 @@ func TestInvitationCreate(t *testing.T) {
invitation: invitations.Invitation{
InvitedBy: testsutil.GenerateUUID(t),
UserID: testsutil.GenerateUUID(t),
Domain: testsutil.GenerateUUID(t),
DomainID: testsutil.GenerateUUID(t),
Token: validToken,
Relation: strings.Repeat("a", 255),
CreatedAt: time.Now(),
@@ -105,7 +105,7 @@ func TestInvitationCreate(t *testing.T) {
invitation: invitations.Invitation{
InvitedBy: testsutil.GenerateUUID(t),
UserID: testsutil.GenerateUUID(t),
Domain: invalidUUID,
DomainID: invalidUUID,
Token: validToken,
Relation: relation,
CreatedAt: time.Now(),
@@ -117,7 +117,7 @@ func TestInvitationCreate(t *testing.T) {
invitation: invitations.Invitation{
InvitedBy: testsutil.GenerateUUID(t),
UserID: invalidUUID,
Domain: testsutil.GenerateUUID(t),
DomainID: testsutil.GenerateUUID(t),
Token: validToken,
Relation: relation,
CreatedAt: time.Now(),
@@ -139,7 +139,7 @@ func TestInvitationCreate(t *testing.T) {
desc: "add invitation with empty invitation user id",
invitation: invitations.Invitation{
InvitedBy: testsutil.GenerateUUID(t),
Domain: testsutil.GenerateUUID(t),
DomainID: testsutil.GenerateUUID(t),
Token: validToken,
Relation: relation,
CreatedAt: time.Now(),
@@ -149,7 +149,7 @@ func TestInvitationCreate(t *testing.T) {
{
desc: "add invitation with empty invitation invited_by",
invitation: invitations.Invitation{
Domain: testsutil.GenerateUUID(t),
DomainID: testsutil.GenerateUUID(t),
UserID: testsutil.GenerateUUID(t),
Token: validToken,
Relation: relation,
@@ -161,7 +161,7 @@ func TestInvitationCreate(t *testing.T) {
desc: "add invitation with empty invitation token",
invitation: invitations.Invitation{
InvitedBy: testsutil.GenerateUUID(t),
Domain: testsutil.GenerateUUID(t),
DomainID: testsutil.GenerateUUID(t),
UserID: testsutil.GenerateUUID(t),
Relation: relation,
CreatedAt: time.Now(),
@@ -189,7 +189,7 @@ func TestInvitationRetrieve(t *testing.T) {
invitation := invitations.Invitation{
InvitedBy: testsutil.GenerateUUID(t),
UserID: testsutil.GenerateUUID(t),
Domain: testsutil.GenerateUUID(t),
DomainID: testsutil.GenerateUUID(t),
Token: validToken,
Relation: relation,
CreatedAt: time.Now().UTC().Truncate(time.Microsecond),
@@ -207,26 +207,26 @@ func TestInvitationRetrieve(t *testing.T) {
{
desc: "retrieve invitations successfully",
userID: invitation.UserID,
domainID: invitation.Domain,
domainID: invitation.DomainID,
response: invitation,
err: nil,
},
{
desc: "retrieve invitations with invalid invitation user id",
userID: testsutil.GenerateUUID(t),
domainID: invitation.Domain,
domainID: invitation.DomainID,
response: invitations.Invitation{},
err: repoerr.ErrNotFound,
},
{
desc: "retrieve invitations with invalid invitation domain",
desc: "retrieve invitations with invalid invitation domain_id",
userID: invitation.UserID,
domainID: testsutil.GenerateUUID(t),
response: invitations.Invitation{},
err: repoerr.ErrNotFound,
},
{
desc: "retrieve invitations with invalid invitation user id and domain",
desc: "retrieve invitations with invalid invitation user id and domain_id",
userID: testsutil.GenerateUUID(t),
domainID: testsutil.GenerateUUID(t),
response: invitations.Invitation{},
@@ -235,19 +235,19 @@ func TestInvitationRetrieve(t *testing.T) {
{
desc: "retrieve invitations with empty invitation user id",
userID: "",
domainID: invitation.Domain,
domainID: invitation.DomainID,
response: invitations.Invitation{},
err: repoerr.ErrNotFound,
},
{
desc: "retrieve invitations with empty invitation domain",
desc: "retrieve invitations with empty invitation domain_id",
userID: invitation.UserID,
domainID: "",
response: invitations.Invitation{},
err: repoerr.ErrNotFound,
},
{
desc: "retrieve invitations with empty invitation user id and domain",
desc: "retrieve invitations with empty invitation user id and domain_id",
userID: "",
domainID: "",
response: invitations.Invitation{},
@@ -275,7 +275,7 @@ func TestInvitationRetrieveAll(t *testing.T) {
invitation := invitations.Invitation{
InvitedBy: testsutil.GenerateUUID(t),
UserID: testsutil.GenerateUUID(t),
Domain: testsutil.GenerateUUID(t),
DomainID: testsutil.GenerateUUID(t),
Token: validToken,
Relation: fmt.Sprintf("%s-%d", relation, i),
CreatedAt: time.Now().UTC().Truncate(time.Microsecond),
@@ -397,9 +397,9 @@ func TestInvitationRetrieveAll(t *testing.T) {
{
desc: "retrieve invitations with domain",
page: invitations.Page{
Domain: items[0].Domain,
Offset: 0,
Limit: 10,
DomainID: items[0].DomainID,
Offset: 0,
Limit: 10,
},
response: invitations.InvitationPage{
Total: 1,
@@ -465,12 +465,12 @@ func TestInvitationRetrieveAll(t *testing.T) {
},
},
{
desc: "retrieve invitations with domain and user id",
desc: "retrieve invitations with domain_id and user id",
page: invitations.Page{
Domain: items[0].Domain,
UserID: items[0].UserID,
Offset: 0,
Limit: 10,
DomainID: items[0].DomainID,
UserID: items[0].UserID,
Offset: 0,
Limit: 10,
},
response: invitations.InvitationPage{
Total: 1,
@@ -480,9 +480,9 @@ func TestInvitationRetrieveAll(t *testing.T) {
},
},
{
desc: "retrieve invitations with domain and invited_by",
desc: "retrieve invitations with domain_id and invited_by",
page: invitations.Page{
Domain: items[0].Domain,
DomainID: items[0].DomainID,
InvitedBy: items[0].InvitedBy,
Offset: 0,
Limit: 10,
@@ -510,9 +510,9 @@ func TestInvitationRetrieveAll(t *testing.T) {
},
},
{
desc: "retrieve invitations with domain, user id and invited_by",
desc: "retrieve invitations with domain_id, user id and invited_by",
page: invitations.Page{
Domain: items[0].Domain,
DomainID: items[0].DomainID,
UserID: items[0].UserID,
InvitedBy: items[0].InvitedBy,
Offset: 0,
@@ -526,9 +526,9 @@ func TestInvitationRetrieveAll(t *testing.T) {
},
},
{
desc: "retrieve invitations with domain, user id, invited_by and relation",
desc: "retrieve invitations with domain_id, user id, invited_by and relation",
page: invitations.Page{
Domain: items[0].Domain,
DomainID: items[0].DomainID,
UserID: items[0].UserID,
InvitedBy: items[0].InvitedBy,
Relation: relation + "-0",
@@ -545,9 +545,9 @@ func TestInvitationRetrieveAll(t *testing.T) {
{
desc: "retrieve invitations with invalid domain",
page: invitations.Page{
Domain: invalidUUID,
Offset: 0,
Limit: 10,
DomainID: invalidUUID,
Offset: 0,
Limit: 10,
},
response: invitations.InvitationPage{
Total: 0,
@@ -616,7 +616,7 @@ func TestInvitationUpdateToken(t *testing.T) {
invitation := invitations.Invitation{
InvitedBy: testsutil.GenerateUUID(t),
UserID: testsutil.GenerateUUID(t),
Domain: testsutil.GenerateUUID(t),
DomainID: testsutil.GenerateUUID(t),
Token: validToken,
CreatedAt: time.Now(),
}
@@ -631,7 +631,7 @@ func TestInvitationUpdateToken(t *testing.T) {
{
desc: "update invitation successfully",
invitation: invitations.Invitation{
Domain: invitation.Domain,
DomainID: invitation.DomainID,
UserID: invitation.UserID,
Token: validToken,
UpdatedAt: time.Now(),
@@ -642,17 +642,17 @@ func TestInvitationUpdateToken(t *testing.T) {
desc: "update invitation with invalid user id",
invitation: invitations.Invitation{
UserID: testsutil.GenerateUUID(t),
Domain: invitation.Domain,
DomainID: invitation.DomainID,
Token: validToken,
UpdatedAt: time.Now(),
},
err: repoerr.ErrNotFound,
},
{
desc: "update invitation with invalid domain",
desc: "update invitation with invalid domain_id",
invitation: invitations.Invitation{
UserID: invitation.UserID,
Domain: testsutil.GenerateUUID(t),
DomainID: testsutil.GenerateUUID(t),
Token: validToken,
UpdatedAt: time.Now(),
},
@@ -675,7 +675,7 @@ func TestInvitationUpdateConfirmation(t *testing.T) {
invitation := invitations.Invitation{
InvitedBy: testsutil.GenerateUUID(t),
UserID: testsutil.GenerateUUID(t),
Domain: testsutil.GenerateUUID(t),
DomainID: testsutil.GenerateUUID(t),
Token: validToken,
CreatedAt: time.Now(),
}
@@ -690,7 +690,7 @@ func TestInvitationUpdateConfirmation(t *testing.T) {
{
desc: "update invitation successfully",
invitation: invitations.Invitation{
Domain: invitation.Domain,
DomainID: invitation.DomainID,
UserID: invitation.UserID,
ConfirmedAt: time.Now(),
},
@@ -700,7 +700,7 @@ func TestInvitationUpdateConfirmation(t *testing.T) {
desc: "update invitation with invalid user id",
invitation: invitations.Invitation{
UserID: testsutil.GenerateUUID(t),
Domain: invitation.UserID,
DomainID: invitation.UserID,
ConfirmedAt: time.Now(),
},
err: repoerr.ErrNotFound,
@@ -709,7 +709,7 @@ func TestInvitationUpdateConfirmation(t *testing.T) {
desc: "update invitation with invalid domain",
invitation: invitations.Invitation{
UserID: invitation.UserID,
Domain: testsutil.GenerateUUID(t),
DomainID: testsutil.GenerateUUID(t),
ConfirmedAt: time.Now(),
},
err: repoerr.ErrNotFound,
@@ -731,7 +731,7 @@ func TestInvitationDelete(t *testing.T) {
invitation := invitations.Invitation{
InvitedBy: testsutil.GenerateUUID(t),
UserID: testsutil.GenerateUUID(t),
Domain: testsutil.GenerateUUID(t),
DomainID: testsutil.GenerateUUID(t),
Token: validToken,
CreatedAt: time.Now(),
}
@@ -746,16 +746,16 @@ func TestInvitationDelete(t *testing.T) {
{
desc: "delete invitation successfully",
invitation: invitations.Invitation{
UserID: invitation.UserID,
Domain: invitation.Domain,
UserID: invitation.UserID,
DomainID: invitation.DomainID,
},
err: nil,
},
{
desc: "delete invitation with invalid invitation id",
invitation: invitations.Invitation{
UserID: testsutil.GenerateUUID(t),
Domain: testsutil.GenerateUUID(t),
UserID: testsutil.GenerateUUID(t),
DomainID: testsutil.GenerateUUID(t),
},
err: repoerr.ErrNotFound,
},
@@ -766,7 +766,7 @@ func TestInvitationDelete(t *testing.T) {
},
}
for _, tc := range cases {
err := repo.Delete(context.Background(), tc.invitation.UserID, tc.invitation.Domain)
err := repo.Delete(context.Background(), tc.invitation.UserID, tc.invitation.DomainID)
assert.Equal(t, tc.err, err, fmt.Sprintf("%s: expected %s got %s\n", tc.desc, tc.err, err))
}
}
+10 -10
View File
@@ -39,11 +39,11 @@ func (svc *service) SendInvitation(ctx context.Context, token string, invitation
}
invitation.InvitedBy = userID
if err := svc.checkAdmin(ctx, userID, invitation.Domain); err != nil {
if err := svc.checkAdmin(ctx, userID, invitation.DomainID); err != nil {
return err
}
joinToken, err := svc.auth.Issue(ctx, &magistrala.IssueReq{UserId: userID, DomainId: &invitation.Domain, Type: uint32(auth.InvitationKey)})
joinToken, err := svc.auth.Issue(ctx, &magistrala.IssueReq{UserId: userID, DomainId: &invitation.DomainID, Type: uint32(auth.InvitationKey)})
if err != nil {
return err
}
@@ -60,12 +60,12 @@ func (svc *service) SendInvitation(ctx context.Context, token string, invitation
return svc.repo.Create(ctx, invitation)
}
func (svc *service) ViewInvitation(ctx context.Context, token, userID, domain string) (invitation Invitation, err error) {
func (svc *service) ViewInvitation(ctx context.Context, token, userID, domainID string) (invitation Invitation, err error) {
tokenUserID, err := svc.identify(ctx, token)
if err != nil {
return Invitation{}, err
}
inv, err := svc.repo.Retrieve(ctx, userID, domain)
inv, err := svc.repo.Retrieve(ctx, userID, domainID)
if err != nil {
return Invitation{}, err
}
@@ -79,7 +79,7 @@ func (svc *service) ViewInvitation(ctx context.Context, token, userID, domain st
return inv, nil
}
if err := svc.checkAdmin(ctx, tokenUserID, domain); err != nil {
if err := svc.checkAdmin(ctx, tokenUserID, domainID); err != nil {
return Invitation{}, err
}
@@ -96,8 +96,8 @@ func (svc *service) ListInvitations(ctx context.Context, token string, page Page
return svc.repo.RetrieveAll(ctx, page)
}
if page.Domain != "" {
if err := svc.checkAdmin(ctx, userID, page.Domain); err != nil {
if page.DomainID != "" {
if err := svc.checkAdmin(ctx, userID, page.DomainID); err != nil {
return InvitationPage{}, err
}
@@ -109,13 +109,13 @@ func (svc *service) ListInvitations(ctx context.Context, token string, page Page
return svc.repo.RetrieveAll(ctx, page)
}
func (svc *service) AcceptInvitation(ctx context.Context, token, domain string) error {
func (svc *service) AcceptInvitation(ctx context.Context, token, domainID string) error {
userID, err := svc.identify(ctx, token)
if err != nil {
return err
}
inv, err := svc.repo.Retrieve(ctx, userID, domain)
inv, err := svc.repo.Retrieve(ctx, userID, domainID)
if err != nil {
return err
}
@@ -125,7 +125,7 @@ func (svc *service) AcceptInvitation(ctx context.Context, token, domain string)
Relation: inv.Relation,
UserIDs: []string{userID},
}
if sdkerr := svc.sdk.AddUserToDomain(inv.Domain, req, inv.Token); sdkerr != nil {
if sdkerr := svc.sdk.AddUserToDomain(inv.DomainID, req, inv.Token); sdkerr != nil {
return sdkerr
}
+27 -27
View File
@@ -24,7 +24,7 @@ import (
var (
validInvitation = invitations.Invitation{
UserID: testsutil.GenerateUUID(&testing.T{}),
Domain: testsutil.GenerateUUID(&testing.T{}),
DomainID: testsutil.GenerateUUID(&testing.T{}),
Relation: auth.ViewerRelation,
}
validToken = "token"
@@ -132,7 +132,7 @@ func TestSendInvitation(t *testing.T) {
tokenUserID: testsutil.GenerateUUID(t),
req: invitations.Invitation{
UserID: testsutil.GenerateUUID(t),
Domain: testsutil.GenerateUUID(t),
DomainID: testsutil.GenerateUUID(t),
Relation: auth.ViewerRelation,
Resend: true,
},
@@ -154,7 +154,7 @@ func TestSendInvitation(t *testing.T) {
Subject: tc.tokenUserID,
Permission: auth.AdminPermission,
ObjectType: auth.DomainType,
Object: tc.req.Domain,
Object: tc.req.DomainID,
}
domaincall := authsvc.On("Authorize", context.Background(), &domainReq).Return(&magistrala.AuthorizeRes{Authorized: tc.authorised}, tc.domainErr)
platformReq := magistrala.AuthorizeReq{
@@ -189,7 +189,7 @@ func TestViewInvitation(t *testing.T) {
validInvitation := invitations.Invitation{
InvitedBy: testsutil.GenerateUUID(t),
UserID: testsutil.GenerateUUID(t),
Domain: testsutil.GenerateUUID(t),
DomainID: testsutil.GenerateUUID(t),
Relation: auth.ViewerRelation,
CreatedAt: time.Now().Add(-time.Hour),
UpdatedAt: time.Now().Add(-time.Hour),
@@ -214,7 +214,7 @@ func TestViewInvitation(t *testing.T) {
token: validToken,
tokenUserID: testsutil.GenerateUUID(t),
userID: validInvitation.UserID,
domainID: validInvitation.Domain,
domainID: validInvitation.DomainID,
resp: validInvitation,
err: nil,
authNErr: nil,
@@ -228,7 +228,7 @@ func TestViewInvitation(t *testing.T) {
token: authmocks.InvalidValue,
tokenUserID: "",
userID: validInvitation.UserID,
domainID: validInvitation.Domain,
domainID: validInvitation.DomainID,
resp: invitations.Invitation{},
err: svcerr.ErrAuthentication,
authNErr: svcerr.ErrAuthentication,
@@ -242,7 +242,7 @@ func TestViewInvitation(t *testing.T) {
token: validToken,
tokenUserID: testsutil.GenerateUUID(t),
userID: validInvitation.UserID,
domainID: validInvitation.Domain,
domainID: validInvitation.DomainID,
resp: invitations.Invitation{},
err: svcerr.ErrNotFound,
authNErr: nil,
@@ -256,7 +256,7 @@ func TestViewInvitation(t *testing.T) {
token: validToken,
tokenUserID: validInvitation.UserID,
userID: validInvitation.UserID,
domainID: validInvitation.Domain,
domainID: validInvitation.DomainID,
resp: validInvitation,
err: nil,
authNErr: nil,
@@ -270,7 +270,7 @@ func TestViewInvitation(t *testing.T) {
token: validToken,
tokenUserID: validInvitation.InvitedBy,
userID: validInvitation.UserID,
domainID: validInvitation.Domain,
domainID: validInvitation.DomainID,
resp: validInvitation,
err: nil,
authNErr: nil,
@@ -284,7 +284,7 @@ func TestViewInvitation(t *testing.T) {
token: validToken,
tokenUserID: testsutil.GenerateUUID(t),
userID: validInvitation.UserID,
domainID: validInvitation.Domain,
domainID: validInvitation.DomainID,
resp: validInvitation,
err: nil,
authNErr: nil,
@@ -298,7 +298,7 @@ func TestViewInvitation(t *testing.T) {
token: validToken,
tokenUserID: testsutil.GenerateUUID(t),
userID: validInvitation.UserID,
domainID: validInvitation.Domain,
domainID: validInvitation.DomainID,
resp: validInvitation,
err: nil,
authNErr: nil,
@@ -371,7 +371,7 @@ func TestListInvitations(t *testing.T) {
{
InvitedBy: testsutil.GenerateUUID(t),
UserID: testsutil.GenerateUUID(t),
Domain: testsutil.GenerateUUID(t),
DomainID: testsutil.GenerateUUID(t),
Relation: auth.ViewerRelation,
CreatedAt: time.Now().Add(-time.Hour),
UpdatedAt: time.Now().Add(-time.Hour),
@@ -434,7 +434,7 @@ func TestListInvitations(t *testing.T) {
desc: "list invitations with admin successful",
token: validToken,
tokenUserID: testsutil.GenerateUUID(t),
page: invitations.Page{Domain: testsutil.GenerateUUID(t)},
page: invitations.Page{DomainID: testsutil.GenerateUUID(t)},
resp: validResp,
err: nil,
authNErr: nil,
@@ -460,7 +460,7 @@ func TestListInvitations(t *testing.T) {
desc: "list invitations with domain successful",
token: validToken,
tokenUserID: testsutil.GenerateUUID(t),
page: invitations.Page{Domain: testsutil.GenerateUUID(t)},
page: invitations.Page{DomainID: testsutil.GenerateUUID(t)},
resp: validResp,
err: nil,
authNErr: nil,
@@ -470,10 +470,10 @@ func TestListInvitations(t *testing.T) {
repoErr: nil,
},
{
desc: "list invitations with domain and error during domain admin check",
desc: "list invitations with domain_id and error during domain admin check",
token: validToken,
tokenUserID: testsutil.GenerateUUID(t),
page: invitations.Page{Domain: testsutil.GenerateUUID(t)},
page: invitations.Page{DomainID: testsutil.GenerateUUID(t)},
err: svcerr.ErrAuthorization,
resp: invitations.InvitationPage{},
authNErr: nil,
@@ -483,10 +483,10 @@ func TestListInvitations(t *testing.T) {
repoErr: nil,
},
{
desc: "list invitations with domain and error during platform admin check",
desc: "list invitations with domain_id and error during platform admin check",
token: validToken,
tokenUserID: testsutil.GenerateUUID(t),
page: invitations.Page{Domain: testsutil.GenerateUUID(t)},
page: invitations.Page{DomainID: testsutil.GenerateUUID(t)},
err: svcerr.ErrAuthorization,
resp: invitations.InvitationPage{},
authNErr: nil,
@@ -505,7 +505,7 @@ func TestListInvitations(t *testing.T) {
Subject: tc.tokenUserID,
Permission: auth.AdminPermission,
ObjectType: auth.DomainType,
Object: tc.page.Domain,
Object: tc.page.DomainID,
}
domaincall := authsvc.On("Authorize", context.Background(), &domainReq).Return(&magistrala.AuthorizeRes{Authorized: tc.authorised}, tc.domainErr)
platformReq := magistrala.AuthorizeReq{
@@ -539,7 +539,7 @@ func TestAcceptInvitation(t *testing.T) {
desc string
token string
tokenUserID string
domain string
domainID string
resp invitations.Invitation
err error
authNErr error
@@ -563,10 +563,10 @@ func TestAcceptInvitation(t *testing.T) {
desc: "list invitations successful that have been confirmed",
token: validToken,
tokenUserID: userID,
domain: "",
domainID: "",
resp: invitations.Invitation{
UserID: userID,
Domain: testsutil.GenerateUUID(t),
DomainID: testsutil.GenerateUUID(t),
Token: validToken,
Relation: auth.ViewerRelation,
ConfirmedAt: time.Now().Add(-time.Second * time.Duration(rand.Intn(100))),
@@ -593,8 +593,8 @@ func TestAcceptInvitation(t *testing.T) {
for _, tc := range cases {
repocall := authsvc.On("Identify", context.Background(), &magistrala.IdentityReq{Token: tc.token}).Return(&magistrala.IdentityRes{UserId: tc.tokenUserID}, tc.authNErr)
repocall1 := repo.On("Retrieve", context.Background(), mock.Anything, tc.domain).Return(tc.resp, tc.repoErr)
err := svc.AcceptInvitation(context.Background(), tc.token, tc.domain)
repocall1 := repo.On("Retrieve", context.Background(), mock.Anything, tc.domainID).Return(tc.resp, tc.repoErr)
err := svc.AcceptInvitation(context.Background(), tc.token, tc.domainID)
assert.Equal(t, tc.err, err, tc.desc)
repocall.Unset()
repocall1.Unset()
@@ -652,7 +652,7 @@ func TestDeleteInvitation(t *testing.T) {
token: validToken,
tokenUserID: validInvitation.UserID,
userID: validInvitation.UserID,
domainID: validInvitation.Domain,
domainID: validInvitation.DomainID,
resp: validInvitation,
err: nil,
authNErr: nil,
@@ -666,7 +666,7 @@ func TestDeleteInvitation(t *testing.T) {
token: validToken,
tokenUserID: validInvitation.InvitedBy,
userID: validInvitation.UserID,
domainID: validInvitation.Domain,
domainID: validInvitation.DomainID,
resp: validInvitation,
err: nil,
authNErr: nil,
@@ -680,7 +680,7 @@ func TestDeleteInvitation(t *testing.T) {
token: validToken,
tokenUserID: testsutil.GenerateUUID(t),
userID: validInvitation.UserID,
domainID: validInvitation.Domain,
domainID: validInvitation.DomainID,
resp: invitations.Invitation{},
err: svcerr.ErrNotFound,
authNErr: nil,