diff --git a/src/main/java/org/icgc/argo/program_service/controller/DataCenterController.java b/src/main/java/org/icgc/argo/program_service/controller/DataCenterController.java index 12cc731a..f15b57d9 100644 --- a/src/main/java/org/icgc/argo/program_service/controller/DataCenterController.java +++ b/src/main/java/org/icgc/argo/program_service/controller/DataCenterController.java @@ -33,7 +33,6 @@ public ResponseEntity> listDataCenters( } @GetMapping(value = "/{datacenter_short_name}/programs") - public ResponseEntity listDataCenterPrograms( @Parameter(hidden = true) @RequestHeader(value = "Authorization", required = true) final String authorization, diff --git a/src/main/java/org/icgc/argo/program_service/controller/ProgramController.java b/src/main/java/org/icgc/argo/program_service/controller/ProgramController.java index 05aae0ca..b1ed440b 100644 --- a/src/main/java/org/icgc/argo/program_service/controller/ProgramController.java +++ b/src/main/java/org/icgc/argo/program_service/controller/ProgramController.java @@ -43,7 +43,8 @@ public ResponseEntity createProgram( grpc2JsonConverter.fromJson( grpc2JsonConverter.getJsonFromObject(createProgramRequestDTO), CreateProgramRequest.class); - CreateProgramResponse response = serviceFacade.createProgram(request); + CreateProgramResponse response = + serviceFacade.createProgram(request, createProgramRequestDTO.getDataCenterId()); return new ResponseEntity( grpc2JsonConverter.prepareCreateProgramResponse(response), HttpStatus.CREATED); } diff --git a/src/main/java/org/icgc/argo/program_service/exception/ExceptionHandlers.java b/src/main/java/org/icgc/argo/program_service/exception/ExceptionHandlers.java index 6ee0b7c0..a5f5ecb6 100644 --- a/src/main/java/org/icgc/argo/program_service/exception/ExceptionHandlers.java +++ b/src/main/java/org/icgc/argo/program_service/exception/ExceptionHandlers.java @@ -7,10 +7,7 @@ import javax.servlet.http.HttpServletRequest; import lombok.extern.slf4j.Slf4j; import lombok.val; -import org.icgc.argo.program_service.model.exceptions.BadRequestException; -import org.icgc.argo.program_service.model.exceptions.ForbiddenException; -import org.icgc.argo.program_service.model.exceptions.NotFoundException; -import org.icgc.argo.program_service.model.exceptions.UnauthorizedException; +import org.icgc.argo.program_service.model.exceptions.*; import org.springframework.http.HttpHeaders; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ControllerAdvice; @@ -79,4 +76,19 @@ public ResponseEntity handleBadRequestException( new HttpHeaders(), BAD_REQUEST); } + + @ExceptionHandler(RecordNotFoundException.class) + public ResponseEntity handleRecordNotFoundException( + HttpServletRequest req, RecordNotFoundException ex) { + val message = ex.getMessage(); + log.error(message); + return new ResponseEntity( + Map.of( + "message", ex.getMessage(), + "timestamp", new Date(), + "path", req.getServletPath(), + "error", BAD_REQUEST.getReasonPhrase()), + new HttpHeaders(), + BAD_REQUEST); + } } diff --git a/src/main/java/org/icgc/argo/program_service/model/dto/CreateProgramRequestDTO.java b/src/main/java/org/icgc/argo/program_service/model/dto/CreateProgramRequestDTO.java index eafec199..b7d7ea7e 100644 --- a/src/main/java/org/icgc/argo/program_service/model/dto/CreateProgramRequestDTO.java +++ b/src/main/java/org/icgc/argo/program_service/model/dto/CreateProgramRequestDTO.java @@ -1,6 +1,7 @@ package org.icgc.argo.program_service.model.dto; import java.util.List; +import java.util.UUID; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; @@ -13,5 +14,6 @@ public class CreateProgramRequestDTO { private Program program; + private UUID dataCenterId; List admins; } diff --git a/src/main/java/org/icgc/argo/program_service/model/exceptions/BadRequestException.java b/src/main/java/org/icgc/argo/program_service/model/exceptions/BadRequestException.java index f868f9e1..9958690e 100644 --- a/src/main/java/org/icgc/argo/program_service/model/exceptions/BadRequestException.java +++ b/src/main/java/org/icgc/argo/program_service/model/exceptions/BadRequestException.java @@ -1,31 +1,38 @@ /* - * Copyright (c) 2023. The Ontario Institute for Cancer Research. All rights reserved. + * Copyright (c) 2023 The Ontario Institute for Cancer Research. All rights reserved * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * This program and the accompanying materials are made available under the terms of the GNU Affero General Public License v3.0. + * You should have received a copy of the GNU Affero General Public License along with + * this program. If not, see . * - * http://www.apache.org/licenses/LICENSE-2.0 + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. * */ package org.icgc.argo.program_service.model.exceptions; -import static org.springframework.http.HttpStatus.BAD_REQUEST; - import lombok.NonNull; -import org.springframework.web.bind.annotation.ResponseStatus; -@ResponseStatus(BAD_REQUEST) public class BadRequestException extends RuntimeException { public BadRequestException(@NonNull String message) { super(message); } + + + public static void checkNotFound( + boolean expression, @NonNull String message, @NonNull Object... args) { + if (!expression) { + throw new BadRequestException(String.format(message, args)); + } + } } diff --git a/src/main/java/org/icgc/argo/program_service/model/exceptions/ForbiddenException.java b/src/main/java/org/icgc/argo/program_service/model/exceptions/ForbiddenException.java index 7e3c0c9f..fe0b423c 100644 --- a/src/main/java/org/icgc/argo/program_service/model/exceptions/ForbiddenException.java +++ b/src/main/java/org/icgc/argo/program_service/model/exceptions/ForbiddenException.java @@ -1,17 +1,20 @@ /* - * Copyright (c) 2019. The Ontario Institute for Cancer Research. All rights reserved. + * Copyright (c) 2023 The Ontario Institute for Cancer Research. All rights reserved * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * This program and the accompanying materials are made available under the terms of the GNU Affero General Public License v3.0. + * You should have received a copy of the GNU Affero General Public License along with + * this program. If not, see . * - * http://www.apache.org/licenses/LICENSE-2.0 + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. * */ diff --git a/src/main/java/org/icgc/argo/program_service/model/exceptions/ProgramRuntimeException.java b/src/main/java/org/icgc/argo/program_service/model/exceptions/ProgramRuntimeException.java new file mode 100644 index 00000000..29ff5578 --- /dev/null +++ b/src/main/java/org/icgc/argo/program_service/model/exceptions/ProgramRuntimeException.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2023 The Ontario Institute for Cancer Research. All rights reserved + * + * This program and the accompanying materials are made available under the terms of the GNU Affero General Public License v3.0. + * You should have received a copy of the GNU Affero General Public License along with + * this program. If not, see . + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + */ + +package org.icgc.argo.program_service.model.exceptions; + +import lombok.NonNull; + +public class ProgramRuntimeException extends RuntimeException { + + public ProgramRuntimeException(@NonNull String message) { + super(message); + } + + + public static void checkNotFound( + boolean expression, @NonNull String message, @NonNull Object... args) { + if (!expression) { + throw new ProgramRuntimeException(String.format(message, args)); + } + } +} diff --git a/src/main/java/org/icgc/argo/program_service/model/exceptions/RecordNotFoundException.java b/src/main/java/org/icgc/argo/program_service/model/exceptions/RecordNotFoundException.java new file mode 100644 index 00000000..8d61f462 --- /dev/null +++ b/src/main/java/org/icgc/argo/program_service/model/exceptions/RecordNotFoundException.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2020 The Ontario Institute for Cancer Research. All rights reserved + * + * This program and the accompanying materials are made available under the terms of the GNU Affero General Public License v3.0. + * You should have received a copy of the GNU Affero General Public License along with + * this program. If not, see . + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + */ + +package org.icgc.argo.program_service.model.exceptions; + +import lombok.NonNull; + +public class RecordNotFoundException extends RuntimeException { + + public RecordNotFoundException(@NonNull String message) { + super(message); + } + + public static void checkNotFound( + boolean expression, @NonNull String message, @NonNull Object... args) { + if (!expression) { + throw new RecordNotFoundException(String.format(message, args)); + } + } +} diff --git a/src/main/java/org/icgc/argo/program_service/model/exceptions/UnauthorizedException.java b/src/main/java/org/icgc/argo/program_service/model/exceptions/UnauthorizedException.java index 96afa776..75a79dea 100644 --- a/src/main/java/org/icgc/argo/program_service/model/exceptions/UnauthorizedException.java +++ b/src/main/java/org/icgc/argo/program_service/model/exceptions/UnauthorizedException.java @@ -1,17 +1,20 @@ /* - * Copyright (c) 2019. The Ontario Institute for Cancer Research. All rights reserved. + * Copyright (c) 2023 The Ontario Institute for Cancer Research. All rights reserved * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * This program and the accompanying materials are made available under the terms of the GNU Affero General Public License v3.0. + * You should have received a copy of the GNU Affero General Public License along with + * this program. If not, see . * - * http://www.apache.org/licenses/LICENSE-2.0 + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. * */ diff --git a/src/main/java/org/icgc/argo/program_service/security/JWTAuthorizationFilter.java b/src/main/java/org/icgc/argo/program_service/security/JWTAuthorizationFilter.java index d1233a5a..2d6811dc 100644 --- a/src/main/java/org/icgc/argo/program_service/security/JWTAuthorizationFilter.java +++ b/src/main/java/org/icgc/argo/program_service/security/JWTAuthorizationFilter.java @@ -1,17 +1,21 @@ /* - * Copyright (c) 2017. The Ontario Institute for Cancer Research. All rights reserved. + * Copyright (c) 2023 The Ontario Institute for Cancer Research. All rights reserved * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * This program and the accompanying materials are made available under the terms of the GNU Affero General Public License v3.0. + * You should have received a copy of the GNU Affero General Public License along with + * this program. If not, see . + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * - * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. */ package org.icgc.argo.program_service.security; diff --git a/src/main/java/org/icgc/argo/program_service/services/ProgramService.java b/src/main/java/org/icgc/argo/program_service/services/ProgramService.java index 5f08187d..c951494a 100644 --- a/src/main/java/org/icgc/argo/program_service/services/ProgramService.java +++ b/src/main/java/org/icgc/argo/program_service/services/ProgramService.java @@ -36,17 +36,23 @@ import java.util.List; import java.util.Optional; import java.util.Set; +import java.util.UUID; import java.util.function.Consumer; import java.util.function.Predicate; import javax.validation.ValidatorFactory; + +import io.netty.util.internal.StringUtil; import lombok.NonNull; import lombok.extern.slf4j.Slf4j; import lombok.val; +import org.apache.commons.lang.StringUtils; import org.icgc.argo.program_service.converter.DataCenterConverter; import org.icgc.argo.program_service.converter.ProgramConverter; import org.icgc.argo.program_service.model.dto.DataCenterRequestDTO; import org.icgc.argo.program_service.model.entity.*; +import org.icgc.argo.program_service.model.exceptions.BadRequestException; import org.icgc.argo.program_service.model.exceptions.NotFoundException; +import org.icgc.argo.program_service.model.exceptions.RecordNotFoundException; import org.icgc.argo.program_service.model.join.*; import org.icgc.argo.program_service.proto.Program; import org.icgc.argo.program_service.repositories.*; @@ -159,16 +165,63 @@ public ProgramEntity getProgram(@NonNull String name, boolean allowInactive) { } public ProgramEntity createWithSideEffect( - @NonNull Program program, Consumer consumer) { + @NonNull Program program, Consumer consumer) { val programEntity = createProgram(program); consumer.accept(programEntity); return programEntity; } + public ProgramEntity createWithSideEffect( + @NonNull Program program, Consumer consumer, UUID dataCenterId) { + val programEntity = createProgram(program, dataCenterId); + consumer.accept(programEntity); + return programEntity; + } public ProgramEntity createProgram(@NonNull Program program) + throws DataIntegrityViolationException { + val programEntity = programConverter.programToProgramEntity(program); + + val now = LocalDateTime.now(ZoneId.of("UTC")); + programEntity.setCreatedAt(now); + programEntity.setUpdatedAt(now); + + val p = programRepository.save(programEntity); + val cancers = cancerRepository.findAllByNameIn(program.getCancerTypesList()); + val primarySites = primarySiteRepository.findAllByNameIn(program.getPrimarySitesList()); + val countries = countryRepository.findAllByNameIn(program.getCountriesList()); + List institutions = + institutionRepository.findAllByNameIn(program.getInstitutionsList()); + if (institutions.size() != program.getInstitutionsList().size()) { + institutions = filterAndAddInstitutions(program.getInstitutionsList()); + } + + List programCancers = mapToList(cancers, x -> createProgramCancer(p, x).get()); + List programPrimarySites = + mapToList(primarySites, x -> createProgramPrimarySite(p, x).get()); + List programInstitutions = + mapToList(institutions, x -> createProgramInstitution(p, x).get()); + List programCountries = + mapToList(countries, x -> createProgramCountry(p, x).get()); + + programCancerRepository.saveAll(programCancers); + programPrimarySiteRepository.saveAll(programPrimarySites); + programInstitutionRepository.saveAll(programInstitutions); + programCountryRepository.saveAll(programCountries); + + return programEntity; + } + public ProgramEntity createProgram(@NonNull Program program, UUID dataCenterId) throws DataIntegrityViolationException { val programEntity = programConverter.programToProgramEntity(program); + if (dataCenterId == null) { + throw new BadRequestException("DataCenterId cannot be null or empty"); + } + val dataCenterEntity = dataCenterRepository.findById(dataCenterId); + if (dataCenterEntity.isEmpty()) { + throw new RecordNotFoundException("DataCenterId '" + dataCenterId + "' not found"); + } + programEntity.setDataCenterId(dataCenterId); val now = LocalDateTime.now(ZoneId.of("UTC")); programEntity.setCreatedAt(now); programEntity.setUpdatedAt(now); diff --git a/src/main/java/org/icgc/argo/program_service/services/ProgramServiceFacade.java b/src/main/java/org/icgc/argo/program_service/services/ProgramServiceFacade.java index b981ff7d..c13142ff 100644 --- a/src/main/java/org/icgc/argo/program_service/services/ProgramServiceFacade.java +++ b/src/main/java/org/icgc/argo/program_service/services/ProgramServiceFacade.java @@ -111,6 +111,32 @@ public CreateProgramResponse createProgram(CreateProgramRequest request) { return programConverter.programEntityToCreateProgramResponse(programEntity); } + @Transactional + public CreateProgramResponse createProgram(CreateProgramRequest request, UUID dataCenterId) { + val errors = validationService.validateCreateProgramRequest(request); + if (errors.size() != 0) { + throw Status.INVALID_ARGUMENT + .augmentDescription( + format("Cannot create program: Program errors are [%s]", join(errors, ","))) + .asRuntimeException(); + } + + val program = request.getProgram(); + val admins = request.getAdminsList(); + + // TODO: Refactor this, having a transactional side effect is no longer needed thanks to the + // facade + val programEntity = + programService.createWithSideEffect( + program, + (ProgramEntity pe) -> { + initializeProgramInEgo(pe, admins); + }, + dataCenterId); + log.debug("Created {}", programEntity.getShortName()); + return programConverter.programEntityToCreateProgramResponse(programEntity); + } + public GetProgramResponse getProgram(GetProgramRequest request) { val shortName = request.getShortName().getValue(); val programEntity = programService.getProgram(shortName);