Skip to content
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

#284 New Observations API Endpoint to update token label #295

Merged
merged 2 commits into from
Jun 3, 2024
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
Expand Up @@ -83,10 +83,10 @@ public ResponseEntity<ObservationDTO> updateObservation(Long studyId, Integer ob

@Override
@RequiresStudyRole({StudyRole.STUDY_ADMIN, StudyRole.STUDY_OPERATOR})
public ResponseEntity<EndpointTokenDTO> createToken(Long studyId, Integer observationId, String tokenLabel) {
if(tokenLabel.isBlank()) { return ResponseEntity.status(HttpStatus.BAD_REQUEST).build(); }
public ResponseEntity<EndpointTokenDTO> createToken(Long studyId, Integer observationId, EndpointTokenDTO token) {
if(token.getTokenLabel().isBlank()) { return ResponseEntity.status(HttpStatus.BAD_REQUEST).build(); }

Optional<EndpointToken> addedToken = integrationService.addToken(studyId, observationId, tokenLabel.replace("\"", ""));
Optional<EndpointToken> addedToken = integrationService.addToken(studyId, observationId, token.getTokenLabel());
if(addedToken.isEmpty()) {
throw new BadRequestException("Token with given label already exists for given observation");
}
Expand All @@ -98,6 +98,16 @@ public ResponseEntity<EndpointTokenDTO> createToken(Long studyId, Integer observ
);
}

@Override
@RequiresStudyRole({StudyRole.STUDY_ADMIN, StudyRole.STUDY_OPERATOR})
public ResponseEntity<EndpointTokenDTO> updateTokenLabel(Long studyId, Integer observationId, Integer tokenId, EndpointTokenDTO endpointToken) {
Optional<EndpointToken> token = integrationService.updateToken(studyId, observationId, tokenId, endpointToken.getTokenLabel());

return ResponseEntity.of(
token.map(EndpointTokenTransformer::toEndpointTokenDTO)
);
}

@Override
@RequiresStudyRole({StudyRole.STUDY_ADMIN, StudyRole.STUDY_OPERATOR})
public ResponseEntity<EndpointTokenDTO> getToken(Long studyId, Integer observationId, Integer tokenId) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,12 @@ public class IntegrationRepository {
"DELETE FROM observation_api_tokens " +
"WHERE study_id = ? AND observation_id = ? AND token_id = ?";
private static final String DELETE_ALL = "DELETE FROM observation_api_tokens";

private static final String UPDATE_TOKEN = """
UPDATE observation_api_tokens
SET token_label = :token_label
WHERE study_id = :study_id AND observation_id = :observation_id AND token_id = :token_id
RETURNING token_id, token_label, created
""";
private static final String DELETE_ALL_FOR_STUDY_ID =
"DELETE FROM observation_api_tokens " +
"WHERE study_id = ?";
Expand Down Expand Up @@ -87,6 +92,23 @@ public void deleteToken(Long studyId, Integer observationId, Integer tokenId) {
template.update(DELETE_TOKEN, studyId, observationId, tokenId);
}

public Optional<EndpointToken> updateToken(Long studyId, Integer observationId, Integer tokenId, String tokenLabel) {
try {
return Optional.ofNullable(
namedTemplate.queryForObject(UPDATE_TOKEN,
new MapSqlParameterSource()
.addValue("study_id", studyId)
.addValue("observation_id", observationId)
.addValue("token_id", tokenId)
.addValue("token_label", tokenLabel),
getHiddenTokenRowMapper()
)
);
} catch (EmptyResultDataAccessException e) {
return Optional.empty();
}
}

private static RowMapper<EndpointToken> getHiddenTokenRowMapper() {
return (rs, rowNum) -> new EndpointToken(
rs.getInt("token_id"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,11 @@ public void deleteToken(Long studyId, Integer observationId, Integer tokenId) {
repository.deleteToken(studyId, observationId, tokenId);
}

public Optional<EndpointToken> updateToken(Long studyId, Integer observationId, Integer tokenId, String tokenLabel) {
studyStateService.assertStudyNotInState(studyId, Study.Status.CLOSED);
return repository.updateToken(studyId, observationId, tokenId, tokenLabel);
}

public void alignIntegrationsWithStudyState(Study study) {
if(study.getStudyState() == Study.Status.CLOSED) {
repository.clearForStudyId(study.getStudyId());
Expand Down
35 changes: 30 additions & 5 deletions studymanager/src/main/resources/openapi/StudyManagerAPI.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -738,7 +738,7 @@ paths:
content:
application/json:
schema:
$ref: '#/components/schemas/TokenLabel'
$ref: '#/components/schemas/EndpointToken'
responses:
'201':
description: Token successfully added
Expand Down Expand Up @@ -789,6 +789,33 @@ paths:
'404':
description: not found

put:
tags:
- observations
description: Update label from observation endpoint token
operationId: updateTokenLabel
parameters:
- $ref: '#/components/parameters/StudyId'
- $ref: '#/components/parameters/ObservationId'
- $ref: '#/components/parameters/TokenId'
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/EndpointToken'
responses:
'200':
description: Token label successfully updated
content:
application/json:
schema:
$ref: '#/components/schemas/EndpointToken'

'400':
description: could not update observation
'404':
description: not found

delete:
tags:
Expand Down Expand Up @@ -1599,22 +1626,20 @@ components:
tokenId:
$ref: '#/components/schemas/Id'
tokenLabel:
$ref: '#/components/schemas/TokenLabel'
type: string
created:
type: string
format: date-time
readOnly: true
token:
type: string
readOnly: true
required:
- tokenId
- tokenLabel
- created
- token

TokenLabel:
type: string

ParticipationData:
type: object
properties:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
package io.redlink.more.studymanager.controller.studymanager;

import com.fasterxml.jackson.databind.ObjectMapper;
import io.redlink.more.studymanager.api.v1.model.EventDTO;
import io.redlink.more.studymanager.api.v1.model.EndpointTokenDTO;
import io.redlink.more.studymanager.api.v1.model.ObservationDTO;
import io.redlink.more.studymanager.api.v1.model.ObservationScheduleDTO;
import io.redlink.more.studymanager.model.*;
Expand All @@ -18,6 +18,7 @@
import io.redlink.more.studymanager.service.OAuth2AuthenticationService;
import io.redlink.more.studymanager.service.ObservationService;
import io.redlink.more.studymanager.utils.MapperUtils;
import java.time.OffsetDateTime;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
Expand Down Expand Up @@ -164,26 +165,26 @@ void testEmptySchedule() throws Exception {
@Test
@DisplayName("Add token should create and return token with id, label, timestamp and secret set, only if label is valid")
void testAddToken() throws Exception{
EndpointToken token = new EndpointToken(
EndpointTokenDTO token = new EndpointTokenDTO(
1,
"testLabel",
Instant.now(),
OffsetDateTime.now(),
"test");
when(integrationService.addToken(anyLong(), anyInt(), anyString()))
.thenAnswer(invocationOnMock -> Optional.of(new EndpointToken(
token.tokenId(),
token.getTokenId(),
invocationOnMock.getArgument(2),
token.created(),
token.token()
Instant.now(),
token.getToken()
)));
mvc.perform(post("/api/v1/studies/1/observations/1/tokens")
.content(token.tokenLabel())
.content(mapper.writeValueAsString(token))
.contentType(MediaType.APPLICATION_JSON))
.andDo(print())
.andExpect(status().isCreated())
.andExpect(jsonPath("$.tokenId").value(token.tokenId()))
.andExpect(jsonPath("$.tokenLabel").value(token.tokenLabel()))
.andExpect(jsonPath("$.token").value(token.token()))
.andExpect(jsonPath("$.tokenId").value(token.getTokenId()))
.andExpect(jsonPath("$.tokenLabel").value(token.getTokenLabel()))
.andExpect(jsonPath("$.token").value(token.getToken()))
.andExpect(jsonPath("$.created").exists());

mvc.perform(post("/api/v1/studies/1/observations/1/tokens")
Expand Down
Loading