Skip to content

Commit

Permalink
feat(notifier): add ability to move contacts and subscriptions betwee…
Browse files Browse the repository at this point in the history
…n users and teams (#1067)
  • Loading branch information
almostinf committed Aug 26, 2024
1 parent d880235 commit 02621ac
Show file tree
Hide file tree
Showing 6 changed files with 121 additions and 17 deletions.
16 changes: 12 additions & 4 deletions api/controller/contact.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,6 @@ func CreateContact(
userLogin,
teamID string,
) *api.ErrorResponse {
if userLogin != "" && teamID != "" {
return api.ErrorInternalServer(fmt.Errorf("CreateContact: cannot create contact when both userLogin and teamID specified"))
}

if !isAllowedToUseContactType(auth, userLogin, contact.Type) {
return api.ErrorInvalidRequest(ErrNotAllowedContactType)
}
Expand Down Expand Up @@ -117,12 +113,20 @@ func UpdateContact(
contactData.Type = contactDTO.Type
contactData.Value = contactDTO.Value
contactData.Name = contactDTO.Name

if contactDTO.User != "" || contactDTO.TeamID != "" {
contactData.User = contactDTO.User
contactData.Team = contactDTO.TeamID
}

if err := dataBase.SaveContact(&contactData); err != nil {
return contactDTO, api.ErrorInternalServer(err)
}

contactDTO.User = contactData.User
contactDTO.TeamID = contactData.Team
contactDTO.ID = contactData.ID

return contactDTO, nil
}

Expand Down Expand Up @@ -221,9 +225,11 @@ func CheckUserPermissionsForContact(
}
return moira.ContactData{}, api.ErrorInternalServer(err)
}

if auth.IsAdmin(userLogin) {
return contactData, nil
}

if contactData.Team != "" {
teamContainsUser, err := dataBase.IsTeamContainUser(contactData.Team, userLogin)
if err != nil {
Expand All @@ -233,9 +239,11 @@ func CheckUserPermissionsForContact(
return contactData, nil
}
}

if contactData.User == userLogin {
return contactData, nil
}

return moira.ContactData{}, api.ErrorForbidden("you are not permitted")
}

Expand Down
54 changes: 45 additions & 9 deletions api/controller/contact_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -366,15 +366,6 @@ func TestCreateContact(t *testing.T) {
})
})
})

Convey("Error on create with both: userLogin and teamID specified", t, func() {
contact := &dto.Contact{
Value: contactValue,
Type: contactType,
}
err := CreateContact(dataBase, auth, contact, userLogin, teamID)
So(err, ShouldResemble, api.ErrorInternalServer(fmt.Errorf("CreateContact: cannot create contact when both userLogin and teamID specified")))
})
}

func TestAdminsCreatesContact(t *testing.T) {
Expand Down Expand Up @@ -504,6 +495,30 @@ func TestUpdateContact(t *testing.T) {
So(expectedContact.Name, ShouldResemble, contactDTO.Name)
})

Convey("Success with rewrite user", func() {
newUser := "testUser"
contactDTO := dto.Contact{
Value: contactValue,
Name: "some-name",
Type: contactType,
User: newUser,
}
contactID := uuid.Must(uuid.NewV4()).String()
contact := moira.ContactData{
Value: contactDTO.Value,
Type: contactDTO.Type,
Name: contactDTO.Name,
ID: contactID,
User: newUser,
}
dataBase.EXPECT().SaveContact(&contact).Return(nil)
expectedContact, err := UpdateContact(dataBase, auth, contactDTO, moira.ContactData{ID: contactID, User: userLogin})
So(err, ShouldBeNil)
So(expectedContact.User, ShouldResemble, newUser)
So(expectedContact.ID, ShouldResemble, contactID)
So(expectedContact.Name, ShouldResemble, contactDTO.Name)
})

