Skip to content

Feature 1361/backend to make templates private or public #1379

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
package com.objectcomputing.checkins.services.feedback.suggestions;

import com.sun.istack.Nullable;
import io.micronaut.core.annotation.Nullable;

import java.util.Objects;
import java.util.UUID;
import javax.validation.constraints.NotNull;

public class FeedbackSuggestionDTO {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,19 +55,27 @@ public class FeedbackTemplate {
@Schema(description = "whether or not the template is allowed to be used for a feedback request", required = true)
private Boolean active;

@Column(name = "is_public")
@NotBlank
@TypeDef(type = DataType.BOOLEAN)
@Schema(description = "whether the template is accessible to everyone or just the creator", required = true)
private Boolean isPublic;

/**
* Constructs a new {@link FeedbackTemplate} to save
*
* @param title The title of the template
* @param description An optional description of the template
* @param creatorId The {@link UUID} of the user who created the template
* @param isPublic Whether the template is public or private
*/
public FeedbackTemplate(String title, @Nullable String description, UUID creatorId) {
public FeedbackTemplate(String title, @Nullable String description, UUID creatorId, Boolean isPublic) {
this.id = null;
this.title = title;
this.description = description;
this.creatorId = creatorId;
this.active = true;
this.isPublic = isPublic;
}

/**
Expand Down Expand Up @@ -132,6 +140,14 @@ public void setActive(Boolean active) {
this.active = active;
}

public void setIsPublic(Boolean isPublic) {
this.isPublic = isPublic;
}

public Boolean getIsPublic() {
return isPublic;
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
Expand All @@ -142,12 +158,13 @@ public boolean equals(Object o) {
Objects.equals(description, that.description) &&
Objects.equals(creatorId, that.creatorId) &&
Objects.equals(dateCreated, that.dateCreated) &&
Objects.equals(active, that.active);
Objects.equals(active, that.active) &&
Objects.equals(isPublic, that.isPublic);
}

@Override
public int hashCode() {
return Objects.hash(id, title, description, creatorId, dateCreated, active);
return Objects.hash(id, title, description, creatorId, dateCreated, active, isPublic);
}

@Override
Expand All @@ -159,6 +176,7 @@ public String toString() {
", creatorId=" + creatorId +
", dateCreated=" + dateCreated +
", active=" + active +
", isPublic=" + isPublic +
'}';
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ public Single<HttpResponse<List<FeedbackTemplateResponseDTO>>> findByValues(@Nul
* @return {@link FeedbackTemplate}
*/
private FeedbackTemplate fromDTO(FeedbackTemplateCreateDTO dto) {
return new FeedbackTemplate(dto.getTitle(), dto.getDescription(), dto.getCreatorId());
return new FeedbackTemplate(dto.getTitle(), dto.getDescription(), dto.getCreatorId(), dto.getIsPublic());
}

/**
Expand All @@ -148,6 +148,7 @@ private FeedbackTemplateResponseDTO fromEntity(FeedbackTemplate feedbackTemplate
dto.setCreatorId(feedbackTemplate.getCreatorId());
dto.setDateCreated(feedbackTemplate.getDateCreated());
dto.setActive(feedbackTemplate.getActive());
dto.setIsPublic(feedbackTemplate.getIsPublic());
return dto;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ public class FeedbackTemplateCreateDTO {
@Schema(description = "whether or not the template is allowed to be used for a feedback request", required = true)
private Boolean active;

@NotBlank
@Schema(description = "whether the template is accessible to everyone or just the creator", required = true)
private Boolean isPublic;

public String getTitle() {
return title;
}
Expand Down Expand Up @@ -58,4 +62,12 @@ public Boolean getActive() {
public void setActive(Boolean active) {
this.active = active;
}

public void setIsPublic(Boolean isPublic) {
this.isPublic = isPublic;
}

public Boolean getIsPublic() {
return isPublic;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ public class FeedbackTemplateResponseDTO {
@Schema(description = "whether or not the template is allowed to be used for a feedback request", required = true)
private Boolean active;

@NotBlank
@Schema(description = "whether the template is accessible to everyone or just the creator", required = true)
private Boolean isPublic;

public UUID getId() {
return id;
}
Expand Down Expand Up @@ -83,4 +87,12 @@ public Boolean getActive() {
public void setActive(Boolean active) {
this.active = active;
}

public Boolean getIsPublic() {
return isPublic;
}

public void setIsPublic(Boolean isPublic) {
this.isPublic = isPublic;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@
import com.objectcomputing.checkins.services.memberprofile.currentuser.CurrentUserServices;
import com.objectcomputing.checkins.util.Util;
import io.micronaut.core.annotation.Nullable;

import javax.inject.Singleton;
import javax.validation.constraints.NotNull;
import java.util.*;
import java.util.stream.Collectors;

@Singleton
public class FeedbackTemplateServicesImpl implements FeedbackTemplateServices {
Expand Down Expand Up @@ -54,6 +54,7 @@ public FeedbackTemplate update(FeedbackTemplate feedbackTemplate) {
feedbackTemplate.setTitle(originalTemplate.get().getTitle());
feedbackTemplate.setDescription(originalTemplate.get().getDescription());
feedbackTemplate.setDateCreated(originalTemplate.get().getDateCreated());
feedbackTemplate.setIsPublic(originalTemplate.get().getIsPublic());

if (!updateIsPermitted(originalTemplate.get().getCreatorId())) {
throw new PermissionException("You are not authorized to do this operation");
Expand Down Expand Up @@ -82,23 +83,26 @@ public FeedbackTemplate getById(UUID id) {
throw new NotFoundException("No feedback template with ID " + id);
}


return feedbackTemplate.get();
}

@Override
public List<FeedbackTemplate> findByFields(@Nullable UUID creatorId, @Nullable String title) {
return feedbackTemplateRepository.searchByValues(Util.nullSafeUUIDToString(creatorId), title);
UUID currentUserId = currentUserServices.getCurrentUser().getId();
boolean isAdmin = currentUserServices.isAdmin();
List <FeedbackTemplate> allTemplates = feedbackTemplateRepository.searchByValues(Util.nullSafeUUIDToString(creatorId), title);
return allTemplates
.stream()
.filter(template -> template.getIsPublic() || isAdmin || template.getCreatorId().equals(currentUserId))
.collect(Collectors.toList());
}


public boolean updateIsPermitted(UUID creatorId) {
UUID currentUserId = currentUserServices.getCurrentUser().getId();
boolean isAdmin = currentUserServices.isAdmin();
return isAdmin || currentUserId.equals(creatorId);
}


public boolean deleteIsPermitted(UUID creatorId) {
return updateIsPermitted(creatorId);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
ALTER TABLE feedback_templates
ADD COLUMN is_public boolean;
22 changes: 16 additions & 6 deletions server/src/main/resources/db/dev/R__Load_testing_data.sql
Original file line number Diff line number Diff line change
Expand Up @@ -409,9 +409,9 @@ VALUES
('cda41eed-70ea-4d3f-a9d7-cd0c5158eb5f', '2021-01-29', '2021-02-02', '8fa673c0-ca19-4271-b759-41cb9db2e83a', PGP_SYM_ENCRYPT('Feeling pretty happy','${aeskey}'), PGP_SYM_ENCRYPT('Feeling really good','${aeskey}'));

INSERT INTO feedback_templates
(id, creator_id, title, description, date_created, updater_id, date_updated)
(id, creator_id, title, description, date_created, updater_id, date_updated, is_public, active)
VALUES
('18ef2032-c264-411e-a8e1-ddda9a714bae', '6207b3fd-042d-49aa-9e28-dcc04f537c2d', 'Q1 Feedback', 'Get feedback for quarter 1', '2021-06-06', null, null);
('18ef2032-c264-411e-a8e1-ddda9a714bae', '6207b3fd-042d-49aa-9e28-dcc04f537c2d', 'Q1 Feedback', 'Get feedback for quarter 1', '2021-06-06', null, null, true, true);

INSERT INTO template_questions
(id, question, template_id, question_number)
Expand All @@ -424,9 +424,9 @@ VALUES
('47f997ca-0045-4147-afcb-0c9ed0b44978', 'In what ways are this team member''s contributions impacting the objectives of the organization, their project, or their team?', '18ef2032-c264-411e-a8e1-ddda9a714bae', 2);

INSERT INTO feedback_templates
(id, creator_id, title, description, date_created, updater_id, date_updated)
(id, creator_id, title, description, date_created, updater_id, date_updated, is_public, active)
VALUES
('97b0a312-e5dd-46f4-a600-d8be2ad925bb', '01b7d769-9fa2-43ff-95c7-f3b950a27bf9', 'Survey 1', 'Make a survey with a few questions', '2021-05-05', null, null);
('97b0a312-e5dd-46f4-a600-d8be2ad925bb', '01b7d769-9fa2-43ff-95c7-f3b950a27bf9', 'Survey 1', 'Make a survey with a few questions', '2021-05-05', null, null, true, true);

INSERT INTO template_questions
(id, question, template_id, question_number)
Expand All @@ -439,9 +439,19 @@ VALUES
('afa7e2cb-366a-4c16-a205-c0d493b80d85', 'In what ways does this team member represent OCI''s values?', '97b0a312-e5dd-46f4-a600-d8be2ad925bb', 2);

INSERT INTO feedback_templates
(id, creator_id, title, description, date_created, updater_id, date_updated)
(id, creator_id, title, description, date_created, updater_id, date_updated, is_public, active)
VALUES
('2cb80a06-e723-482f-af9b-6b9516cabfcd', '2559a257-ae84-4076-9ed4-3820c427beeb', 'Sample Template', 'This template does not have any questions on it', '2020-04-04', null, null);
('2cb80a06-e723-482f-af9b-6b9516cabfcd', '2559a257-ae84-4076-9ed4-3820c427beeb', 'Sample Template', 'This template does not have any questions on it', '2020-04-04', null, null, true, true);

INSERT INTO feedback_templates
(id, creator_id, title, description, date_created, updater_id, date_updated, is_public, active)
VALUES
('492e4f61-c7e3-4c30-a650-7ec74f2ba545', '7a6a2d4e-e435-4ec9-94d8-f1ed7c779498', 'Private template', 'This template is private', '2020-06-07', null, null, false, true);

INSERT INTO feedback_templates
(id, creator_id, title, description, date_created, updater_id, date_updated, is_public, active)
VALUES
('c5d10880-f561-11eb-9a03-0242ac130003', '7a6a2d4e-e435-4ec9-94d8-f1ed7c779498', 'Private template 2', 'This template is private 2', '2020-06-10', null, null, false, true);

INSERT INTO feedback_requests
(id, creator_id, requestee_id, recipient_id, template_id, send_date, due_date, submit_date, status)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ FeedbackTemplateCreateDTO createDTO(FeedbackTemplate feedbackTemplate) {
dto.setTitle(feedbackTemplate.getTitle());
dto.setDescription(feedbackTemplate.getDescription());
dto.setCreatorId(feedbackTemplate.getCreatorId());
dto.setIsPublic(feedbackTemplate.getIsPublic());
return dto;
}

Expand All @@ -97,6 +98,7 @@ void assertContentEqualsEntity(FeedbackTemplate content, FeedbackTemplateRespons
assertEquals(content.getDescription(), dto.getDescription());
assertEquals(content.getCreatorId(), dto.getCreatorId());
assertEquals(content.getActive(), dto.getActive());
assertEquals(content.getIsPublic(), dto.getIsPublic());
}

void assertUnauthorized(HttpClientResponseException exception) {
Expand Down Expand Up @@ -451,6 +453,71 @@ void testGetByTitleAndCreatedByUnauthorized() {
assertUnauthorized(exception);
}

@Test
void testGetPrivateTemplateByAdmin() {
final MemberProfile admin = createADefaultMemberProfile();
final MemberProfile memberOne = createASecondDefaultMemberProfile();
createDefaultAdminRole(admin);

final FeedbackTemplate privateTemplate = createFeedbackTemplate(memberOne.getId());
privateTemplate.setIsPublic(false);
getFeedbackTemplateRepository().save(privateTemplate);
final FeedbackTemplate publicTemplate = saveAnotherDefaultFeedbackTemplate(memberOne.getId());

final HttpRequest<?> request = HttpRequest.GET("/")
.basicAuth(admin.getWorkEmail(), RoleType.Constants.ADMIN_ROLE);
final HttpResponse<List<FeedbackTemplateResponseDTO>> response = client.toBlocking()
.exchange(request, Argument.listOf(FeedbackTemplateResponseDTO.class));

assertTrue(response.getBody().isPresent());
assertEquals(HttpStatus.OK, response.getStatus());
assertEquals(2, response.getBody().get().size());
assertContentEqualsEntity(privateTemplate, response.getBody().get().get(0));
assertContentEqualsEntity(publicTemplate, response.getBody().get().get(1));
}

@Test
void testGetPrivateTemplateByCreator() {
final MemberProfile memberOne = createASecondDefaultMemberProfile();

final FeedbackTemplate privateTemplate = createFeedbackTemplate(memberOne.getId());
privateTemplate.setIsPublic(false);
getFeedbackTemplateRepository().save(privateTemplate);
final FeedbackTemplate publicTemplate = saveAnotherDefaultFeedbackTemplate(memberOne.getId());

final HttpRequest<?> request = HttpRequest.GET("/")
.basicAuth(memberOne.getWorkEmail(), RoleType.Constants.MEMBER_ROLE);
final HttpResponse<List<FeedbackTemplateResponseDTO>> response = client.toBlocking()
.exchange(request, Argument.listOf(FeedbackTemplateResponseDTO.class));

assertTrue(response.getBody().isPresent());
assertEquals(HttpStatus.OK, response.getStatus());
assertEquals(2, response.getBody().get().size());
assertContentEqualsEntity(privateTemplate, response.getBody().get().get(0));
assertContentEqualsEntity(publicTemplate, response.getBody().get().get(1));
}

@Test
void testGetPrivateTemplateNotPermitted() {
final MemberProfile memberOne = createASecondDefaultMemberProfile();
final MemberProfile random = createAnUnrelatedUser();

final FeedbackTemplate privateTemplate = createFeedbackTemplate(memberOne.getId());
privateTemplate.setIsPublic(false);
getFeedbackTemplateRepository().save(privateTemplate);
final FeedbackTemplate publicTemplate = saveAnotherDefaultFeedbackTemplate(memberOne.getId());

final HttpRequest<?> request = HttpRequest.GET("/")
.basicAuth(random.getWorkEmail(), RoleType.Constants.MEMBER_ROLE);
final HttpResponse<List<FeedbackTemplateResponseDTO>> response = client.toBlocking()
.exchange(request, Argument.listOf(FeedbackTemplateResponseDTO.class));

assertTrue(response.getBody().isPresent());
assertEquals(HttpStatus.OK, response.getStatus());
assertEquals(1, response.getBody().get().size());
assertContentEqualsEntity(publicTemplate, response.getBody().get().get(0));
}

@Test
void testDeleteValidAuthorized() {
final MemberProfile memberOne = createADefaultMemberProfile();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,18 @@
public interface FeedbackTemplateFixture extends RepositoryFixture{

default FeedbackTemplate createFeedbackTemplate(UUID creatorId) {
return new FeedbackTemplate("Fake Title", "Fake Title Description amazing feedback template", creatorId);
return new FeedbackTemplate("Fake Title", "Fake Title Description amazing feedback template", creatorId, true);
}

default FeedbackTemplate createAnotherFeedbackTemplate(UUID creatorId) {
return new FeedbackTemplate( "Fake Title 2", "Fake Title Private Description amazing feedback template 2", creatorId);
return new FeedbackTemplate( "Fake Title 2", "Fake Title Private Description amazing feedback template 2", creatorId,true);
}

default FeedbackTemplate createAThirdFeedbackTemplate(UUID creatorId) {
return new FeedbackTemplate( "Something completely different", "Fake Title Private Description amazing feedback template 3", creatorId);
return new FeedbackTemplate( "Something completely different", "Fake Title Private Description amazing feedback template 3", creatorId, true);
}

default FeedbackTemplate saveFeedbackTemplate(UUID creatorId) {
return getFeedbackTemplateRepository().save(new FeedbackTemplate("Sample Template", "A saved feedback template", creatorId));
return getFeedbackTemplateRepository().save(new FeedbackTemplate("Sample Template", "A saved feedback template", creatorId, true));
}
}
4 changes: 4 additions & 0 deletions web-ui/src/api/feedbacktemplate.js
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,10 @@ export const getFeedbackTemplateWithQuestions = async (templateId, cookie) => {
export const getAllFeedbackTemplates = async (cookie) => {
return resolve({
url: feedbackTemplateUrl,
params: {
creatorId: null,
title: null
},
responseType: "json",
headers: { "X-CSRF-Header": cookie },
});
Expand Down