Skip to content

Commit

Permalink
add updateSpecimenType mutation (#7017)
Browse files Browse the repository at this point in the history
* get initial wiring added

* add update type and endpoint

* add tests and move some error handling around

* lint codegen file

* update comment and switch error handling order

* update error message

* typo

* typo v2

* fix code smells
  • Loading branch information
fzhao99 authored Dec 4, 2023
1 parent c25256c commit e3cee0a
Show file tree
Hide file tree
Showing 9 changed files with 176 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package gov.cdc.usds.simplereport.api.devicetype;

import gov.cdc.usds.simplereport.api.model.CreateSpecimenType;
import gov.cdc.usds.simplereport.api.model.UpdateSpecimenType;
import gov.cdc.usds.simplereport.api.model.errors.IllegalGraphqlArgumentException;
import gov.cdc.usds.simplereport.db.model.SpecimenType;
import gov.cdc.usds.simplereport.service.SpecimenTypeService;
Expand All @@ -20,4 +21,10 @@ public SpecimenType createSpecimenType(@Argument CreateSpecimenType input)
throws IllegalGraphqlArgumentException {
return specimenTypeService.createSpecimenType(input);
}

@MutationMapping
public SpecimenType updateSpecimenType(@Argument UpdateSpecimenType input)
throws IllegalGraphqlArgumentException {
return specimenTypeService.updateSpecimenType(input);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package gov.cdc.usds.simplereport.api.model;

import lombok.Builder;
import lombok.Getter;

@Getter
@Builder
public class UpdateSpecimenType {
private final String name;
private final String typeCode;
private final String collectionLocationName;
private final String collectionLocationCode;

// can potentially add isDeleted here if needed in the future
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package gov.cdc.usds.simplereport.api.model.errors;

import graphql.ErrorClassification;
import graphql.ErrorType;
import graphql.GraphQLError;
import graphql.language.SourceLocation;
import java.util.Collections;
import java.util.List;

/** Exception to throw when a specimen type code can't be found */
public class UnidentifiedSpecimenTypeException extends RuntimeException implements GraphQLError {

private static final long serialVersionUID = 1L;

public UnidentifiedSpecimenTypeException(String typeCode) {
super("Specimen with type code " + typeCode + " wasn't found. Make sure that type code exists");
}

@Override // should-be-defaulted unused interface method
public List<SourceLocation> getLocations() {
return Collections.emptyList();
}

@Override
public ErrorClassification getErrorType() {
return ErrorType.ExecutionAborted;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import gov.cdc.usds.simplereport.api.model.errors.RestrictedAccessUserException;
import gov.cdc.usds.simplereport.api.model.errors.TestEventSerializationFailureException;
import gov.cdc.usds.simplereport.api.model.errors.UnidentifiedFacilityException;
import gov.cdc.usds.simplereport.api.model.errors.UnidentifiedSpecimenTypeException;
import gov.cdc.usds.simplereport.config.scalars.datetime.DateTimeScalar;
import gov.cdc.usds.simplereport.config.scalars.localdate.LocalDateScalar;
import graphql.validation.rules.OnValidationErrorStrategy;
Expand Down Expand Up @@ -120,6 +121,12 @@ public DataFetcherExceptionResolver dataFetcherExceptionResolver() {
return Mono.just(singletonList(new GenericGraphqlException(errorMessage, errorPath)));
}

if (exception instanceof UnidentifiedSpecimenTypeException) {
String errorMessage =
"header: Error updating specimen type details; body: " + exception.getMessage();
return Mono.just(singletonList(new GenericGraphqlException(errorMessage, errorPath)));
}

return Mono.just(singletonList(new GenericGraphqlException((errorPath))));
};
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,25 @@
package gov.cdc.usds.simplereport.service;

import gov.cdc.usds.simplereport.api.model.CreateSpecimenType;
import gov.cdc.usds.simplereport.api.model.UpdateSpecimenType;
import gov.cdc.usds.simplereport.api.model.errors.IllegalGraphqlArgumentException;
import gov.cdc.usds.simplereport.api.model.errors.UnidentifiedSpecimenTypeException;
import gov.cdc.usds.simplereport.config.AuthorizationConfiguration;
import gov.cdc.usds.simplereport.db.model.SpecimenType;
import gov.cdc.usds.simplereport.db.repository.SpecimenTypeRepository;
import java.util.List;
import java.util.UUID;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@Slf4j
public class SpecimenTypeService {
private SpecimenTypeRepository _specimenTypeRepo;

private static final String NUMERIC_REGEX = "^[0-9]*$";

public SpecimenTypeService(SpecimenTypeRepository specimenTypeRepo) {
_specimenTypeRepo = specimenTypeRepo;
}
Expand All @@ -33,4 +41,28 @@ public SpecimenType createSpecimenType(CreateSpecimenType input) {
input.getCollectionLocationName(),
input.getCollectionLocationCode()));
}

@Transactional(readOnly = false)
@AuthorizationConfiguration.RequireGlobalAdminUser
public SpecimenType updateSpecimenType(UpdateSpecimenType input) {
boolean collectionCodeValid =
input.getCollectionLocationCode() == null
|| input.getCollectionLocationCode().matches(NUMERIC_REGEX);
if (!collectionCodeValid) {
throw new IllegalGraphqlArgumentException(
"If specified, collection location code needs to be a numeric string");
}

String typeCodeToMatch = input.getTypeCode();
SpecimenType specimenToUpdate =
_specimenTypeRepo
.findByTypeCode(typeCodeToMatch)
.orElseThrow(() -> new UnidentifiedSpecimenTypeException(typeCodeToMatch));

specimenToUpdate.setName(input.getName());
specimenToUpdate.setCollectionLocationCode(input.getCollectionLocationCode());
specimenToUpdate.setCollectionLocationName(input.getCollectionLocationName());

return _specimenTypeRepo.save(specimenToUpdate);
}
}
1 change: 1 addition & 0 deletions backend/src/main/resources/graphql/admin.graphqls
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ extend type Mutation {
createDeviceType(input: CreateDeviceType!): DeviceType
updateDeviceType(input: UpdateDeviceType!): DeviceType
createSpecimenType(input: CreateSpecimenType!): SpecimenType
updateSpecimenType(input: UpdateSpecimenType!): SpecimenType
addUser( # used in AddOrganizationAdminFormContainer
user: UserInput!
): User
Expand Down
7 changes: 7 additions & 0 deletions backend/src/main/resources/graphql/main.graphqls
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,13 @@ input CreateSpecimenType {
collectionLocationCode: String
}

input UpdateSpecimenType {
name: String!
typeCode: String!
collectionLocationName: String
collectionLocationCode: String
}

type SpecimenType {
internalId: ID!
name: String!
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
import static org.junit.jupiter.api.Assertions.assertThrows;

import gov.cdc.usds.simplereport.api.model.CreateSpecimenType;
import gov.cdc.usds.simplereport.api.model.UpdateSpecimenType;
import gov.cdc.usds.simplereport.api.model.errors.IllegalGraphqlArgumentException;
import gov.cdc.usds.simplereport.api.model.errors.UnidentifiedSpecimenTypeException;
import gov.cdc.usds.simplereport.db.repository.SpecimenTypeRepository;
import gov.cdc.usds.simplereport.test_util.SliceTestConfiguration;
import org.junit.jupiter.api.Test;
Expand Down Expand Up @@ -68,4 +71,68 @@ void createNewSpecimenType_failsWithTooShortLoinc() {
});
assert (exception.getMessage()).contains("Could not commit JPA transaction");
}

@Test
@SliceTestConfiguration.WithSimpleReportSiteAdminUser
void updateNewSpecimenType_success() {
_service.createSpecimenType(
CreateSpecimenType.builder()
.name("Nasal swab")
.typeCode("012345678")
.collectionLocationName("Nasopharangyal Structure")
.collectionLocationCode("123456789")
.build());

_service.updateSpecimenType(
UpdateSpecimenType.builder()
.build()
.builder()
.name("Nasal swab with updates")
.typeCode("012345678")
.collectionLocationName("Nasopharangyal Structure with updates")
.collectionLocationCode("123456788")
.build());

assertEquals(1, _service.fetchSpecimenTypes().size());
assertEquals("Nasal swab with updates", _service.fetchSpecimenTypes().get(0).getName());
assertEquals(
"Nasopharangyal Structure with updates",
_service.fetchSpecimenTypes().get(0).getCollectionLocationName());
assertEquals("123456788", _service.fetchSpecimenTypes().get(0).getCollectionLocationCode());
}

@Test
@SliceTestConfiguration.WithSimpleReportSiteAdminUser
void updateNewSpecimenType_throws_UnidentifiedSpecimenTypeError_withBadTypeCode() {
UpdateSpecimenType updateSpecimen = UpdateSpecimenType.builder().typeCode("012345678").build();
assertThrows(
UnidentifiedSpecimenTypeException.class,
() -> {
_service.updateSpecimenType(updateSpecimen);
});
}

@Test
@SliceTestConfiguration.WithSimpleReportSiteAdminUser
void
updateNewSpecimenType_throws_IllegalGraphQlArgumentError_withNonNumericCollectionLocationCode() {
_service.createSpecimenType(
CreateSpecimenType.builder()
.name("Nasal swab")
.typeCode("012345678")
.collectionLocationName("Nasopharangyal Structure")
.collectionLocationCode("123456789")
.build());

UpdateSpecimenType updateSpecimen =
UpdateSpecimenType.builder()
.typeCode("012345678")
.collectionLocationCode("some non numeric string")
.build();
assertThrows(
IllegalGraphqlArgumentException.class,
() -> {
_service.updateSpecimenType(updateSpecimen);
});
}
}
12 changes: 12 additions & 0 deletions frontend/src/generated/graphql.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,7 @@ export type Mutation = {
updateOrganization?: Maybe<Scalars["String"]["output"]>;
updatePatient?: Maybe<Patient>;
updateRegistrationLink?: Maybe<Scalars["String"]["output"]>;
updateSpecimenType?: Maybe<SpecimenType>;
updateTimeOfTestQuestions?: Maybe<Scalars["String"]["output"]>;
updateUser?: Maybe<User>;
updateUserEmail?: Maybe<User>;
Expand Down Expand Up @@ -506,6 +507,10 @@ export type MutationUpdateRegistrationLinkArgs = {
newLink: Scalars["String"]["input"];
};

export type MutationUpdateSpecimenTypeArgs = {
input: UpdateSpecimenType;
};

export type MutationUpdateTimeOfTestQuestionsArgs = {
genderOfSexualPartners?: InputMaybe<
Array<InputMaybe<Scalars["String"]["input"]>>
Expand Down Expand Up @@ -1034,6 +1039,13 @@ export type UpdateFacilityInput = {
phone?: InputMaybe<Scalars["String"]["input"]>;
};

export type UpdateSpecimenType = {
collectionLocationCode?: InputMaybe<Scalars["String"]["input"]>;
collectionLocationName?: InputMaybe<Scalars["String"]["input"]>;
name: Scalars["String"]["input"];
typeCode: Scalars["String"]["input"];
};

export type UploadResponse = {
__typename?: "UploadResponse";
createdAt: Scalars["DateTime"]["output"];
Expand Down

0 comments on commit e3cee0a

Please sign in to comment.