Convey("Error update not allowed contact", func() {
contactDTO := dto.Contact{
Value: contactValue,
Expand Down Expand Up @@ -587,6 +602,27 @@ func TestUpdateContact(t *testing.T) {
So(expectedContact.ID, ShouldResemble, contactID)
})

Convey("Success with rewrite team", func() {
newTeam := "testTeam"
contactDTO := dto.Contact{
Value: contactValue,
Type: contactType,
TeamID: newTeam,
}
contactID := uuid.Must(uuid.NewV4()).String()
contact := moira.ContactData{
Value: contactDTO.Value,
Type: contactDTO.Type,
ID: contactID,
Team: newTeam,
}
dataBase.EXPECT().SaveContact(&contact).Return(nil)
expectedContact, err := UpdateContact(dataBase, auth, contactDTO, moira.ContactData{ID: contactID, Team: teamID})
So(err, ShouldBeNil)
So(expectedContact.TeamID, ShouldResemble, newTeam)
So(expectedContact.ID, ShouldResemble, contactID)
})

Convey("Error save", func() {
contactDTO := dto.Contact{
Value: contactValue,
Expand Down
10 changes: 9 additions & 1 deletion api/controller/subscription.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,13 +81,16 @@ func GetSubscription(dataBase moira.Database, subscriptionID string) (*dto.Subsc
// UpdateSubscription updates existing subscription.
func UpdateSubscription(dataBase moira.Database, subscriptionID string, userLogin string, subscription *dto.Subscription) *api.ErrorResponse {
subscription.ID = subscriptionID
if subscription.TeamID == "" {
if subscription.TeamID == "" && subscription.User == "" {
subscription.User = userLogin
}

data := moira.SubscriptionData(*subscription)

if err := dataBase.SaveSubscription(&data); err != nil {
return api.ErrorInternalServer(err)
}

return nil
}

Expand Down Expand Up @@ -131,21 +134,26 @@ func CheckUserPermissionsForSubscription(
}
return moira.SubscriptionData{}, api.ErrorInternalServer(err)
}

if auth.IsAdmin(userLogin) {
return subscription, nil
}

if subscription.TeamID != "" {
teamContainsUser, err := dataBase.IsTeamContainUser(subscription.TeamID, userLogin)
if err != nil {
return moira.SubscriptionData{}, api.ErrorInternalServer(err)
}

if teamContainsUser {
return subscription, nil
}
}

if subscription.User == userLogin {
return subscription, nil
}

return moira.SubscriptionData{}, api.ErrorForbidden("you are not permitted")
}

Expand Down
3 changes: 3 additions & 0 deletions api/dto/contact.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,5 +36,8 @@ func (contact *Contact) Bind(r *http.Request) error {
if contact.Value == "" {
return fmt.Errorf("contact value of type %s can not be empty", contact.Type)
}
if contact.User != "" && contact.TeamID != "" {
return fmt.Errorf("contact cannot have both the user field and the team_id field filled in")
}
return nil
}
4 changes: 4 additions & 0 deletions api/dto/subscription.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ func (subscription *Subscription) checkContacts(request *http.Request) error {
if subscription.User != "" && teamID != "" {
return ErrSubscriptionContainsTeamAndUser{}
}

var contactIDs []string
var err error
if teamID != "" {
Expand Down Expand Up @@ -112,6 +113,7 @@ func (subscription *Subscription) checkContacts(request *http.Request) error {
if err != nil {
return ErrProvidedContactsForbidden{contactIds: subscriptionContactIDs}
}

anotherUserContactValues := make([]string, 0)
anotherUserContactIDs := make([]string, 0)
for i, contact := range contacts {
Expand All @@ -121,11 +123,13 @@ func (subscription *Subscription) checkContacts(request *http.Request) error {
anotherUserContactValues = append(anotherUserContactValues, contact.Value)
}
}

return ErrProvidedContactsForbidden{
contactNames: anotherUserContactValues,
contactIds: subscriptionContactIDs,
}
}

return nil
}

Expand Down
51 changes: 48 additions & 3 deletions api/handler/contact_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -358,8 +358,8 @@ func TestCreateNewContact(t *testing.T) {
}()

expected := &api.ErrorResponse{
StatusText: "Internal Server Error",
ErrorText: "CreateContact: cannot create contact when both userLogin and teamID specified",
StatusText: "Invalid request",
ErrorText: "contact cannot have both the user field and the team_id field filled in",
}
jsonContact, err := json.Marshal(newContactDto)
So(err, ShouldBeNil)
Expand All @@ -381,7 +381,7 @@ func TestCreateNewContact(t *testing.T) {
So(err, ShouldBeNil)

So(actual, ShouldResemble, expected)
So(response.StatusCode, ShouldEqual, http.StatusInternalServerError)
So(response.StatusCode, ShouldEqual, http.StatusBadRequest)
})
})
}
Expand Down Expand Up @@ -450,6 +450,51 @@ func TestUpdateContact(t *testing.T) {
So(response.StatusCode, ShouldEqual, http.StatusOK)
})

Convey("Failed to update a contact with the specified user and team field", func() {
updatedContactDto.TeamID = defaultTeamID
defer func() {
updatedContactDto.TeamID = ""
}()

jsonContact, err := json.Marshal(updatedContactDto)
So(err, ShouldBeNil)

testRequest := httptest.NewRequest(http.MethodPut, "/contact/"+contactID, bytes.NewBuffer(jsonContact))

testRequest = testRequest.WithContext(middleware.SetContextValueForTest(testRequest.Context(), ContactKey, moira.ContactData{
ID: contactID,
Name: updatedContactDto.Name,
Type: updatedContactDto.Type,
Value: updatedContactDto.Value,
User: updatedContactDto.User,
}))
testRequest = testRequest.WithContext(middleware.SetContextValueForTest(testRequest.Context(), AuthKey, &api.Authorization{
AllowedContactTypes: map[string]struct{}{
updatedContactDto.Type: {},
},
}))

testRequest.Header.Add("content-type", "application/json")

updateContact(responseWriter, testRequest)

expected := &api.ErrorResponse{
StatusText: "Invalid request",
ErrorText: "contact cannot have both the user field and the team_id field filled in",
}
response := responseWriter.Result()
defer response.Body.Close()
contentBytes, err := io.ReadAll(response.Body)
So(err, ShouldBeNil)
contents := string(contentBytes)
actual := &api.ErrorResponse{}
err = json.Unmarshal([]byte(contents), actual)
So(err, ShouldBeNil)

So(actual, ShouldResemble, expected)
So(response.StatusCode, ShouldEqual, http.StatusBadRequest)
})

Convey("Internal error when trying to update contact", func() {
expected := &api.ErrorResponse{
StatusText: "Internal Server Error",
Expand Down

0 comments on commit 02621ac

Please sign in to comment.