From 72ada25ebf18431d9e0d2ffe39ef0c1fbfd08556 Mon Sep 17 00:00:00 2001 From: pierrehenri-dauvergne Date: Tue, 23 Apr 2024 12:03:12 +0200 Subject: [PATCH 01/80] shanoir-issue#2199: add studycard policy to study details + save in database --- .../src/app/studies/shared/study.dto.ts | 3 + .../src/app/studies/shared/study.model.ts | 1 + .../app/studies/study/study.component.html | 100 +++++++++--------- .../src/app/studies/study/study.component.ts | 5 + .../org/shanoir/ng/study/dto/StudyDTO.java | 12 ++- .../org/shanoir/ng/study/model/Study.java | 10 ++ .../ng/study/model/StudyCardPolicy.java | 58 ++++++++++ .../ng/study/service/StudyServiceImpl.java | 49 ++++----- 8 files changed, 163 insertions(+), 75 deletions(-) create mode 100644 shanoir-ng-studies/src/main/java/org/shanoir/ng/study/model/StudyCardPolicy.java diff --git a/shanoir-ng-front/src/app/studies/shared/study.dto.ts b/shanoir-ng-front/src/app/studies/shared/study.dto.ts index ff96825417..183b92f8ba 100644 --- a/shanoir-ng-front/src/app/studies/shared/study.dto.ts +++ b/shanoir-ng-front/src/app/studies/shared/study.dto.ts @@ -138,6 +138,7 @@ export class StudyDTOService { //entity.timepoints = dto.timepoints; entity.visibleByDefault = dto.visibleByDefault; entity.withExamination = dto.withExamination; + entity.studyCardPolicy = dto.studyCardPolicy; if (dto.studyUserList) { entity.nbMembers = dto.studyUserList.length; } @@ -287,6 +288,7 @@ export class StudyDTO { //timepoints: Timepoint[]; visibleByDefault: boolean; withExamination: boolean; + studyCardPolicy: string; tags: Tag[]; studyTags: Tag[]; studyCards: StudyCardDTO[]; @@ -325,6 +327,7 @@ export class StudyDTO { return dto; }) : null; this.visibleByDefault = study.visibleByDefault; + this.studyCardPolicy = study.studyCardPolicy; this.withExamination = study.withExamination; this.tags = study.tags; this.studyTags = study.studyTags; diff --git a/shanoir-ng-front/src/app/studies/shared/study.model.ts b/shanoir-ng-front/src/app/studies/shared/study.model.ts index 80b4379f5b..e6147b88da 100644 --- a/shanoir-ng-front/src/app/studies/shared/study.model.ts +++ b/shanoir-ng-front/src/app/studies/shared/study.model.ts @@ -51,6 +51,7 @@ export class Study extends Entity { timepoints: Timepoint[]; visibleByDefault: boolean = false; withExamination: boolean; + studyCardPolicy: string = "OPTIONAL"; studyCardList: StudyCard[]; tags: Tag[]; studyTags: Tag[]; diff --git a/shanoir-ng-front/src/app/studies/study/study.component.html b/shanoir-ng-front/src/app/studies/study/study.component.html index 744ddf5fe9..0eebef43a8 100644 --- a/shanoir-ng-front/src/app/studies/study/study.component.html +++ b/shanoir-ng-front/src/app/studies/study/study.component.html @@ -147,10 +147,25 @@

Profile is required! - -
  • - - +
  • +
  • + + + + + {{studyCardPolicyStr()}} + + + + Optional + Mandatory + Disabled + + +
  • +
  • + +
    @@ -202,17 +217,17 @@

    - -
  • - - - - - - - - - +
  • +
  • + + + + + + + + +
  • @@ -254,15 +269,12 @@

    - - - - -
    - Access level -
      -
    1. - - +
    2. +
    +
      +
    1. + + @@ -271,32 +283,20 @@

      -

    2. -
    3. - - - - - - - - - - -
    4. -
    -
    -
    - Subject tags - - -
    -
    - List of centers -
      -
    1. - - +
    2. +
    +
    +
    + Subject tags + + +
    +
    + List of centers +
      +
    1. + + Single center Multicenter diff --git a/shanoir-ng-front/src/app/studies/study/study.component.ts b/shanoir-ng-front/src/app/studies/study/study.component.ts index 720ce221a9..0a0ba326f6 100644 --- a/shanoir-ng-front/src/app/studies/study/study.component.ts +++ b/shanoir-ng-front/src/app/studies/study/study.component.ts @@ -235,6 +235,7 @@ export class StudyComponent extends EntityComponent { 'studyStatus': [this.study.studyStatus, [Validators.required]], 'profile': [this.study.profile, [Validators.required]], 'withExamination': [this.study.withExamination], + 'studyCardPolicy': [this.study.studyCardPolicy], 'clinical': [this.study.clinical], 'description': [this.study.description], 'license': [this.study.license], @@ -668,4 +669,8 @@ export class StudyComponent extends EntityComponent { storageVolumePrettyPrint(size: number) { return this.studyService.storageVolumePrettyPrint(size); } + + studyCardPolicyStr() { + return capitalsAndUnderscoresToDisplayable(this.study.studyCardPolicy); + } } diff --git a/shanoir-ng-studies/src/main/java/org/shanoir/ng/study/dto/StudyDTO.java b/shanoir-ng-studies/src/main/java/org/shanoir/ng/study/dto/StudyDTO.java index 0a533a6ee3..afc4046bf6 100644 --- a/shanoir-ng-studies/src/main/java/org/shanoir/ng/study/dto/StudyDTO.java +++ b/shanoir-ng-studies/src/main/java/org/shanoir/ng/study/dto/StudyDTO.java @@ -83,7 +83,9 @@ public class StudyDTO { private boolean visibleByDefault; private boolean withExamination; - + + private String studyCardPolicy; + private List studyUserList; private boolean challenge; @@ -401,6 +403,14 @@ public void setWithExamination(boolean withExamination) { this.withExamination = withExamination; } + public String getStudyCardPolicy() { + return studyCardPolicy; + } + + public void setStudyCardPolicy(String studyCardPolicy) { + this.studyCardPolicy = studyCardPolicy; + } + /** * @return the studyUserList */ diff --git a/shanoir-ng-studies/src/main/java/org/shanoir/ng/study/model/Study.java b/shanoir-ng-studies/src/main/java/org/shanoir/ng/study/model/Study.java index 35133ac1df..3c7a772de5 100644 --- a/shanoir-ng-studies/src/main/java/org/shanoir/ng/study/model/Study.java +++ b/shanoir-ng-studies/src/main/java/org/shanoir/ng/study/model/Study.java @@ -148,6 +148,8 @@ public class Study extends HalEntity { /** Is with examination. */ private boolean withExamination; + private StudyCardPolicy studyCardPolicy; + private boolean challenge; @OneToMany(fetch = FetchType.LAZY, mappedBy = "study", cascade = CascadeType.ALL, orphanRemoval = true) @@ -480,6 +482,14 @@ public void setWithExamination(boolean withExamination) { this.withExamination = withExamination; } + public StudyCardPolicy getStudyCardPolicy() { + return studyCardPolicy; + } + + public void setStudyCardPolicy(StudyCardPolicy studyCardPolicy) { + this.studyCardPolicy = studyCardPolicy; + } + /** * Is the study a challenge. * @return the challenge diff --git a/shanoir-ng-studies/src/main/java/org/shanoir/ng/study/model/StudyCardPolicy.java b/shanoir-ng-studies/src/main/java/org/shanoir/ng/study/model/StudyCardPolicy.java new file mode 100644 index 0000000000..1d011a0a92 --- /dev/null +++ b/shanoir-ng-studies/src/main/java/org/shanoir/ng/study/model/StudyCardPolicy.java @@ -0,0 +1,58 @@ +package org.shanoir.ng.study.model; + +public enum StudyCardPolicy { + + /** + * Study card is optional during import + */ + OPTIONAL(1), + + /** + * Study card is mandatory during import + */ + MANDATORY(2), + + /** + * Study card is disabled during import + */ + DISABLED(3); + + private int id; + + /** + * Constructor. + * + * @param id + * id + */ + private StudyCardPolicy(final int id) { + this.id = id; + } + + /** + * Get the study card policy type by its id. + * + * @param id + * type id. + * @return dataset modality type. + */ + public static StudyCardPolicy getType(final Integer id) { + if (id == null) { + return null; + } + for (StudyCardPolicy type : StudyCardPolicy.values()) { + if (id.equals(type.getId())) { + return type; + } + } + throw new IllegalArgumentException("No matching study card policy type for id " + id); + } + + /** + * @return the id + */ + public int getId() { + return id; + } + +} diff --git a/shanoir-ng-studies/src/main/java/org/shanoir/ng/study/service/StudyServiceImpl.java b/shanoir-ng-studies/src/main/java/org/shanoir/ng/study/service/StudyServiceImpl.java index c7c0d0a75a..3367460371 100644 --- a/shanoir-ng-studies/src/main/java/org/shanoir/ng/study/service/StudyServiceImpl.java +++ b/shanoir-ng-studies/src/main/java/org/shanoir/ng/study/service/StudyServiceImpl.java @@ -2,12 +2,12 @@ * Shanoir NG - Import, manage and share neuroimaging data * Copyright (C) 2009-2019 Inria - https://www.inria.fr/ * Contact us on https://project.inria.fr/shanoir/ - * + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * You should have received a copy of the GNU General Public License * along with this program. If not, see https://www.gnu.org/licenses/gpl-3.0.html */ @@ -73,7 +73,7 @@ /** * Implementation of study service. - * + * * @author msimon * @author mkain * @@ -266,6 +266,7 @@ public Study update(Study study) throws EntityNotFoundException, MicroServiceCom studyDb.setLicense(study.getLicense()); studyDb.setStudyStatus(study.getStudyStatus()); studyDb.setVisibleByDefault(study.isVisibleByDefault()); + studyDb.setStudyCardPolicy(study.getStudyCardPolicy()); studyDb.setWithExamination(study.isWithExamination()); studyDb.setMonoCenter(study.isMonoCenter()); @@ -343,7 +344,7 @@ public Study update(Study study) throws EntityNotFoundException, MicroServiceCom } studyDb = studyRepository.save(studyDb); } - + // Actually delete subjects for (Subject subjectToDelete : toBeDeleted) { subjectService.deleteById(subjectToDelete.getId()); @@ -366,7 +367,7 @@ public Study update(Study study) throws EntityNotFoundException, MicroServiceCom /** * For each subject study tag of study, set the fresh tag id by looking into studyDb tags, * then update db subject study tags lists with the given study - * + * * @param subjectStudyList * @param dbStudyTags * @return updated study @@ -377,19 +378,19 @@ private void updateTags(List subjectStudyList, List dbStudyTa if (subjectStudy.getTags() != null) { for (Tag tag : subjectStudy.getTags()) { if (tag.getId() == null) { - Tag dbTag = dbStudyTags.stream().filter(upTag -> - upTag.getColor().equals(tag.getColor()) && upTag.getName().equals(tag.getName()) - ).findFirst().orElse(null); + Tag dbTag = dbStudyTags.stream().filter(upTag -> + upTag.getColor().equals(tag.getColor()) && upTag.getName().equals(tag.getName()) + ).findFirst().orElse(null); if (dbTag != null) { - tag.setId(dbTag.getId()); + tag.setId(dbTag.getId()); } else { throw new IllegalStateException("Cannot link a new tag to a subject-study, this tag does not exist in the study"); } } } } - } - } + } + } } private List getTagsToDelete(Study study, Study studyDb) { @@ -428,7 +429,7 @@ private List getStudyTagsToDelete(Study study, Study studyDb) { /** * Gets the protocol or data user agreement file path - * + * * @param studyId id of the study * @param fileName name of the file * @return the file path of the file @@ -464,12 +465,12 @@ protected void updateStudyUsers(Study studyDb, Study study) { for (StudyUser su : studyDb.getStudyUserList()) { existing.put(su.getId(), su); } - + boolean addNewDua = CollectionUtils.isEmpty(studyDb.getDataUserAgreementPaths()) && !CollectionUtils.isEmpty(study.getDataUserAgreementPaths()); boolean deleteDua = !CollectionUtils.isEmpty(studyDb.getDataUserAgreementPaths()) && CollectionUtils.isEmpty(study.getDataUserAgreementPaths()); - boolean updateDua = !CollectionUtils.isEmpty(studyDb.getDataUserAgreementPaths()) - && !CollectionUtils.isEmpty(study.getDataUserAgreementPaths()) - && !study.getDataUserAgreementPaths().get(0).equals(studyDb.getDataUserAgreementPaths().get(0)); + boolean updateDua = !CollectionUtils.isEmpty(studyDb.getDataUserAgreementPaths()) + && !CollectionUtils.isEmpty(study.getDataUserAgreementPaths()) + && !study.getDataUserAgreementPaths().get(0).equals(studyDb.getDataUserAgreementPaths().get(0)); Map replacing = new HashMap<>(); for (StudyUser su : study.getStudyUserList()) { @@ -583,7 +584,7 @@ private void archiveDuaFile(Study study) { File archiveFile = new File(this.getStudyFilePath(study.getId(), "archive_" + formatter.format(new Date()) + "_" + study.getDataUserAgreementPaths().get(0))); deletedFile.renameTo(archiveFile); } - + private void sendStudyUserReport(Study study, List created) { // Get all recipients List recipients = new ArrayList(); @@ -745,14 +746,14 @@ public Map getDetailedStorageVolumeByStudy(List { - if(!detailedStorageVolumes.containsKey(study.getId())){ - return; + if(!detailedStorageVolumes.containsKey(study.getId())){ + return; + } + Long filesSize = this.getStudyFilesSize(study); + StudyStorageVolumeDTO dto = detailedStorageVolumes.get(study.getId()); + dto.setExtraDataSize(filesSize + dto.getExtraDataSize()); + dto.setTotal(filesSize + dto.getTotal()); } - Long filesSize = this.getStudyFilesSize(study); - StudyStorageVolumeDTO dto = detailedStorageVolumes.get(study.getId()); - dto.setExtraDataSize(filesSize + dto.getExtraDataSize()); - dto.setTotal(filesSize + dto.getTotal()); - } ); return detailedStorageVolumes; From d1916c7926c7a76186bd06c3b33d42fd3aa61946 Mon Sep 17 00:00:00 2001 From: pierrehenri-dauvergne Date: Wed, 24 Apr 2024 10:14:25 +0200 Subject: [PATCH 02/80] shanoir-issue#2199: add db-changes script to initialize studycardpolicy to mandatory --- .../studies/0020_set_default_studycardpolicy.sql | 1 + shanoir-ng-front/src/app/studies/shared/study.model.ts | 2 +- .../src/app/studies/study/study.component.html | 2 +- .../java/org/shanoir/ng/study/model/StudyCardPolicy.java | 8 ++++---- 4 files changed, 7 insertions(+), 6 deletions(-) create mode 100644 docker-compose/database/db-changes/studies/0020_set_default_studycardpolicy.sql diff --git a/docker-compose/database/db-changes/studies/0020_set_default_studycardpolicy.sql b/docker-compose/database/db-changes/studies/0020_set_default_studycardpolicy.sql new file mode 100644 index 0000000000..6c2feb1618 --- /dev/null +++ b/docker-compose/database/db-changes/studies/0020_set_default_studycardpolicy.sql @@ -0,0 +1 @@ +UPDATE study SET study_card_policy=0; \ No newline at end of file diff --git a/shanoir-ng-front/src/app/studies/shared/study.model.ts b/shanoir-ng-front/src/app/studies/shared/study.model.ts index e6147b88da..e5d246b189 100644 --- a/shanoir-ng-front/src/app/studies/shared/study.model.ts +++ b/shanoir-ng-front/src/app/studies/shared/study.model.ts @@ -51,7 +51,7 @@ export class Study extends Entity { timepoints: Timepoint[]; visibleByDefault: boolean = false; withExamination: boolean; - studyCardPolicy: string = "OPTIONAL"; + studyCardPolicy: string = "MANDATORY"; studyCardList: StudyCard[]; tags: Tag[]; studyTags: Tag[]; diff --git a/shanoir-ng-front/src/app/studies/study/study.component.html b/shanoir-ng-front/src/app/studies/study/study.component.html index 0eebef43a8..abcabd76f0 100644 --- a/shanoir-ng-front/src/app/studies/study/study.component.html +++ b/shanoir-ng-front/src/app/studies/study/study.component.html @@ -157,8 +157,8 @@

      Optional Mandatory + Optional Disabled diff --git a/shanoir-ng-studies/src/main/java/org/shanoir/ng/study/model/StudyCardPolicy.java b/shanoir-ng-studies/src/main/java/org/shanoir/ng/study/model/StudyCardPolicy.java index 1d011a0a92..5360de7810 100644 --- a/shanoir-ng-studies/src/main/java/org/shanoir/ng/study/model/StudyCardPolicy.java +++ b/shanoir-ng-studies/src/main/java/org/shanoir/ng/study/model/StudyCardPolicy.java @@ -3,14 +3,14 @@ public enum StudyCardPolicy { /** - * Study card is optional during import + * Study card is mandatory during import */ - OPTIONAL(1), + MANDATORY(1), /** - * Study card is mandatory during import + * Study card is optional during import */ - MANDATORY(2), + OPTIONAL(2), /** * Study card is disabled during import From bd119a8f531f1874da6ccba28f268ca9bff4aa88 Mon Sep 17 00:00:00 2001 From: Youenn Merel Date: Thu, 25 Apr 2024 11:35:06 +0200 Subject: [PATCH 03/80] [fli-iam#2194] Add models --- shanoir-downloader | 2 +- .../amqp/RabbitMQDatasetsService.java | 1 + .../org/shanoir/ng/tag/model/DatasetTag.java | 34 +++++++++++ .../org/shanoir/ng/tag/model/StudyTag.java | 58 +++++++++++++++++++ .../repository/DatasetTagRepository.java | 27 +++++++++ .../repository/StudyTagRepository.java | 27 +++++++++ 6 files changed, 148 insertions(+), 1 deletion(-) create mode 100644 shanoir-ng-datasets/src/main/java/org/shanoir/ng/tag/model/DatasetTag.java create mode 100644 shanoir-ng-datasets/src/main/java/org/shanoir/ng/tag/model/StudyTag.java create mode 100644 shanoir-ng-datasets/src/main/java/org/shanoir/ng/tag/repository/repository/DatasetTagRepository.java create mode 100644 shanoir-ng-datasets/src/main/java/org/shanoir/ng/tag/repository/repository/StudyTagRepository.java diff --git a/shanoir-downloader b/shanoir-downloader index dcb62e540d..f1a32543fd 160000 --- a/shanoir-downloader +++ b/shanoir-downloader @@ -1 +1 @@ -Subproject commit dcb62e540d2a452b78bec6e16e4238ac422a47d1 +Subproject commit f1a32543fd5be9d6620a9a67b498d67a65c87d96 diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/configuration/amqp/RabbitMQDatasetsService.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/configuration/amqp/RabbitMQDatasetsService.java index b0c002468b..d1e956c839 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/configuration/amqp/RabbitMQDatasetsService.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/configuration/amqp/RabbitMQDatasetsService.java @@ -87,6 +87,7 @@ public class RabbitMQDatasetsService { @Autowired private DatasetService datasetService; + @Autowired private DatasetCopyService datasetCopyService; diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/tag/model/DatasetTag.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/tag/model/DatasetTag.java new file mode 100644 index 0000000000..fc2cbaa544 --- /dev/null +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/tag/model/DatasetTag.java @@ -0,0 +1,34 @@ +package org.shanoir.ng.tag.model; + +import jakarta.persistence.Entity; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import org.shanoir.ng.dataset.model.Dataset; +import org.shanoir.ng.shared.hateoas.HalEntity; + +@Entity +public class DatasetTag extends HalEntity { + + @ManyToOne + @JoinColumn(name = "tag_id") + private StudyTag tag; + @ManyToOne + @JoinColumn(name = "dataset_id") + private Dataset dataset; + + public StudyTag getTag() { + return tag; + } + + public void setTag(StudyTag tag) { + this.tag = tag; + } + + public Dataset getDataset() { + return dataset; + } + + public void setDataset(Dataset dataset) { + this.dataset = dataset; + } +} diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/tag/model/StudyTag.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/tag/model/StudyTag.java new file mode 100644 index 0000000000..0e10a148d8 --- /dev/null +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/tag/model/StudyTag.java @@ -0,0 +1,58 @@ +package org.shanoir.ng.tag.model; + +import jakarta.persistence.Entity; +import org.shanoir.ng.shared.hateoas.HalEntity; + +@Entity +public class StudyTag extends HalEntity { + + private static final long serialVersionUID = 1L; + + private String name; + + private String color; + + private long studyId; + + /** + * @return the name + */ + public String getName() { + return name; + } + + /** + * @param name the name to set + */ + public void setName(String name) { + this.name = name; + } + + /** + * @return the color + */ + public String getColor() { + return color; + } + + /** + * @param color the color to set + */ + public void setColor(String color) { + this.color = color; + } + + /** + * @return the study + */ + public long getStudyId() { + return studyId; + } + + /** + * @param study the study to set + */ + public void setStudyId(long study) { + this.studyId = study; + } +} diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/tag/repository/repository/DatasetTagRepository.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/tag/repository/repository/DatasetTagRepository.java new file mode 100644 index 0000000000..4b882c3e77 --- /dev/null +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/tag/repository/repository/DatasetTagRepository.java @@ -0,0 +1,27 @@ +/** + * Shanoir NG - Import, manage and share neuroimaging data + * Copyright (C) 2009-2019 Inria - https://www.inria.fr/ + * Contact us on https://project.inria.fr/shanoir/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see https://www.gnu.org/licenses/gpl-3.0.html + */ + +package org.shanoir.ng.tag.repository.repository; + +import org.shanoir.ng.tag.model.DatasetTag; +import org.springframework.data.repository.CrudRepository; + +/** + * Repository for Subject. + * + * @author msimon + */ +public interface DatasetTagRepository extends CrudRepository { + +} diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/tag/repository/repository/StudyTagRepository.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/tag/repository/repository/StudyTagRepository.java new file mode 100644 index 0000000000..d0d1aa7981 --- /dev/null +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/tag/repository/repository/StudyTagRepository.java @@ -0,0 +1,27 @@ +/** + * Shanoir NG - Import, manage and share neuroimaging data + * Copyright (C) 2009-2019 Inria - https://www.inria.fr/ + * Contact us on https://project.inria.fr/shanoir/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see https://www.gnu.org/licenses/gpl-3.0.html + */ + +package org.shanoir.ng.tag.repository.repository; + +import org.shanoir.ng.tag.model.StudyTag; +import org.springframework.data.repository.CrudRepository; + +/** + * Repository for Subject. + * + * @author msimon + */ +public interface StudyTagRepository extends CrudRepository { + +} From 2006dddeb4b2731f663fd73f67c85c16cbe0c119 Mon Sep 17 00:00:00 2001 From: Anthony Baire Date: Tue, 30 Apr 2024 14:54:21 +0200 Subject: [PATCH 04/80] install dcm2niix and mriconverter in a separate stage --- docker-compose/nifti-conversion/Dockerfile | 61 +++++++++++++--------- 1 file changed, 36 insertions(+), 25 deletions(-) diff --git a/docker-compose/nifti-conversion/Dockerfile b/docker-compose/nifti-conversion/Dockerfile index ba58146ca2..46a493d67e 100644 --- a/docker-compose/nifti-conversion/Dockerfile +++ b/docker-compose/nifti-conversion/Dockerfile @@ -10,7 +10,36 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see https://www.gnu.org/licenses/gpl-3.0.html -FROM debian:bookworm +ARG BASE_IMAGE="debian:bookworm" + + +FROM $BASE_IMAGE as builder + +RUN apt-get -qqy update \ + && apt-get -qqy install \ + build-essential \ + curl \ + cmake \ + git \ + pkg-config + +# Compile DCM2NIIX from source +ENV DCM2NIIX_VERSION=v1.0.20210317 +RUN mkdir /src && cd /src \ + && curl -LSsf https://github.com/rordenlab/dcm2niix/archive/refs/tags/$DCM2NIIX_VERSION.tar.gz \ + | tar zx \ + && cd dcm2niix-* && mkdir build \ + && cd build && cmake .. && make -j4 && make install DESTDIR=/target + +# Install mri_conv +RUN mkdir -p /target/opt/nifti-converters/mriconverter \ + && cd /target/opt/nifti-converters/mriconverter \ + && curl -LSsf https://github.com/populse/mri_conv/archive/refs/heads/master.tar.gz \ + | tar zx --strip-components 1 \ + && chmod 0777 . MRIFileManager/MRIManager.jar + + +FROM $BASE_IMAGE as final # NOTE: using bookworm-proposed-updates because of https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1039472 RUN echo "deb http://deb.debian.org/debian bookworm-proposed-updates main" >> /etc/apt/sources.list \ @@ -61,24 +90,9 @@ RUN wget \ && bash Miniconda3-py311_24.1.2-0-Linux-x86_64.sh -b \ && rm -f Miniconda3-py311_24.1.2-0-Linux-x86_64.sh - -# Compile DCM2NIIX from source -ENV DCMCOMMIT_VERSION=v1.0.20210317 -ENV DCMCOMMIT=1.0.20210317 ENV LC_ALL fr_FR.UTF-8 ENV LANG fr_FR.UTF-8 ENV LANGUAGE fr_FR.UTF-8 - -WORKDIR /usr/local/ -RUN curl -#L https://github.com/rordenlab/dcm2niix/archive/refs/tags/$DCMCOMMIT_VERSION.zip | bsdtar -xf- -C . -WORKDIR /usr/local/dcm2niix-${DCMCOMMIT} -RUN mkdir build -WORKDIR /usr/local/dcm2niix-${DCMCOMMIT}/build -RUN cmake .. -RUN make install - -WORKDIR / - RUN mkdir -p /opt/nifti-converters # Copy converters files @@ -90,15 +104,6 @@ RUN install -m 0755 external/dcm2nii/linux/dcm2nii /opt/nifti-converters/dcm2nii RUN install -m 0755 external/mcverter/linux/mcverter_* /opt/nifti-converters/ RUN cp -n external/mcverter/linux/lib/lib*.so.* /usr/lib/x86_64-linux-gnu/ -WORKDIR /opt/nifti-converters -RUN mkdir mriconverter -RUN curl -#L https://github.com/populse/mri_conv/archive/refs/heads/master.zip | bsdtar -xf- -C mriconverter --strip-components 1 - -RUN chmod 777 /opt/nifti-converters/mriconverter -RUN chmod 777 /opt/nifti-converters/mriconverter/MRIFileManager/MRIManager.jar - -WORKDIR / - RUN mkdir -m 1777 /.dcm2nii_2008-03-31 RUN mkdir -m 1777 /.dcm2nii_2014-08-04 @@ -109,6 +114,12 @@ RUN conda install -c conda-forge dicomifier -y # install animaConvertImage to convert Analyze format into nifti RUN install -m 755 external/anima/animaConvertImage /usr/local/bin/ +# install the binaries built in the 'builder' stage +COPY --link --from=builder /target/. / + +# update the ld cache (so that the new libraries can be loaded) +RUN ldconfig + RUN mkdir -pv /var/log/shanoir-ng-logs ADD nifti-conversion.jar nifti-conversion.jar From 9723b68bf2b3adf88c0abbadbb40b9233ec8b537 Mon Sep 17 00:00:00 2001 From: Anthony Baire Date: Tue, 30 Apr 2024 15:04:59 +0200 Subject: [PATCH 05/80] install conda in a separate stage --- docker-compose/nifti-conversion/Dockerfile | 38 ++++++++++++---------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/docker-compose/nifti-conversion/Dockerfile b/docker-compose/nifti-conversion/Dockerfile index 46a493d67e..650bfe0c27 100644 --- a/docker-compose/nifti-conversion/Dockerfile +++ b/docker-compose/nifti-conversion/Dockerfile @@ -13,6 +13,20 @@ ARG BASE_IMAGE="debian:bookworm" +FROM $BASE_IMAGE as conda + +RUN apt-get -qqy update \ + && apt-get -qqy --no-install-recommends install curl ca-certificates + +# Install miniconda +RUN curl -LSsf -o miniconda.sh https://repo.anaconda.com/miniconda/Miniconda3-py311_24.1.2-0-Linux-x86_64.sh \ + && bash miniconda.sh -b \ + && rm miniconda.sh + +# Install dicomifier +RUN /root/miniconda3/bin/conda install -c conda-forge dicomifier -y + + FROM $BASE_IMAGE as builder RUN apt-get -qqy update \ @@ -80,19 +94,6 @@ RUN apt-get update -qq \ python3-yaml \ software-properties-common -ENV PATH="/root/miniconda3/bin:${PATH}" -ARG PATH="/root/miniconda3/bin:${PATH}" - -#Install miniconda \ -RUN wget \ - https://repo.anaconda.com/miniconda/Miniconda3-py311_24.1.2-0-Linux-x86_64.sh \ - && mkdir /root/.conda \ - && bash Miniconda3-py311_24.1.2-0-Linux-x86_64.sh -b \ - && rm -f Miniconda3-py311_24.1.2-0-Linux-x86_64.sh - -ENV LC_ALL fr_FR.UTF-8 -ENV LANG fr_FR.UTF-8 -ENV LANGUAGE fr_FR.UTF-8 RUN mkdir -p /opt/nifti-converters # Copy converters files @@ -107,15 +108,13 @@ RUN cp -n external/mcverter/linux/lib/lib*.so.* /usr/lib/x86_64-linux- RUN mkdir -m 1777 /.dcm2nii_2008-03-31 RUN mkdir -m 1777 /.dcm2nii_2014-08-04 -# Install dicomifier -RUN git -C /opt/nifti-converters clone https://github.com/lamyj/dicomifier.git -RUN conda install -c conda-forge dicomifier -y # install animaConvertImage to convert Analyze format into nifti RUN install -m 755 external/anima/animaConvertImage /usr/local/bin/ -# install the binaries built in the 'builder' stage +# install the binaries built in the 'builder' & 'conda' stages COPY --link --from=builder /target/. / +COPY --link --from=conda /root/miniconda3 /root/miniconda3 # update the ld cache (so that the new libraries can be loaded) RUN ldconfig @@ -125,4 +124,9 @@ RUN mkdir -pv /var/log/shanoir-ng-logs ADD nifti-conversion.jar nifti-conversion.jar COPY entrypoint entrypoint_common oneshot /bin/ +ENV LC_ALL fr_FR.UTF-8 +ENV LANG fr_FR.UTF-8 +ENV LANGUAGE fr_FR.UTF-8 +ENV PATH="/root/miniconda3/bin:${PATH}" + ENTRYPOINT ["/bin/entrypoint", "java", "-Djava.security.egd=file:/dev/./urandom", "-Djavax.net.ssl.trustStorePassword=changeit", "-jar", "/nifti-conversion.jar"] From f276a2514ae14a15bcfd5a9cbba3cd48fe6f9c94 Mon Sep 17 00:00:00 2001 From: Anthony Baire Date: Tue, 30 Apr 2024 18:49:49 +0200 Subject: [PATCH 06/80] download binaries in a separate 'downloader' stage --- docker-compose/datasets/Dockerfile | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/docker-compose/datasets/Dockerfile b/docker-compose/datasets/Dockerfile index 3436b49379..94e83c9bb1 100644 --- a/docker-compose/datasets/Dockerfile +++ b/docker-compose/datasets/Dockerfile @@ -10,6 +10,18 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see https://www.gnu.org/licenses/gpl-3.0.html +FROM alpine as downloader + +# Installation of dcm4che into /opt/dcm4che to manage the store-scu into the +# PACS dcm4che3, used last version 5.21.0 as available on the 2020-02-14 +# https://sourceforge.net/projects/dcm4che/files/dcm4che3/ +RUN wget -qO dcm4che-bin.zip \ + https://downloads.sourceforge.net/project/dcm4che/dcm4che3/5.21.0/dcm4che-5.21.0-bin.zip \ + && mkdir -p /target/opt && cd /target/opt \ + && unzip /dcm4che-bin.zip && mv dcm4che-* dcm4che \ + && rm /dcm4che-bin.zip + + #--------------- common jre base image ------------------------------------- FROM debian:bookworm # NOTE: using bookworm-proposed-updates because of https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1039472 @@ -21,25 +33,20 @@ RUN echo "deb http://deb.debian.org/debian bookworm-proposed-updates main" >> /e RUN apt-get update -qq \ && apt-get install -qqy \ - unzip \ pigz \ gzip \ locales \ - locales-all \ - wget - -# Installation of dcm4che to manage the store-scu into the PACS -# dcm4che3, used last version 5.21.0 as available on the 2020-02-14 -# https://sourceforge.net/projects/dcm4che/files/dcm4che3/ -RUN wget -O dcm4che-5.21.0-bin.zip https://downloads.sourceforge.net/project/dcm4che/dcm4che3/5.21.0/dcm4che-5.21.0-bin.zip -RUN unzip dcm4che-5.21.0-bin.zip + locales-all # take care of path -ENV PATH /dcm4che-5.21.0/bin:$PATH +ENV PATH /opt/dcm4che/bin:$PATH ENV LC_ALL en_US.UTF-8 ENV LANG en_US.UTF-8 ENV LANGUAGE en_US.UTF-8 +# install the files from the 'dowloader' stage +COPY --link --from=downloader /target/. / + RUN mkdir -pv /var/log/shanoir-ng-logs ADD shanoir-ng-datasets.jar shanoir-ng-datasets.jar COPY entrypoint entrypoint_common oneshot /bin/ From b17678a7037bd271177f2bd8118ae0b2cd6dba52 Mon Sep 17 00:00:00 2001 From: Anthony Baire Date: Tue, 30 Apr 2024 15:20:43 +0200 Subject: [PATCH 07/80] remove unused packages --- docker-compose/import/Dockerfile | 26 -------------- docker-compose/nifti-conversion/Dockerfile | 42 ++++++---------------- 2 files changed, 10 insertions(+), 58 deletions(-) diff --git a/docker-compose/import/Dockerfile b/docker-compose/import/Dockerfile index c6951ed77d..67dcc41813 100644 --- a/docker-compose/import/Dockerfile +++ b/docker-compose/import/Dockerfile @@ -18,32 +18,6 @@ RUN echo "deb http://deb.debian.org/debian bookworm-proposed-updates main" >> /e && apt-get install -qqy openjdk-17-jre-headless ca-certificates-java #---------------------------------------------------------------------------- - -#32 bits packages are necessary -RUN dpkg --add-architecture i386 - -RUN apt-get update -qq \ - && apt-get install -qqy \ - git \ - curl \ - build-essential \ - cmake \ - pkg-config \ - libgdcm-tools \ - libarchive-tools \ - unzip \ - pigz \ - gzip \ - wget \ - jq \ - lib32z1 \ - libgtk2.0-0 \ - libsm6 \ - libxext6 \ - lib32stdc++6 \ - libtiff6 \ - libglib2.0-0:i386 - RUN mkdir -pv /var/log/shanoir-ng-logs ADD shanoir-ng-import.jar shanoir-ng-import.jar diff --git a/docker-compose/nifti-conversion/Dockerfile b/docker-compose/nifti-conversion/Dockerfile index 650bfe0c27..d928a8de2e 100644 --- a/docker-compose/nifti-conversion/Dockerfile +++ b/docker-compose/nifti-conversion/Dockerfile @@ -58,41 +58,19 @@ FROM $BASE_IMAGE as final # NOTE: using bookworm-proposed-updates because of https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1039472 RUN echo "deb http://deb.debian.org/debian bookworm-proposed-updates main" >> /etc/apt/sources.list \ && apt-get update -qq \ - && apt-get install -qqy openjdk-17-jdk ca-certificates-java - -#32 bits packages are necessary -RUN dpkg --add-architecture i386 -RUN apt-get update - -RUN apt-get update -qq \ - && apt-get install -y \ - git \ - curl \ - cmake \ - build-essential \ - pkg-config \ + && apt-get install -qqy openjdk-17-jre ca-certificates-java + +# xvfb+gtk2 needed by mri_conv (headless mode not supported by DicomToNifti) +# see: https://populse.github.io/mri_conv/Installation/installation.html#scriptwithoutGUI +RUN apt-get update -qqy \ + && apt-get install -qqy \ libgdcm-tools \ - libarchive-tools \ - unzip \ - pigz \ - gzip \ - gnupg \ - wget \ - jq \ - lib32z1 \ - libgtk2.0-0 \ - libsm6 \ - libxext6 \ - lib32stdc++6 \ - libtiff6 \ - libglib2.0-0:i386 \ locales \ locales-all \ - xvfb \ - python3.11 \ - python3-werkzeug \ - python3-yaml \ - software-properties-common + jq \ + libgtk2.0-0 \ + pigz \ + xvfb RUN mkdir -p /opt/nifti-converters From bed864e91dd44b6f78f92ec25eab1e2b4fc58398 Mon Sep 17 00:00:00 2001 From: Anthony Baire Date: Tue, 30 Apr 2024 15:31:24 +0200 Subject: [PATCH 08/80] reduce the number of steps, use COPY as much as possible --- docker-compose/database/Dockerfile | 20 +++++-------- docker-compose/datasets/Dockerfile | 4 +-- docker-compose/import/Dockerfile | 4 +-- docker-compose/keycloak-database/Dockerfile | 4 +-- docker-compose/nginx/Dockerfile | 10 +++---- docker-compose/nifti-conversion/Dockerfile | 33 +++++++++------------ docker-compose/preclinical/Dockerfile | 4 +-- docker-compose/solr/Dockerfile | 6 ++-- docker-compose/studies/Dockerfile | 4 +-- docker-compose/users/Dockerfile | 4 +-- 10 files changed, 41 insertions(+), 52 deletions(-) diff --git a/docker-compose/database/Dockerfile b/docker-compose/database/Dockerfile index c1d2b34518..59df6253c6 100644 --- a/docker-compose/database/Dockerfile +++ b/docker-compose/database/Dockerfile @@ -12,19 +12,15 @@ FROM mysql/mysql-server:5.7 -COPY /shanoir-entrypoint.sh / +COPY --link --chmod=0755 /shanoir-entrypoint.sh / ENTRYPOINT ["/shanoir-entrypoint.sh"] CMD ["mysqld"] -ADD 1_create_databases.sh /docker-entrypoint-initdb.d/ -ADD 2_add_users.sql /docker-entrypoint-initdb.d/ -ADD 3_add_statistics_procedure.sql /docker-entrypoint-initdb.d/ -ADD 4_add_studyStatistics_procedure.sql /docker-entrypoint-initdb.d/ +COPY --link --chmod=0755 \ + 1_create_databases.sh \ + 2_add_users.sql \ + 3_add_statistics_procedure.sql \ + 4_add_studyStatistics_procedure.sql \ + /docker-entrypoint-initdb.d/ -RUN chmod a+x /docker-entrypoint-initdb.d/1_create_databases.sh -RUN chmod a+x /docker-entrypoint-initdb.d/2_add_users.sql -RUN chmod a+x /docker-entrypoint-initdb.d/3_add_statistics_procedure.sql -RUN chmod a+x /docker-entrypoint-initdb.d/4_add_studyStatistics_procedure.sql -RUN chmod a+x /shanoir-entrypoint.sh - -COPY db-changes /opt/db-changes +COPY --link db-changes /opt/db-changes diff --git a/docker-compose/datasets/Dockerfile b/docker-compose/datasets/Dockerfile index 94e83c9bb1..569e4beca6 100644 --- a/docker-compose/datasets/Dockerfile +++ b/docker-compose/datasets/Dockerfile @@ -48,8 +48,8 @@ ENV LANGUAGE en_US.UTF-8 COPY --link --from=downloader /target/. / RUN mkdir -pv /var/log/shanoir-ng-logs -ADD shanoir-ng-datasets.jar shanoir-ng-datasets.jar -COPY entrypoint entrypoint_common oneshot /bin/ +COPY --link shanoir-ng-datasets.jar shanoir-ng-datasets.jar +COPY --link entrypoint entrypoint_common oneshot /bin/ # Use the below line for remote debugging and to active dev profile #ENTRYPOINT ["/bin/entrypoint", "java", "-Xdebug", "-Xrunjdwp:transport=dt_socket,server=y,address=0.0.0.0:9914,suspend=y", "-Djava.security.egd=file:/dev/./urandom", "-Djavax.net.ssl.trustStorePassword=changeit", "-jar", "/shanoir-ng-datasets.jar"] diff --git a/docker-compose/import/Dockerfile b/docker-compose/import/Dockerfile index 67dcc41813..7bfdd13752 100644 --- a/docker-compose/import/Dockerfile +++ b/docker-compose/import/Dockerfile @@ -20,8 +20,8 @@ RUN echo "deb http://deb.debian.org/debian bookworm-proposed-updates main" >> /e RUN mkdir -pv /var/log/shanoir-ng-logs -ADD shanoir-ng-import.jar shanoir-ng-import.jar -COPY entrypoint entrypoint_common oneshot /bin/ +COPY --link shanoir-ng-import.jar shanoir-ng-import.jar +COPY --link entrypoint entrypoint_common oneshot /bin/ # Use the below line for remote debugging and to active dev profile #ENTRYPOINT ["/bin/entrypoint", "java", "-Xdebug", "-Xrunjdwp:transport=dt_socket,server=y,address=0.0.0.0:9913,suspend=y", "-Djava.security.egd=file:/dev/./urandom", "-Djavax.net.ssl.trustStorePassword=changeit", "-jar", "/shanoir-ng-import.jar"] diff --git a/docker-compose/keycloak-database/Dockerfile b/docker-compose/keycloak-database/Dockerfile index 1801c3e607..e793219198 100644 --- a/docker-compose/keycloak-database/Dockerfile +++ b/docker-compose/keycloak-database/Dockerfile @@ -12,6 +12,4 @@ FROM mysql/mysql-server:5.7 -ADD 1_add_users.sql /docker-entrypoint-initdb.d/ - -RUN chmod a+x /docker-entrypoint-initdb.d/1_add_users.sql \ No newline at end of file +COPY --link --chmod=0755 1_add_users.sql /docker-entrypoint-initdb.d/ diff --git a/docker-compose/nginx/Dockerfile b/docker-compose/nginx/Dockerfile index d377254912..5038c413a1 100644 --- a/docker-compose/nginx/Dockerfile +++ b/docker-compose/nginx/Dockerfile @@ -14,16 +14,16 @@ FROM ohif/viewer:v4.12.32.19362 AS viewer FROM nginx -COPY nginx.conf http.conf https.conf shanoir.template.conf shanoir.template.dev.conf shanoir.template.prod.conf /etc/nginx/ +COPY --link nginx.conf http.conf https.conf shanoir.template.conf shanoir.template.dev.conf shanoir.template.prod.conf /etc/nginx/ -COPY viewer/app-config.js viewer/ohif-viewer.template.conf /etc/nginx/viewer/ -COPY --from=viewer /usr/share/nginx/html/. /etc/nginx/viewer/html/ +COPY --link viewer/app-config.js viewer/ohif-viewer.template.conf /etc/nginx/viewer/ +COPY --link --from=viewer /usr/share/nginx/html/. /etc/nginx/viewer/html/ -COPY entrypoint entrypoint_common /bin/ +COPY --link entrypoint entrypoint_common /bin/ -COPY webapp/ /etc/nginx/html/ +COPY --link webapp/ /etc/nginx/html/ ENTRYPOINT ["/bin/entrypoint"] CMD ["nginx", "-g", "daemon off;"] diff --git a/docker-compose/nifti-conversion/Dockerfile b/docker-compose/nifti-conversion/Dockerfile index d928a8de2e..951ab15c32 100644 --- a/docker-compose/nifti-conversion/Dockerfile +++ b/docker-compose/nifti-conversion/Dockerfile @@ -72,35 +72,30 @@ RUN apt-get update -qqy \ pigz \ xvfb -RUN mkdir -p /opt/nifti-converters - # Copy converters files -RUN mkdir external -COPY external ./external -RUN install -m 0755 external/dcm2nii/linux/31MARCH2008/dcm2nii /opt/nifti-converters/dcm2nii_2008-03-31 -RUN install -m 0755 external/dcm2nii/linux/dcm2nii /opt/nifti-converters/dcm2nii_2014-08-04 - -RUN install -m 0755 external/mcverter/linux/mcverter_* /opt/nifti-converters/ -RUN cp -n external/mcverter/linux/lib/lib*.so.* /usr/lib/x86_64-linux-gnu/ - -RUN mkdir -m 1777 /.dcm2nii_2008-03-31 -RUN mkdir -m 1777 /.dcm2nii_2014-08-04 - +COPY --link --chmod=0755 external/dcm2nii/linux/31MARCH2008/dcm2nii /opt/nifti-converters/dcm2nii_2008-03-31 +COPY --link --chmod=0755 external/dcm2nii/linux/dcm2nii /opt/nifti-converters/dcm2nii_2014-08-04 +COPY --link --chmod=0755 external/mcverter/linux/mcverter_* /opt/nifti-converters/ +COPY --link external/mcverter/linux/lib/lib*.so.* /usr/local/lib/x86_64-linux-gnu/ +RUN mkdir -m 1777 \ + /.dcm2nii_2008-03-31 \ + /.dcm2nii_2014-08-04 # install animaConvertImage to convert Analyze format into nifti -RUN install -m 755 external/anima/animaConvertImage /usr/local/bin/ +COPY --link --chmod=0755 external/anima/animaConvertImage /usr/local/bin/ # install the binaries built in the 'builder' & 'conda' stages COPY --link --from=builder /target/. / COPY --link --from=conda /root/miniconda3 /root/miniconda3 # update the ld cache (so that the new libraries can be loaded) -RUN ldconfig - -RUN mkdir -pv /var/log/shanoir-ng-logs +# and make the log dir +RUN ldconfig \ + && mkdir -pv /var/log/shanoir-ng-logs -ADD nifti-conversion.jar nifti-conversion.jar -COPY entrypoint entrypoint_common oneshot /bin/ +# install the microservice +COPY --link nifti-conversion.jar nifti-conversion.jar +COPY --link entrypoint entrypoint_common oneshot /bin/ ENV LC_ALL fr_FR.UTF-8 ENV LANG fr_FR.UTF-8 diff --git a/docker-compose/preclinical/Dockerfile b/docker-compose/preclinical/Dockerfile index 8cbe707a26..1e66ef8f11 100644 --- a/docker-compose/preclinical/Dockerfile +++ b/docker-compose/preclinical/Dockerfile @@ -8,9 +8,9 @@ RUN echo "deb http://deb.debian.org/debian bookworm-proposed-updates main" >> /e RUN mkdir -pv /var/log/shanoir-ng-logs -ADD shanoir-ng-preclinical.jar shanoir-ng-preclinical.jar +COPY --link shanoir-ng-preclinical.jar shanoir-ng-preclinical.jar -COPY entrypoint entrypoint_common oneshot /bin/ +COPY --link entrypoint entrypoint_common oneshot /bin/ # Use the below line for remote debugging #ENTRYPOINT ["/bin/entrypoint", "java", "-Xdebug", "-Xrunjdwp:transport=dt_socket,server=y,address=0.0.0.0:9915,suspend=y", "-Djava.security.egd=file:/dev/./urandom", "-Djavax.net.ssl.trustStorePassword=changeit", "-Dspring.profiles.active=dev", "-jar", "/shanoir-ng-preclinical.jar"] diff --git a/docker-compose/solr/Dockerfile b/docker-compose/solr/Dockerfile index 44dc12cdb5..d81a15839c 100644 --- a/docker-compose/solr/Dockerfile +++ b/docker-compose/solr/Dockerfile @@ -13,10 +13,10 @@ FROM solr:8.1 USER root -RUN mkdir -p /etc/shanoir-core-template -RUN chown solr:solr /etc/shanoir-core-template +RUN mkdir -p /etc/shanoir-core-template \ + && chown solr:solr /etc/shanoir-core-template USER solr -COPY ./core /etc/shanoir-core-template +COPY --link ./core /etc/shanoir-core-template CMD ["solr-precreate", "shanoir", "/etc/shanoir-core-template"] diff --git a/docker-compose/studies/Dockerfile b/docker-compose/studies/Dockerfile index a78e044a86..987a5f9120 100644 --- a/docker-compose/studies/Dockerfile +++ b/docker-compose/studies/Dockerfile @@ -20,9 +20,9 @@ RUN echo "deb http://deb.debian.org/debian bookworm-proposed-updates main" >> /e RUN mkdir -pv /var/log/shanoir-ng-logs -ADD shanoir-ng-studies.jar shanoir-ng-studies.jar +COPY --link shanoir-ng-studies.jar shanoir-ng-studies.jar -COPY entrypoint entrypoint_common oneshot /bin/ +COPY --link entrypoint entrypoint_common oneshot /bin/ # Use the below line for remote debugging and to active dev profile #ENTRYPOINT ["/bin/entrypoint", "java", "-Xdebug", "-Xrunjdwp:transport=dt_socket,server=y,address=0.0.0.0:9912,suspend=y", "-Djava.security.egd=file:/dev/./urandom", "-Djavax.net.ssl.trustStorePassword=changeit", "-jar", "/shanoir-ng-studies.jar"] diff --git a/docker-compose/users/Dockerfile b/docker-compose/users/Dockerfile index 536f0fc6db..83d1e3e5c9 100644 --- a/docker-compose/users/Dockerfile +++ b/docker-compose/users/Dockerfile @@ -22,8 +22,8 @@ RUN echo "deb http://deb.debian.org/debian bookworm-proposed-updates main" >> /e RUN apt-get update -qq && apt-get install -qqy openssl RUN mkdir -pv /var/log/shanoir-ng-logs -ADD shanoir-ng-users.jar shanoir-ng-users.jar -COPY entrypoint entrypoint_common oneshot /bin/ +COPY --link shanoir-ng-users.jar shanoir-ng-users.jar +COPY --link entrypoint entrypoint_common oneshot /bin/ # Use the below line for remote debugging and development profile purpose #ENTRYPOINT ["/bin/entrypoint", "java", "-Xdebug", "-Xrunjdwp:transport=dt_socket,server=y,address=0.0.0.0:9911,suspend=y", "-Djava.security.egd=file:/dev/./urandom", "-Djavax.net.ssl.trustStorePassword=changeit", "-Dspring.profiles.active=dev", "-jar", "/shanoir-ng-users.jar", "--syncAllUsersToKeycloak=true"] From ca5ae31d6ebd0cdfcb4ab4dadfeb54efb6e313c4 Mon Sep 17 00:00:00 2001 From: Anthony Baire Date: Tue, 30 Apr 2024 16:03:17 +0200 Subject: [PATCH 09/80] store the debian and conda caches in external volumes --- docker-compose/datasets/Dockerfile | 13 ++++++--- docker-compose/import/Dockerfile | 9 ++++-- docker-compose/nifti-conversion/Dockerfile | 33 ++++++++++++++-------- docker-compose/preclinical/Dockerfile | 9 ++++-- docker-compose/studies/Dockerfile | 9 ++++-- docker-compose/users/Dockerfile | 13 ++++++--- 6 files changed, 61 insertions(+), 25 deletions(-) diff --git a/docker-compose/datasets/Dockerfile b/docker-compose/datasets/Dockerfile index 569e4beca6..dcbda69c73 100644 --- a/docker-compose/datasets/Dockerfile +++ b/docker-compose/datasets/Dockerfile @@ -24,14 +24,19 @@ RUN wget -qO dcm4che-bin.zip \ #--------------- common jre base image ------------------------------------- FROM debian:bookworm -# NOTE: using bookworm-proposed-updates because of https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1039472 -RUN echo "deb http://deb.debian.org/debian bookworm-proposed-updates main" >> /etc/apt/sources.list \ +# - disable the automatic "apt-get clean" command (because we mount +# /var/cache/apt from an external volume to speed-up the build) +# - NOTE: using bookworm-proposed-updates because of +# https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1039472 +RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ + rm /etc/apt/apt.conf.d/docker-clean \ + && echo "deb http://deb.debian.org/debian bookworm-proposed-updates main" >> /etc/apt/sources.list \ && apt-get update -qq \ && apt-get install -qqy openjdk-17-jre-headless ca-certificates-java #---------------------------------------------------------------------------- - -RUN apt-get update -qq \ +RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ + apt-get update -qq \ && apt-get install -qqy \ pigz \ gzip \ diff --git a/docker-compose/import/Dockerfile b/docker-compose/import/Dockerfile index 7bfdd13752..431c16a53d 100644 --- a/docker-compose/import/Dockerfile +++ b/docker-compose/import/Dockerfile @@ -12,8 +12,13 @@ #--------------- common jre base image ------------------------------------- FROM debian:bookworm -# NOTE: using bookworm-proposed-updates because of https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1039472 -RUN echo "deb http://deb.debian.org/debian bookworm-proposed-updates main" >> /etc/apt/sources.list \ +# - disable the automatic "apt-get clean" command (because we mount +# /var/cache/apt from an external volume to speed-up the build) +# - NOTE: using bookworm-proposed-updates because of +# https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1039472 +RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ + rm /etc/apt/apt.conf.d/docker-clean \ + && echo "deb http://deb.debian.org/debian bookworm-proposed-updates main" >> /etc/apt/sources.list \ && apt-get update -qq \ && apt-get install -qqy openjdk-17-jre-headless ca-certificates-java #---------------------------------------------------------------------------- diff --git a/docker-compose/nifti-conversion/Dockerfile b/docker-compose/nifti-conversion/Dockerfile index 951ab15c32..251c076392 100644 --- a/docker-compose/nifti-conversion/Dockerfile +++ b/docker-compose/nifti-conversion/Dockerfile @@ -10,26 +10,35 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see https://www.gnu.org/licenses/gpl-3.0.html -ARG BASE_IMAGE="debian:bookworm" +FROM debian:bookworm as base_image +# - disable the automatic "apt-get clean" command (because we mount +# /var/cache/apt from an external volume to speed-up the build) +# - run "apt-get update" now (to avoid downloading the lists 3 times) +RUN rm /etc/apt/apt.conf.d/docker-clean \ + && apt-get update -qq -FROM $BASE_IMAGE as conda +FROM base_image as conda -RUN apt-get -qqy update \ +RUN --mount=type=cache,target=/var/cache/apt,sharing=locked,id=apt-conda \ + apt-get -qqy update \ && apt-get -qqy --no-install-recommends install curl ca-certificates # Install miniconda -RUN curl -LSsf -o miniconda.sh https://repo.anaconda.com/miniconda/Miniconda3-py311_24.1.2-0-Linux-x86_64.sh \ - && bash miniconda.sh -b \ +RUN --mount=type=cache,target=/root/miniconda3/pkgs \ + curl -LSsf -o miniconda.sh https://repo.anaconda.com/miniconda/Miniconda3-py311_24.1.2-0-Linux-x86_64.sh \ + && bash miniconda.sh -fb \ && rm miniconda.sh # Install dicomifier -RUN /root/miniconda3/bin/conda install -c conda-forge dicomifier -y +RUN --mount=type=cache,target=/root/miniconda3/pkgs \ + /root/miniconda3/bin/conda install -c conda-forge dicomifier -y -FROM $BASE_IMAGE as builder +FROM base_image as builder -RUN apt-get -qqy update \ +RUN --mount=type=cache,target=/var/cache/apt,sharing=locked,id=apt-builder \ + apt-get -qqy update \ && apt-get -qqy install \ build-essential \ curl \ @@ -53,16 +62,18 @@ RUN mkdir -p /target/opt/nifti-converters/mriconverter \ && chmod 0777 . MRIFileManager/MRIManager.jar -FROM $BASE_IMAGE as final +FROM base_image as final # NOTE: using bookworm-proposed-updates because of https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1039472 -RUN echo "deb http://deb.debian.org/debian bookworm-proposed-updates main" >> /etc/apt/sources.list \ +RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ + echo "deb http://deb.debian.org/debian bookworm-proposed-updates main" >> /etc/apt/sources.list \ && apt-get update -qq \ && apt-get install -qqy openjdk-17-jre ca-certificates-java # xvfb+gtk2 needed by mri_conv (headless mode not supported by DicomToNifti) # see: https://populse.github.io/mri_conv/Installation/installation.html#scriptwithoutGUI -RUN apt-get update -qqy \ +RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ + apt-get update -qqy \ && apt-get install -qqy \ libgdcm-tools \ locales \ diff --git a/docker-compose/preclinical/Dockerfile b/docker-compose/preclinical/Dockerfile index 1e66ef8f11..1506c42088 100644 --- a/docker-compose/preclinical/Dockerfile +++ b/docker-compose/preclinical/Dockerfile @@ -1,7 +1,12 @@ #--------------- common jre base image ------------------------------------- FROM debian:bookworm -# NOTE: using bookworm-proposed-updates because of https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1039472 -RUN echo "deb http://deb.debian.org/debian bookworm-proposed-updates main" >> /etc/apt/sources.list \ +# - disable the automatic "apt-get clean" command (because we mount +# /var/cache/apt from an external volume to speed-up the build) +# - NOTE: using bookworm-proposed-updates because of +# https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1039472 +RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ + rm /etc/apt/apt.conf.d/docker-clean \ + && echo "deb http://deb.debian.org/debian bookworm-proposed-updates main" >> /etc/apt/sources.list \ && apt-get update -qq \ && apt-get install -qqy openjdk-17-jre-headless ca-certificates-java #---------------------------------------------------------------------------- diff --git a/docker-compose/studies/Dockerfile b/docker-compose/studies/Dockerfile index 987a5f9120..d412065475 100644 --- a/docker-compose/studies/Dockerfile +++ b/docker-compose/studies/Dockerfile @@ -12,8 +12,13 @@ #--------------- common jre base image ------------------------------------- FROM debian:bookworm -# NOTE: using bookworm-proposed-updates because of https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1039472 -RUN echo "deb http://deb.debian.org/debian bookworm-proposed-updates main" >> /etc/apt/sources.list \ +# - disable the automatic "apt-get clean" command (because we mount +# /var/cache/apt from an external volume to speed-up the build) +# - NOTE: using bookworm-proposed-updates because of +# https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1039472 +RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ + rm /etc/apt/apt.conf.d/docker-clean \ + && echo "deb http://deb.debian.org/debian bookworm-proposed-updates main" >> /etc/apt/sources.list \ && apt-get update -qq \ && apt-get install -qqy openjdk-17-jre-headless ca-certificates-java #---------------------------------------------------------------------------- diff --git a/docker-compose/users/Dockerfile b/docker-compose/users/Dockerfile index 83d1e3e5c9..4a81514cb6 100644 --- a/docker-compose/users/Dockerfile +++ b/docker-compose/users/Dockerfile @@ -12,14 +12,19 @@ #--------------- common jre base image ------------------------------------- FROM debian:bookworm -# NOTE: using bookworm-proposed-updates because of https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1039472 -RUN echo "deb http://deb.debian.org/debian bookworm-proposed-updates main" >> /etc/apt/sources.list \ +# - disable the automatic "apt-get clean" command (because we mount +# /var/cache/apt from an external volume to speed-up the build) +# - NOTE: using bookworm-proposed-updates because of +# https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1039472 +RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ + rm /etc/apt/apt.conf.d/docker-clean \ + && echo "deb http://deb.debian.org/debian bookworm-proposed-updates main" >> /etc/apt/sources.list \ && apt-get update -qq \ && apt-get install -qqy openjdk-17-jre-headless ca-certificates-java #---------------------------------------------------------------------------- - -RUN apt-get update -qq && apt-get install -qqy openssl +RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ + apt-get update -qq && apt-get install -qqy openssl RUN mkdir -pv /var/log/shanoir-ng-logs COPY --link shanoir-ng-users.jar shanoir-ng-users.jar From f02ea5f9b0b31ffb13525bc3f01273ec6d750e3f Mon Sep 17 00:00:00 2001 From: Anthony Baire Date: Tue, 30 Apr 2024 18:11:05 +0200 Subject: [PATCH 10/80] move conda into /opt/ (so that it is usable by any user) --- docker-compose/nifti-conversion/Dockerfile | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docker-compose/nifti-conversion/Dockerfile b/docker-compose/nifti-conversion/Dockerfile index 251c076392..663d7430bc 100644 --- a/docker-compose/nifti-conversion/Dockerfile +++ b/docker-compose/nifti-conversion/Dockerfile @@ -25,14 +25,14 @@ RUN --mount=type=cache,target=/var/cache/apt,sharing=locked,id=apt-conda \ && apt-get -qqy --no-install-recommends install curl ca-certificates # Install miniconda -RUN --mount=type=cache,target=/root/miniconda3/pkgs \ +RUN --mount=type=cache,target=/opt/miniconda3/pkgs \ curl -LSsf -o miniconda.sh https://repo.anaconda.com/miniconda/Miniconda3-py311_24.1.2-0-Linux-x86_64.sh \ - && bash miniconda.sh -fb \ + && bash miniconda.sh -fb -p /opt/miniconda3 \ && rm miniconda.sh # Install dicomifier -RUN --mount=type=cache,target=/root/miniconda3/pkgs \ - /root/miniconda3/bin/conda install -c conda-forge dicomifier -y +RUN --mount=type=cache,target=/opt/miniconda3/pkgs \ + /opt/miniconda3/bin/conda install -c conda-forge dicomifier -y FROM base_image as builder @@ -97,7 +97,7 @@ COPY --link --chmod=0755 external/anima/animaConvertImage /usr/local/bin/ # install the binaries built in the 'builder' & 'conda' stages COPY --link --from=builder /target/. / -COPY --link --from=conda /root/miniconda3 /root/miniconda3 +COPY --link --from=conda /opt/miniconda3 /opt/miniconda3 # update the ld cache (so that the new libraries can be loaded) # and make the log dir @@ -111,6 +111,6 @@ COPY --link entrypoint entrypoint_common oneshot /bin/ ENV LC_ALL fr_FR.UTF-8 ENV LANG fr_FR.UTF-8 ENV LANGUAGE fr_FR.UTF-8 -ENV PATH="/root/miniconda3/bin:${PATH}" +ENV PATH="/opt/miniconda3/bin:${PATH}" ENTRYPOINT ["/bin/entrypoint", "java", "-Djava.security.egd=file:/dev/./urandom", "-Djavax.net.ssl.trustStorePassword=changeit", "-jar", "/nifti-conversion.jar"] From 42beb61a17a773d5698c641b902053ec37b6fc21 Mon Sep 17 00:00:00 2001 From: Anthony Baire Date: Thu, 2 May 2024 20:01:09 +0200 Subject: [PATCH 11/80] document dockerfile optimisations --- docker-compose/README.md | 41 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 docker-compose/README.md diff --git a/docker-compose/README.md b/docker-compose/README.md new file mode 100644 index 0000000000..5cbe802313 --- /dev/null +++ b/docker-compose/README.md @@ -0,0 +1,41 @@ + +## Optimisations it the dockerfiles + +We use multiple strategies to decrease the time to build the docker images and +the overall size of the final image: + +- **common base image**: all images requiring a JRE begin with identical steps + (delimited with `-- common jre base image --` in the dockerfiles), so as to + be built only once. Extra packages are installed using an additional RUN + step. + +- [**multi-stage dockerfiles**][multi-stage] are used for: + - running time-consuming steps in parallel (eg: in *nifti-conversion* we + have a separate stage for installing dicomifier and building dcm2niix) + - reducing the size of the final image: + - by not installing packages that are only needed at build time (eg: gcc, + curl, ...) + - by not storing intermediate files (eg: dcm4che zip archive in + *datasets*) + +- **reduced number of steps**: prefer a single RUN step with all commands + in a shell script rather than a series short RUN steps + +- **caching**: incremental build using the buildkit cache + + - use [**RUN mount --type=cache**][mount-cache] to store the cache of package + managers (eg: apt, conda) in external volumes, which yields two benefits: + - reduced build time (no need to re-download the packages in future builds) + - reduced image size (the package cache is not stored in the final image) + + - use [**COPY --link**][copy-link] as much as possible to make the step + independent from the previous ones, which yields two benefits: + - it does not need to be rebuilt when the previous step have changed + - it can be build without extracting the image of the previous steps, this + is significant for large images (eg. the extraction of *nifti-conversion* + takes around 50 seconds) + + +[multi-stage]: https://docs.docker.com/build/building/multi-stage/ +[copy-link]: https://docs.docker.com/reference/dockerfile/#copy---link +[mount-cache]: https://docs.docker.com/reference/dockerfile/#run---mounttypecache From 5e587e25ae1c145956d238b4105cd8c18a3f1878 Mon Sep 17 00:00:00 2001 From: Anthony Baire Date: Thu, 2 May 2024 20:35:56 +0200 Subject: [PATCH 12/80] rebase the nifti-conversion image on the common jre image --- docker-compose/nifti-conversion/Dockerfile | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/docker-compose/nifti-conversion/Dockerfile b/docker-compose/nifti-conversion/Dockerfile index 663d7430bc..e449db545a 100644 --- a/docker-compose/nifti-conversion/Dockerfile +++ b/docker-compose/nifti-conversion/Dockerfile @@ -62,13 +62,18 @@ RUN mkdir -p /target/opt/nifti-converters/mriconverter \ && chmod 0777 . MRIFileManager/MRIManager.jar -FROM base_image as final - -# NOTE: using bookworm-proposed-updates because of https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1039472 +#--------------- common jre base image ------------------------------------- +FROM debian:bookworm as final +# - disable the automatic "apt-get clean" command (because we mount +# /var/cache/apt from an external volume to speed-up the build) +# - NOTE: using bookworm-proposed-updates because of +# https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1039472 RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ - echo "deb http://deb.debian.org/debian bookworm-proposed-updates main" >> /etc/apt/sources.list \ + rm /etc/apt/apt.conf.d/docker-clean \ + && echo "deb http://deb.debian.org/debian bookworm-proposed-updates main" >> /etc/apt/sources.list \ && apt-get update -qq \ - && apt-get install -qqy openjdk-17-jre ca-certificates-java + && apt-get install -qqy openjdk-17-jre-headless ca-certificates-java +#---------------------------------------------------------------------------- # xvfb+gtk2 needed by mri_conv (headless mode not supported by DicomToNifti) # see: https://populse.github.io/mri_conv/Installation/installation.html#scriptwithoutGUI @@ -80,6 +85,7 @@ RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ locales-all \ jq \ libgtk2.0-0 \ + openjdk-17-jre \ pigz \ xvfb From e193d7ed40c2872d2bd16b17901471250bd68452 Mon Sep 17 00:00:00 2001 From: pierrehenri-dauvergne Date: Mon, 13 May 2024 11:37:58 +0200 Subject: [PATCH 13/80] shanoir-issue#2204: allow research in examination list --- .../examination/controler/ExaminationApi.java | 2 +- .../controler/ExaminationApiController.java | 11 ++- .../repository/ExaminationRepository.java | 10 +++ .../ExaminationRepositoryCustom.java | 6 +- .../repository/ExaminationRepositoryImpl.java | 82 ++++++++++++++----- .../service/ExaminationService.java | 2 +- .../service/ExaminationServiceImpl.java | 18 +++- .../dicom/web/DICOMJsonApiControllerTest.java | 2 +- .../ExaminationApiControllerTest.java | 2 +- .../ExaminationApiSecurityTest.java | 8 +- .../ExaminationServiceSecurityTest.java | 8 +- .../examination-list.component.html | 3 +- .../examination-list.component.ts | 2 +- .../shared/examination.service.ts | 7 +- .../list/animal-examination-list.component.ts | 2 +- .../table/search/search.component.html | 7 +- 16 files changed, 122 insertions(+), 50 deletions(-) diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/examination/controler/ExaminationApi.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/examination/controler/ExaminationApi.java index cb1ec8640a..5537af2adb 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/examination/controler/ExaminationApi.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/examination/controler/ExaminationApi.java @@ -82,7 +82,7 @@ ResponseEntity findExaminationById( @GetMapping(value = "", produces = { "application/json" }) @PreAuthorize("hasAnyRole('ADMIN', 'EXPERT', 'USER')") @PostAuthorize("hasRole('ADMIN') or @datasetSecurityService.filterExaminationDTOPage(returnObject.getBody(), 'CAN_SEE_ALL')") - ResponseEntity> findExaminations(Pageable pageable); + ResponseEntity> findExaminations(Pageable pageable, String searchStr, String searchField); @Operation(summary = "", description = "Returns all the examinations") @ApiResponses(value = { diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/examination/controler/ExaminationApiController.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/examination/controler/ExaminationApiController.java index c8b7021ae0..c826c0fce3 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/examination/controler/ExaminationApiController.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/examination/controler/ExaminationApiController.java @@ -34,6 +34,7 @@ import org.shanoir.ng.shared.event.ShanoirEventService; import org.shanoir.ng.shared.event.ShanoirEventType; import org.shanoir.ng.shared.exception.*; +import org.shanoir.ng.shared.model.Center; import org.shanoir.ng.shared.model.Subject; import org.shanoir.ng.shared.repository.CenterRepository; import org.shanoir.ng.shared.repository.SubjectRepository; @@ -60,6 +61,7 @@ import java.util.ArrayList; import java.util.Comparator; import java.util.List; +import java.util.Optional; @Controller public class ExaminationApiController implements ExaminationApi { @@ -137,13 +139,14 @@ public ResponseEntity findExaminationById( } @Override - public ResponseEntity> findExaminations(final Pageable pageable) { - Page examinations = examinationService.findPage(pageable, false); - if (examinations.getContent().isEmpty()) { + public ResponseEntity> findExaminations(final Pageable pageable, String searchStr, String searchField) { + Page examinations = examinationService.findPage(pageable, false, searchStr, searchField); + if (examinations != null && examinations.getContent().isEmpty()) { return new ResponseEntity<>(HttpStatus.NO_CONTENT); } return new ResponseEntity<>(examinationMapper.examinationsToExaminationDTOs(examinations), HttpStatus.OK); } + @Override public ResponseEntity> findPreclinicalExaminations( @@ -151,7 +154,7 @@ public ResponseEntity> findPreclinicalExaminations( Page examinations; // Get examinations reachable by connected user - examinations = examinationService.findPage(pageable, isPreclinical); + examinations = examinationService.findPage(pageable, isPreclinical, null, null); if (examinations.getContent().isEmpty()) { return new ResponseEntity<>(HttpStatus.NO_CONTENT); } diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/examination/repository/ExaminationRepository.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/examination/repository/ExaminationRepository.java index e62b239f1f..e861741e00 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/examination/repository/ExaminationRepository.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/examination/repository/ExaminationRepository.java @@ -67,6 +67,15 @@ public interface ExaminationRepository extends PagingAndSortingRepository findAllByPreclinical(Pageable pageable, boolean preclinical); + /** + * Get a paginated list of examinations + * + * @param preclinical preclinical examination + * @param pageable pagination data. + * @return list of examinations. + */ + Page findAllByPreclinicalAndComment(Pageable pageable, boolean preclinical, String comment); + /** * Get a list of examinations for a study. * @@ -117,4 +126,5 @@ public interface ExaminationRepository extends PagingAndSortingRepository findPageByComment(String comment, Pageable pageable); } diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/examination/repository/ExaminationRepositoryCustom.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/examination/repository/ExaminationRepositoryCustom.java index f59150a956..8a5ffaf1b3 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/examination/repository/ExaminationRepositoryCustom.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/examination/repository/ExaminationRepositoryCustom.java @@ -14,7 +14,9 @@ public interface ExaminationRepositoryCustom { Page findPageByStudyCenterOrStudyIdIn(Iterable> studyCenterIds, Iterable studyIds, Pageable pageable); Page findPageByStudyCenterOrStudyIdInAndSubjectName(Iterable> studyCenterIds, Iterable studyIds, String subjectName, Pageable pageable); - + + Page findPageByStudyCenterOrStudyIdInAndSearch(Iterable> studyCenterIds, Iterable studyIds, Pageable pageable, Boolean preclinical, String searchStr, String searchField); + List findAllByStudyCenterOrStudyIdIn(Iterable> studyCenterIds, Iterable studyIds); - + } diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/examination/repository/ExaminationRepositoryImpl.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/examination/repository/ExaminationRepositoryImpl.java index f21e63a35b..5a37a0f844 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/examination/repository/ExaminationRepositoryImpl.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/examination/repository/ExaminationRepositoryImpl.java @@ -3,6 +3,8 @@ import jakarta.persistence.EntityManager; import jakarta.persistence.PersistenceContext; import jakarta.persistence.Query; +import jakarta.persistence.TemporalType; +import org.joda.time.DateTime; import org.shanoir.ng.examination.model.Examination; import org.shanoir.ng.shared.paging.PageImpl; import org.slf4j.Logger; @@ -13,7 +15,12 @@ import org.springframework.data.util.Pair; import org.springframework.stereotype.Component; +import java.text.SimpleDateFormat; +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; +import java.util.Date; import java.util.List; +import java.util.Locale; @Component public class ExaminationRepositoryImpl implements ExaminationRepositoryCustom { @@ -28,7 +35,7 @@ public class ExaminationRepositoryImpl implements ExaminationRepositoryCustom { public Page findPageByStudyCenterOrStudyIdIn(Iterable> studyCenterIds, Iterable studyIds, Pageable pageable, Boolean preclinical) { - Pair, Long> pair = find(studyCenterIds, studyIds, pageable, preclinical, null); + Pair, Long> pair = find(studyCenterIds, studyIds, pageable, preclinical, null, null, null); return new PageImpl(pair.getFirst(), pageable, pair.getSecond()); } @@ -36,33 +43,45 @@ public Page findPageByStudyCenterOrStudyIdIn(Iterable findPageByStudyCenterOrStudyIdIn(Iterable> studyCenterIds, Iterable studyIds, Pageable pageable) { - Pair, Long> pair = find(studyCenterIds, studyIds, pageable, null, null); + Pair, Long> pair = find(studyCenterIds, studyIds, pageable, null, null, null, null); return new PageImpl(pair.getFirst(), pageable, pair.getSecond()); } - + @Override public Page findPageByStudyCenterOrStudyIdInAndSubjectName(Iterable> studyCenterIds, - Iterable studyIds, String subjectName, Pageable pageable) { + Iterable studyIds, String subjectName, Pageable pageable) { - Pair, Long> pair = find(studyCenterIds, studyIds, pageable, null, subjectName); - return new PageImpl(pair.getFirst(), pageable, pair.getSecond()); + Pair, Long> pair = find(studyCenterIds, studyIds, pageable, null, subjectName, null, null); + return new PageImpl(pair.getFirst(), pageable, pair.getSecond()); } - + @Override + public Page findPageByStudyCenterOrStudyIdInAndSearch(Iterable> studyCenterIds, + Iterable studyIds, Pageable pageable, Boolean preclinical, String searchStr, String searchField) { + + Pair, Long> pair = find(studyCenterIds, studyIds, pageable, preclinical, null, searchStr, searchField); + return new PageImpl(pair.getFirst(), pageable, pair.getSecond()); + } + @Override public List findAllByStudyCenterOrStudyIdIn(Iterable> studyCenterIds, Iterable studyIds) { - Pair, Long> pair = find(studyCenterIds, studyIds, null, null, null); + Pair, Long> pair = find(studyCenterIds, studyIds, null, null, null, null, null); return pair.getFirst(); } @SuppressWarnings("unchecked") private Pair, Long> find(Iterable> studyCenterIds, - Iterable studyIds, Pageable pageable, Boolean preclinical, String subjectName) { - + Iterable studyIds, Pageable pageable, Boolean preclinical, String subjectName, String searchStr, String searchField) { + String queryEndStr = "from Examination as ex "; int nbPreParams = 1; int preclinicalIndex = -1; int subjectNameIndex = -1; + int searchStrIndex = -1; + + if (searchField != null && (searchField.equals("center.name") || searchField.equals(""))) { + queryEndStr += "inner join Center as c on ex.centerId = c.id "; + } if (preclinical != null) { nbPreParams++; preclinicalIndex = nbPreParams; @@ -72,9 +91,32 @@ private Pair, Long> find(Iterable> studyCente nbPreParams++; subjectNameIndex = nbPreParams; queryEndStr += "and ex.subject.name is ?" + subjectNameIndex + " "; - } + } + if (searchStr != null && !searchStr.isEmpty()) { + nbPreParams++; + searchStrIndex = nbPreParams; + if (searchField != null && !searchField.isEmpty()) { + if (searchField.equals("id")) { + queryEndStr += "and CAST(ex.id as String) LIKE CONCAT('%', ?" + searchStrIndex + ", '%') "; + } else if (searchField.equals("center.name")) { + queryEndStr += "and c.name LIKE CONCAT('%', ?" + searchStrIndex + ", '%') "; + } else if (searchField.equals("examinationDate")) { + queryEndStr += "and CAST(DATE_FORMAT(ex." + searchField + ", '%d/%m/%Y') as String) LIKE CONCAT('%', ?" + searchStrIndex + ", '%') "; + } else { + queryEndStr += "and ex." + searchField + " LIKE CONCAT('%', ?" + searchStrIndex + ", '%') "; + } + } else { + // filter '*' + queryEndStr += "and (CAST(ex.id as String) LIKE CONCAT('%', ?" + searchStrIndex + ", '%') "; + queryEndStr += "or c.name LIKE CONCAT('%', ?" + searchStrIndex + ", '%') "; + queryEndStr += "or CAST(DATE_FORMAT(ex.examinationDate, '%d/%m/%Y') as String) LIKE CONCAT('%', ?" + searchStrIndex + ", '%') "; + queryEndStr += "or ex.study.name LIKE CONCAT('%', ?" + searchStrIndex + ", '%') "; + queryEndStr += "or ex.subject.name LIKE CONCAT('%', ?" + searchStrIndex + ", '%') "; + queryEndStr += "or ex.comment LIKE CONCAT('%', ?" + searchStrIndex + ", '%')) "; + } + } queryEndStr += "and (ex.study.id in ?1 "; - + int i = nbPreParams + 1; for (@SuppressWarnings("unused") Pair studyCenter : studyCenterIds) { queryEndStr += "or (ex.study.id = ?" + i + " and ex.centerId = ?" + (i + 1) + ") "; @@ -96,11 +138,9 @@ private Pair, Long> find(Iterable> studyCente isort ++; } } - - LOG.debug("examination paging hql query : " + queryStr); - + Query query = entityManager.createQuery(queryStr); - + query.setParameter(1, studyIds); if (preclinical != null) { query.setParameter(preclinicalIndex, preclinical); @@ -108,6 +148,9 @@ private Pair, Long> find(Iterable> studyCente if (subjectName != null) { query.setParameter(subjectNameIndex, subjectName); } + if (searchStr != null) { + query.setParameter(searchStrIndex, searchStr); + } i = nbPreParams + 1; for (Pair studyCenter : studyCenterIds) { query.setParameter(i, studyCenter.getFirst()); @@ -118,7 +161,7 @@ private Pair, Long> find(Iterable> studyCente Long total = null; if (pageable != null) { String queryCountStr = "select count(ex) " + queryEndStr; - Query queryCount = entityManager.createQuery(queryCountStr); + Query queryCount = entityManager.createQuery(queryCountStr); queryCount.setParameter(1, studyIds); if (preclinical != null) { queryCount.setParameter(preclinicalIndex, preclinical); @@ -126,6 +169,9 @@ private Pair, Long> find(Iterable> studyCente if (subjectName != null) { queryCount.setParameter(subjectNameIndex, subjectName); } + if (searchStr != null) { + queryCount.setParameter(searchStrIndex, searchStr); + } i = nbPreParams + 1; for (Pair studyCenter : studyCenterIds) { queryCount.setParameter(i, studyCenter.getFirst()); @@ -136,8 +182,6 @@ private Pair, Long> find(Iterable> studyCente query.setFirstResult(Math.toIntExact(pageable.getPageNumber() * pageable.getPageSize())); query.setMaxResults(pageable.getPageSize()); } - - LOG.debug("examination paging query : " + query); return Pair.of(query.getResultList(), total); } diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/examination/service/ExaminationService.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/examination/service/ExaminationService.java index d0bbf3b354..192404a119 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/examination/service/ExaminationService.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/examination/service/ExaminationService.java @@ -64,7 +64,7 @@ public interface ExaminationService { */ @PreAuthorize("hasAnyRole('ADMIN', 'EXPERT', 'USER')") @PostAuthorize("hasRole('ADMIN') or @datasetSecurityService.filterExaminationPage(returnObject, 'CAN_SEE_ALL')") - Page findPage(final Pageable pageable, boolean preclinical); + Page findPage(final Pageable pageable, boolean preclinical, String searchStr, String searchField); /** * Get a paginated list of examinations reachable by connected user. diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/examination/service/ExaminationServiceImpl.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/examination/service/ExaminationServiceImpl.java index 1ef371369d..d9cd02e9f5 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/examination/service/ExaminationServiceImpl.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/examination/service/ExaminationServiceImpl.java @@ -139,14 +139,26 @@ public List findAll() { } @Override - public Page findPage(final Pageable pageable, boolean preclinical) { + public Page findPage(final Pageable pageable, boolean preclinical, String searchStr, String searchField) { if (KeycloakUtil.getTokenRoles().contains("ROLE_ADMIN")) { - return examinationRepository.findAllByPreclinical(pageable, preclinical); + System.out.println("findPage admin"); + if (searchStr != null && searchStr.length() > 1) { + return examinationRepository.findAllByPreclinicalAndComment(pageable, preclinical, searchStr); + } else { + return examinationRepository.findAllByPreclinical(pageable, preclinical); + } + } else { List> studyCenters = new ArrayList<>(); Set unrestrictedStudies = new HashSet(); securityService.getStudyCentersAndUnrestrictedStudies(studyCenters, unrestrictedStudies); - return examinationRepository.findPageByStudyCenterOrStudyIdIn(studyCenters, unrestrictedStudies, pageable, preclinical); + + if (searchStr != null && searchStr.length() > 1) { + return examinationRepository.findPageByStudyCenterOrStudyIdInAndSearch(studyCenters, unrestrictedStudies, pageable, preclinical, searchStr, searchField); + } else { + return examinationRepository.findPageByStudyCenterOrStudyIdIn(studyCenters, unrestrictedStudies, pageable, preclinical); + } + } } diff --git a/shanoir-ng-datasets/src/test/java/org/shanoir/ng/dicom/web/DICOMJsonApiControllerTest.java b/shanoir-ng-datasets/src/test/java/org/shanoir/ng/dicom/web/DICOMJsonApiControllerTest.java index d6c1526431..2cfecd422d 100644 --- a/shanoir-ng-datasets/src/test/java/org/shanoir/ng/dicom/web/DICOMJsonApiControllerTest.java +++ b/shanoir-ng-datasets/src/test/java/org/shanoir/ng/dicom/web/DICOMJsonApiControllerTest.java @@ -76,7 +76,7 @@ public class DICOMJsonApiControllerTest { public void setup() throws ShanoirException, SolrServerException, IOException, RestServiceException { gson = new GsonBuilder().setDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'").create(); doNothing().when(examinationServiceMock).deleteById(1L); - given(examinationServiceMock.findPage(Mockito.any(Pageable.class), Mockito.eq(false))).willReturn(new PageImpl(Arrays.asList(new Examination()))); + given(examinationServiceMock.findPage(Mockito.any(Pageable.class), Mockito.eq(false), Mockito.eq(""), Mockito.eq(""))).willReturn(new PageImpl(Arrays.asList(new Examination()))); Examination exam = new Examination(); exam.setId(Long.valueOf(123)); given(examinationServiceMock.save(Mockito.any(Examination.class))).willReturn(exam); diff --git a/shanoir-ng-datasets/src/test/java/org/shanoir/ng/examination/ExaminationApiControllerTest.java b/shanoir-ng-datasets/src/test/java/org/shanoir/ng/examination/ExaminationApiControllerTest.java index a729cdd920..c4b14fc18d 100644 --- a/shanoir-ng-datasets/src/test/java/org/shanoir/ng/examination/ExaminationApiControllerTest.java +++ b/shanoir-ng-datasets/src/test/java/org/shanoir/ng/examination/ExaminationApiControllerTest.java @@ -139,7 +139,7 @@ public void beforeClass() { @BeforeEach public void setup() throws ShanoirException, SolrServerException, IOException, RestServiceException { doNothing().when(examinationServiceMock).deleteById(1L); - given(examinationServiceMock.findPage(Mockito.any(Pageable.class), Mockito.eq(false))).willReturn(new PageImpl(Arrays.asList(new Examination()))); + given(examinationServiceMock.findPage(Mockito.any(Pageable.class), Mockito.eq(false), Mockito.eq(""), Mockito.eq(""))).willReturn(new PageImpl(Arrays.asList(new Examination()))); Examination exam = new Examination(); exam.setId(Long.valueOf(123)); given(examinationServiceMock.save(Mockito.any(Examination.class))).willReturn(exam); diff --git a/shanoir-ng-datasets/src/test/java/org/shanoir/ng/examination/ExaminationApiSecurityTest.java b/shanoir-ng-datasets/src/test/java/org/shanoir/ng/examination/ExaminationApiSecurityTest.java index 89a1001492..e7490f39cf 100644 --- a/shanoir-ng-datasets/src/test/java/org/shanoir/ng/examination/ExaminationApiSecurityTest.java +++ b/shanoir-ng-datasets/src/test/java/org/shanoir/ng/examination/ExaminationApiSecurityTest.java @@ -97,7 +97,7 @@ public void testAsAnonymous() throws ShanoirException, RestServiceException { given(rightsService.hasRightOnStudies(ids, Mockito.anyString())).willReturn(ids); assertAccessDenied(api::deleteExamination, 1L); assertAccessDenied(api::findExaminationById, 1L); - assertAccessDenied(api::findExaminations, PageRequest.of(0, 10)); + assertAccessDenied(api::findExaminations, PageRequest.of(0, 10), "", ""); assertAccessDenied((subjectId, studyId) -> api.findExaminationsBySubjectIdStudyId(subjectId, studyId), 1L, 1L); assertAccessDenied(api::findExaminationsBySubjectId, 1L); assertAccessDenied(api::saveNewExamination, new ExaminationDTO(), mockBindingResult); @@ -121,7 +121,7 @@ public void testAsExpert() throws ShanoirException, RestServiceException { public void testAsAdmin() throws ShanoirException, RestServiceException { assertAccessAuthorized(api::deleteExamination, 1L); assertAccessAuthorized(api::findExaminationById, 1L); - assertAccessAuthorized(api::findExaminations, PageRequest.of(0, 10)); + assertAccessAuthorized(api::findExaminations, PageRequest.of(0, 10), "", ""); assertAccessAuthorized((subjectId, studyId) -> api.findExaminationsBySubjectIdStudyId(subjectId, studyId), 1L, 1L); assertAccessAuthorized(api::findExaminationsBySubjectId, 1L); assertAccessAuthorized(api::saveNewExamination, new ExaminationDTO(), mockBindingResult); @@ -230,8 +230,8 @@ private void testAll(String role) throws ShanoirException, RestServiceException assertAccessDenied(api::findExaminationById, 4L); // findExaminations(Pageable) - assertAccessAuthorized(api::findExaminations, PageRequest.of(0, 10)); - assertThat(api.findExaminations(PageRequest.of(0, 10)).getBody()).hasSize(1); + assertAccessAuthorized(api::findExaminations, PageRequest.of(0, 10), "", ""); + assertThat(api.findExaminations(PageRequest.of(0, 10), "", "").getBody()).hasSize(1); // findPreclinicalExaminations(Boolean, Pageable) assertAccessAuthorized(api::findPreclinicalExaminations, true, PageRequest.of(0, 10)); diff --git a/shanoir-ng-datasets/src/test/java/org/shanoir/ng/examination/ExaminationServiceSecurityTest.java b/shanoir-ng-datasets/src/test/java/org/shanoir/ng/examination/ExaminationServiceSecurityTest.java index 065e15e614..27897a3656 100644 --- a/shanoir-ng-datasets/src/test/java/org/shanoir/ng/examination/ExaminationServiceSecurityTest.java +++ b/shanoir-ng-datasets/src/test/java/org/shanoir/ng/examination/ExaminationServiceSecurityTest.java @@ -95,7 +95,7 @@ public void testAsAnonymous() throws ShanoirException { Set ids = Mockito.anySet(); given(rightsService.hasRightOnStudies(ids, Mockito.anyString())).willReturn(ids); assertAccessDenied(service::findById, ENTITY_ID); - assertAccessDenied(service::findPage, PageRequest.of(0, 10), false); + assertAccessDenied(service::findPage, PageRequest.of(0, 10), false, "", ""); assertAccessDenied(service::findBySubjectId, 1L); assertAccessDenied(service::findBySubjectIdStudyId, 1L, 1L); assertAccessDenied(service::save, mockExam(null)); @@ -135,7 +135,7 @@ public void testAsExpert() throws ShanoirException { @WithMockKeycloakUser(id = LOGGED_USER_ID, username = LOGGED_USER_USERNAME, authorities = { "ROLE_ADMIN" }) public void testAsAdmin() throws ShanoirException { assertAccessAuthorized(service::findById, ENTITY_ID); - assertAccessAuthorized(service::findPage, PageRequest.of(0, 10), false); + assertAccessAuthorized(service::findPage, PageRequest.of(0, 10), false, "", ""); assertAccessAuthorized(service::findBySubjectId, 1L); assertAccessAuthorized(service::findBySubjectIdStudyId, 1L, 1L); assertAccessAuthorized(service::save, mockExam(null)); @@ -153,8 +153,8 @@ private void testFindOne() throws ShanoirException { private void testFindPage() throws ShanoirException { - assertAccessAuthorized(service::findPage, PageRequest.of(0, 10), false); - assertThat(service.findPage(PageRequest.of(0, 10), false)).hasSize(1); + assertAccessAuthorized(service::findPage, PageRequest.of(0, 10), false, "", ""); + assertThat(service.findPage(PageRequest.of(0, 10), false, "", "")).hasSize(1); } private void testFindBySubjectId() throws ShanoirException { diff --git a/shanoir-ng-front/src/app/examinations/examination-list/examination-list.component.html b/shanoir-ng-front/src/app/examinations/examination-list/examination-list.component.html index ba65e4656b..1f0a12d423 100644 --- a/shanoir-ng-front/src/app/examinations/examination-list/examination-list.component.html +++ b/shanoir-ng-front/src/app/examinations/examination-list/examination-list.component.html @@ -18,6 +18,5 @@

      Manage examinations + [rowRoute]="getRowRoute.bind(this)"> diff --git a/shanoir-ng-front/src/app/examinations/examination-list/examination-list.component.ts b/shanoir-ng-front/src/app/examinations/examination-list/examination-list.component.ts index 35c1a01579..e192f7014d 100644 --- a/shanoir-ng-front/src/app/examinations/examination-list/examination-list.component.ts +++ b/shanoir-ng-front/src/app/examinations/examination-list/examination-list.component.ts @@ -46,7 +46,7 @@ export class ExaminationListComponent extends EntityListComponent{ } getPage(pageable: Pageable): Promise> { - return this.examinationService.getPage(pageable).then(page => { + return this.examinationService.getPage(pageable, false, this.table.filter.searchStr? this.table.filter.searchStr : "", this.table.filter.searchField ? this.table.filter.searchField : "").then(page => { return page; }); } diff --git a/shanoir-ng-front/src/app/examinations/shared/examination.service.ts b/shanoir-ng-front/src/app/examinations/shared/examination.service.ts index 024d2f9636..4b6bb99e83 100644 --- a/shanoir-ng-front/src/app/examinations/shared/examination.service.ts +++ b/shanoir-ng-front/src/app/examinations/shared/examination.service.ts @@ -52,10 +52,13 @@ export class ExaminationService extends EntityService { .toPromise(); } - getPage(pageable: Pageable, preclinical: boolean = false): Promise> { + getPage(pageable: Pageable, preclinical: boolean = false, searchStr : string, searchField : string): Promise> { + let params = { 'params': pageable.toParams() }; + params['params']['searchStr'] = searchStr; + params['params']['searchField'] = searchField; return this.http.get>( (!preclinical) ? AppUtils.BACKEND_API_EXAMINATION_URL : (AppUtils.BACKEND_API_EXAMINATION_PRECLINICAL_URL+'/1'), - { 'params': pageable.toParams() } + params ) .toPromise() .then(this.mapPage); diff --git a/shanoir-ng-front/src/app/preclinical/examination/list/animal-examination-list.component.ts b/shanoir-ng-front/src/app/preclinical/examination/list/animal-examination-list.component.ts index c94110ae9d..283ee2e97b 100644 --- a/shanoir-ng-front/src/app/preclinical/examination/list/animal-examination-list.component.ts +++ b/shanoir-ng-front/src/app/preclinical/examination/list/animal-examination-list.component.ts @@ -52,7 +52,7 @@ export class AnimalExaminationListComponent extends EntityListComponent> { - return this.examinationService.getPage(pageable, true); + return this.examinationService.getPage(pageable, true, "", ""); } getColumnDefs(): ColumnDefinition[] { diff --git a/shanoir-ng-front/src/app/shared/components/table/search/search.component.html b/shanoir-ng-front/src/app/shared/components/table/search/search.component.html index 3d920acd7a..8651e1d07a 100644 --- a/shanoir-ng-front/src/app/shared/components/table/search/search.component.html +++ b/shanoir-ng-front/src/app/shared/components/table/search/search.component.html @@ -11,13 +11,12 @@ You should have received a copy of the GNU General Public License along with this program. If not, see https://www.gnu.org/licenses/gpl-3.0.html --> - - + to filter - - \ No newline at end of file + From 66c87434af22798f786c8d27d5aed0311ad65d87 Mon Sep 17 00:00:00 2001 From: Youenn Merel Date: Mon, 13 May 2024 18:29:28 +0200 Subject: [PATCH 14/80] [fli-iam#2194] Add repository, service, endpoints --- .../ng/dataset/controler/DatasetApi.java | 14 ++ .../controler/DatasetApiController.java | 21 ++- .../shanoir/ng/dataset/dto/DatasetDTO.java | 12 +- .../ng/dataset/dto/mapper/DatasetMapper.java | 4 +- .../org/shanoir/ng/dataset/model/Dataset.java | 12 ++ .../shanoir/ng/tag/mapper/StudyTagMapper.java | 20 +++ .../org/shanoir/ng/tag/model/DatasetTag.java | 34 ----- .../org/shanoir/ng/tag/model/StudyTag.java | 126 ++++++++++-------- .../org/shanoir/ng/tag/model/StudyTagDTO.java | 46 +++++++ .../{repository => }/StudyTagRepository.java | 8 +- .../repository/DatasetTagRepository.java | 27 ---- .../ng/tag/service/StudyTagService.java | 21 +++ .../ng/tag/repository/StudyTagRepository.java | 7 + .../ng/tag/service/StudyTagService.java | 18 +++ .../ng/tag/service/StudyTagServiceImpl.java | 42 ++++++ 15 files changed, 286 insertions(+), 126 deletions(-) create mode 100644 shanoir-ng-datasets/src/main/java/org/shanoir/ng/tag/mapper/StudyTagMapper.java delete mode 100644 shanoir-ng-datasets/src/main/java/org/shanoir/ng/tag/model/DatasetTag.java create mode 100644 shanoir-ng-datasets/src/main/java/org/shanoir/ng/tag/model/StudyTagDTO.java rename shanoir-ng-datasets/src/main/java/org/shanoir/ng/tag/repository/{repository => }/StudyTagRepository.java (88%) delete mode 100644 shanoir-ng-datasets/src/main/java/org/shanoir/ng/tag/repository/repository/DatasetTagRepository.java create mode 100644 shanoir-ng-datasets/src/main/java/org/shanoir/ng/tag/service/StudyTagService.java create mode 100644 shanoir-ng-studies/src/main/java/org/shanoir/ng/tag/repository/StudyTagRepository.java create mode 100644 shanoir-ng-studies/src/main/java/org/shanoir/ng/tag/service/StudyTagService.java create mode 100644 shanoir-ng-studies/src/main/java/org/shanoir/ng/tag/service/StudyTagServiceImpl.java diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/dataset/controler/DatasetApi.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/dataset/controler/DatasetApi.java index 75b372426e..ee2c993835 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/dataset/controler/DatasetApi.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/dataset/controler/DatasetApi.java @@ -325,4 +325,18 @@ ResponseEntity downloadStatistics( ResponseEntity> findDatasetsByIds( @RequestParam(value = "datasetIds", required = true) List datasetIds); + @Operation(summary = "", description = "Updates the study tags of a dataset") + @ApiResponses(value = { @ApiResponse(responseCode = "204", description = "dataset updated"), + @ApiResponse(responseCode = "401", description = "unauthorized"), + @ApiResponse(responseCode = "403", description = "forbidden"), + @ApiResponse(responseCode = "422", description = "bad parameters"), + @ApiResponse(responseCode = "500", description = "unexpected error") }) + @PutMapping(value = "/{datasetId}/tags", produces = { "application/json" }, consumes = { + "application/json" }) + @PreAuthorize("@controlerSecurityService.idMatches(#datasetId, #dataset) and hasRole('ADMIN') or (hasRole('EXPERT') and @datasetSecurityService.hasUpdateRightOnDataset(#dataset, 'CAN_ADMINISTRATE'))") + ResponseEntity updateDatasetTags( + @Parameter(name = "id of the dataset", required = true) @PathVariable("datasetId") Long datasetId, + @RequestParam(value = "studyTagIds", name="studyTagIds") List studyTagIds, + BindingResult result) throws RestServiceException, EntityNotFoundException; + } diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/dataset/controler/DatasetApiController.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/dataset/controler/DatasetApiController.java index 76f2169ce7..5fe346f42c 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/dataset/controler/DatasetApiController.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/dataset/controler/DatasetApiController.java @@ -34,7 +34,6 @@ import jakarta.mail.MessagingException; import jakarta.servlet.http.HttpServletResponse; import jakarta.validation.Valid; -import org.apache.solr.client.solrj.SolrServerException; import org.shanoir.ng.dataset.dto.DatasetAndProcessingsDTOInterface; import org.shanoir.ng.dataset.dto.DatasetDTO; import org.shanoir.ng.dataset.dto.mapper.DatasetMapper; @@ -55,13 +54,12 @@ import org.shanoir.ng.importer.service.ImporterService; import org.shanoir.ng.shared.configuration.RabbitMQConfiguration; import org.shanoir.ng.shared.error.FieldErrorMap; -import org.shanoir.ng.shared.event.ShanoirEvent; import org.shanoir.ng.shared.event.ShanoirEventService; -import org.shanoir.ng.shared.event.ShanoirEventType; import org.shanoir.ng.shared.exception.*; import org.shanoir.ng.solr.service.SolrService; +import org.shanoir.ng.tag.model.StudyTag; +import org.shanoir.ng.tag.service.StudyTagService; import org.shanoir.ng.utils.DatasetFileUtils; -import org.shanoir.ng.utils.KeycloakUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.amqp.rabbit.core.RabbitTemplate; @@ -135,6 +133,9 @@ public class DatasetApiController implements DatasetApi { @Autowired private ObjectMapper objectMapper; + @Autowired + private StudyTagService studyTagService; + /** Number of downloadable datasets. */ private static final int DATASET_LIMIT = 500; @@ -242,6 +243,18 @@ public ResponseEntity> findDatasetsByIds return new ResponseEntity<>(dtos, HttpStatus.OK); } + @Override + public ResponseEntity updateDatasetTags(Long datasetId, List studyTagIds, BindingResult result) throws RestServiceException, EntityNotFoundException { + Dataset ds = datasetService.findById(datasetId); + if (ds == null) { + return new ResponseEntity<>(HttpStatus.NOT_FOUND); + } + List tags = studyTagService.findByIds(studyTagIds); + ds.setTags(tags); + datasetService.update(ds); + return new ResponseEntity<>(HttpStatus.OK); + } + @Override public ResponseEntity> findDatasetsByExaminationId(@Parameter(name = "id of the examination", required = true) @PathVariable("examinationId") Long examinationId) { List datasets = datasetService.findByExaminationId(examinationId); diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/dataset/dto/DatasetDTO.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/dataset/dto/DatasetDTO.java index 8d6ab8c435..bb06d34b4c 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/dataset/dto/DatasetDTO.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/dataset/dto/DatasetDTO.java @@ -14,8 +14,8 @@ package org.shanoir.ng.dataset.dto; -import org.shanoir.ng.dataset.model.DatasetExpression; import org.shanoir.ng.shared.dateTime.LocalDateAnnotations; +import org.shanoir.ng.tag.model.StudyTagDTO; import java.time.LocalDate; import java.util.List; @@ -50,6 +50,8 @@ public class DatasetDTO { private String type; + private List tags; + /** * @return the creationDate */ @@ -178,4 +180,12 @@ public Long getCenterId() { public void setCenterId(Long centerId) { this.centerId = centerId; } + + public List getTags() { + return tags; + } + + public void setTags(List tags) { + this.tags = tags; + } } diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/dataset/dto/mapper/DatasetMapper.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/dataset/dto/mapper/DatasetMapper.java index 356ca9f241..b416cedf08 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/dataset/dto/mapper/DatasetMapper.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/dataset/dto/mapper/DatasetMapper.java @@ -14,8 +14,6 @@ package org.shanoir.ng.dataset.dto.mapper; -import java.util.List; - import org.mapstruct.DecoratedWith; import org.mapstruct.IterableMapping; import org.mapstruct.Mapper; @@ -29,6 +27,8 @@ import org.shanoir.ng.shared.paging.PageImpl; import org.springframework.data.domain.Page; +import java.util.List; + /** * Mapper for datasets. * diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/dataset/model/Dataset.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/dataset/model/Dataset.java index f7b87922f8..408bc123dc 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/dataset/model/Dataset.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/dataset/model/Dataset.java @@ -26,6 +26,7 @@ import org.shanoir.ng.processing.model.DatasetProcessing; import org.shanoir.ng.shared.core.model.AbstractEntity; import org.shanoir.ng.shared.dateTime.LocalDateAnnotations; +import org.shanoir.ng.tag.model.StudyTag; import java.time.LocalDate; import java.util.ArrayList; @@ -131,6 +132,9 @@ public abstract class Dataset extends AbstractEntity { @OneToOne(cascade = CascadeType.ALL) private DatasetMetadata updatedMetadata; + @OneToMany(fetch = FetchType.LAZY, mappedBy = "dataset", cascade = CascadeType.ALL, orphanRemoval = true) + private List tags; + private Long sourceId; @JsonIgnore @@ -448,4 +452,12 @@ public String getSOPInstanceUID() { public void setSOPInstanceUID(String sOPInstanceUID) { SOPInstanceUID = sOPInstanceUID; } + + public List getTags() { + return tags; + } + + public void setTags(List studyTags) { + this.tags = studyTags; + } } diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/tag/mapper/StudyTagMapper.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/tag/mapper/StudyTagMapper.java new file mode 100644 index 0000000000..82ea5e85b4 --- /dev/null +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/tag/mapper/StudyTagMapper.java @@ -0,0 +1,20 @@ +package org.shanoir.ng.tag.mapper; + +import org.mapstruct.Mapper; +import org.mapstruct.ReportingPolicy; +import org.shanoir.ng.tag.model.StudyTag; +import org.shanoir.ng.tag.model.StudyTagDTO; + +import java.util.List; + +@Mapper(componentModel = "spring",unmappedTargetPolicy = ReportingPolicy.IGNORE) +public interface StudyTagMapper { + + List studyTagListToStudyTagDTOList(List studyTags); + + StudyTagDTO studyTagToStudyTagDTO(StudyTag studyTag); + + List StudyTagDTOListToStudyTagList(List dtos); + + StudyTag StudyTagDTOToStudyTag(StudyTagDTO dto); +} diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/tag/model/DatasetTag.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/tag/model/DatasetTag.java deleted file mode 100644 index fc2cbaa544..0000000000 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/tag/model/DatasetTag.java +++ /dev/null @@ -1,34 +0,0 @@ -package org.shanoir.ng.tag.model; - -import jakarta.persistence.Entity; -import jakarta.persistence.JoinColumn; -import jakarta.persistence.ManyToOne; -import org.shanoir.ng.dataset.model.Dataset; -import org.shanoir.ng.shared.hateoas.HalEntity; - -@Entity -public class DatasetTag extends HalEntity { - - @ManyToOne - @JoinColumn(name = "tag_id") - private StudyTag tag; - @ManyToOne - @JoinColumn(name = "dataset_id") - private Dataset dataset; - - public StudyTag getTag() { - return tag; - } - - public void setTag(StudyTag tag) { - this.tag = tag; - } - - public Dataset getDataset() { - return dataset; - } - - public void setDataset(Dataset dataset) { - this.dataset = dataset; - } -} diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/tag/model/StudyTag.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/tag/model/StudyTag.java index 0e10a148d8..6dd6b5fe5f 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/tag/model/StudyTag.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/tag/model/StudyTag.java @@ -1,58 +1,78 @@ package org.shanoir.ng.tag.model; -import jakarta.persistence.Entity; -import org.shanoir.ng.shared.hateoas.HalEntity; +import com.fasterxml.jackson.annotation.JsonIgnore; +import jakarta.persistence.*; +import org.shanoir.ng.shared.model.Study; @Entity -public class StudyTag extends HalEntity { - - private static final long serialVersionUID = 1L; - - private String name; - - private String color; - - private long studyId; - - /** - * @return the name - */ - public String getName() { - return name; - } - - /** - * @param name the name to set - */ - public void setName(String name) { - this.name = name; - } - - /** - * @return the color - */ - public String getColor() { - return color; - } - - /** - * @param color the color to set - */ - public void setColor(String color) { - this.color = color; - } - - /** - * @return the study - */ - public long getStudyId() { - return studyId; - } - - /** - * @param study the study to set - */ - public void setStudyId(long study) { - this.studyId = study; - } +@Table +public class StudyTag { + @Id + private Long id; + + private String name; + + private String color; + + @JsonIgnore + @ManyToOne + @JoinColumn(name = "study_id") + private Study study; + + /** + * @return the id + */ + public Long getId() { + return id; + } + + /** + * @param id + * the id to set + */ + public void setId(Long id) { + this.id = id; + } + + /** + * @return the name + */ + public String getName() { + return name; + } + + /** + * @param name the name to set + */ + public void setName(String name) { + this.name = name; + } + + /** + * @return the color + */ + public String getColor() { + return color; + } + + /** + * @param color the color to set + */ + public void setColor(String color) { + this.color = color; + } + + /** + * @return the study + */ + public Study getStudy() { + return study; + } + + /** + * @param study the study to set + */ + public void setStudy(Study study) { + this.study = study; + } } diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/tag/model/StudyTagDTO.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/tag/model/StudyTagDTO.java new file mode 100644 index 0000000000..6a310ae39e --- /dev/null +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/tag/model/StudyTagDTO.java @@ -0,0 +1,46 @@ +package org.shanoir.ng.tag.model; + +import org.shanoir.ng.dicom.web.dto.StudyDTO; + +public class StudyTagDTO { + + private Long id; + + private String name; + + private String color; + + private StudyDTO study; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getColor() { + return color; + } + + public void setColor(String color) { + this.color = color; + } + + public StudyDTO getStudy() { + return study; + } + + public void setStudy(StudyDTO study) { + this.study = study; + } +} diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/tag/repository/repository/StudyTagRepository.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/tag/repository/StudyTagRepository.java similarity index 88% rename from shanoir-ng-datasets/src/main/java/org/shanoir/ng/tag/repository/repository/StudyTagRepository.java rename to shanoir-ng-datasets/src/main/java/org/shanoir/ng/tag/repository/StudyTagRepository.java index d0d1aa7981..e2dadfd01b 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/tag/repository/repository/StudyTagRepository.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/tag/repository/StudyTagRepository.java @@ -11,17 +11,15 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see https://www.gnu.org/licenses/gpl-3.0.html */ - -package org.shanoir.ng.tag.repository.repository; +package org.shanoir.ng.tag.repository; import org.shanoir.ng.tag.model.StudyTag; import org.springframework.data.repository.CrudRepository; /** - * Repository for Subject. + * @author yyao * - * @author msimon */ public interface StudyTagRepository extends CrudRepository { - + } diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/tag/repository/repository/DatasetTagRepository.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/tag/repository/repository/DatasetTagRepository.java deleted file mode 100644 index 4b882c3e77..0000000000 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/tag/repository/repository/DatasetTagRepository.java +++ /dev/null @@ -1,27 +0,0 @@ -/** - * Shanoir NG - Import, manage and share neuroimaging data - * Copyright (C) 2009-2019 Inria - https://www.inria.fr/ - * Contact us on https://project.inria.fr/shanoir/ - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see https://www.gnu.org/licenses/gpl-3.0.html - */ - -package org.shanoir.ng.tag.repository.repository; - -import org.shanoir.ng.tag.model.DatasetTag; -import org.springframework.data.repository.CrudRepository; - -/** - * Repository for Subject. - * - * @author msimon - */ -public interface DatasetTagRepository extends CrudRepository { - -} diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/tag/service/StudyTagService.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/tag/service/StudyTagService.java new file mode 100644 index 0000000000..73256f4b12 --- /dev/null +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/tag/service/StudyTagService.java @@ -0,0 +1,21 @@ +package org.shanoir.ng.tag.service; + +import org.apache.commons.collections4.IterableUtils; +import org.shanoir.ng.tag.model.StudyTag; +import org.shanoir.ng.tag.repository.StudyTagRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +public class StudyTagService { + + @Autowired + private StudyTagRepository repository; + + public List findByIds(List ids){ + return IterableUtils.toList(repository.findAllById(ids)); + } + +} diff --git a/shanoir-ng-studies/src/main/java/org/shanoir/ng/tag/repository/StudyTagRepository.java b/shanoir-ng-studies/src/main/java/org/shanoir/ng/tag/repository/StudyTagRepository.java new file mode 100644 index 0000000000..d91a0afae4 --- /dev/null +++ b/shanoir-ng-studies/src/main/java/org/shanoir/ng/tag/repository/StudyTagRepository.java @@ -0,0 +1,7 @@ +package org.shanoir.ng.tag.repository; + +import org.shanoir.ng.tag.model.StudyTag; +import org.springframework.data.repository.CrudRepository; + +public interface StudyTagRepository extends CrudRepository { +} diff --git a/shanoir-ng-studies/src/main/java/org/shanoir/ng/tag/service/StudyTagService.java b/shanoir-ng-studies/src/main/java/org/shanoir/ng/tag/service/StudyTagService.java new file mode 100644 index 0000000000..1451127f27 --- /dev/null +++ b/shanoir-ng-studies/src/main/java/org/shanoir/ng/tag/service/StudyTagService.java @@ -0,0 +1,18 @@ +package org.shanoir.ng.tag.service; + +import org.shanoir.ng.shared.exception.EntityNotFoundException; +import org.shanoir.ng.study.model.Study; +import org.shanoir.ng.tag.model.StudyTag; +import org.shanoir.ng.tag.model.StudyTagDTO; +import org.springframework.security.access.prepost.PreAuthorize; + +public interface StudyTagService { + + @PreAuthorize("hasAnyRole('ADMIN', 'EXPERT') and @studySecurityService.hasRightOnStudy(#study.id, 'CAN_ADMINISTRATE') and @studySecurityService.studyUsersMatchStudy(#study)") + StudyTag create(Study study, StudyTagDTO dto); + + + void update(StudyTagDTO dto) throws EntityNotFoundException; + + void delete(Long id); +} diff --git a/shanoir-ng-studies/src/main/java/org/shanoir/ng/tag/service/StudyTagServiceImpl.java b/shanoir-ng-studies/src/main/java/org/shanoir/ng/tag/service/StudyTagServiceImpl.java new file mode 100644 index 0000000000..6127ab238e --- /dev/null +++ b/shanoir-ng-studies/src/main/java/org/shanoir/ng/tag/service/StudyTagServiceImpl.java @@ -0,0 +1,42 @@ +package org.shanoir.ng.tag.service; + +import org.shanoir.ng.shared.exception.EntityNotFoundException; +import org.shanoir.ng.study.model.Study; +import org.shanoir.ng.tag.model.StudyTag; +import org.shanoir.ng.tag.model.StudyTagDTO; +import org.shanoir.ng.tag.repository.StudyTagRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class StudyTagServiceImpl implements StudyTagService { + + @Autowired + private StudyTagRepository repository; + + @Override + public StudyTag create(Study study, StudyTagDTO dto) { + StudyTag tag = new StudyTag(); + tag.setStudy(study); + tag.setColor(dto.getColor()); + tag.setName(tag.getName()); + return repository.save(tag); + } + + @Override + public void update(StudyTagDTO dto) throws EntityNotFoundException { + StudyTag tag = repository.findById(dto.getId()) + .orElseThrow(() -> + new EntityNotFoundException("Cannot find study tag with id [" + dto.getId() + "]")); + tag.setColor(dto.getColor()); + tag.setName(tag.getName()); + repository.save(tag); + } + + @Override + public void delete(Long id){ + repository.deleteById(id); + } + + +} From f8d60173c98c334d4881d2e6bf79cf8d68761cb8 Mon Sep 17 00:00:00 2001 From: pierrehenri-dauvergne Date: Tue, 14 May 2024 16:37:52 +0200 Subject: [PATCH 15/80] shanoir-issue#2204: fix search of admins --- .../controler/ExaminationApiController.java | 2 +- .../repository/ExaminationRepository.java | 9 ------- .../repository/ExaminationRepositoryImpl.java | 5 ++-- .../service/ExaminationServiceImpl.java | 21 +++++---------- .../ng/shared/service/SecurityService.java | 27 ++++++++++++------- 5 files changed, 28 insertions(+), 36 deletions(-) diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/examination/controler/ExaminationApiController.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/examination/controler/ExaminationApiController.java index c826c0fce3..3c0c3cac00 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/examination/controler/ExaminationApiController.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/examination/controler/ExaminationApiController.java @@ -141,7 +141,7 @@ public ResponseEntity findExaminationById( @Override public ResponseEntity> findExaminations(final Pageable pageable, String searchStr, String searchField) { Page examinations = examinationService.findPage(pageable, false, searchStr, searchField); - if (examinations != null && examinations.getContent().isEmpty()) { + if (examinations == null || examinations.getContent().isEmpty()) { return new ResponseEntity<>(HttpStatus.NO_CONTENT); } return new ResponseEntity<>(examinationMapper.examinationsToExaminationDTOs(examinations), HttpStatus.OK); diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/examination/repository/ExaminationRepository.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/examination/repository/ExaminationRepository.java index e861741e00..2a9fa3d0fd 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/examination/repository/ExaminationRepository.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/examination/repository/ExaminationRepository.java @@ -67,15 +67,6 @@ public interface ExaminationRepository extends PagingAndSortingRepository findAllByPreclinical(Pageable pageable, boolean preclinical); - /** - * Get a paginated list of examinations - * - * @param preclinical preclinical examination - * @param pageable pagination data. - * @return list of examinations. - */ - Page findAllByPreclinicalAndComment(Pageable pageable, boolean preclinical, String comment); - /** * Get a list of examinations for a study. * diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/examination/repository/ExaminationRepositoryImpl.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/examination/repository/ExaminationRepositoryImpl.java index 5a37a0f844..8c1d81baca 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/examination/repository/ExaminationRepositoryImpl.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/examination/repository/ExaminationRepositoryImpl.java @@ -4,6 +4,7 @@ import jakarta.persistence.PersistenceContext; import jakarta.persistence.Query; import jakarta.persistence.TemporalType; +import org.apache.commons.lang3.StringUtils; import org.joda.time.DateTime; import org.shanoir.ng.examination.model.Examination; import org.shanoir.ng.shared.paging.PageImpl; @@ -79,7 +80,7 @@ private Pair, Long> find(Iterable> studyCente int subjectNameIndex = -1; int searchStrIndex = -1; - if (searchField != null && (searchField.equals("center.name") || searchField.equals(""))) { + if (StringUtils.isEmpty(searchField) || searchField.equals("center.name")) { queryEndStr += "inner join Center as c on ex.centerId = c.id "; } if (preclinical != null) { @@ -92,7 +93,7 @@ private Pair, Long> find(Iterable> studyCente subjectNameIndex = nbPreParams; queryEndStr += "and ex.subject.name is ?" + subjectNameIndex + " "; } - if (searchStr != null && !searchStr.isEmpty()) { + if (!StringUtils.isEmpty(searchStr)) { nbPreParams++; searchStrIndex = nbPreParams; if (searchField != null && !searchField.isEmpty()) { diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/examination/service/ExaminationServiceImpl.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/examination/service/ExaminationServiceImpl.java index d9cd02e9f5..070d5448f0 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/examination/service/ExaminationServiceImpl.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/examination/service/ExaminationServiceImpl.java @@ -140,25 +140,16 @@ public List findAll() { @Override public Page findPage(final Pageable pageable, boolean preclinical, String searchStr, String searchField) { - if (KeycloakUtil.getTokenRoles().contains("ROLE_ADMIN")) { - System.out.println("findPage admin"); - if (searchStr != null && searchStr.length() > 1) { - return examinationRepository.findAllByPreclinicalAndComment(pageable, preclinical, searchStr); - } else { - return examinationRepository.findAllByPreclinical(pageable, preclinical); - } - + if (searchStr != null && searchStr.length() >= 1) { + List> studyCenters = new ArrayList<>(); + Set unrestrictedStudies = new HashSet(); + securityService.getStudyCentersAndUnrestrictedStudies(studyCenters, unrestrictedStudies); + return examinationRepository.findPageByStudyCenterOrStudyIdInAndSearch(studyCenters, unrestrictedStudies, pageable, preclinical, searchStr, searchField); } else { List> studyCenters = new ArrayList<>(); Set unrestrictedStudies = new HashSet(); securityService.getStudyCentersAndUnrestrictedStudies(studyCenters, unrestrictedStudies); - - if (searchStr != null && searchStr.length() > 1) { - return examinationRepository.findPageByStudyCenterOrStudyIdInAndSearch(studyCenters, unrestrictedStudies, pageable, preclinical, searchStr, searchField); - } else { - return examinationRepository.findPageByStudyCenterOrStudyIdIn(studyCenters, unrestrictedStudies, pageable, preclinical); - } - + return examinationRepository.findPageByStudyCenterOrStudyIdIn(studyCenters, unrestrictedStudies, pageable, preclinical); } } diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/shared/service/SecurityService.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/shared/service/SecurityService.java index a79c6f126a..56dc13d11d 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/shared/service/SecurityService.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/shared/service/SecurityService.java @@ -14,6 +14,8 @@ package org.shanoir.ng.shared.service; +import org.shanoir.ng.shared.model.Study; +import org.shanoir.ng.shared.repository.StudyRepository; import org.shanoir.ng.shared.security.rights.StudyUserRight; import org.shanoir.ng.study.rights.StudyUser; import org.shanoir.ng.study.rights.StudyUserRightsRepository; @@ -32,6 +34,9 @@ public class SecurityService { @Autowired StudyUserRightsRepository rightsRepository; + + @Autowired + StudyRepository studyRepository; /** * Get study center rights for the current user as two separate variable. @@ -42,15 +47,19 @@ public class SecurityService { public void getStudyCentersAndUnrestrictedStudies(List> studyCenters, Set unrestrictedStudies) { Long userId = KeycloakUtil.getTokenUserId(); // Check if user has restrictions. - List studyUsers = Utils.toList(rightsRepository.findByUserIdAndRight(userId, StudyUserRight.CAN_SEE_ALL.getId())); -// List> studyCenters = new ArrayList<>(); -// Set unrestrictedStudies = new HashSet(); - for (StudyUser studyUser : studyUsers) { - if (CollectionUtils.isEmpty(studyUser.getCenterIds()) && studyUser.isConfirmed()) { - unrestrictedStudies.add(studyUser.getStudyId()); - } else { - for (Long centerId : studyUser.getCenterIds()) { - studyCenters.add(Pair.of(studyUser.getStudyId(), centerId)); + if (KeycloakUtil.getTokenRoles().contains("ROLE_ADMIN")) { + for (Study stud : studyRepository.findAll()) { + unrestrictedStudies.add(stud.getId()); + } + } else { + List studyUsers = Utils.toList(rightsRepository.findByUserIdAndRight(userId, StudyUserRight.CAN_SEE_ALL.getId())); + for (StudyUser studyUser : studyUsers) { + if (CollectionUtils.isEmpty(studyUser.getCenterIds()) && studyUser.isConfirmed()) { + unrestrictedStudies.add(studyUser.getStudyId()); + } else { + for (Long centerId : studyUser.getCenterIds()) { + studyCenters.add(Pair.of(studyUser.getStudyId(), centerId)); + } } } } From 3f6f1db3d91a1d14873c7c08749b7363e322d93d Mon Sep 17 00:00:00 2001 From: pierrehenri-dauvergne Date: Tue, 14 May 2024 17:38:32 +0200 Subject: [PATCH 16/80] shanoir-issue#2204: fix unit test --- docker-compose-dev.yml | 2 +- .../shanoir/ng/examination/ExaminationApiControllerTest.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docker-compose-dev.yml b/docker-compose-dev.yml index 935c7d2baf..1d5dd33d49 100644 --- a/docker-compose-dev.yml +++ b/docker-compose-dev.yml @@ -24,7 +24,7 @@ services: build: ./docker-compose/front-dev volumes: - "./shanoir-ng-front:/app/" - command: ng serve --host 0.0.0.0 --disable-host-check + command: ng serve --host 0.0.0.0 --disable-host-check --poll 10000 ports: - "4200:4200" tty: true diff --git a/shanoir-ng-datasets/src/test/java/org/shanoir/ng/examination/ExaminationApiControllerTest.java b/shanoir-ng-datasets/src/test/java/org/shanoir/ng/examination/ExaminationApiControllerTest.java index c4b14fc18d..ba29fd4237 100644 --- a/shanoir-ng-datasets/src/test/java/org/shanoir/ng/examination/ExaminationApiControllerTest.java +++ b/shanoir-ng-datasets/src/test/java/org/shanoir/ng/examination/ExaminationApiControllerTest.java @@ -139,7 +139,7 @@ public void beforeClass() { @BeforeEach public void setup() throws ShanoirException, SolrServerException, IOException, RestServiceException { doNothing().when(examinationServiceMock).deleteById(1L); - given(examinationServiceMock.findPage(Mockito.any(Pageable.class), Mockito.eq(false), Mockito.eq(""), Mockito.eq(""))).willReturn(new PageImpl(Arrays.asList(new Examination()))); + given(examinationServiceMock.findPage(Mockito.any(Pageable.class), Mockito.eq(false), Mockito.eq(null), Mockito.eq(null))).willReturn(new PageImpl(Arrays.asList(new Examination()))); Examination exam = new Examination(); exam.setId(Long.valueOf(123)); given(examinationServiceMock.save(Mockito.any(Examination.class))).willReturn(exam); From 396a9a8f9ca03f2b184c91184447c426dda8148e Mon Sep 17 00:00:00 2001 From: Youenn Merel Date: Tue, 14 May 2024 17:41:45 +0200 Subject: [PATCH 17/80] [fli-iam#2194] Synchronize StudyTag in datasets --- .../amqp/RabbitMQDatasetsService.java | 15 ++++ .../org/shanoir/ng/shared/model/Study.java | 13 +++- .../java/org/shanoir/ng/shared/model/Tag.java | 5 +- .../org/shanoir/ng/tag/model/StudyTag.java | 1 - .../ng/tag/repository/StudyTagRepository.java | 3 +- .../core/repository/ReadOnlyRepository.java | 21 ++++++ .../shanoir/ng/study/controler/StudyApi.java | 16 ++++ .../study/controler/StudyApiController.java | 74 ++++++++++--------- .../org/shanoir/ng/study/dto/StudyDTO.java | 7 +- .../ng/tag/model/StudyTagDecorator.java | 16 +++- .../shanoir/ng/tag/model/StudyTagMapper.java | 20 ++++- 11 files changed, 145 insertions(+), 46 deletions(-) create mode 100644 shanoir-ng-ms-common/src/main/java/org/shanoir/ng/shared/core/repository/ReadOnlyRepository.java diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/configuration/amqp/RabbitMQDatasetsService.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/configuration/amqp/RabbitMQDatasetsService.java index d1e956c839..2d37b5981f 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/configuration/amqp/RabbitMQDatasetsService.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/configuration/amqp/RabbitMQDatasetsService.java @@ -53,6 +53,7 @@ import org.shanoir.ng.study.rights.ampq.RabbitMqStudyUserService; import org.shanoir.ng.studycard.model.StudyCard; import org.shanoir.ng.studycard.repository.StudyCardRepository; +import org.shanoir.ng.tag.model.StudyTag; import org.shanoir.ng.utils.SecurityContextUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -197,6 +198,20 @@ public void receiveStudyNameUpdate(final String studyStr) { for (Tag tag : stud.getTags()) { tag.setStudy(stud); } + + // STUDY TAGS + if (stud.getStudyTags() != null) { + stud.getStudyTags().clear(); + } else { + stud.setTags(new ArrayList<>()); + } + if (received.getStudyTags() != null) { + stud.getStudyTags().addAll(received.getStudyTags()); + } + for (StudyTag tag : stud.getStudyTags()) { + tag.setStudy(stud); + } + if (stud.getId() == null) throw new IllegalStateException("The entity should must have an id ! Received string : \"" + studyStr + "\""); Study studyDb = this.studyRepository.save(stud); diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/shared/model/Study.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/shared/model/Study.java index aece41b6b5..47a510c52e 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/shared/model/Study.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/shared/model/Study.java @@ -18,6 +18,7 @@ import org.shanoir.ng.dataset.model.Dataset; import org.shanoir.ng.examination.model.Examination; import org.shanoir.ng.shared.core.model.IdName; +import org.shanoir.ng.tag.model.StudyTag; import java.util.List; @@ -52,6 +53,9 @@ public class Study extends IdName { /** Relations between the subjects and the studies. */ @OneToMany(mappedBy = "study", fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true) private List examinations; + + @OneToMany(fetch = FetchType.LAZY, mappedBy = "study", cascade = CascadeType.ALL, orphanRemoval = true) + private List studyTags; /** @@ -134,5 +138,12 @@ public List getExaminations() { public void setExaminations(List examinations) { this.examinations = examinations; } - + + public List getStudyTags() { + return studyTags; + } + + public void setStudyTags(List studyTags) { + this.studyTags = studyTags; + } } diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/shared/model/Tag.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/shared/model/Tag.java index f06c449328..ee2ed8d62e 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/shared/model/Tag.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/shared/model/Tag.java @@ -1,10 +1,7 @@ package org.shanoir.ng.shared.model; import com.fasterxml.jackson.annotation.JsonIgnore; -import jakarta.persistence.Entity; -import jakarta.persistence.Id; -import jakarta.persistence.JoinColumn; -import jakarta.persistence.ManyToOne; +import jakarta.persistence.*; @Entity public class Tag { diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/tag/model/StudyTag.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/tag/model/StudyTag.java index 6dd6b5fe5f..bac5463cd8 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/tag/model/StudyTag.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/tag/model/StudyTag.java @@ -5,7 +5,6 @@ import org.shanoir.ng.shared.model.Study; @Entity -@Table public class StudyTag { @Id private Long id; diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/tag/repository/StudyTagRepository.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/tag/repository/StudyTagRepository.java index e2dadfd01b..4e25206947 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/tag/repository/StudyTagRepository.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/tag/repository/StudyTagRepository.java @@ -13,6 +13,7 @@ */ package org.shanoir.ng.tag.repository; +import org.shanoir.ng.shared.core.repository.ReadOnlyRepository; import org.shanoir.ng.tag.model.StudyTag; import org.springframework.data.repository.CrudRepository; @@ -20,6 +21,6 @@ * @author yyao * */ -public interface StudyTagRepository extends CrudRepository { +public interface StudyTagRepository extends ReadOnlyRepository { } diff --git a/shanoir-ng-ms-common/src/main/java/org/shanoir/ng/shared/core/repository/ReadOnlyRepository.java b/shanoir-ng-ms-common/src/main/java/org/shanoir/ng/shared/core/repository/ReadOnlyRepository.java new file mode 100644 index 0000000000..cdeefe822a --- /dev/null +++ b/shanoir-ng-ms-common/src/main/java/org/shanoir/ng/shared/core/repository/ReadOnlyRepository.java @@ -0,0 +1,21 @@ +package org.shanoir.ng.shared.core.repository; + +import org.springframework.data.repository.NoRepositoryBean; +import org.springframework.data.repository.Repository; + +import java.util.Optional; + +@NoRepositoryBean +public interface ReadOnlyRepository extends Repository { + + Optional findById(ID id); + + boolean existsById(ID id); + + Iterable findAll(); + + Iterable findAllById(Iterable ids); + + long count(); + +} diff --git a/shanoir-ng-studies/src/main/java/org/shanoir/ng/study/controler/StudyApi.java b/shanoir-ng-studies/src/main/java/org/shanoir/ng/study/controler/StudyApi.java index bedf92a298..5ed13a4878 100644 --- a/shanoir-ng-studies/src/main/java/org/shanoir/ng/study/controler/StudyApi.java +++ b/shanoir-ng-studies/src/main/java/org/shanoir/ng/study/controler/StudyApi.java @@ -19,6 +19,7 @@ import java.util.Map; import org.shanoir.ng.shared.core.model.IdName; +import org.shanoir.ng.shared.exception.EntityNotFoundException; import org.shanoir.ng.shared.exception.MicroServiceCommunicationException; import org.shanoir.ng.shared.exception.RestServiceException; import org.shanoir.ng.shared.exception.ShanoirException; @@ -31,6 +32,7 @@ import org.shanoir.ng.study.dua.DataUserAgreement; import org.shanoir.ng.study.model.Study; import org.shanoir.ng.study.model.StudyUser; +import org.shanoir.ng.tag.model.StudyTagDTO; import org.springframework.http.ResponseEntity; import org.springframework.security.access.prepost.PostAuthorize; import org.springframework.security.access.prepost.PreAuthorize; @@ -365,4 +367,18 @@ ResponseEntity deleteStudyUser( ResponseEntity> getStudyStatistics( @Parameter(name = "id of the study", required = true) @PathVariable("studyId") Long studyId) throws RestServiceException, IOException; + @Operation(summary = "", description = "Updates study tags") + @ApiResponses(value = { @ApiResponse(responseCode = "204", description = "study tags updated"), + @ApiResponse(responseCode = "401", description = "unauthorized"), + @ApiResponse(responseCode = "403", description = "forbidden"), + @ApiResponse(responseCode = "422", description = "bad parameters"), + @ApiResponse(responseCode = "500", description = "unexpected error") }) + @PutMapping(value = "/{studyId}/tags", produces = { "application/json" }, consumes = { + "application/json" }) + @PreAuthorize("hasAnyRole('ADMIN', 'EXPERT') and @controlerSecurityService.idMatches(#studyId, #study) and @studySecurityService.hasRightOnStudy(#studyId, 'CAN_ADMINISTRATE')") + ResponseEntity updateStudyTags( + @Parameter(name = "id of the study", required = true) @PathVariable("studyId") Long studyId, + @RequestParam(value = "studyTags", name="studyTags") List studyTags, + BindingResult result) throws RestServiceException, EntityNotFoundException, MicroServiceCommunicationException; + } \ No newline at end of file diff --git a/shanoir-ng-studies/src/main/java/org/shanoir/ng/study/controler/StudyApiController.java b/shanoir-ng-studies/src/main/java/org/shanoir/ng/study/controler/StudyApiController.java index 361d59900b..f3ba03448d 100644 --- a/shanoir-ng-studies/src/main/java/org/shanoir/ng/study/controler/StudyApiController.java +++ b/shanoir-ng-studies/src/main/java/org/shanoir/ng/study/controler/StudyApiController.java @@ -14,40 +14,19 @@ package org.shanoir.ng.study.controler; -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.net.MalformedURLException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Map; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; +import io.swagger.v3.oas.annotations.Parameter; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.Valid; import org.apache.commons.io.FileUtils; -import org.shanoir.ng.shared.configuration.RabbitMQConfiguration; import org.shanoir.ng.shared.core.model.IdName; import org.shanoir.ng.shared.error.FieldErrorMap; import org.shanoir.ng.shared.event.ShanoirEvent; import org.shanoir.ng.shared.event.ShanoirEventService; import org.shanoir.ng.shared.event.ShanoirEventType; -import org.shanoir.ng.shared.exception.EntityNotFoundException; -import org.shanoir.ng.shared.exception.ErrorDetails; -import org.shanoir.ng.shared.exception.ErrorModel; -import org.shanoir.ng.shared.exception.MicroServiceCommunicationException; -import org.shanoir.ng.shared.exception.RestServiceException; -import org.shanoir.ng.shared.exception.ShanoirException; +import org.shanoir.ng.shared.exception.*; import org.shanoir.ng.shared.security.rights.StudyUserRight; -import org.shanoir.ng.study.dto.IdNameCenterStudyDTO; -import org.shanoir.ng.study.dto.PublicStudyDTO; -import org.shanoir.ng.study.dto.StudyDTO; -import org.shanoir.ng.study.dto.StudyStorageVolumeDTO; -import org.shanoir.ng.study.dto.StudyStatisticsDTO; +import org.shanoir.ng.study.dto.*; import org.shanoir.ng.study.dto.mapper.StudyMapper; import org.shanoir.ng.study.dua.DataUserAgreement; import org.shanoir.ng.study.dua.DataUserAgreementService; @@ -58,11 +37,11 @@ import org.shanoir.ng.study.service.StudyService; import org.shanoir.ng.study.service.StudyUniqueConstraintManager; import org.shanoir.ng.study.service.StudyUserService; +import org.shanoir.ng.tag.model.StudyTagDTO; +import org.shanoir.ng.tag.model.StudyTagMapper; import org.shanoir.ng.utils.KeycloakUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.amqp.AmqpException; -import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.HttpStatus; @@ -76,10 +55,17 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.multipart.MultipartFile; -import io.swagger.v3.oas.annotations.Parameter; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import jakarta.validation.Valid; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; @Controller public class StudyApiController implements StudyApi { @@ -95,6 +81,9 @@ public class StudyApiController implements StudyApi { @Autowired private StudyMapper studyMapper; + @Autowired + private StudyTagMapper studyTagMapper; + @Autowired private StudyFieldEditionSecurityManager fieldEditionSecurityManager; @@ -566,4 +555,23 @@ public ResponseEntity> getStudyStatistics(@Parameter(na } } + @Override + public ResponseEntity updateStudyTags(Long studyId, List studyTags, BindingResult result) throws MicroServiceCommunicationException, RestServiceException { + Study study = studyService.findById(studyId); + + if(study == null){ + return new ResponseEntity<>(HttpStatus.NOT_FOUND); + } + + study.setStudyTags(studyTagMapper.studyTagDTOListToStudyTagList(studyTags)); + + try { + studyService.update(study); + } catch (EntityNotFoundException e) { + throw new RestServiceException(new ErrorModel(HttpStatus.NOT_FOUND.value(), "Study [" + studyId + "] not found.", e)); + } + + return new ResponseEntity<>(HttpStatus.OK); + } + } diff --git a/shanoir-ng-studies/src/main/java/org/shanoir/ng/study/dto/StudyDTO.java b/shanoir-ng-studies/src/main/java/org/shanoir/ng/study/dto/StudyDTO.java index 0a533a6ee3..627fdc8b1a 100644 --- a/shanoir-ng-studies/src/main/java/org/shanoir/ng/study/dto/StudyDTO.java +++ b/shanoir-ng-studies/src/main/java/org/shanoir/ng/study/dto/StudyDTO.java @@ -25,6 +25,7 @@ import org.shanoir.ng.study.model.StudyUser; import org.shanoir.ng.studycenter.StudyCenterDTO; import org.shanoir.ng.subjectstudy.dto.SubjectStudyDTO; +import org.shanoir.ng.tag.model.StudyTagDTO; import org.shanoir.ng.tag.model.TagDTO; import org.shanoir.ng.timepoint.TimepointDTO; @@ -63,7 +64,7 @@ public class StudyDTO { private List tags; - private List studyTags; + private List studyTags; @LocalDateAnnotations private LocalDate startDate; @@ -451,11 +452,11 @@ public void setTags(List tags) { this.tags = tags; } - public List getStudyTags() { + public List getStudyTags() { return studyTags; } - public void setStudyTags(List studyTags) { + public void setStudyTags(List studyTags) { this.studyTags = studyTags; } diff --git a/shanoir-ng-studies/src/main/java/org/shanoir/ng/tag/model/StudyTagDecorator.java b/shanoir-ng-studies/src/main/java/org/shanoir/ng/tag/model/StudyTagDecorator.java index e39638fdde..bf45201b07 100644 --- a/shanoir-ng-studies/src/main/java/org/shanoir/ng/tag/model/StudyTagDecorator.java +++ b/shanoir-ng-studies/src/main/java/org/shanoir/ng/tag/model/StudyTagDecorator.java @@ -12,7 +12,21 @@ public class StudyTagDecorator implements StudyTagMapper { @Override public StudyTagDTO studyTagToStudyTagDTO(StudyTag studyTag) { return delegate.studyTagToStudyTagDTO(studyTag); } - + + @Override + public StudyTag studyTagDTOToStudyTag(StudyTagDTO studyTagDTO) { return delegate.studyTagDTOToStudyTag(studyTagDTO); } + + @Override + public List studyTagDTOListToStudyTagList(List studyTagDTOs) { + final List studyTags = new ArrayList<>(); + if (studyTagDTOs != null) { + for (StudyTagDTO studyTagDTO : studyTagDTOs) { + studyTags.add(studyTagDTOToStudyTag(studyTagDTO)); + } + } + return studyTags; + } + @Override public List studyTagListToStudyTagDTOList(List studyTagList) { final List studyTagDTOs = new ArrayList<>(); diff --git a/shanoir-ng-studies/src/main/java/org/shanoir/ng/tag/model/StudyTagMapper.java b/shanoir-ng-studies/src/main/java/org/shanoir/ng/tag/model/StudyTagMapper.java index bc1be80fce..776b4461c8 100644 --- a/shanoir-ng-studies/src/main/java/org/shanoir/ng/tag/model/StudyTagMapper.java +++ b/shanoir-ng-studies/src/main/java/org/shanoir/ng/tag/model/StudyTagMapper.java @@ -10,7 +10,7 @@ public interface StudyTagMapper { /** - * Map list of @Tag to list of @TagDTO. + * Map list of @StudyTag to list of @StudyTagDTO. * * @param studyTagList * list of tags @@ -19,10 +19,26 @@ public interface StudyTagMapper { List studyTagListToStudyTagDTOList(List studyTagList); /** - * Map a @Tag to a @TagDTO. + * Map list of @StudyTagDTO to list of @StudyTag. + * + * @param studyTags + * @return + */ + List studyTagDTOListToStudyTagList(List studyTags); + + /** + * Map a @StudyTag to a @StudyTagDTO. * * @param studyTag * @return DTO. */ StudyTagDTO studyTagToStudyTagDTO(StudyTag studyTag); + + /** + * Map a @StudyTag to a @StudyTagDTO. + * + * @param studyTag + * @return DTO. + */ + StudyTag studyTagDTOToStudyTag(StudyTagDTO studyTagDTO); } From e242d6b9a56dbe292e861a8ca872ab0bc9acdec1 Mon Sep 17 00:00:00 2001 From: pierrehenri-dauvergne Date: Wed, 15 May 2024 14:25:43 +0200 Subject: [PATCH 18/80] shanoir-issue#2199: update db-script to add study_card_policy column --- .../db-changes/studies/0020_set_default_studycardpolicy.sql | 1 + 1 file changed, 1 insertion(+) diff --git a/docker-compose/database/db-changes/studies/0020_set_default_studycardpolicy.sql b/docker-compose/database/db-changes/studies/0020_set_default_studycardpolicy.sql index 6c2feb1618..8c8f1fdd6b 100644 --- a/docker-compose/database/db-changes/studies/0020_set_default_studycardpolicy.sql +++ b/docker-compose/database/db-changes/studies/0020_set_default_studycardpolicy.sql @@ -1 +1,2 @@ +ALTER TABLE study ADD COLUMN study_card_policy tinyint(4) DEFAULT NULL; UPDATE study SET study_card_policy=0; \ No newline at end of file From 0cbe33f296460706e3d97070c4ad6bd2dc37b533 Mon Sep 17 00:00:00 2001 From: Youenn Merel Date: Wed, 15 May 2024 15:39:49 +0200 Subject: [PATCH 19/80] [fli-iam#2194] Correct unit tests --- .../main/java/org/shanoir/ng/dataset/model/Dataset.java | 3 ++- .../shanoir/ng/dataset/service/DatasetServiceImpl.java | 1 + .../org/shanoir/ng/dataset/DatasetApiControllerTest.java | 8 ++++++++ .../java/org/shanoir/ng/study/StudyApiControllerTest.java | 4 ++++ 4 files changed, 15 insertions(+), 1 deletion(-) diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/dataset/model/Dataset.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/dataset/model/Dataset.java index 408bc123dc..3ee1cf68ed 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/dataset/model/Dataset.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/dataset/model/Dataset.java @@ -132,7 +132,8 @@ public abstract class Dataset extends AbstractEntity { @OneToOne(cascade = CascadeType.ALL) private DatasetMetadata updatedMetadata; - @OneToMany(fetch = FetchType.LAZY, mappedBy = "dataset", cascade = CascadeType.ALL, orphanRemoval = true) + @ManyToMany + @JoinTable(name = "DATASET_TAG", joinColumns = @JoinColumn(name = "DATASET_ID"), inverseJoinColumns = @JoinColumn(name = "STUDY_TAG_ID")) private List tags; private Long sourceId; diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/dataset/service/DatasetServiceImpl.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/dataset/service/DatasetServiceImpl.java index 1953e45d4e..98fdd6c086 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/dataset/service/DatasetServiceImpl.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/dataset/service/DatasetServiceImpl.java @@ -100,6 +100,7 @@ public class DatasetServiceImpl implements DatasetService { @Value("${dcm4chee-arc.dicom.web}") private boolean dicomWeb; + @Autowired private DatasetProcessingService processingService; diff --git a/shanoir-ng-datasets/src/test/java/org/shanoir/ng/dataset/DatasetApiControllerTest.java b/shanoir-ng-datasets/src/test/java/org/shanoir/ng/dataset/DatasetApiControllerTest.java index 61711e73a8..b163d485f6 100644 --- a/shanoir-ng-datasets/src/test/java/org/shanoir/ng/dataset/DatasetApiControllerTest.java +++ b/shanoir-ng-datasets/src/test/java/org/shanoir/ng/dataset/DatasetApiControllerTest.java @@ -48,6 +48,8 @@ import org.shanoir.ng.shared.repository.SubjectRepository; import org.shanoir.ng.shared.security.ControlerSecurityService; import org.shanoir.ng.solr.service.SolrService; +import org.shanoir.ng.tag.mapper.StudyTagMapper; +import org.shanoir.ng.tag.service.StudyTagService; import org.shanoir.ng.utils.ModelsUtil; import org.shanoir.ng.utils.usermock.WithMockKeycloakUser; import org.springframework.amqp.rabbit.core.RabbitTemplate; @@ -92,9 +94,15 @@ public class DatasetApiControllerTest { @MockBean private DatasetService datasetServiceMock; + @MockBean + private StudyTagService studyTagServiceMock; + @MockBean private DatasetMapper datasetMapperMock; + @MockBean + private StudyTagMapper studyTagMapperMock; + @MockBean private MrDatasetMapper mrDatasetMapperMock; diff --git a/shanoir-ng-studies/src/test/java/org/shanoir/ng/study/StudyApiControllerTest.java b/shanoir-ng-studies/src/test/java/org/shanoir/ng/study/StudyApiControllerTest.java index 0e07cad193..a7d3bab2c8 100644 --- a/shanoir-ng-studies/src/test/java/org/shanoir/ng/study/StudyApiControllerTest.java +++ b/shanoir-ng-studies/src/test/java/org/shanoir/ng/study/StudyApiControllerTest.java @@ -49,6 +49,7 @@ import org.shanoir.ng.study.service.StudyService; import org.shanoir.ng.study.service.StudyUniqueConstraintManager; import org.shanoir.ng.study.service.StudyUserService; +import org.shanoir.ng.tag.model.StudyTagMapper; import org.shanoir.ng.utils.ModelsUtil; import org.shanoir.ng.utils.usermock.WithMockKeycloakUser; import org.springframework.beans.factory.annotation.Autowired; @@ -87,6 +88,9 @@ public class StudyApiControllerTest { @MockBean private StudyMapper studyMapperMock; + @MockBean + private StudyTagMapper studyTagMapperMock; + @MockBean private StudyService studyServiceMock; From b0182187b6ecbaeaaf238b9f52e1361e268d3268 Mon Sep 17 00:00:00 2001 From: pierrehenri-dauvergne Date: Thu, 16 May 2024 11:46:36 +0200 Subject: [PATCH 20/80] shanoir-issue#2214: filters studycards by centers on which the user has rights in import form --- .../clinical-context.abstract.component.ts | 24 ++++++++++++------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/shanoir-ng-front/src/app/import/clinical-context/clinical-context.abstract.component.ts b/shanoir-ng-front/src/app/import/clinical-context/clinical-context.abstract.component.ts index c2a94d7a15..9b04545e86 100644 --- a/shanoir-ng-front/src/app/import/clinical-context/clinical-context.abstract.component.ts +++ b/shanoir-ng-front/src/app/import/clinical-context/clinical-context.abstract.component.ts @@ -247,19 +247,25 @@ export abstract class AbstractClinicalContextComponent implements OnDestroy, OnI let studyEquipments: AcquisitionEquipment[] = []; if (!study) return Promise.resolve([]); /* find equipments for this study - needed for checking studycards compatibilities */ - study.studyCenterList.forEach(sc => { - sc.center.acquisitionEquipments.forEach(eq => { + study.studyCenterList.forEach(studyCenter => { + studyCenter.center.acquisitionEquipments.forEach(eq => { if (studyEquipments.findIndex(se => se.id == eq.id) == -1) studyEquipments.push(eq); }); }); /* build the studycards options and set their compatibilies */ - return this.studycardService.getAllForStudy(study.id).then(studycards => { - if (!studycards) studycards = []; - return studycards.map(sc => { - let opt = new Option(sc, sc.name); - let scEq = sc.acquisitionEquipment ? studyEquipments.find(se => se.id == sc.acquisitionEquipment.id) : null; - opt.compatible = this.acqEqCompatible(scEq); - return opt; + return this.centerService.getCentersByStudyId(study.id).then(centers => { + let accessibleCenterIds = centers.map(center => center.id); + return this.studycardService.getAllForStudy(study.id).then(studyCards => { + if (!studyCards) studyCards = []; + + return studyCards.filter(studyCard => { + return accessibleCenterIds.includes(studyCard.acquisitionEquipment.center.id); + }).map(studyCard => { + let opt = new Option(studyCard, studyCard.name); + let scEq = studyCard.acquisitionEquipment ? studyEquipments.find(se => se.id == studyCard.acquisitionEquipment.id) : null; + opt.compatible = this.acqEqCompatible(scEq); + return opt; + }); }); }); } From 338777c755f42e4716a0f2f40fdc5523f30e9d04 Mon Sep 17 00:00:00 2001 From: pierrehenri-dauvergne Date: Thu, 16 May 2024 14:46:58 +0200 Subject: [PATCH 21/80] shanoir-issue#2204: remove poll option in docker-compose-dev.yml --- docker-compose-dev.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-compose-dev.yml b/docker-compose-dev.yml index 1d5dd33d49..935c7d2baf 100644 --- a/docker-compose-dev.yml +++ b/docker-compose-dev.yml @@ -24,7 +24,7 @@ services: build: ./docker-compose/front-dev volumes: - "./shanoir-ng-front:/app/" - command: ng serve --host 0.0.0.0 --disable-host-check --poll 10000 + command: ng serve --host 0.0.0.0 --disable-host-check ports: - "4200:4200" tty: true From 672cddc52e708ba89e3028b0312f900c1dfec375 Mon Sep 17 00:00:00 2001 From: Youenn Merel Date: Fri, 17 May 2024 16:35:16 +0200 Subject: [PATCH 22/80] [fli-iam#2194] Tag datasets in OFSEPSeqIdHandler --- .../amqp/RabbitMQDatasetsService.java | 3 +- .../org/shanoir/ng/dataset/model/Dataset.java | 7 +- .../org/shanoir/ng/shared/model/Study.java | 1 + .../shanoir/ng/shared/model/SubjectStudy.java | 1 + .../ng/shared/model/SubjectStudyDTO.java | 1 + .../ng/solr/service/SolrServiceImpl.java | 89 +++++++++---------- .../org/shanoir/ng/tag/model/StudyTag.java | 4 +- .../org/shanoir/ng/tag/model/StudyTagDTO.java | 1 + .../shanoir/ng/{shared => tag}/model/Tag.java | 3 +- .../vip/resulthandler/OFSEPSeqIdHandler.java | 27 ++++++ 10 files changed, 83 insertions(+), 54 deletions(-) rename shanoir-ng-datasets/src/main/java/org/shanoir/ng/{shared => tag}/model/Tag.java (92%) diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/configuration/amqp/RabbitMQDatasetsService.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/configuration/amqp/RabbitMQDatasetsService.java index 2d37b5981f..07df5d6f60 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/configuration/amqp/RabbitMQDatasetsService.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/configuration/amqp/RabbitMQDatasetsService.java @@ -16,7 +16,6 @@ import java.io.IOException; import java.util.*; -import java.util.stream.Collectors; import org.apache.solr.client.solrj.SolrServerException; import org.shanoir.ng.bids.service.BIDSService; @@ -41,7 +40,7 @@ import org.shanoir.ng.shared.model.Study; import org.shanoir.ng.shared.model.Subject; import org.shanoir.ng.shared.model.SubjectStudy; -import org.shanoir.ng.shared.model.Tag; +import org.shanoir.ng.tag.model.Tag; import org.shanoir.ng.shared.repository.AcquisitionEquipmentRepository; import org.shanoir.ng.shared.repository.CenterRepository; import org.shanoir.ng.shared.repository.StudyRepository; diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/dataset/model/Dataset.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/dataset/model/Dataset.java index 3ee1cf68ed..07db9b3ff9 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/dataset/model/Dataset.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/dataset/model/Dataset.java @@ -27,6 +27,7 @@ import org.shanoir.ng.shared.core.model.AbstractEntity; import org.shanoir.ng.shared.dateTime.LocalDateAnnotations; import org.shanoir.ng.tag.model.StudyTag; +import org.shanoir.ng.tag.model.Tag; import java.time.LocalDate; import java.util.ArrayList; @@ -133,7 +134,7 @@ public abstract class Dataset extends AbstractEntity { private DatasetMetadata updatedMetadata; @ManyToMany - @JoinTable(name = "DATASET_TAG", joinColumns = @JoinColumn(name = "DATASET_ID"), inverseJoinColumns = @JoinColumn(name = "STUDY_TAG_ID")) + @JoinTable(name = "DATASET_TAG", joinColumns = @JoinColumn(name = "DATASET_ID"), inverseJoinColumns = @JoinColumn(name = "TAG_ID")) private List tags; private Long sourceId; @@ -458,7 +459,7 @@ public List getTags() { return tags; } - public void setTags(List studyTags) { - this.tags = studyTags; + public void setTags(List tags) { + this.tags = tags; } } diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/shared/model/Study.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/shared/model/Study.java index 47a510c52e..0bb9bb91d2 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/shared/model/Study.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/shared/model/Study.java @@ -19,6 +19,7 @@ import org.shanoir.ng.examination.model.Examination; import org.shanoir.ng.shared.core.model.IdName; import org.shanoir.ng.tag.model.StudyTag; +import org.shanoir.ng.tag.model.Tag; import java.util.List; diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/shared/model/SubjectStudy.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/shared/model/SubjectStudy.java index 2e78062660..543555220e 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/shared/model/SubjectStudy.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/shared/model/SubjectStudy.java @@ -28,6 +28,7 @@ import jakarta.persistence.UniqueConstraint; import jakarta.validation.constraints.NotNull; import org.shanoir.ng.shared.quality.QualityTag; +import org.shanoir.ng.tag.model.Tag; import java.util.List; diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/shared/model/SubjectStudyDTO.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/shared/model/SubjectStudyDTO.java index 6d1c6125bb..0ddf9310ce 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/shared/model/SubjectStudyDTO.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/shared/model/SubjectStudyDTO.java @@ -1,6 +1,7 @@ package org.shanoir.ng.shared.model; import org.shanoir.ng.shared.subjectstudy.SubjectType; +import org.shanoir.ng.tag.model.Tag; import java.util.List; diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/solr/service/SolrServiceImpl.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/solr/service/SolrServiceImpl.java index 791561f3c1..0f4cff9a38 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/solr/service/SolrServiceImpl.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/solr/service/SolrServiceImpl.java @@ -20,20 +20,19 @@ package org.shanoir.ng.solr.service; import java.io.IOException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.stream.Collectors; import org.apache.solr.client.solrj.SolrServerException; +import org.shanoir.ng.dataset.model.Dataset; +import org.shanoir.ng.dataset.repository.DatasetRepository; +import org.shanoir.ng.dataset.service.DatasetService; import org.shanoir.ng.shared.dateTime.DateTimeUtils; import org.shanoir.ng.shared.exception.RestServiceException; import org.shanoir.ng.shared.model.Center; import org.shanoir.ng.shared.model.SubjectStudy; -import org.shanoir.ng.shared.model.Tag; +import org.shanoir.ng.tag.model.StudyTag; +import org.shanoir.ng.tag.model.Tag; import org.shanoir.ng.shared.paging.PageImpl; import org.shanoir.ng.shared.repository.CenterRepository; import org.shanoir.ng.shared.repository.SubjectStudyRepository; @@ -45,6 +44,7 @@ import org.shanoir.ng.solr.solrj.SolrJWrapper; import org.shanoir.ng.study.rights.StudyUser; import org.shanoir.ng.study.rights.StudyUserRightsRepository; +import org.shanoir.ng.tag.repository.StudyTagRepository; import org.shanoir.ng.utils.KeycloakUtil; import org.shanoir.ng.utils.Utils; import org.springframework.beans.factory.annotation.Autowired; @@ -82,6 +82,9 @@ public class SolrServiceImpl implements SolrService { @Autowired private CenterRepository centerRepository; + @Autowired + private DatasetRepository dsRepository; + public void addToIndex (final ShanoirSolrDocument document) throws SolrServerException, IOException { solrJWrapper.addToIndex(document); } @@ -117,9 +120,8 @@ public void indexAll() throws SolrServerException, IOException { @Override public void indexDatasets(List datasetIds) throws SolrServerException, IOException { // Get all associated datasets and index them to solr - List shanoirMetadatas = shanoirMetadataRepository.findSolrDocs(datasetIds); - - indexDocumentsInSolr(shanoirMetadatas); + List metadatas = shanoirMetadataRepository.findSolrDocs(datasetIds); + indexDocumentsInSolr(metadatas); } @Override @@ -129,22 +131,40 @@ public void indexDataset(Long datasetId) throws SolrServerException, IOException if (shanoirMetadata == null) throw new IllegalStateException("shanoir metadata with id " + datasetId + " query failed to return any result"); ShanoirSolrDocument doc = getShanoirSolrDocument(shanoirMetadata); - // Get tags - List list = subjectStudyRepo.findByStudy_IdInAndSubjectIdIn(Collections.singletonList(shanoirMetadata.getStudyId()), Collections.singletonList(shanoirMetadata.getSubjectId())); + List tags = this.getTagsAsStrings(shanoirMetadata); + doc.setTags(tags); + + solrJWrapper.addToIndex(doc); + } + + private List getTagsAsStrings(ShanoirMetadata metadata){ List tags = new ArrayList<>(); - if (list != null) { - for (SubjectStudy susu : list) { - if (susu.getTags() != null) { - for (Tag tag : susu.getTags()) { - tags.add(tag.getName()); - } + + // SubjectStudy tags + List subjectStudies = subjectStudyRepo.findByStudy_IdInAndSubjectIdIn( + Collections.singletonList(metadata.getStudyId()), + Collections.singletonList(metadata.getSubjectId()) + ); + + for (SubjectStudy subStu : subjectStudies) { + if (subStu.getTags() != null) { + for (Tag tag : subStu.getTags()) { + tags.add(tag.getName()); } } } - doc.setTags(tags); - solrJWrapper.addToIndex(doc); + // Dataset tags + Optional ds = dsRepository.findById(metadata.getDatasetId()); + if(ds.isEmpty()){ + return tags; + } + for(StudyTag tag : ds.get().getTags()){ + tags.add(tag.getName()); + } + + return tags; } private void indexDocumentsInSolr(List metadatas) throws SolrServerException, IOException { @@ -155,34 +175,9 @@ private void indexDocumentsInSolr(List metadatas) throws SolrSe while (docIt.hasNext()) { ShanoirMetadata shanoirMetadata = docIt.next(); ShanoirSolrDocument doc = getShanoirSolrDocument(shanoirMetadata); + doc.setTags(this.getTagsAsStrings(shanoirMetadata)); solrDocuments.add(doc); } - - if (CollectionUtils.isEmpty(solrDocuments)) { - return; - } - - List subjstuds = Utils.toList(subjectStudyRepo.findAll()); - - Map>> tags = new HashMap<>(); - - for (SubjectStudy subjstud : subjstuds) { - if (tags.get(subjstud.getStudy().getId()) == null) { - tags.put(subjstud.getStudy().getId(), new HashMap<>()); - } - tags.get(subjstud.getStudy().getId()).put(subjstud.getSubject().getName(), subjstud.getTags() != null ? subjstud.getTags() : Collections.emptyList()); - } - - // Update tags - for (ShanoirSolrDocument doc : solrDocuments) { - if (doc != null && tags != null && tags.get(doc.getStudyId()) != null) { - List list = tags.get(doc.getStudyId()).get(doc.getSubjectName()); - if (list != null && !list.isEmpty()) { - doc.setTags(list.stream().map(Tag::getName).collect(Collectors.toList())); - } - } - } - solrJWrapper.addAllToIndex(solrDocuments); } @@ -198,7 +193,7 @@ private ShanoirSolrDocument getShanoirSolrDocument(ShanoirMetadata shanoirMetada @Transactional @Override public SolrResultPage facetSearch(ShanoirSolrQuery query, Pageable pageable) throws RestServiceException { - SolrResultPage result = null; + SolrResultPage result; pageable = prepareTextFields(pageable); if (KeycloakUtil.getTokenRoles().contains("ROLE_ADMIN")) { result = solrJWrapper.findByFacetCriteriaForAdmin(query, pageable); diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/tag/model/StudyTag.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/tag/model/StudyTag.java index bac5463cd8..9f004ba9bf 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/tag/model/StudyTag.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/tag/model/StudyTag.java @@ -5,7 +5,8 @@ import org.shanoir.ng.shared.model.Study; @Entity -public class StudyTag { +public class StudyTag { + @Id private Long id; @@ -74,4 +75,5 @@ public Study getStudy() { public void setStudy(Study study) { this.study = study; } + } diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/tag/model/StudyTagDTO.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/tag/model/StudyTagDTO.java index 6a310ae39e..454726f8cc 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/tag/model/StudyTagDTO.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/tag/model/StudyTagDTO.java @@ -43,4 +43,5 @@ public StudyDTO getStudy() { public void setStudy(StudyDTO study) { this.study = study; } + } diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/shared/model/Tag.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/tag/model/Tag.java similarity index 92% rename from shanoir-ng-datasets/src/main/java/org/shanoir/ng/shared/model/Tag.java rename to shanoir-ng-datasets/src/main/java/org/shanoir/ng/tag/model/Tag.java index ee2ed8d62e..278e7a887b 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/shared/model/Tag.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/tag/model/Tag.java @@ -1,7 +1,8 @@ -package org.shanoir.ng.shared.model; +package org.shanoir.ng.tag.model; import com.fasterxml.jackson.annotation.JsonIgnore; import jakarta.persistence.*; +import org.shanoir.ng.shared.model.Study; @Entity public class Tag { diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/vip/resulthandler/OFSEPSeqIdHandler.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/vip/resulthandler/OFSEPSeqIdHandler.java index 492b5ee891..4fa5dc6fa8 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/vip/resulthandler/OFSEPSeqIdHandler.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/vip/resulthandler/OFSEPSeqIdHandler.java @@ -13,6 +13,8 @@ import org.shanoir.ng.datasetacquisition.model.mr.MrDatasetAcquisition; import org.shanoir.ng.datasetacquisition.service.DatasetAcquisitionService; import org.shanoir.ng.download.WADODownloaderService; +import org.shanoir.ng.shared.service.StudyService; +import org.shanoir.ng.tag.model.StudyTag; import org.shanoir.ng.vip.monitoring.model.ExecutionMonitoring; import org.shanoir.ng.property.model.DatasetProperty; import org.shanoir.ng.property.service.DatasetPropertyService; @@ -35,6 +37,8 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Map; +import java.util.function.Function; import java.util.stream.Collectors; @Service @@ -103,6 +107,9 @@ public class OFSEPSeqIdHandler extends ResultHandler { @Autowired private SolrService solrService; + @Autowired + private StudyService studyService; + @Override public boolean canProcess(ExecutionMonitoring processing) throws ResultHandlerException { @@ -189,6 +196,9 @@ public void processSeries(JSONArray series, ExecutionMonitoring execution) throw } List properties = this.getDatasetPropertiesFromVolume(ds, vol, execution); + + this.setDatasetTags(ds, properties); + properties.addAll(this.getDatasetPropertiesFromDicom(attributes, ds, execution)); datasetPropertyService.createAll(properties); @@ -211,6 +221,7 @@ public void updateDataset(JSONObject serie, Dataset ds, JSONObject vol) throws J DatasetMetadataField.NAME.update(ds, vol.getString(TYPE)); datasetRepository.save(ds); + if(ds.getDatasetAcquisition() instanceof MrDatasetAcquisition){ DatasetAcquisition acq = ds.getDatasetAcquisition(); DatasetAcquisitionMetadataField.MR_SEQUENCE_NAME.update(acq, serie.getString(TYPE)); @@ -221,6 +232,22 @@ public void updateDataset(JSONObject serie, Dataset ds, JSONObject vol) throws J } + private void setDatasetTags(Dataset ds, List properties) { + Map studyTagsByName = studyService.findById(ds.getStudyId()).getStudyTags().stream() + .collect(Collectors.toMap(StudyTag::getName, Function.identity())); + + for(DatasetProperty property : properties){ + String tagName = property.getName() + ":" + property.getValue(); + if(studyTagsByName.containsKey(tagName)){ + StudyTag tag = studyTagsByName.get(tagName); + if(!ds.getTags().contains(tag)){ + ds.getTags().add(tag); + } + } + } + datasetRepository.save(ds); + } + /** * Create dataset properties from pipeline output volume From ea72e9425ab6003a92722fdebbfaa8080353ca2d Mon Sep 17 00:00:00 2001 From: Youenn Merel Date: Fri, 17 May 2024 17:25:09 +0200 Subject: [PATCH 23/80] [fli-iam#2194] Show dataset tags in study tree --- .../tree/dataset-acquisition-node.component.ts | 1 + .../src/app/datasets/shared/dataset.dto.ts | 6 +++++- .../src/app/datasets/shared/dataset.model.ts | 10 ++++++---- .../src/app/datasets/tree/dataset-node.component.html | 1 + .../examinations/tree/examination-node.component.ts | 1 + .../app/studies/tree/reverse-study-node.component.ts | 1 + .../src/app/subjects/tree/subject-node.component.ts | 1 + shanoir-ng-front/src/app/tree/tree.model.ts | 3 +++ 8 files changed, 19 insertions(+), 5 deletions(-) diff --git a/shanoir-ng-front/src/app/dataset-acquisitions/tree/dataset-acquisition-node.component.ts b/shanoir-ng-front/src/app/dataset-acquisitions/tree/dataset-acquisition-node.component.ts index 1b26303e95..e65e31da2d 100644 --- a/shanoir-ng-front/src/app/dataset-acquisitions/tree/dataset-acquisition-node.component.ts +++ b/shanoir-ng-front/src/app/dataset-acquisitions/tree/dataset-acquisition-node.component.ts @@ -111,6 +111,7 @@ export class DatasetAcquisitionNodeComponent implements OnChanges, OnDestroy { return new DatasetNode( dataset.id, dataset.name, + dataset.tags, dataset.type, dataset.processings ? dataset.processings.map(proc => this.mapProcessingNode(proc)) : [], processed, diff --git a/shanoir-ng-front/src/app/datasets/shared/dataset.dto.ts b/shanoir-ng-front/src/app/datasets/shared/dataset.dto.ts index 4b4b731ed2..c818b0c851 100644 --- a/shanoir-ng-front/src/app/datasets/shared/dataset.dto.ts +++ b/shanoir-ng-front/src/app/datasets/shared/dataset.dto.ts @@ -28,6 +28,7 @@ import { DatasetProcessingService } from '../../datasets/shared/dataset-processi import { DatasetAcquisitionDTO, DatasetAcquisitionDTOService } from '../../dataset-acquisitions/shared/dataset-acquisition.dto'; import { DatasetAcquisitionUtils } from '../../dataset-acquisitions/shared/dataset-acquisition.utils'; import {DatasetExpressionFormat} from "../../enum/dataset-expression-format.enum"; +import {Tag} from "../../tags/tag.model"; @Injectable() export class DatasetDTOService { @@ -43,7 +44,7 @@ export class DatasetDTOService { * Warning : DO NOT USE THIS IN A LOOP, use toEntityList instead * @param result can be used to get an immediate temporary result without waiting async data */ - public toEntity(dto: DatasetDTO, result?: Dataset, mode: 'eager' | 'lazy' = 'eager'): Promise { + public toEntity(dto: DatasetDTO, result?: Dataset, mode: 'eager' | 'lazy' = 'eager'): Promise { if(!this.datasetProcessingService) { this.datasetProcessingService = this.injector.get(DatasetProcessingService); } @@ -151,6 +152,7 @@ export class DatasetDTOService { process.id = dto.datasetProcessing.id; entity.datasetProcessing = process; } + entity.tags = dto.tags ? dto.tags : []; return entity; } @@ -194,6 +196,7 @@ export class DatasetDTO { processings: {id: number}[]; datasetProcessing: {id: number}; datasetAcquisition: DatasetAcquisitionDTO; + tags: Tag[]; constructor(dataset?: Dataset) { if (dataset) { @@ -210,6 +213,7 @@ export class DatasetDTO { if(dataset.datasetAcquisition) { this.datasetAcquisition = new DatasetAcquisitionDTO(dataset.datasetAcquisition); } + this.tags = dataset.tags; } } } diff --git a/shanoir-ng-front/src/app/datasets/shared/dataset.model.ts b/shanoir-ng-front/src/app/datasets/shared/dataset.model.ts index fb4c550936..88128e8742 100644 --- a/shanoir-ng-front/src/app/datasets/shared/dataset.model.ts +++ b/shanoir-ng-front/src/app/datasets/shared/dataset.model.ts @@ -2,12 +2,12 @@ * Shanoir NG - Import, manage and share neuroimaging data * Copyright (C) 2009-2019 Inria - https://www.inria.fr/ * Contact us on https://project.inria.fr/shanoir/ - * + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * You should have received a copy of the GNU General Public License * along with this program. If not, see https://www.gnu.org/licenses/gpl-3.0.html */ @@ -21,9 +21,10 @@ import { Subject } from '../../subjects/shared/subject.model'; import { DatasetType } from './dataset-type.model'; import { DatasetAcquisition } from '../../dataset-acquisitions/shared/dataset-acquisition.model'; import { BidsDataType } from '../../enum/bids-data-type.enum'; +import {Tag} from "../../tags/tag.model"; export abstract class Dataset extends Entity { - + id: number; creationDate: Date; name: string; @@ -39,6 +40,7 @@ export abstract class Dataset extends Entity { originMetadata: DatasetMetadata; updatedMetadata : DatasetMetadata = new DatasetMetadata(); processings: DatasetProcessing[] = []; + tags: Tag[]; } export class DatasetMetadata { @@ -49,4 +51,4 @@ export class DatasetMetadata { processedDatasetType: ProcessedDatasetType; cardinalityOfRelatedSubjects: CardinalityOfRelatedSubjects = CardinalityOfRelatedSubjects.SINGLE_SUBJECT_DATASET; bidsDataType: BidsDataType; -} \ No newline at end of file +} diff --git a/shanoir-ng-front/src/app/datasets/tree/dataset-node.component.html b/shanoir-ng-front/src/app/datasets/tree/dataset-node.component.html index 7aa8f48497..2cd5f776e7 100644 --- a/shanoir-ng-front/src/app/datasets/tree/dataset-node.component.html +++ b/shanoir-ng-front/src/app/datasets/tree/dataset-node.component.html @@ -16,6 +16,7 @@ *ngIf="node" [class.selected]="this.menuOpened" [label]="node.label" + [tags]="node.tags" [awesome]="node.awesome" [(opened)]="node.open" (labelClick)="toggleMenu()" diff --git a/shanoir-ng-front/src/app/examinations/tree/examination-node.component.ts b/shanoir-ng-front/src/app/examinations/tree/examination-node.component.ts index 23b887d593..45ada42035 100644 --- a/shanoir-ng-front/src/app/examinations/tree/examination-node.component.ts +++ b/shanoir-ng-front/src/app/examinations/tree/examination-node.component.ts @@ -152,6 +152,7 @@ export class ExaminationNodeComponent implements OnChanges { return new DatasetNode( dataset.id, dataset.name, + dataset.tags, dataset.type, dataset.processings ? dataset.processings.map(proc => this.mapProcessingNode(proc)) : [], processed, diff --git a/shanoir-ng-front/src/app/studies/tree/reverse-study-node.component.ts b/shanoir-ng-front/src/app/studies/tree/reverse-study-node.component.ts index c7761d3e16..ee7612e1d3 100644 --- a/shanoir-ng-front/src/app/studies/tree/reverse-study-node.component.ts +++ b/shanoir-ng-front/src/app/studies/tree/reverse-study-node.component.ts @@ -129,6 +129,7 @@ export class ReverseStudyNodeComponent implements OnChanges { return new DatasetNode( dataset.id, dataset.name, + dataset.tags, dataset.type, dataset.processings ? dataset.processings.map(proc => this.mapProcessingNode(proc)) : [], processed, diff --git a/shanoir-ng-front/src/app/subjects/tree/subject-node.component.ts b/shanoir-ng-front/src/app/subjects/tree/subject-node.component.ts index a901230a1e..9611ca7d3b 100644 --- a/shanoir-ng-front/src/app/subjects/tree/subject-node.component.ts +++ b/shanoir-ng-front/src/app/subjects/tree/subject-node.component.ts @@ -135,6 +135,7 @@ export class SubjectNodeComponent implements OnChanges { return new DatasetNode( dataset.id, dataset.name, + dataset.tags, dataset.type, dataset.processings ? dataset.processings.map(proc => this.mapProcessingNode(proc)) : [], processed, diff --git a/shanoir-ng-front/src/app/tree/tree.model.ts b/shanoir-ng-front/src/app/tree/tree.model.ts index 900edc9437..3727c7ba31 100644 --- a/shanoir-ng-front/src/app/tree/tree.model.ts +++ b/shanoir-ng-front/src/app/tree/tree.model.ts @@ -129,11 +129,14 @@ export class DatasetNode implements ShanoirNode { constructor( public id: number, public label: string, + public tags: Tag[], public type: string, public processings: ProcessingNode[] | UNLOADED, public processed: boolean, public canDelete: boolean ) { + if (!tags) tags = []; + else tags = tags.map(t => t.clone()); if(processed){ this.title = "processed-dataset"; this.awesome = "fas fa-camera-rotate"; From de6782ff509fcc2ce8964b21e337c8f865dab1b7 Mon Sep 17 00:00:00 2001 From: Youenn Merel Date: Tue, 21 May 2024 14:02:12 +0200 Subject: [PATCH 24/80] [fli-iam#2194] Correct APIs params and doc --- .../java/org/shanoir/ng/dataset/controler/DatasetApi.java | 7 ++++--- .../main/java/org/shanoir/ng/study/controler/StudyApi.java | 5 +++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/dataset/controler/DatasetApi.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/dataset/controler/DatasetApi.java index ee2c993835..58cc7820b1 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/dataset/controler/DatasetApi.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/dataset/controler/DatasetApi.java @@ -326,17 +326,18 @@ ResponseEntity> findDatasetsByIds( @RequestParam(value = "datasetIds", required = true) List datasetIds); @Operation(summary = "", description = "Updates the study tags of a dataset") - @ApiResponses(value = { @ApiResponse(responseCode = "204", description = "dataset updated"), + @ApiResponses(value = { @ApiResponse(responseCode = "204", description = "dataset study tags updated"), @ApiResponse(responseCode = "401", description = "unauthorized"), @ApiResponse(responseCode = "403", description = "forbidden"), + @ApiResponse(responseCode = "404", description = "dataset does not exists"), @ApiResponse(responseCode = "422", description = "bad parameters"), @ApiResponse(responseCode = "500", description = "unexpected error") }) @PutMapping(value = "/{datasetId}/tags", produces = { "application/json" }, consumes = { "application/json" }) @PreAuthorize("@controlerSecurityService.idMatches(#datasetId, #dataset) and hasRole('ADMIN') or (hasRole('EXPERT') and @datasetSecurityService.hasUpdateRightOnDataset(#dataset, 'CAN_ADMINISTRATE'))") ResponseEntity updateDatasetTags( - @Parameter(name = "id of the dataset", required = true) @PathVariable("datasetId") Long datasetId, - @RequestParam(value = "studyTagIds", name="studyTagIds") List studyTagIds, + @Parameter(description = "id of the dataset", required = true) @PathVariable("datasetId") Long datasetId, + @Parameter(description = "array of study tag ids", required = true) @RequestBody List studyTagIds, BindingResult result) throws RestServiceException, EntityNotFoundException; } diff --git a/shanoir-ng-studies/src/main/java/org/shanoir/ng/study/controler/StudyApi.java b/shanoir-ng-studies/src/main/java/org/shanoir/ng/study/controler/StudyApi.java index 5ed13a4878..fa886c0795 100644 --- a/shanoir-ng-studies/src/main/java/org/shanoir/ng/study/controler/StudyApi.java +++ b/shanoir-ng-studies/src/main/java/org/shanoir/ng/study/controler/StudyApi.java @@ -371,14 +371,15 @@ ResponseEntity> getStudyStatistics( @ApiResponses(value = { @ApiResponse(responseCode = "204", description = "study tags updated"), @ApiResponse(responseCode = "401", description = "unauthorized"), @ApiResponse(responseCode = "403", description = "forbidden"), + @ApiResponse(responseCode = "404", description = "study does not exists"), @ApiResponse(responseCode = "422", description = "bad parameters"), @ApiResponse(responseCode = "500", description = "unexpected error") }) @PutMapping(value = "/{studyId}/tags", produces = { "application/json" }, consumes = { "application/json" }) @PreAuthorize("hasAnyRole('ADMIN', 'EXPERT') and @controlerSecurityService.idMatches(#studyId, #study) and @studySecurityService.hasRightOnStudy(#studyId, 'CAN_ADMINISTRATE')") ResponseEntity updateStudyTags( - @Parameter(name = "id of the study", required = true) @PathVariable("studyId") Long studyId, - @RequestParam(value = "studyTags", name="studyTags") List studyTags, + @Parameter(description = "id of the study", required = true) @PathVariable("studyId") Long studyId, + @Parameter(description = "study tags", required = true) @RequestBody List studyTags, BindingResult result) throws RestServiceException, EntityNotFoundException, MicroServiceCommunicationException; } \ No newline at end of file From 4d132a36ddc1e859903730849947ef23edbfa7de Mon Sep 17 00:00:00 2001 From: Youenn Merel Date: Tue, 21 May 2024 14:10:30 +0200 Subject: [PATCH 25/80] [fli-iam#2194] Correct APIs body doc --- .../main/java/org/shanoir/ng/dataset/controler/DatasetApi.java | 2 +- .../src/main/java/org/shanoir/ng/study/controler/StudyApi.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/dataset/controler/DatasetApi.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/dataset/controler/DatasetApi.java index 58cc7820b1..c71cead2cd 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/dataset/controler/DatasetApi.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/dataset/controler/DatasetApi.java @@ -337,7 +337,7 @@ ResponseEntity> findDatasetsByIds( @PreAuthorize("@controlerSecurityService.idMatches(#datasetId, #dataset) and hasRole('ADMIN') or (hasRole('EXPERT') and @datasetSecurityService.hasUpdateRightOnDataset(#dataset, 'CAN_ADMINISTRATE'))") ResponseEntity updateDatasetTags( @Parameter(description = "id of the dataset", required = true) @PathVariable("datasetId") Long datasetId, - @Parameter(description = "array of study tag ids", required = true) @RequestBody List studyTagIds, + @io.swagger.v3.oas.annotations.parameters.RequestBody(description = "array of study tag ids", required = true) @RequestBody List studyTagIds, BindingResult result) throws RestServiceException, EntityNotFoundException; } diff --git a/shanoir-ng-studies/src/main/java/org/shanoir/ng/study/controler/StudyApi.java b/shanoir-ng-studies/src/main/java/org/shanoir/ng/study/controler/StudyApi.java index fa886c0795..074d733f3e 100644 --- a/shanoir-ng-studies/src/main/java/org/shanoir/ng/study/controler/StudyApi.java +++ b/shanoir-ng-studies/src/main/java/org/shanoir/ng/study/controler/StudyApi.java @@ -379,7 +379,7 @@ ResponseEntity> getStudyStatistics( @PreAuthorize("hasAnyRole('ADMIN', 'EXPERT') and @controlerSecurityService.idMatches(#studyId, #study) and @studySecurityService.hasRightOnStudy(#studyId, 'CAN_ADMINISTRATE')") ResponseEntity updateStudyTags( @Parameter(description = "id of the study", required = true) @PathVariable("studyId") Long studyId, - @Parameter(description = "study tags", required = true) @RequestBody List studyTags, + @io.swagger.v3.oas.annotations.parameters.RequestBody(description = "array of study tags", required = true) @RequestBody List studyTags, BindingResult result) throws RestServiceException, EntityNotFoundException, MicroServiceCommunicationException; } \ No newline at end of file From e8dbf4fe4c1c57175b4b04c692f157056201c64f Mon Sep 17 00:00:00 2001 From: Anthony Baire Date: Wed, 22 May 2024 11:25:35 +0200 Subject: [PATCH 26/80] relocate executables into /usr/bin (instead of /bin) in recent debian releases, /bin is a symlink to /usr/bin which interacts badly with 'COPY --link' ('COPY --link xxxx /bin/' would override the /bin symlink with an empty dir) --- docker-compose/datasets/Dockerfile | 2 +- docker-compose/import/Dockerfile | 2 +- docker-compose/nginx/Dockerfile | 2 +- docker-compose/nifti-conversion/Dockerfile | 2 +- docker-compose/preclinical/Dockerfile | 2 +- docker-compose/studies/Dockerfile | 2 +- docker-compose/users/Dockerfile | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/docker-compose/datasets/Dockerfile b/docker-compose/datasets/Dockerfile index dcbda69c73..dbeadd834b 100644 --- a/docker-compose/datasets/Dockerfile +++ b/docker-compose/datasets/Dockerfile @@ -54,7 +54,7 @@ COPY --link --from=downloader /target/. / RUN mkdir -pv /var/log/shanoir-ng-logs COPY --link shanoir-ng-datasets.jar shanoir-ng-datasets.jar -COPY --link entrypoint entrypoint_common oneshot /bin/ +COPY --link entrypoint entrypoint_common oneshot /usr/bin/ # Use the below line for remote debugging and to active dev profile #ENTRYPOINT ["/bin/entrypoint", "java", "-Xdebug", "-Xrunjdwp:transport=dt_socket,server=y,address=0.0.0.0:9914,suspend=y", "-Djava.security.egd=file:/dev/./urandom", "-Djavax.net.ssl.trustStorePassword=changeit", "-jar", "/shanoir-ng-datasets.jar"] diff --git a/docker-compose/import/Dockerfile b/docker-compose/import/Dockerfile index 431c16a53d..f02b684869 100644 --- a/docker-compose/import/Dockerfile +++ b/docker-compose/import/Dockerfile @@ -26,7 +26,7 @@ RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ RUN mkdir -pv /var/log/shanoir-ng-logs COPY --link shanoir-ng-import.jar shanoir-ng-import.jar -COPY --link entrypoint entrypoint_common oneshot /bin/ +COPY --link entrypoint entrypoint_common oneshot /usr/bin/ # Use the below line for remote debugging and to active dev profile #ENTRYPOINT ["/bin/entrypoint", "java", "-Xdebug", "-Xrunjdwp:transport=dt_socket,server=y,address=0.0.0.0:9913,suspend=y", "-Djava.security.egd=file:/dev/./urandom", "-Djavax.net.ssl.trustStorePassword=changeit", "-jar", "/shanoir-ng-import.jar"] diff --git a/docker-compose/nginx/Dockerfile b/docker-compose/nginx/Dockerfile index 5038c413a1..f0dc24753a 100644 --- a/docker-compose/nginx/Dockerfile +++ b/docker-compose/nginx/Dockerfile @@ -19,7 +19,7 @@ COPY --link nginx.conf http.conf https.conf shanoir.template.conf shanoir.templa COPY --link viewer/app-config.js viewer/ohif-viewer.template.conf /etc/nginx/viewer/ COPY --link --from=viewer /usr/share/nginx/html/. /etc/nginx/viewer/html/ -COPY --link entrypoint entrypoint_common /bin/ +COPY --link entrypoint entrypoint_common /usr/bin/ diff --git a/docker-compose/nifti-conversion/Dockerfile b/docker-compose/nifti-conversion/Dockerfile index e449db545a..66882a09fb 100644 --- a/docker-compose/nifti-conversion/Dockerfile +++ b/docker-compose/nifti-conversion/Dockerfile @@ -112,7 +112,7 @@ RUN ldconfig \ # install the microservice COPY --link nifti-conversion.jar nifti-conversion.jar -COPY --link entrypoint entrypoint_common oneshot /bin/ +COPY --link entrypoint entrypoint_common oneshot /usr/bin/ ENV LC_ALL fr_FR.UTF-8 ENV LANG fr_FR.UTF-8 diff --git a/docker-compose/preclinical/Dockerfile b/docker-compose/preclinical/Dockerfile index 1506c42088..68e49cb8aa 100644 --- a/docker-compose/preclinical/Dockerfile +++ b/docker-compose/preclinical/Dockerfile @@ -15,7 +15,7 @@ RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ RUN mkdir -pv /var/log/shanoir-ng-logs COPY --link shanoir-ng-preclinical.jar shanoir-ng-preclinical.jar -COPY --link entrypoint entrypoint_common oneshot /bin/ +COPY --link entrypoint entrypoint_common oneshot /usr/bin/ # Use the below line for remote debugging #ENTRYPOINT ["/bin/entrypoint", "java", "-Xdebug", "-Xrunjdwp:transport=dt_socket,server=y,address=0.0.0.0:9915,suspend=y", "-Djava.security.egd=file:/dev/./urandom", "-Djavax.net.ssl.trustStorePassword=changeit", "-Dspring.profiles.active=dev", "-jar", "/shanoir-ng-preclinical.jar"] diff --git a/docker-compose/studies/Dockerfile b/docker-compose/studies/Dockerfile index d412065475..0ff5475849 100644 --- a/docker-compose/studies/Dockerfile +++ b/docker-compose/studies/Dockerfile @@ -27,7 +27,7 @@ RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ RUN mkdir -pv /var/log/shanoir-ng-logs COPY --link shanoir-ng-studies.jar shanoir-ng-studies.jar -COPY --link entrypoint entrypoint_common oneshot /bin/ +COPY --link entrypoint entrypoint_common oneshot /usr/bin/ # Use the below line for remote debugging and to active dev profile #ENTRYPOINT ["/bin/entrypoint", "java", "-Xdebug", "-Xrunjdwp:transport=dt_socket,server=y,address=0.0.0.0:9912,suspend=y", "-Djava.security.egd=file:/dev/./urandom", "-Djavax.net.ssl.trustStorePassword=changeit", "-jar", "/shanoir-ng-studies.jar"] diff --git a/docker-compose/users/Dockerfile b/docker-compose/users/Dockerfile index 4a81514cb6..67ab7a19a0 100644 --- a/docker-compose/users/Dockerfile +++ b/docker-compose/users/Dockerfile @@ -28,7 +28,7 @@ RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ RUN mkdir -pv /var/log/shanoir-ng-logs COPY --link shanoir-ng-users.jar shanoir-ng-users.jar -COPY --link entrypoint entrypoint_common oneshot /bin/ +COPY --link entrypoint entrypoint_common oneshot /usr/bin/ # Use the below line for remote debugging and development profile purpose #ENTRYPOINT ["/bin/entrypoint", "java", "-Xdebug", "-Xrunjdwp:transport=dt_socket,server=y,address=0.0.0.0:9911,suspend=y", "-Djava.security.egd=file:/dev/./urandom", "-Djavax.net.ssl.trustStorePassword=changeit", "-Dspring.profiles.active=dev", "-jar", "/shanoir-ng-users.jar", "--syncAllUsersToKeycloak=true"] From b54a55faf2857f02f1fc19dc15569af3b90e408b Mon Sep 17 00:00:00 2001 From: Youenn Merel Date: Wed, 22 May 2024 15:25:28 +0200 Subject: [PATCH 27/80] [fli-iam#2194] Correct join table --- .../java/org/shanoir/ng/dataset/controler/DatasetApi.java | 2 +- .../shanoir/ng/dataset/controler/DatasetApiController.java | 6 +++++- .../src/main/java/org/shanoir/ng/dataset/model/Dataset.java | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/dataset/controler/DatasetApi.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/dataset/controler/DatasetApi.java index c71cead2cd..0f5d944905 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/dataset/controler/DatasetApi.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/dataset/controler/DatasetApi.java @@ -334,7 +334,7 @@ ResponseEntity> findDatasetsByIds( @ApiResponse(responseCode = "500", description = "unexpected error") }) @PutMapping(value = "/{datasetId}/tags", produces = { "application/json" }, consumes = { "application/json" }) - @PreAuthorize("@controlerSecurityService.idMatches(#datasetId, #dataset) and hasRole('ADMIN') or (hasRole('EXPERT') and @datasetSecurityService.hasUpdateRightOnDataset(#dataset, 'CAN_ADMINISTRATE'))") + @PreAuthorize("hasRole('ADMIN') or (hasRole('EXPERT') and @datasetSecurityService.hasUpdateRightOnDataset(#dataset, 'CAN_ADMINISTRATE'))") ResponseEntity updateDatasetTags( @Parameter(description = "id of the dataset", required = true) @PathVariable("datasetId") Long datasetId, @io.swagger.v3.oas.annotations.parameters.RequestBody(description = "array of study tag ids", required = true) @RequestBody List studyTagIds, diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/dataset/controler/DatasetApiController.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/dataset/controler/DatasetApiController.java index 5fe346f42c..3b4d9f1d79 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/dataset/controler/DatasetApiController.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/dataset/controler/DatasetApiController.java @@ -43,6 +43,7 @@ import org.shanoir.ng.dataset.modality.MrDatasetMapper; import org.shanoir.ng.dataset.model.Dataset; import org.shanoir.ng.dataset.model.DatasetExpressionFormat; +import org.shanoir.ng.dataset.repository.DatasetRepository; import org.shanoir.ng.dataset.service.DatasetDownloaderServiceImpl; import org.shanoir.ng.dataset.service.DatasetService; import org.shanoir.ng.datasetacquisition.model.DatasetAcquisition; @@ -136,6 +137,9 @@ public class DatasetApiController implements DatasetApi { @Autowired private StudyTagService studyTagService; + @Autowired + private DatasetRepository datasetRepository; + /** Number of downloadable datasets. */ private static final int DATASET_LIMIT = 500; @@ -251,7 +255,7 @@ public ResponseEntity updateDatasetTags(Long datasetId, List studyTa } List tags = studyTagService.findByIds(studyTagIds); ds.setTags(tags); - datasetService.update(ds); + datasetRepository.save(ds); return new ResponseEntity<>(HttpStatus.OK); } diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/dataset/model/Dataset.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/dataset/model/Dataset.java index 07db9b3ff9..62ba74ab5a 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/dataset/model/Dataset.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/dataset/model/Dataset.java @@ -134,7 +134,7 @@ public abstract class Dataset extends AbstractEntity { private DatasetMetadata updatedMetadata; @ManyToMany - @JoinTable(name = "DATASET_TAG", joinColumns = @JoinColumn(name = "DATASET_ID"), inverseJoinColumns = @JoinColumn(name = "TAG_ID")) + @JoinTable(name = "DATASET_TAG", joinColumns = @JoinColumn(name = "DATASET_ID"), inverseJoinColumns = @JoinColumn(name = "STUDY_TAG_ID")) private List tags; private Long sourceId; From 1ecbfcc015a2fb086d285a3697f8df4cd089e525 Mon Sep 17 00:00:00 2001 From: Youenn Merel Date: Wed, 22 May 2024 15:49:55 +0200 Subject: [PATCH 28/80] [fli-iam#2194] Manage non existing study tag --- .../ng/dataset/controler/DatasetApiController.java | 8 +++++++- .../java/org/shanoir/ng/tag/service/StudyTagService.java | 4 ++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/dataset/controler/DatasetApiController.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/dataset/controler/DatasetApiController.java index 3b4d9f1d79..d406519d78 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/dataset/controler/DatasetApiController.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/dataset/controler/DatasetApiController.java @@ -251,7 +251,13 @@ public ResponseEntity> findDatasetsByIds public ResponseEntity updateDatasetTags(Long datasetId, List studyTagIds, BindingResult result) throws RestServiceException, EntityNotFoundException { Dataset ds = datasetService.findById(datasetId); if (ds == null) { - return new ResponseEntity<>(HttpStatus.NOT_FOUND); + throw new EntityNotFoundException(Dataset.class, datasetId); + } + for(Long id : studyTagIds){ + if(!studyTagService.existsById(id)){ + throw new RestServiceException( + new ErrorModel(HttpStatus.NOT_FOUND.value(), "Study tag [" + id + "] does not exists.")); + } } List tags = studyTagService.findByIds(studyTagIds); ds.setTags(tags); diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/tag/service/StudyTagService.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/tag/service/StudyTagService.java index 73256f4b12..8c1544f034 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/tag/service/StudyTagService.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/tag/service/StudyTagService.java @@ -18,4 +18,8 @@ public List findByIds(List ids){ return IterableUtils.toList(repository.findAllById(ids)); } + public boolean existsById(Long id){ + return repository.existsById(id); + } + } From be85f697097da1dc82b99fa6150bd977b8afed6a Mon Sep 17 00:00:00 2001 From: Anthony Baire Date: Thu, 23 May 2024 12:28:59 +0200 Subject: [PATCH 29/80] merge all dockerfiles into a single dockerfile Using a single Dockerfile allows sharing intermediate images between multiple builds, thus removing duplicated code (installation of the jvm, entrypoint_common/oneshot scripts, common entrypoint definition, ...) --- .gitignore | 2 - docker-compose-dev.yml | 4 +- docker-compose.yml | 44 ++- docker-compose/Dockerfile | 370 ++++++++++++++++++ .../common}/entrypoint_common | 0 {utils => docker-compose/common}/oneshot | 0 docker-compose/database/Dockerfile | 26 -- docker-compose/datasets/Dockerfile | 62 --- docker-compose/front-dev/Dockerfile | 17 - docker-compose/import/Dockerfile | 33 -- docker-compose/keycloak-database/Dockerfile | 15 - docker-compose/keycloak/Dockerfile | 52 --- docker-compose/nginx/Dockerfile | 29 -- docker-compose/nifti-conversion/Dockerfile | 122 ------ docker-compose/preclinical/Dockerfile | 23 -- docker-compose/solr/Dockerfile | 22 -- docker-compose/studies/Dockerfile | 35 -- docker-compose/users/Dockerfile | 40 -- shanoir-ng-datasets/pom.xml | 6 - shanoir-ng-front/pom.xml | 19 - shanoir-ng-import/pom.xml | 6 - shanoir-ng-keycloak-auth/pom.xml | 2 - shanoir-ng-nifti-conversion/pom.xml | 6 - shanoir-ng-preclinical/pom.xml | 5 - shanoir-ng-studies/pom.xml | 6 - shanoir-ng-users/pom.xml | 5 - 26 files changed, 406 insertions(+), 545 deletions(-) create mode 100644 docker-compose/Dockerfile rename {utils => docker-compose/common}/entrypoint_common (100%) rename {utils => docker-compose/common}/oneshot (100%) delete mode 100644 docker-compose/database/Dockerfile delete mode 100644 docker-compose/datasets/Dockerfile delete mode 100644 docker-compose/front-dev/Dockerfile delete mode 100644 docker-compose/import/Dockerfile delete mode 100644 docker-compose/keycloak-database/Dockerfile delete mode 100644 docker-compose/keycloak/Dockerfile delete mode 100644 docker-compose/nginx/Dockerfile delete mode 100644 docker-compose/nifti-conversion/Dockerfile delete mode 100644 docker-compose/preclinical/Dockerfile delete mode 100644 docker-compose/solr/Dockerfile delete mode 100644 docker-compose/studies/Dockerfile delete mode 100644 docker-compose/users/Dockerfile diff --git a/.gitignore b/.gitignore index dbd12221d2..1fe10ec012 100644 --- a/.gitignore +++ b/.gitignore @@ -44,8 +44,6 @@ shanoir-ng-front/.angular .env.bak /docker-compose/nginx/webapp/ /docker-compose/*/shanoir-ng-*.jar -/docker-compose/*/entrypoint_common -/docker-compose/*/oneshot /tmp shanoir-ng-tests/tests/.Python shanoir-ng-tests/tests/include/ diff --git a/docker-compose-dev.yml b/docker-compose-dev.yml index 935c7d2baf..9436aca5c1 100644 --- a/docker-compose-dev.yml +++ b/docker-compose-dev.yml @@ -21,7 +21,9 @@ services: front-dev: container_name: front-dev - build: ./docker-compose/front-dev + build: + context: docker-compose + target: front-dev volumes: - "./shanoir-ng-front:/app/" command: ng serve --host 0.0.0.0 --disable-host-check diff --git a/docker-compose.yml b/docker-compose.yml index fd52868e4c..6bfe441f8b 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -17,7 +17,9 @@ services: # keycloak-database: container_name: "${SHANOIR_PREFIX}keycloak-database" - build: ./docker-compose/keycloak-database + build: + context: docker-compose + target: keycloak-database environment: - MYSQL_DATABASE=keycloak ulimits: @@ -43,7 +45,9 @@ services: - SHANOIR_KEYCLOAK_USER - SHANOIR_KEYCLOAK_PASSWORD - SHANOIR_ALLOWED_ADMIN_IPS - build: ./docker-compose/keycloak + build: + context: docker-compose + target: keycloak volumes: - "keycloak-logs:/opt/keycloak/data/log" networks: @@ -70,7 +74,9 @@ services: # database: container_name: "${SHANOIR_PREFIX}database" - build: ./docker-compose/database + build: + context: docker-compose + target: database command: --max_allowed_packet 20000000 env_file: - ./docker-compose/database/variables.env @@ -91,7 +97,9 @@ services: # users: container_name: "${SHANOIR_PREFIX}users" - build: ./docker-compose/users + build: + context: docker-compose + target: users environment: - SHANOIR_PREFIX - SHANOIR_URL_SCHEME @@ -121,7 +129,9 @@ services: # studies: container_name: "${SHANOIR_PREFIX}studies" - build: ./docker-compose/studies + build: + context: docker-compose + target: studies environment: - SHANOIR_PREFIX - SHANOIR_URL_SCHEME @@ -146,7 +156,9 @@ services: # import: container_name: "${SHANOIR_PREFIX}import" - build: ./docker-compose/import + build: + context: docker-compose + target: import environment: - SHANOIR_PREFIX - SHANOIR_URL_SCHEME @@ -170,7 +182,9 @@ services: # datasets: container_name: "${SHANOIR_PREFIX}datasets" - build: ./docker-compose/datasets + build: + context: docker-compose + target: datasets environment: - SHANOIR_PREFIX - SHANOIR_URL_SCHEME @@ -198,7 +212,9 @@ services: preclinical: container_name: "${SHANOIR_PREFIX}preclinical" - build: ./docker-compose/preclinical + build: + context: docker-compose + target: preclinical environment: - SHANOIR_PREFIX - SHANOIR_URL_SCHEME @@ -223,7 +239,9 @@ services: # nifti-conversion: container_name: "${SHANOIR_PREFIX}nifti-conversion" - build: ./docker-compose/nifti-conversion + build: + context: docker-compose + target: nifti-conversion environment: - SHANOIR_PREFIX - SHANOIR_URL_SCHEME @@ -244,7 +262,9 @@ services: # solr: container_name: "${SHANOIR_PREFIX}solr" - build: ./docker-compose/solr + build: + context: docker-compose + target: solr environment: - SOLR_LOG_LEVEL=SEVERE volumes: @@ -317,7 +337,9 @@ services: # nginx: container_name: shanoir-ng-nginx - build: ./docker-compose/nginx + build: + context: docker-compose + target: nginx environment: - SHANOIR_PREFIX - SHANOIR_URL_SCHEME diff --git a/docker-compose/Dockerfile b/docker-compose/Dockerfile new file mode 100644 index 0000000000..3af0255641 --- /dev/null +++ b/docker-compose/Dockerfile @@ -0,0 +1,370 @@ +# Shanoir NG - Import, manage and share neuroimaging data +# Copyright (C) 2009-2024 Inria - https://www.inria.fr/ +# Contact us on https://project.inria.fr/shanoir/ +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see https://www.gnu.org/licenses/gpl-3.0.html + + +################ base debian image ######################################### + +FROM debian:bookworm as base-debian + +# - disable the automatic "apt-get clean" command (because we mount +# /var/cache/apt from an external volume to speed-up the build) +# - run "apt-get update" now (to avoid downloading the lists multiple times) +RUN rm /etc/apt/apt.conf.d/docker-clean \ + && apt-get update -qq + + + +################ common image for the java microservices ################### + +FROM base-debian as base-microservice + +# - NOTE: using bookworm-proposed-updates because of +# https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1039472 +RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ + echo "deb http://deb.debian.org/debian bookworm-proposed-updates main" >> /etc/apt/sources.list \ + && apt-get update -qq \ + && apt-get install -qqy openjdk-17-jre-headless ca-certificates-java + +RUN mkdir -pv /var/log/shanoir-ng-logs + +COPY --link \ + common/entrypoint_common \ + common/oneshot \ + /usr/bin/ + +ENTRYPOINT ["/bin/entrypoint", "java", "-Djava.security.egd=file:/dev/urandom", "-Djavax.net.ssl.trustStorePassword=changeit"] + + + +################ datasets ################################################## + +FROM alpine as datasets-download + +# Installation of dcm4che into /opt/dcm4che to manage the store-scu into the +# PACS dcm4che3, used last version 5.21.0 as available on the 2020-02-14 +# https://sourceforge.net/projects/dcm4che/files/dcm4che3/ +RUN wget -qO dcm4che-bin.zip \ + https://downloads.sourceforge.net/project/dcm4che/dcm4che3/5.21.0/dcm4che-5.21.0-bin.zip \ + && mkdir -p /target/opt && cd /target/opt \ + && unzip /dcm4che-bin.zip && mv dcm4che-* dcm4che \ + && rm /dcm4che-bin.zip + + +FROM base-microservice as datasets + +RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ + apt-get update -qq \ + && apt-get install -qqy \ + pigz \ + gzip \ + locales \ + locales-all + +# take care of path +ENV PATH /opt/dcm4che/bin:$PATH +ENV LC_ALL en_US.UTF-8 +ENV LANG en_US.UTF-8 +ENV LANGUAGE en_US.UTF-8 + +# install the files from the 'dowloader' stage +COPY --link --from=datasets-download /target/. / + +COPY --link datasets/entrypoint /usr/bin/ +COPY --link datasets/shanoir-ng-datasets.jar shanoir-ng-datasets.jar + +# Use the below line for remote debugging and to active dev profile +#CMD ["-jar", "/shanoir-ng-datasets.jar", "-Xdebug", "-Xrunjdwp:transport=dt_socket,server=y,address=0.0.0.0:9914,suspend=y"] +CMD ["-jar", "/shanoir-ng-datasets.jar"] + + + +################ import #################################################### + +FROM base-microservice as import + +COPY --link import/entrypoint /usr/bin/ +COPY --link import/shanoir-ng-import.jar shanoir-ng-import.jar + +# Use the below line for remote debugging and to active dev profile +#CMD ["-jar", "/shanoir-ng-import.jar", "-Xdebug", "-Xrunjdwp:transport=dt_socket,server=y,address=0.0.0.0:9913,suspend=y"] +CMD ["-jar", "/shanoir-ng-import.jar"] + + + +################ nifti-conversion ########################################## + +FROM base-debian as nifti-conversion-conda + +RUN --mount=type=cache,target=/var/cache/apt,sharing=locked,id=apt-conda \ + apt-get -qqy update \ + && apt-get -qqy --no-install-recommends install curl ca-certificates + +# Install miniconda +RUN --mount=type=cache,target=/opt/miniconda3/pkgs \ + curl -LSsf -o miniconda.sh https://repo.anaconda.com/miniconda/Miniconda3-py311_24.1.2-0-Linux-x86_64.sh \ + && bash miniconda.sh -fb -p /opt/miniconda3 \ + && rm miniconda.sh + +# Install dicomifier +RUN --mount=type=cache,target=/opt/miniconda3/pkgs \ + /opt/miniconda3/bin/conda install -c conda-forge dicomifier -y + + +FROM base-debian as nifti-conversion-builder + +RUN --mount=type=cache,target=/var/cache/apt,sharing=locked,id=apt-builder \ + apt-get -qqy update \ + && apt-get -qqy install \ + build-essential \ + curl \ + cmake \ + git \ + pkg-config + +# Compile DCM2NIIX from source +ENV DCM2NIIX_VERSION=v1.0.20210317 +RUN mkdir /src && cd /src \ + && curl -LSsf https://github.com/rordenlab/dcm2niix/archive/refs/tags/$DCM2NIIX_VERSION.tar.gz \ + | tar zx \ + && cd dcm2niix-* && mkdir build \ + && cd build && cmake .. && make -j4 && make install DESTDIR=/target + +# Install mri_conv +RUN mkdir -p /target/opt/nifti-converters/mriconverter \ + && cd /target/opt/nifti-converters/mriconverter \ + && curl -LSsf https://github.com/populse/mri_conv/archive/refs/heads/master.tar.gz \ + | tar zx --strip-components 1 \ + && chmod 0777 . MRIFileManager/MRIManager.jar + + +FROM base-microservice as nifti-conversion + +# xvfb+gtk2 needed by mri_conv (headless mode not supported by DicomToNifti) +# see: https://populse.github.io/mri_conv/Installation/installation.html#scriptwithoutGUI +RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ + apt-get update -qqy \ + && apt-get install -qqy \ + libgdcm-tools \ + locales \ + locales-all \ + jq \ + libgtk2.0-0 \ + openjdk-17-jre \ + pigz \ + xvfb + +# Copy converters files +COPY --link --chmod=0755 nifti-conversion/external/dcm2nii/linux/31MARCH2008/dcm2nii /opt/nifti-converters/dcm2nii_2008-03-31 +COPY --link --chmod=0755 nifti-conversion/external/dcm2nii/linux/dcm2nii /opt/nifti-converters/dcm2nii_2014-08-04 +COPY --link --chmod=0755 nifti-conversion/external/mcverter/linux/mcverter_* /opt/nifti-converters/ +COPY --link nifti-conversion/external/mcverter/linux/lib/lib*.so.* /usr/local/lib/x86_64-linux-gnu/ +RUN mkdir -m 1777 \ + /.dcm2nii_2008-03-31 \ + /.dcm2nii_2014-08-04 + +# install animaConvertImage to convert Analyze format into nifti +COPY --link --chmod=0755 nifti-conversion/external/anima/animaConvertImage /usr/local/bin/ + +# install the binaries built in the 'builder' & 'conda' stages +COPY --link --from=nifti-conversion-builder /target/. / +COPY --link --from=nifti-conversion-conda /opt/miniconda3 /opt/miniconda3 + +# update the ld cache (so that the new libraries can be loaded) +RUN ldconfig + +# install the microservice +COPY --link nifti-conversion/nifti-conversion.jar nifti-conversion.jar +COPY --link nifti-conversion/entrypoint /usr/bin/ + +ENV LC_ALL fr_FR.UTF-8 +ENV LANG fr_FR.UTF-8 +ENV LANGUAGE fr_FR.UTF-8 +ENV PATH="/opt/miniconda3/bin:${PATH}" + +CMD ["-jar", "/nifti-conversion.jar"] + + + +################ preclinical ############################################### + +FROM base-microservice as preclinical + +COPY --link preclinical/shanoir-ng-preclinical.jar shanoir-ng-preclinical.jar +COPY --link preclinical/entrypoint /usr/bin/ + +# Use the below line for remote debugging +#CMD ["-jar", "/shanoir-ng-preclinical.jar", "-Xmx6g", "-Xms1g", "-Xdebug", "-Xrunjdwp:transport=dt_socket,server=y,address=0.0.0.0:9915,suspend=y", "-Dspring.profiles.active=dev"] +CMD ["-jar", "/shanoir-ng-preclinical.jar", "-Xmx6g", "-Xms1g"] + + + +################ studies ################################################### + +FROM base-microservice as studies + +COPY --link studies/shanoir-ng-studies.jar shanoir-ng-studies.jar +COPY --link studies/entrypoint /usr/bin/ + +# Use the below line for remote debugging and to active dev profile +#CMD ["-jar", "/shanoir-ng-studies.jar", "-Xdebug", "-Xrunjdwp:transport=dt_socket,server=y,address=0.0.0.0:9912,suspend=y"] +CMD ["-jar", "/shanoir-ng-studies.jar"] + + + +################ users ##################################################### + +FROM base-microservice as users + +RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ + apt-get update -qq && apt-get install -qqy openssl + +COPY --link users/shanoir-ng-users.jar shanoir-ng-users.jar +COPY --link users/entrypoint /usr/bin/ + + +ENV \ + kc.admin.client.client.id="admin-cli" \ + kc.admin.client.realm="master" + +# Use the below line for remote debugging and development profile purpose +#CMD ["-jar", "/shanoir-ng-users.jar", "-Xdebug", "-Xrunjdwp:transport=dt_socket,server=y,address=0.0.0.0:9911,suspend=y", "-Dspring.profiles.active=dev", "--syncAllUsersToKeycloak=true"] +CMD ["-jar", "/shanoir-ng-users.jar"] + + + +################ keycloak ################################################## + +FROM quay.io/keycloak/keycloak:23.0.5 as keycloak-base + +# keycloak options (https://www.keycloak.org/server/all-config) +# +# Here we list only the variables that need to be set at both runtime *and* at +# buildtime ("kc.sh build"). If they do not have the same values then the +# quarkus image has to be rebuilt at runtime, which delays container startup +# with a message like: "(Quarkus augmentation completed in 180315ms)" +# +# The relevant variables can be listed with: /opt/keycloak/bin/kc.sh show-config +# +# All other variables are set in the entrypoint. +ENV KC_DB="mysql" \ + KC_LOG="console,file" \ + KC_HTTP_RELATIVE_PATH="/auth" + +# +# Use builder to integrate custom provider +# +FROM keycloak-base as keycloak-builder + +COPY keycloak/shanoir-ng-keycloak-auth.jar /opt/keycloak/providers + +WORKDIR /opt/keycloak +RUN /opt/keycloak/bin/kc.sh build + +# +# Create actual image, based on builder before +# +FROM keycloak-base as keycloak + +COPY --from=keycloak-builder /opt/keycloak/ /opt/keycloak/ +COPY --chown=keycloak keycloak/themes/. /opt/keycloak/themes +COPY keycloak/cfg/. /opt/keycloak/ +RUN mkdir /opt/keycloak/data/log + +COPY common/entrypoint_common /usr/bin/ +COPY keycloak/entrypoint /usr/bin/ + +ENTRYPOINT ["/bin/entrypoint", "/opt/keycloak/bin/kc.sh"] +CMD [] + + + +################ nginx ##################################################### + +FROM ohif/viewer:v4.12.32.19362 AS nginx-viewer + +FROM nginx as nginx + +COPY --link \ + nginx/http.conf \ + nginx/https.conf \ + nginx/nginx.conf \ + nginx/shanoir.template.conf \ + nginx/shanoir.template.dev.conf \ + nginx/shanoir.template.prod.conf \ + /etc/nginx/ + +COPY --link \ + nginx/viewer/app-config.js \ + nginx/viewer/ohif-viewer.template.conf \ + /etc/nginx/viewer/ +COPY --link --from=nginx-viewer /usr/share/nginx/html/. /etc/nginx/viewer/html/ + +COPY --link common/entrypoint_common /usr/bin/ +COPY --link nginx/entrypoint /usr/bin/ + +COPY --link nginx/webapp/ /etc/nginx/html/ + +ENTRYPOINT ["/bin/entrypoint"] +CMD ["nginx", "-g", "daemon off;"] + + + +################ front-dev ################################################# + +FROM node:lts-alpine3.17 as front-dev + +WORKDIR /app + +RUN npm install -g @angular/cli@11.2.14 + + + +################ database ################################################## + +FROM mysql/mysql-server:5.7 as database + +COPY --link --chmod=0755 database/shanoir-entrypoint.sh / +ENTRYPOINT ["/shanoir-entrypoint.sh"] +CMD ["mysqld"] + +COPY --link --chmod=0755 \ + database/1_create_databases.sh \ + database/2_add_users.sql \ + database/3_add_statistics_procedure.sql \ + database/4_add_studyStatistics_procedure.sql \ + /docker-entrypoint-initdb.d/ + +COPY --link database/db-changes /opt/db-changes + + + +################ keycloak-database ################################################## + +FROM mysql/mysql-server:5.7 as keycloak-database + +COPY --link --chmod=0755 keycloak-database/1_add_users.sql /docker-entrypoint-initdb.d/ + + + +################ solr ############################################################### + +FROM solr:8.1 as solr + +USER root +RUN mkdir -p /etc/shanoir-core-template \ + && chown solr:solr /etc/shanoir-core-template +USER solr + +COPY --link solr/core /etc/shanoir-core-template/ + +CMD ["solr-precreate", "shanoir", "/etc/shanoir-core-template"] + diff --git a/utils/entrypoint_common b/docker-compose/common/entrypoint_common similarity index 100% rename from utils/entrypoint_common rename to docker-compose/common/entrypoint_common diff --git a/utils/oneshot b/docker-compose/common/oneshot similarity index 100% rename from utils/oneshot rename to docker-compose/common/oneshot diff --git a/docker-compose/database/Dockerfile b/docker-compose/database/Dockerfile deleted file mode 100644 index 59df6253c6..0000000000 --- a/docker-compose/database/Dockerfile +++ /dev/null @@ -1,26 +0,0 @@ -# Shanoir NG - Import, manage and share neuroimaging data -# Copyright (C) 2009-2019 Inria - https://www.inria.fr/ -# Contact us on https://project.inria.fr/shanoir/ -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see https://www.gnu.org/licenses/gpl-3.0.html - -FROM mysql/mysql-server:5.7 - -COPY --link --chmod=0755 /shanoir-entrypoint.sh / -ENTRYPOINT ["/shanoir-entrypoint.sh"] -CMD ["mysqld"] - -COPY --link --chmod=0755 \ - 1_create_databases.sh \ - 2_add_users.sql \ - 3_add_statistics_procedure.sql \ - 4_add_studyStatistics_procedure.sql \ - /docker-entrypoint-initdb.d/ - -COPY --link db-changes /opt/db-changes diff --git a/docker-compose/datasets/Dockerfile b/docker-compose/datasets/Dockerfile deleted file mode 100644 index dbeadd834b..0000000000 --- a/docker-compose/datasets/Dockerfile +++ /dev/null @@ -1,62 +0,0 @@ -# Shanoir NG - Import, manage and share neuroimaging data -# Copyright (C) 2009-2019 Inria - https://www.inria.fr/ -# Contact us on https://project.inria.fr/shanoir/ -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see https://www.gnu.org/licenses/gpl-3.0.html - -FROM alpine as downloader - -# Installation of dcm4che into /opt/dcm4che to manage the store-scu into the -# PACS dcm4che3, used last version 5.21.0 as available on the 2020-02-14 -# https://sourceforge.net/projects/dcm4che/files/dcm4che3/ -RUN wget -qO dcm4che-bin.zip \ - https://downloads.sourceforge.net/project/dcm4che/dcm4che3/5.21.0/dcm4che-5.21.0-bin.zip \ - && mkdir -p /target/opt && cd /target/opt \ - && unzip /dcm4che-bin.zip && mv dcm4che-* dcm4che \ - && rm /dcm4che-bin.zip - - -#--------------- common jre base image ------------------------------------- -FROM debian:bookworm -# - disable the automatic "apt-get clean" command (because we mount -# /var/cache/apt from an external volume to speed-up the build) -# - NOTE: using bookworm-proposed-updates because of -# https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1039472 -RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ - rm /etc/apt/apt.conf.d/docker-clean \ - && echo "deb http://deb.debian.org/debian bookworm-proposed-updates main" >> /etc/apt/sources.list \ - && apt-get update -qq \ - && apt-get install -qqy openjdk-17-jre-headless ca-certificates-java -#---------------------------------------------------------------------------- - -RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ - apt-get update -qq \ - && apt-get install -qqy \ - pigz \ - gzip \ - locales \ - locales-all - -# take care of path -ENV PATH /opt/dcm4che/bin:$PATH -ENV LC_ALL en_US.UTF-8 -ENV LANG en_US.UTF-8 -ENV LANGUAGE en_US.UTF-8 - -# install the files from the 'dowloader' stage -COPY --link --from=downloader /target/. / - -RUN mkdir -pv /var/log/shanoir-ng-logs -COPY --link shanoir-ng-datasets.jar shanoir-ng-datasets.jar -COPY --link entrypoint entrypoint_common oneshot /usr/bin/ - -# Use the below line for remote debugging and to active dev profile -#ENTRYPOINT ["/bin/entrypoint", "java", "-Xdebug", "-Xrunjdwp:transport=dt_socket,server=y,address=0.0.0.0:9914,suspend=y", "-Djava.security.egd=file:/dev/./urandom", "-Djavax.net.ssl.trustStorePassword=changeit", "-jar", "/shanoir-ng-datasets.jar"] - -ENTRYPOINT ["/bin/entrypoint", "java", "-Djava.security.egd=file:/dev/./urandom", "-Djavax.net.ssl.trustStorePassword=changeit", "-jar", "/shanoir-ng-datasets.jar"] diff --git a/docker-compose/front-dev/Dockerfile b/docker-compose/front-dev/Dockerfile deleted file mode 100644 index a475521564..0000000000 --- a/docker-compose/front-dev/Dockerfile +++ /dev/null @@ -1,17 +0,0 @@ -# Shanoir NG - Import, manage and share neuroimaging data -# Copyright (C) 2009-2019 Inria - https://www.inria.fr/ -# Contact us on https://project.inria.fr/shanoir/ -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see https://www.gnu.org/licenses/gpl-3.0.html - -FROM node:lts-alpine3.17 - -WORKDIR /app - -RUN npm install -g @angular/cli@11.2.14 diff --git a/docker-compose/import/Dockerfile b/docker-compose/import/Dockerfile deleted file mode 100644 index f02b684869..0000000000 --- a/docker-compose/import/Dockerfile +++ /dev/null @@ -1,33 +0,0 @@ -# Shanoir NG - Import, manage and share neuroimaging data -# Copyright (C) 2009-2019 Inria - https://www.inria.fr/ -# Contact us on https://project.inria.fr/shanoir/ -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see https://www.gnu.org/licenses/gpl-3.0.html - -#--------------- common jre base image ------------------------------------- -FROM debian:bookworm -# - disable the automatic "apt-get clean" command (because we mount -# /var/cache/apt from an external volume to speed-up the build) -# - NOTE: using bookworm-proposed-updates because of -# https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1039472 -RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ - rm /etc/apt/apt.conf.d/docker-clean \ - && echo "deb http://deb.debian.org/debian bookworm-proposed-updates main" >> /etc/apt/sources.list \ - && apt-get update -qq \ - && apt-get install -qqy openjdk-17-jre-headless ca-certificates-java -#---------------------------------------------------------------------------- - -RUN mkdir -pv /var/log/shanoir-ng-logs - -COPY --link shanoir-ng-import.jar shanoir-ng-import.jar -COPY --link entrypoint entrypoint_common oneshot /usr/bin/ - -# Use the below line for remote debugging and to active dev profile -#ENTRYPOINT ["/bin/entrypoint", "java", "-Xdebug", "-Xrunjdwp:transport=dt_socket,server=y,address=0.0.0.0:9913,suspend=y", "-Djava.security.egd=file:/dev/./urandom", "-Djavax.net.ssl.trustStorePassword=changeit", "-jar", "/shanoir-ng-import.jar"] -ENTRYPOINT ["/bin/entrypoint", "java", "-Djava.security.egd=file:/dev/./urandom", "-Djavax.net.ssl.trustStorePassword=changeit", "-jar", "/shanoir-ng-import.jar"] diff --git a/docker-compose/keycloak-database/Dockerfile b/docker-compose/keycloak-database/Dockerfile deleted file mode 100644 index e793219198..0000000000 --- a/docker-compose/keycloak-database/Dockerfile +++ /dev/null @@ -1,15 +0,0 @@ -# Shanoir NG - Import, manage and share neuroimaging data -# Copyright (C) 2009-2019 Inria - https://www.inria.fr/ -# Contact us on https://project.inria.fr/shanoir/ -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see https://www.gnu.org/licenses/gpl-3.0.html - -FROM mysql/mysql-server:5.7 - -COPY --link --chmod=0755 1_add_users.sql /docker-entrypoint-initdb.d/ diff --git a/docker-compose/keycloak/Dockerfile b/docker-compose/keycloak/Dockerfile deleted file mode 100644 index f2324de207..0000000000 --- a/docker-compose/keycloak/Dockerfile +++ /dev/null @@ -1,52 +0,0 @@ -# Shanoir NG - Import, manage and share neuroimaging data -# Copyright (C) 2009-2019 Inria - https://www.inria.fr/ -# Contact us on https://project.inria.fr/shanoir/ -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see https://www.gnu.org/licenses/gpl-3.0.html - -FROM quay.io/keycloak/keycloak:23.0.5 as base_image - -# keycloak options (https://www.keycloak.org/server/all-config) -# -# Here we list only the variables that need to be set at both runtime *and* at -# buildtime ("kc.sh build"). If they do not have the same values then the -# quarkus image has to be rebuilt at runtime, which delays container startup -# with a message like: "(Quarkus augmentation completed in 180315ms)" -# -# The relevant variables can be listed with: /opt/keycloak/bin/kc.sh show-config -# -# All other variables are set in the entrypoint. -ENV KC_DB="mysql" \ - KC_LOG="console,file" \ - KC_HTTP_RELATIVE_PATH="/auth" - -# -# Use builder to integrate custom provider -# -FROM base_image as builder - -COPY shanoir-ng-keycloak-auth.jar /opt/keycloak/providers - -WORKDIR /opt/keycloak -RUN /opt/keycloak/bin/kc.sh build - -# -# Create actual image, based on builder before -# -FROM base_image - -COPY --from=builder /opt/keycloak/ /opt/keycloak/ -COPY --chown=keycloak themes/. /opt/keycloak/themes -COPY cfg/. /opt/keycloak/ -RUN mkdir /opt/keycloak/data/log - -COPY entrypoint entrypoint_common /bin/ - -ENTRYPOINT ["/bin/entrypoint", "/opt/keycloak/bin/kc.sh"] -CMD [] diff --git a/docker-compose/nginx/Dockerfile b/docker-compose/nginx/Dockerfile deleted file mode 100644 index f0dc24753a..0000000000 --- a/docker-compose/nginx/Dockerfile +++ /dev/null @@ -1,29 +0,0 @@ -# Shanoir NG - Import, manage and share neuroimaging data -# Copyright (C) 2009-2019 Inria - https://www.inria.fr/ -# Contact us on https://project.inria.fr/shanoir/ -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see https://www.gnu.org/licenses/gpl-3.0.html - -FROM ohif/viewer:v4.12.32.19362 AS viewer - -FROM nginx - -COPY --link nginx.conf http.conf https.conf shanoir.template.conf shanoir.template.dev.conf shanoir.template.prod.conf /etc/nginx/ - -COPY --link viewer/app-config.js viewer/ohif-viewer.template.conf /etc/nginx/viewer/ -COPY --link --from=viewer /usr/share/nginx/html/. /etc/nginx/viewer/html/ - -COPY --link entrypoint entrypoint_common /usr/bin/ - - - -COPY --link webapp/ /etc/nginx/html/ - -ENTRYPOINT ["/bin/entrypoint"] -CMD ["nginx", "-g", "daemon off;"] diff --git a/docker-compose/nifti-conversion/Dockerfile b/docker-compose/nifti-conversion/Dockerfile deleted file mode 100644 index 66882a09fb..0000000000 --- a/docker-compose/nifti-conversion/Dockerfile +++ /dev/null @@ -1,122 +0,0 @@ -# Shanoir NG - Import, manage and share neuroimaging data -# Copyright (C) 2009-2019 Inria - https://www.inria.fr/ -# Contact us on https://project.inria.fr/shanoir/ -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see https://www.gnu.org/licenses/gpl-3.0.html - -FROM debian:bookworm as base_image - -# - disable the automatic "apt-get clean" command (because we mount -# /var/cache/apt from an external volume to speed-up the build) -# - run "apt-get update" now (to avoid downloading the lists 3 times) -RUN rm /etc/apt/apt.conf.d/docker-clean \ - && apt-get update -qq - -FROM base_image as conda - -RUN --mount=type=cache,target=/var/cache/apt,sharing=locked,id=apt-conda \ - apt-get -qqy update \ - && apt-get -qqy --no-install-recommends install curl ca-certificates - -# Install miniconda -RUN --mount=type=cache,target=/opt/miniconda3/pkgs \ - curl -LSsf -o miniconda.sh https://repo.anaconda.com/miniconda/Miniconda3-py311_24.1.2-0-Linux-x86_64.sh \ - && bash miniconda.sh -fb -p /opt/miniconda3 \ - && rm miniconda.sh - -# Install dicomifier -RUN --mount=type=cache,target=/opt/miniconda3/pkgs \ - /opt/miniconda3/bin/conda install -c conda-forge dicomifier -y - - -FROM base_image as builder - -RUN --mount=type=cache,target=/var/cache/apt,sharing=locked,id=apt-builder \ - apt-get -qqy update \ - && apt-get -qqy install \ - build-essential \ - curl \ - cmake \ - git \ - pkg-config - -# Compile DCM2NIIX from source -ENV DCM2NIIX_VERSION=v1.0.20210317 -RUN mkdir /src && cd /src \ - && curl -LSsf https://github.com/rordenlab/dcm2niix/archive/refs/tags/$DCM2NIIX_VERSION.tar.gz \ - | tar zx \ - && cd dcm2niix-* && mkdir build \ - && cd build && cmake .. && make -j4 && make install DESTDIR=/target - -# Install mri_conv -RUN mkdir -p /target/opt/nifti-converters/mriconverter \ - && cd /target/opt/nifti-converters/mriconverter \ - && curl -LSsf https://github.com/populse/mri_conv/archive/refs/heads/master.tar.gz \ - | tar zx --strip-components 1 \ - && chmod 0777 . MRIFileManager/MRIManager.jar - - -#--------------- common jre base image ------------------------------------- -FROM debian:bookworm as final -# - disable the automatic "apt-get clean" command (because we mount -# /var/cache/apt from an external volume to speed-up the build) -# - NOTE: using bookworm-proposed-updates because of -# https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1039472 -RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ - rm /etc/apt/apt.conf.d/docker-clean \ - && echo "deb http://deb.debian.org/debian bookworm-proposed-updates main" >> /etc/apt/sources.list \ - && apt-get update -qq \ - && apt-get install -qqy openjdk-17-jre-headless ca-certificates-java -#---------------------------------------------------------------------------- - -# xvfb+gtk2 needed by mri_conv (headless mode not supported by DicomToNifti) -# see: https://populse.github.io/mri_conv/Installation/installation.html#scriptwithoutGUI -RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ - apt-get update -qqy \ - && apt-get install -qqy \ - libgdcm-tools \ - locales \ - locales-all \ - jq \ - libgtk2.0-0 \ - openjdk-17-jre \ - pigz \ - xvfb - -# Copy converters files -COPY --link --chmod=0755 external/dcm2nii/linux/31MARCH2008/dcm2nii /opt/nifti-converters/dcm2nii_2008-03-31 -COPY --link --chmod=0755 external/dcm2nii/linux/dcm2nii /opt/nifti-converters/dcm2nii_2014-08-04 -COPY --link --chmod=0755 external/mcverter/linux/mcverter_* /opt/nifti-converters/ -COPY --link external/mcverter/linux/lib/lib*.so.* /usr/local/lib/x86_64-linux-gnu/ -RUN mkdir -m 1777 \ - /.dcm2nii_2008-03-31 \ - /.dcm2nii_2014-08-04 - -# install animaConvertImage to convert Analyze format into nifti -COPY --link --chmod=0755 external/anima/animaConvertImage /usr/local/bin/ - -# install the binaries built in the 'builder' & 'conda' stages -COPY --link --from=builder /target/. / -COPY --link --from=conda /opt/miniconda3 /opt/miniconda3 - -# update the ld cache (so that the new libraries can be loaded) -# and make the log dir -RUN ldconfig \ - && mkdir -pv /var/log/shanoir-ng-logs - -# install the microservice -COPY --link nifti-conversion.jar nifti-conversion.jar -COPY --link entrypoint entrypoint_common oneshot /usr/bin/ - -ENV LC_ALL fr_FR.UTF-8 -ENV LANG fr_FR.UTF-8 -ENV LANGUAGE fr_FR.UTF-8 -ENV PATH="/opt/miniconda3/bin:${PATH}" - -ENTRYPOINT ["/bin/entrypoint", "java", "-Djava.security.egd=file:/dev/./urandom", "-Djavax.net.ssl.trustStorePassword=changeit", "-jar", "/nifti-conversion.jar"] diff --git a/docker-compose/preclinical/Dockerfile b/docker-compose/preclinical/Dockerfile deleted file mode 100644 index 68e49cb8aa..0000000000 --- a/docker-compose/preclinical/Dockerfile +++ /dev/null @@ -1,23 +0,0 @@ -#--------------- common jre base image ------------------------------------- -FROM debian:bookworm -# - disable the automatic "apt-get clean" command (because we mount -# /var/cache/apt from an external volume to speed-up the build) -# - NOTE: using bookworm-proposed-updates because of -# https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1039472 -RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ - rm /etc/apt/apt.conf.d/docker-clean \ - && echo "deb http://deb.debian.org/debian bookworm-proposed-updates main" >> /etc/apt/sources.list \ - && apt-get update -qq \ - && apt-get install -qqy openjdk-17-jre-headless ca-certificates-java -#---------------------------------------------------------------------------- - - -RUN mkdir -pv /var/log/shanoir-ng-logs -COPY --link shanoir-ng-preclinical.jar shanoir-ng-preclinical.jar - -COPY --link entrypoint entrypoint_common oneshot /usr/bin/ - -# Use the below line for remote debugging -#ENTRYPOINT ["/bin/entrypoint", "java", "-Xdebug", "-Xrunjdwp:transport=dt_socket,server=y,address=0.0.0.0:9915,suspend=y", "-Djava.security.egd=file:/dev/./urandom", "-Djavax.net.ssl.trustStorePassword=changeit", "-Dspring.profiles.active=dev", "-jar", "/shanoir-ng-preclinical.jar"] - -ENTRYPOINT ["/bin/entrypoint", "java","-Xmx6g", "-Xms1g", "-Djava.security.egd=file:/dev/./urandom", "-Djavax.net.ssl.trustStorePassword=changeit", "-jar", "/shanoir-ng-preclinical.jar"] diff --git a/docker-compose/solr/Dockerfile b/docker-compose/solr/Dockerfile deleted file mode 100644 index d81a15839c..0000000000 --- a/docker-compose/solr/Dockerfile +++ /dev/null @@ -1,22 +0,0 @@ -# Shanoir NG - Import, manage and share neuroimaging data -# Copyright (C) 2009-2019 Inria - https://www.inria.fr/ -# Contact us on https://project.inria.fr/shanoir/ -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see https://www.gnu.org/licenses/gpl-3.0.html - -FROM solr:8.1 - -USER root -RUN mkdir -p /etc/shanoir-core-template \ - && chown solr:solr /etc/shanoir-core-template -USER solr - -COPY --link ./core /etc/shanoir-core-template - -CMD ["solr-precreate", "shanoir", "/etc/shanoir-core-template"] diff --git a/docker-compose/studies/Dockerfile b/docker-compose/studies/Dockerfile deleted file mode 100644 index 0ff5475849..0000000000 --- a/docker-compose/studies/Dockerfile +++ /dev/null @@ -1,35 +0,0 @@ -# Shanoir NG - Import, manage and share neuroimaging data -# Copyright (C) 2009-2019 Inria - https://www.inria.fr/ -# Contact us on https://project.inria.fr/shanoir/ -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see https://www.gnu.org/licenses/gpl-3.0.html - -#--------------- common jre base image ------------------------------------- -FROM debian:bookworm -# - disable the automatic "apt-get clean" command (because we mount -# /var/cache/apt from an external volume to speed-up the build) -# - NOTE: using bookworm-proposed-updates because of -# https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1039472 -RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ - rm /etc/apt/apt.conf.d/docker-clean \ - && echo "deb http://deb.debian.org/debian bookworm-proposed-updates main" >> /etc/apt/sources.list \ - && apt-get update -qq \ - && apt-get install -qqy openjdk-17-jre-headless ca-certificates-java -#---------------------------------------------------------------------------- - - -RUN mkdir -pv /var/log/shanoir-ng-logs -COPY --link shanoir-ng-studies.jar shanoir-ng-studies.jar - -COPY --link entrypoint entrypoint_common oneshot /usr/bin/ - -# Use the below line for remote debugging and to active dev profile -#ENTRYPOINT ["/bin/entrypoint", "java", "-Xdebug", "-Xrunjdwp:transport=dt_socket,server=y,address=0.0.0.0:9912,suspend=y", "-Djava.security.egd=file:/dev/./urandom", "-Djavax.net.ssl.trustStorePassword=changeit", "-jar", "/shanoir-ng-studies.jar"] - -ENTRYPOINT ["/bin/entrypoint", "java", "-Djava.security.egd=file:/dev/./urandom", "-Djavax.net.ssl.trustStorePassword=changeit", "-jar", "/shanoir-ng-studies.jar"] diff --git a/docker-compose/users/Dockerfile b/docker-compose/users/Dockerfile deleted file mode 100644 index 67ab7a19a0..0000000000 --- a/docker-compose/users/Dockerfile +++ /dev/null @@ -1,40 +0,0 @@ -# Shanoir NG - Import, manage and share neuroimaging data -# Copyright (C) 2009-2019 Inria - https://www.inria.fr/ -# Contact us on https://project.inria.fr/shanoir/ -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see https://www.gnu.org/licenses/gpl-3.0.html - -#--------------- common jre base image ------------------------------------- -FROM debian:bookworm -# - disable the automatic "apt-get clean" command (because we mount -# /var/cache/apt from an external volume to speed-up the build) -# - NOTE: using bookworm-proposed-updates because of -# https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1039472 -RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ - rm /etc/apt/apt.conf.d/docker-clean \ - && echo "deb http://deb.debian.org/debian bookworm-proposed-updates main" >> /etc/apt/sources.list \ - && apt-get update -qq \ - && apt-get install -qqy openjdk-17-jre-headless ca-certificates-java -#---------------------------------------------------------------------------- - -RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ - apt-get update -qq && apt-get install -qqy openssl - -RUN mkdir -pv /var/log/shanoir-ng-logs -COPY --link shanoir-ng-users.jar shanoir-ng-users.jar -COPY --link entrypoint entrypoint_common oneshot /usr/bin/ - -# Use the below line for remote debugging and development profile purpose -#ENTRYPOINT ["/bin/entrypoint", "java", "-Xdebug", "-Xrunjdwp:transport=dt_socket,server=y,address=0.0.0.0:9911,suspend=y", "-Djava.security.egd=file:/dev/./urandom", "-Djavax.net.ssl.trustStorePassword=changeit", "-Dspring.profiles.active=dev", "-jar", "/shanoir-ng-users.jar", "--syncAllUsersToKeycloak=true"] - -ENV \ - kc.admin.client.client.id="admin-cli" \ - kc.admin.client.realm="master" - -ENTRYPOINT ["/bin/entrypoint", "java", "-Djava.security.egd=file:/dev/./urandom", "-Djavax.net.ssl.trustStorePassword=changeit", "-jar", "/shanoir-ng-users.jar"] diff --git a/shanoir-ng-datasets/pom.xml b/shanoir-ng-datasets/pom.xml index 23f96f8168..0bf2ffefcb 100644 --- a/shanoir-ng-datasets/pom.xml +++ b/shanoir-ng-datasets/pom.xml @@ -170,12 +170,6 @@ - - - diff --git a/shanoir-ng-front/pom.xml b/shanoir-ng-front/pom.xml index cdbef46c0a..3ac3e6c287 100644 --- a/shanoir-ng-front/pom.xml +++ b/shanoir-ng-front/pom.xml @@ -158,25 +158,6 @@ along with this program. If not, see https://www.gnu.org/licenses/gpl-3.0.html - - org.apache.maven.plugins - maven-antrun-plugin - 1.7 - - - copy-entrypoint_common-nginx - package - - - - - - - run - - - - diff --git a/shanoir-ng-import/pom.xml b/shanoir-ng-import/pom.xml index 836fd0ee9d..38975bb40d 100644 --- a/shanoir-ng-import/pom.xml +++ b/shanoir-ng-import/pom.xml @@ -161,12 +161,6 @@ - - - diff --git a/shanoir-ng-keycloak-auth/pom.xml b/shanoir-ng-keycloak-auth/pom.xml index 34b06544a9..ea5cff19b5 100644 --- a/shanoir-ng-keycloak-auth/pom.xml +++ b/shanoir-ng-keycloak-auth/pom.xml @@ -107,8 +107,6 @@ along with this program. If not, see https://www.gnu.org/licenses/gpl-3.0.html - diff --git a/shanoir-ng-nifti-conversion/pom.xml b/shanoir-ng-nifti-conversion/pom.xml index 83c8f85101..15d7e5e34b 100644 --- a/shanoir-ng-nifti-conversion/pom.xml +++ b/shanoir-ng-nifti-conversion/pom.xml @@ -110,12 +110,6 @@ along with this program. If not, see https://www.gnu.org/licenses/gpl-3.0.html - - - diff --git a/shanoir-ng-preclinical/pom.xml b/shanoir-ng-preclinical/pom.xml index 95085b207e..d1cb066c0c 100644 --- a/shanoir-ng-preclinical/pom.xml +++ b/shanoir-ng-preclinical/pom.xml @@ -95,11 +95,6 @@ along with this program. If not, see https://www.gnu.org/licenses/gpl-3.0.html - - - diff --git a/shanoir-ng-studies/pom.xml b/shanoir-ng-studies/pom.xml index add9b66edc..88fc88490e 100644 --- a/shanoir-ng-studies/pom.xml +++ b/shanoir-ng-studies/pom.xml @@ -72,12 +72,6 @@ - - - diff --git a/shanoir-ng-users/pom.xml b/shanoir-ng-users/pom.xml index aa7fd920dc..1ac1fbbb84 100644 --- a/shanoir-ng-users/pom.xml +++ b/shanoir-ng-users/pom.xml @@ -106,11 +106,6 @@ along with this program. If not, see https://www.gnu.org/licenses/gpl-3.0.html - - - From 1616cf0aabef5596792ad4b243d8284d69f03d0e Mon Sep 17 00:00:00 2001 From: pierrehenri-dauvergne Date: Fri, 24 May 2024 16:48:08 +0200 Subject: [PATCH 30/80] shanoir-issue#2228: remove condition 'is admin' to make a study a challenge --- shanoir-ng-front/src/app/studies/study/study.component.html | 2 +- .../java/org/shanoir/ng/study/service/StudyServiceImpl.java | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/shanoir-ng-front/src/app/studies/study/study.component.html b/shanoir-ng-front/src/app/studies/study/study.component.html index abcabd76f0..7992fb10d1 100644 --- a/shanoir-ng-front/src/app/studies/study/study.component.html +++ b/shanoir-ng-front/src/app/studies/study/study.component.html @@ -206,7 +206,7 @@

      +
    2. diff --git a/shanoir-ng-studies/src/main/java/org/shanoir/ng/study/service/StudyServiceImpl.java b/shanoir-ng-studies/src/main/java/org/shanoir/ng/study/service/StudyServiceImpl.java index 3367460371..e2b54efd7b 100644 --- a/shanoir-ng-studies/src/main/java/org/shanoir/ng/study/service/StudyServiceImpl.java +++ b/shanoir-ng-studies/src/main/java/org/shanoir/ng/study/service/StudyServiceImpl.java @@ -257,9 +257,7 @@ public Study update(Study study) throws EntityNotFoundException, MicroServiceCom studyDb.setClinical(study.isClinical()); studyDb.setDownloadableByDefault(study.isDownloadableByDefault()); studyDb.setEndDate(study.getEndDate()); - if (KeycloakUtil.getTokenRoles().contains("ROLE_ADMIN")) { - studyDb.setChallenge(study.isChallenge()); - } + studyDb.setChallenge(study.isChallenge()); studyDb.setName(study.getName()); studyDb.setProfile(study.getProfile()); studyDb.setDescription(study.getDescription()); From 666c48cf21e936373346ce39995d4c0d004fe1d2 Mon Sep 17 00:00:00 2001 From: Youenn Merel Date: Tue, 28 May 2024 16:13:07 +0200 Subject: [PATCH 31/80] [fli-iam#2194] Add migration script --- .../datasets/0049_create_tables_tags.sql | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 docker-compose/database/db-changes/datasets/0049_create_tables_tags.sql diff --git a/docker-compose/database/db-changes/datasets/0049_create_tables_tags.sql b/docker-compose/database/db-changes/datasets/0049_create_tables_tags.sql new file mode 100644 index 0000000000..3adee9c14d --- /dev/null +++ b/docker-compose/database/db-changes/datasets/0049_create_tables_tags.sql @@ -0,0 +1,18 @@ +CREATE TABLE `dataset_tag` ( + `dataset_id` bigint(20) NOT NULL, + `study_tag_id` bigint(20) NOT NULL, + KEY `FKkh92b0ddi9nxrevqkdmvqpcm3` (`study_tag_id`), + KEY `FKd0dkfmqchgw18bxirml5an8ex` (`dataset_id`), + CONSTRAINT `FKd0dkfmqchgw18bxirml5an8ex` FOREIGN KEY (`dataset_id`) REFERENCES `dataset` (`id`), + CONSTRAINT `FKkh92b0ddi9nxrevqkdmvqpcm3` FOREIGN KEY (`study_tag_id`) REFERENCES `study_tag` (`id`) +); + + CREATE TABLE `study_tag` ( + `id` bigint(20) NOT NULL, + `color` varchar(255) DEFAULT NULL, + `name` varchar(255) DEFAULT NULL, + `study_id` bigint(20) DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `FKboew1v3lqqa0afxnigq4fxhf3` (`study_id`), + CONSTRAINT `FKboew1v3lqqa0afxnigq4fxhf3` FOREIGN KEY (`study_id`) REFERENCES `study` (`id`) + ); From 5b88fa90ea5acbd6cfc9e84bb0c85db865f7dbec Mon Sep 17 00:00:00 2001 From: Youenn Merel Date: Tue, 28 May 2024 16:14:15 +0200 Subject: [PATCH 32/80] [fli-iam#2194] Fail study_tag deletion if synchro with dataset fail --- .../amqp/RabbitMQDatasetsService.java | 184 ++++++++++-------- .../ng/dataset/controler/DatasetApi.java | 3 +- .../controler/DatasetApiController.java | 4 +- .../ng/solr/service/SolrServiceImpl.java | 9 +- .../shanoir/ng/study/controler/StudyApi.java | 2 +- .../study/controler/StudyApiController.java | 20 +- .../ng/study/service/StudyService.java | 3 +- .../ng/study/service/StudyServiceImpl.java | 75 +++---- .../ng/study/service/StudyServiceTest.java | 5 +- 9 files changed, 166 insertions(+), 139 deletions(-) diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/configuration/amqp/RabbitMQDatasetsService.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/configuration/amqp/RabbitMQDatasetsService.java index 07df5d6f60..4893941f34 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/configuration/amqp/RabbitMQDatasetsService.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/configuration/amqp/RabbitMQDatasetsService.java @@ -14,9 +14,9 @@ package org.shanoir.ng.configuration.amqp; -import java.io.IOException; -import java.util.*; - +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import jakarta.persistence.EntityManager; import org.apache.solr.client.solrj.SolrServerException; import org.shanoir.ng.bids.service.BIDSService; import org.shanoir.ng.dataset.dto.StudyStorageVolumeDTO; @@ -35,17 +35,8 @@ import org.shanoir.ng.shared.event.ShanoirEvent; import org.shanoir.ng.shared.event.ShanoirEventService; import org.shanoir.ng.shared.event.ShanoirEventType; -import org.shanoir.ng.shared.model.AcquisitionEquipment; -import org.shanoir.ng.shared.model.Center; -import org.shanoir.ng.shared.model.Study; -import org.shanoir.ng.shared.model.Subject; -import org.shanoir.ng.shared.model.SubjectStudy; -import org.shanoir.ng.tag.model.Tag; -import org.shanoir.ng.shared.repository.AcquisitionEquipmentRepository; -import org.shanoir.ng.shared.repository.CenterRepository; -import org.shanoir.ng.shared.repository.StudyRepository; -import org.shanoir.ng.shared.repository.SubjectRepository; -import org.shanoir.ng.shared.repository.SubjectStudyRepository; +import org.shanoir.ng.shared.model.*; +import org.shanoir.ng.shared.repository.*; import org.shanoir.ng.shared.subjectstudy.SubjectStudyDTO; import org.shanoir.ng.shared.subjectstudy.SubjectType; import org.shanoir.ng.solr.service.SolrService; @@ -53,17 +44,16 @@ import org.shanoir.ng.studycard.model.StudyCard; import org.shanoir.ng.studycard.repository.StudyCardRepository; import org.shanoir.ng.tag.model.StudyTag; +import org.shanoir.ng.tag.model.Tag; import org.shanoir.ng.utils.SecurityContextUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.amqp.AmqpRejectAndDontRequeueException; import org.springframework.amqp.core.ExchangeTypes; -import org.springframework.amqp.rabbit.annotation.Exchange; import org.springframework.amqp.rabbit.annotation.Queue; -import org.springframework.amqp.rabbit.annotation.QueueBinding; -import org.springframework.amqp.rabbit.annotation.RabbitHandler; -import org.springframework.amqp.rabbit.annotation.RabbitListener; +import org.springframework.amqp.rabbit.annotation.*; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.dao.DataIntegrityViolationException; import org.springframework.data.repository.CrudRepository; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Component; @@ -72,10 +62,8 @@ import org.springframework.transaction.annotation.Transactional; import org.springframework.util.CollectionUtils; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; - -import jakarta.persistence.EntityManager; +import java.io.IOException; +import java.util.*; /** * RabbitMQ configuration. @@ -179,74 +167,100 @@ private SubjectStudy dtoToSubjectStudy(SubjectStudyDTO dto) { @Transactional @RabbitListener(queues = RabbitMQConfiguration.STUDY_NAME_UPDATE_QUEUE, containerFactory = "singleConsumerFactory") @RabbitHandler - public void receiveStudyNameUpdate(final String studyStr) { - try { - Study received = objectMapper.readValue(studyStr, Study.class); - bidsService.deleteBidsFolder(received.getId(), null); - Study stud = receiveAndUpdateIdNameEntity(studyStr, Study.class, studyRepository); - - // TAGS - if (stud.getTags() != null) { - stud.getTags().clear(); - } else { - stud.setTags(new ArrayList<>()); - } - if (received.getTags() != null) { - stud.getTags().addAll(received.getTags()); - } - for (Tag tag : stud.getTags()) { - tag.setStudy(stud); + public boolean receiveStudyNameUpdate(final String studyStr) { + + try { + + Study received = objectMapper.readValue(studyStr, Study.class); + + bidsService.deleteBidsFolder(received.getId(), null); + Study stud = receiveAndUpdateIdNameEntity(studyStr, Study.class, studyRepository); + + // TAGS + if (stud.getTags() != null) { + stud.getTags().clear(); + } else { + stud.setTags(new ArrayList<>()); + } + if (received.getTags() != null) { + stud.getTags().addAll(received.getTags()); + } + for (Tag tag : stud.getTags()) { + tag.setStudy(stud); + } + + // STUDY TAGS + if (stud.getStudyTags() != null) { + stud.getStudyTags().clear(); + } else { + stud.setTags(new ArrayList<>()); + } + if (received.getStudyTags() != null) { + stud.getStudyTags().addAll(received.getStudyTags()); + } + for (StudyTag tag : stud.getStudyTags()) { + tag.setStudy(stud); + } + + if (stud.getId() == null) + throw new IllegalStateException("The entity should must have an id ! Received string : \"" + studyStr + "\""); + + Study studyDb; + try { + studyDb = this.studyRepository.save(stud); + } catch (Exception ex){ + LOG.error("Data integrity error while saving study [{}].", stud.getId(), ex); + return false; } - // STUDY TAGS - if (stud.getStudyTags() != null) { - stud.getStudyTags().clear(); - } else { - stud.setTags(new ArrayList<>()); - } - if (received.getStudyTags() != null) { - stud.getStudyTags().addAll(received.getStudyTags()); - } - for (StudyTag tag : stud.getStudyTags()) { - tag.setStudy(stud); + // SUBJECT_STUDY + if (stud.getSubjectStudyList() != null) { + stud.getSubjectStudyList().clear(); + } else { + stud.setSubjectStudyList(new ArrayList<>()); + } + if (received.getSubjectStudyList() != null) { + stud.getSubjectStudyList().addAll(received.getSubjectStudyList()); + } + for (SubjectStudy sustu : stud.getSubjectStudyList()) { + sustu.setStudy(stud); + for (Tag tag : sustu.getTags()) { + if (tag.getId() == null) { + Tag dbTag = studyDb.getTags().stream().filter(upTag -> + upTag.getColor().equals(tag.getColor()) && upTag.getName().equals(tag.getName()) + ).findFirst().orElse(null); + if (dbTag != null) { + tag.setId(dbTag.getId()); + } else { + throw new IllegalStateException("Cannot link a new tag to a subject-study, this tag does not exist in the study"); + } + } + } + } + if (stud.getId() == null) { + throw new IllegalStateException("The entity should must have an id ! Received string : \"" + studyStr + "\""); + } + + try { + this.studyRepository.save(stud); + } catch (Exception ex){ + LOG.error("Data integrity error while saving study [{}].", stud.getId(), ex); + return false; } - if (stud.getId() == null) throw new IllegalStateException("The entity should must have an id ! Received string : \"" + studyStr + "\""); - Study studyDb = this.studyRepository.save(stud); + List subjectIds = new ArrayList<>(); + stud.getSubjectStudyList().forEach(subStu -> subjectIds.add(subStu.getSubject().getId())); - // SUBJECT_STUDY - if (stud.getSubjectStudyList() != null) { - stud.getSubjectStudyList().clear(); - } else { - stud.setSubjectStudyList(new ArrayList<>()); - } - if (received.getSubjectStudyList() != null) { - stud.getSubjectStudyList().addAll(received.getSubjectStudyList()); - } - for (SubjectStudy sustu : stud.getSubjectStudyList()) { - sustu.setStudy(stud); - for (Tag tag : sustu.getTags()) { - if (tag.getId() == null) { - Tag dbTag = studyDb.getTags().stream().filter(upTag -> - upTag.getColor().equals(tag.getColor()) && upTag.getName().equals(tag.getName()) - ).findFirst().orElse(null); - if (dbTag != null) { - tag.setId(dbTag.getId()); - } else { - throw new IllegalStateException("Cannot link a new tag to a subject-study, this tag does not exist in the study"); - } - } - } - } - if (stud.getId() == null) throw new IllegalStateException("The entity should must have an id ! Received string : \"" + studyStr + "\""); - this.studyRepository.save(stud); - List subjectIds = new ArrayList<>(); - stud.getSubjectStudyList().forEach(subStu -> subjectIds.add(subStu.getSubject().getId())); - updateSolr(subjectIds); - } catch (Exception e) { - throw new AmqpRejectAndDontRequeueException(RABBIT_MQ_ERROR, e); - } - } + updateSolr(subjectIds); + + return true; + + } catch (Exception ex) { + LOG.error("An error occured while processing study update", ex); + throw new AmqpRejectAndDontRequeueException(RABBIT_MQ_ERROR, ex); + } + + } @Transactional @RabbitListener(queues = RabbitMQConfiguration.SUBJECT_NAME_UPDATE_QUEUE, containerFactory = "singleConsumerFactory") diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/dataset/controler/DatasetApi.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/dataset/controler/DatasetApi.java index 0f5d944905..33edc305df 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/dataset/controler/DatasetApi.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/dataset/controler/DatasetApi.java @@ -22,6 +22,7 @@ import jakarta.mail.MessagingException; import jakarta.servlet.http.HttpServletResponse; import jakarta.validation.Valid; +import org.apache.solr.client.solrj.SolrServerException; import org.shanoir.ng.dataset.dto.DatasetAndProcessingsDTOInterface; import org.shanoir.ng.dataset.dto.DatasetDTO; import org.shanoir.ng.dataset.model.Dataset; @@ -338,6 +339,6 @@ ResponseEntity> findDatasetsByIds( ResponseEntity updateDatasetTags( @Parameter(description = "id of the dataset", required = true) @PathVariable("datasetId") Long datasetId, @io.swagger.v3.oas.annotations.parameters.RequestBody(description = "array of study tag ids", required = true) @RequestBody List studyTagIds, - BindingResult result) throws RestServiceException, EntityNotFoundException; + BindingResult result) throws RestServiceException, EntityNotFoundException, SolrServerException, IOException; } diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/dataset/controler/DatasetApiController.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/dataset/controler/DatasetApiController.java index d406519d78..f0e3d031e2 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/dataset/controler/DatasetApiController.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/dataset/controler/DatasetApiController.java @@ -34,6 +34,7 @@ import jakarta.mail.MessagingException; import jakarta.servlet.http.HttpServletResponse; import jakarta.validation.Valid; +import org.apache.solr.client.solrj.SolrServerException; import org.shanoir.ng.dataset.dto.DatasetAndProcessingsDTOInterface; import org.shanoir.ng.dataset.dto.DatasetDTO; import org.shanoir.ng.dataset.dto.mapper.DatasetMapper; @@ -248,7 +249,7 @@ public ResponseEntity> findDatasetsByIds } @Override - public ResponseEntity updateDatasetTags(Long datasetId, List studyTagIds, BindingResult result) throws RestServiceException, EntityNotFoundException { + public ResponseEntity updateDatasetTags(Long datasetId, List studyTagIds, BindingResult result) throws RestServiceException, EntityNotFoundException, SolrServerException, IOException { Dataset ds = datasetService.findById(datasetId); if (ds == null) { throw new EntityNotFoundException(Dataset.class, datasetId); @@ -262,6 +263,7 @@ public ResponseEntity updateDatasetTags(Long datasetId, List studyTa List tags = studyTagService.findByIds(studyTagIds); ds.setTags(tags); datasetRepository.save(ds); + solrService.indexDataset(datasetId); return new ResponseEntity<>(HttpStatus.OK); } diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/solr/service/SolrServiceImpl.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/solr/service/SolrServiceImpl.java index 0f4cff9a38..0cee0f7134 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/solr/service/SolrServiceImpl.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/solr/service/SolrServiceImpl.java @@ -121,7 +121,7 @@ public void indexAll() throws SolrServerException, IOException { public void indexDatasets(List datasetIds) throws SolrServerException, IOException { // Get all associated datasets and index them to solr List metadatas = shanoirMetadataRepository.findSolrDocs(datasetIds); - indexDocumentsInSolr(metadatas); + this.indexDocumentsInSolr(metadatas); } @Override @@ -130,10 +130,7 @@ public void indexDataset(Long datasetId) throws SolrServerException, IOException ShanoirMetadata shanoirMetadata = shanoirMetadataRepository.findOneSolrDoc(datasetId); if (shanoirMetadata == null) throw new IllegalStateException("shanoir metadata with id " + datasetId + " query failed to return any result"); ShanoirSolrDocument doc = getShanoirSolrDocument(shanoirMetadata); - - List tags = this.getTagsAsStrings(shanoirMetadata); - doc.setTags(tags); - + doc.setTags(this.getTagsAsStrings(shanoirMetadata)); solrJWrapper.addToIndex(doc); } @@ -174,7 +171,7 @@ private void indexDocumentsInSolr(List metadatas) throws SolrSe while (docIt.hasNext()) { ShanoirMetadata shanoirMetadata = docIt.next(); - ShanoirSolrDocument doc = getShanoirSolrDocument(shanoirMetadata); + ShanoirSolrDocument doc = this.getShanoirSolrDocument(shanoirMetadata); doc.setTags(this.getTagsAsStrings(shanoirMetadata)); solrDocuments.add(doc); } diff --git a/shanoir-ng-studies/src/main/java/org/shanoir/ng/study/controler/StudyApi.java b/shanoir-ng-studies/src/main/java/org/shanoir/ng/study/controler/StudyApi.java index 074d733f3e..a02b184d56 100644 --- a/shanoir-ng-studies/src/main/java/org/shanoir/ng/study/controler/StudyApi.java +++ b/shanoir-ng-studies/src/main/java/org/shanoir/ng/study/controler/StudyApi.java @@ -376,7 +376,7 @@ ResponseEntity> getStudyStatistics( @ApiResponse(responseCode = "500", description = "unexpected error") }) @PutMapping(value = "/{studyId}/tags", produces = { "application/json" }, consumes = { "application/json" }) - @PreAuthorize("hasAnyRole('ADMIN', 'EXPERT') and @controlerSecurityService.idMatches(#studyId, #study) and @studySecurityService.hasRightOnStudy(#studyId, 'CAN_ADMINISTRATE')") + @PreAuthorize("hasAnyRole('ADMIN', 'EXPERT') and @studySecurityService.hasRightOnStudy(#studyId, 'CAN_ADMINISTRATE')") ResponseEntity updateStudyTags( @Parameter(description = "id of the study", required = true) @PathVariable("studyId") Long studyId, @io.swagger.v3.oas.annotations.parameters.RequestBody(description = "array of study tags", required = true) @RequestBody List studyTags, diff --git a/shanoir-ng-studies/src/main/java/org/shanoir/ng/study/controler/StudyApiController.java b/shanoir-ng-studies/src/main/java/org/shanoir/ng/study/controler/StudyApiController.java index f3ba03448d..6056704885 100644 --- a/shanoir-ng-studies/src/main/java/org/shanoir/ng/study/controler/StudyApiController.java +++ b/shanoir-ng-studies/src/main/java/org/shanoir/ng/study/controler/StudyApiController.java @@ -37,6 +37,7 @@ import org.shanoir.ng.study.service.StudyService; import org.shanoir.ng.study.service.StudyUniqueConstraintManager; import org.shanoir.ng.study.service.StudyUserService; +import org.shanoir.ng.tag.model.StudyTag; import org.shanoir.ng.tag.model.StudyTagDTO; import org.shanoir.ng.tag.model.StudyTagMapper; import org.shanoir.ng.utils.KeycloakUtil; @@ -285,11 +286,11 @@ public ResponseEntity updateStudy(@PathVariable("studyId") final Long stud KeycloakUtil.getTokenUserId(), "", ShanoirEvent.SUCCESS, studyId)); } catch (EntityNotFoundException e) { return new ResponseEntity<>(HttpStatus.NOT_FOUND); - } catch (MicroServiceCommunicationException e) { + } catch (ShanoirException e) { throw new RestServiceException( - new ErrorModel(HttpStatus.UNPROCESSABLE_ENTITY.value(), "Microservice communication error", e)); - } - return new ResponseEntity<>(HttpStatus.NO_CONTENT); + new ErrorModel(HttpStatus.UNPROCESSABLE_ENTITY.value(), "Study [" + studyId + "] couldn't be updated.", e)); + } + return new ResponseEntity<>(HttpStatus.NO_CONTENT); } @Override @@ -435,7 +436,7 @@ public ResponseEntity uploadDataUserAgreement( Files.createFile(duaPath); file.transferTo(duaPath); } catch (Exception e) { - LOG.error("Error while loading files on study: {}. File not uploaded. {}", studyId, e); + LOG.error("Error while loading files on study: {}. File not uploaded.", studyId, e); } return new ResponseEntity<>(HttpStatus.OK); } @@ -563,15 +564,22 @@ public ResponseEntity updateStudyTags(Long studyId, List stud return new ResponseEntity<>(HttpStatus.NOT_FOUND); } + List tags = studyTagMapper.studyTagDTOListToStudyTagList(studyTags); + for(StudyTag tag : tags){ + tag.setStudy(study); + } + study.setStudyTags(studyTagMapper.studyTagDTOListToStudyTagList(studyTags)); try { studyService.update(study); } catch (EntityNotFoundException e) { throw new RestServiceException(new ErrorModel(HttpStatus.NOT_FOUND.value(), "Study [" + studyId + "] not found.", e)); + } catch (ShanoirException e) { + throw new RestServiceException(new ErrorModel(HttpStatus.UNPROCESSABLE_ENTITY.value(), "Study [" + studyId + "] tags couldn't be updated.", e)); } - return new ResponseEntity<>(HttpStatus.OK); + return new ResponseEntity<>(HttpStatus.OK); } } diff --git a/shanoir-ng-studies/src/main/java/org/shanoir/ng/study/service/StudyService.java b/shanoir-ng-studies/src/main/java/org/shanoir/ng/study/service/StudyService.java index 9d3b95a525..d7eab0b89a 100644 --- a/shanoir-ng-studies/src/main/java/org/shanoir/ng/study/service/StudyService.java +++ b/shanoir-ng-studies/src/main/java/org/shanoir/ng/study/service/StudyService.java @@ -20,6 +20,7 @@ import org.shanoir.ng.shared.exception.AccessDeniedException; import org.shanoir.ng.shared.exception.EntityNotFoundException; import org.shanoir.ng.shared.exception.MicroServiceCommunicationException; +import org.shanoir.ng.shared.exception.ShanoirException; import org.shanoir.ng.study.dto.StudyStatisticsDTO; import org.shanoir.ng.study.dto.StudyStorageVolumeDTO; import org.shanoir.ng.study.model.Study; @@ -98,7 +99,7 @@ public interface StudyService { * @throws AccessDeniedException */ @PreAuthorize("hasAnyRole('ADMIN', 'EXPERT') and @studySecurityService.hasRightOnStudy(#study.id, 'CAN_ADMINISTRATE') and @studySecurityService.studyUsersMatchStudy(#study)") - Study update(Study study) throws EntityNotFoundException, MicroServiceCommunicationException; + Study update(Study study) throws ShanoirException; /** * Adds one studyUser to a study. diff --git a/shanoir-ng-studies/src/main/java/org/shanoir/ng/study/service/StudyServiceImpl.java b/shanoir-ng-studies/src/main/java/org/shanoir/ng/study/service/StudyServiceImpl.java index e2b54efd7b..add3f67c6f 100644 --- a/shanoir-ng-studies/src/main/java/org/shanoir/ng/study/service/StudyServiceImpl.java +++ b/shanoir-ng-studies/src/main/java/org/shanoir/ng/study/service/StudyServiceImpl.java @@ -14,13 +14,10 @@ package org.shanoir.ng.study.service; -import java.io.File; -import java.text.SimpleDateFormat; -import java.util.*; -import java.util.stream.Collectors; -import java.util.stream.Stream; - +import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import jakarta.transaction.Transactional; import org.apache.commons.io.FileUtils; import org.shanoir.ng.center.model.Center; import org.shanoir.ng.center.repository.CenterRepository; @@ -29,6 +26,7 @@ import org.shanoir.ng.shared.email.EmailStudyUsersAdded; import org.shanoir.ng.shared.exception.EntityNotFoundException; import org.shanoir.ng.shared.exception.MicroServiceCommunicationException; +import org.shanoir.ng.shared.exception.ShanoirException; import org.shanoir.ng.shared.security.rights.StudyUserRight; import org.shanoir.ng.study.dto.StudyDTO; import org.shanoir.ng.study.dto.StudyStatisticsDTO; @@ -61,15 +59,14 @@ import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Component; import org.springframework.util.CollectionUtils; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; - -import jakarta.transaction.Transactional; +import java.io.File; +import java.text.SimpleDateFormat; +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; /** * Implementation of study service. @@ -219,7 +216,7 @@ public Study create(final Study study) throws MicroServiceCommunicationException studyDb = studyRepository.save(studyDb); } - updateStudyName(studyMapper.studyToStudyDTO(studyDb)); + this.updateStudyName(studyMapper.studyToStudyDTO(studyDb)); if (studyDb.getStudyUserList() != null) { List commands = new ArrayList<>(); @@ -244,7 +241,7 @@ public Study create(final Study study) throws MicroServiceCommunicationException } @Override - public Study update(Study study) throws EntityNotFoundException, MicroServiceCommunicationException { + public Study update(Study study) throws ShanoirException { Study studyDb = studyRepository.findById(study.getId()).orElse(null); List tagsToDelete = getTagsToDelete(study, studyDb); @@ -357,7 +354,11 @@ public Study update(Study study) throws EntityNotFoundException, MicroServiceCom studyDb = studyRepository.save(studyDb); } - updateStudyName(studyMapper.studyToStudyDTO(studyDb)); + boolean synchro = this.updateStudyName(studyMapper.studyToStudyDTO(studyDb)); + + if(!synchro){ + throw new ShanoirException("Study [" + studyDb.getId() + "] couldn't be sync with other microservices. This entity and dependencies may be linked to others."); + } return studyDb; } @@ -371,25 +372,27 @@ public Study update(Study study) throws EntityNotFoundException, MicroServiceCom * @return updated study */ private void updateTags(List subjectStudyList, List dbStudyTags) { - if (subjectStudyList != null && dbStudyTags != null) { - for (SubjectStudy subjectStudy : subjectStudyList) { - if (subjectStudy.getTags() != null) { - for (Tag tag : subjectStudy.getTags()) { - if (tag.getId() == null) { - Tag dbTag = dbStudyTags.stream().filter(upTag -> - upTag.getColor().equals(tag.getColor()) && upTag.getName().equals(tag.getName()) - ).findFirst().orElse(null); - if (dbTag != null) { - tag.setId(dbTag.getId()); - } else { - throw new IllegalStateException("Cannot link a new tag to a subject-study, this tag does not exist in the study"); - } - } - } - } - } - } - } + if (subjectStudyList == null || dbStudyTags == null) { + return; + } + for (SubjectStudy subjectStudy : subjectStudyList) { + if (subjectStudy.getTags() == null) { + continue; + } + for (Tag tag : subjectStudy.getTags()) { + if (tag.getId() == null) { + Tag dbTag = dbStudyTags.stream().filter( + upTag -> upTag.getColor().equals(tag.getColor()) + && upTag.getName().equals(tag.getName()) + ).findFirst().orElse(null); + if (dbTag == null) { + throw new IllegalStateException("Cannot link a new tag to a subject-study, this tag does not exist in the study"); + } + tag.setId(dbTag.getId()); + } + } + } + } private List getTagsToDelete(Study study, Study studyDb) { List tagsToDelete = new ArrayList<>(); @@ -645,9 +648,9 @@ public void removeStudyUserFromStudy(Long studyId, Long userId) { private boolean updateStudyName(StudyDTO study) throws MicroServiceCommunicationException { try { - rabbitTemplate.convertAndSend(RabbitMQConfiguration.STUDY_NAME_UPDATE_QUEUE, + Boolean result = (Boolean) rabbitTemplate.convertSendAndReceive(RabbitMQConfiguration.STUDY_NAME_UPDATE_QUEUE, objectMapper.writeValueAsString(study)); - return true; + return result != null && result; } catch (AmqpException | JsonProcessingException e) { throw new MicroServiceCommunicationException( "Error while communicating with datasets MS to update study name.", e); diff --git a/shanoir-ng-studies/src/test/java/org/shanoir/ng/study/service/StudyServiceTest.java b/shanoir-ng-studies/src/test/java/org/shanoir/ng/study/service/StudyServiceTest.java index a2f6e3f112..0db2423dac 100644 --- a/shanoir-ng-studies/src/test/java/org/shanoir/ng/study/service/StudyServiceTest.java +++ b/shanoir-ng-studies/src/test/java/org/shanoir/ng/study/service/StudyServiceTest.java @@ -41,6 +41,7 @@ import org.shanoir.ng.shared.exception.AccessDeniedException; import org.shanoir.ng.shared.exception.EntityNotFoundException; import org.shanoir.ng.shared.exception.MicroServiceCommunicationException; +import org.shanoir.ng.shared.exception.ShanoirException; import org.shanoir.ng.shared.security.rights.StudyUserRight; import org.shanoir.ng.study.dto.mapper.StudyMapper; import org.shanoir.ng.study.dua.DataUserAgreementService; @@ -160,7 +161,7 @@ public void saveTest() throws MicroServiceCommunicationException, JsonMappingExc @Test @WithMockKeycloakUser(id = 3, username = "jlouis", authorities = { "ROLE_EXPERT" }) - public void updateTest() throws AccessDeniedException, EntityNotFoundException, MicroServiceCommunicationException, IOException { + public void updateTest() throws ShanoirException, IOException { // Also test protocol file path File protocol = new File(tempFolder.getAbsolutePath() + "/tmp/study-1/old.txt"); @@ -191,7 +192,7 @@ public void updateTest() throws AccessDeniedException, EntityNotFoundException, @Test @WithMockKeycloakUser(id = 3, username = "jlouis", authorities = { "ROLE_EXPERT" }) - public void updateStudyUsersTest() throws EntityNotFoundException, MicroServiceCommunicationException { + public void updateStudyUsersTest() throws ShanoirException { Study existing = createStudy(); existing.setStudyUserList(new ArrayList()); existing.getStudyUserList().add(createStudyUsers(1L, 1L, existing, true, StudyUserRight.CAN_SEE_ALL, StudyUserRight.CAN_IMPORT)); From 2bf609fa65e46486575aa9a0a0be3c4f9dbcd4b4 Mon Sep 17 00:00:00 2001 From: Youenn Merel Date: Tue, 28 May 2024 16:49:06 +0200 Subject: [PATCH 33/80] [fli-iam#2194] Oneshot script for organ tags --- .../oneshot-updates/tag_ofsep_organs.sql | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 docker-compose/database/oneshot-updates/tag_ofsep_organs.sql diff --git a/docker-compose/database/oneshot-updates/tag_ofsep_organs.sql b/docker-compose/database/oneshot-updates/tag_ofsep_organs.sql new file mode 100644 index 0000000000..4a73ebedee --- /dev/null +++ b/docker-compose/database/oneshot-updates/tag_ofsep_organs.sql @@ -0,0 +1,42 @@ +-- Create study_tags for all relevant studies + +INSERT INTO study_tag (color, name, study_id) +SELECT DISTINCT '#e74c3c', 'volume.organ:brain', proc.study_id +FROM dataset_property prop +INNER JOIN dataset_processing proc ON proc.id = prop.dataset_processing_id +WHERE prop.name = 'volume.organ' AND prop.value = 'brain'; + +INSERT INTO study_tag (color, name, study_id) +SELECT DISTINCT '#27ae60', 'volume.organ:spine', proc.study_id +FROM dataset_property prop +INNER JOIN dataset_processing proc ON proc.id = prop.dataset_processing_id +WHERE prop.name = 'volume.organ' AND prop.value = 'spine'; + +INSERT INTO study_tag (color, name, study_id) +SELECT DISTINCT '#198bda', 'volume.organ:null', proc.study_id +FROM dataset_property prop +INNER JOIN dataset_processing proc ON proc.id = prop.dataset_processing_id +WHERE prop.name = 'volume.organ' AND prop.value = 'null'; + +-- Create dataset_tag for all relevant dataset + +INSERT INTO dataset_tag (dataset_id, study_tag_id) +SELECT DISTINCT prop.dataset_id, proc.study_id +FROM dataset_property prop +INNER JOIN dataset_processing proc ON proc.id = prop.dataset_processing_id +INNER JOIN study_tag tag ON tag.study_id = proc.study_id AND tag.name = 'volume.organ:brain' +WHERE prop.name = 'volume.organ' AND prop.value = 'brain'; + +INSERT INTO dataset_tag (dataset_id, study_tag_id) +SELECT DISTINCT prop.dataset_id, proc.study_id +FROM dataset_property prop +INNER JOIN dataset_processing proc ON proc.id = prop.dataset_processing_id +INNER JOIN study_tag tag ON tag.study_id = proc.study_id AND tag.name = 'volume.organ:spine' +WHERE prop.name = 'volume.organ' AND prop.value = 'spine'; + +INSERT INTO dataset_tag (dataset_id, study_tag_id) +SELECT DISTINCT prop.dataset_id, proc.study_id +FROM dataset_property prop +INNER JOIN dataset_processing proc ON proc.id = prop.dataset_processing_id +INNER JOIN study_tag tag ON tag.study_id = proc.study_id AND tag.name = 'volume.organ:null' +WHERE prop.name = 'volume.organ' AND prop.value = 'null'; From 9aef9cec4f544e6b32eb957da6d51edde804cc12 Mon Sep 17 00:00:00 2001 From: Youenn Merel Date: Tue, 28 May 2024 16:49:29 +0200 Subject: [PATCH 34/80] [fli-iam#2194] Correct study tests --- .../java/org/shanoir/ng/study/service/StudyServiceImpl.java | 2 +- .../java/org/shanoir/ng/study/service/StudyServiceTest.java | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/shanoir-ng-studies/src/main/java/org/shanoir/ng/study/service/StudyServiceImpl.java b/shanoir-ng-studies/src/main/java/org/shanoir/ng/study/service/StudyServiceImpl.java index add3f67c6f..dc492fd470 100644 --- a/shanoir-ng-studies/src/main/java/org/shanoir/ng/study/service/StudyServiceImpl.java +++ b/shanoir-ng-studies/src/main/java/org/shanoir/ng/study/service/StudyServiceImpl.java @@ -646,7 +646,7 @@ public void removeStudyUserFromStudy(Long studyId, Long userId) { } } - private boolean updateStudyName(StudyDTO study) throws MicroServiceCommunicationException { + public boolean updateStudyName(StudyDTO study) throws MicroServiceCommunicationException { try { Boolean result = (Boolean) rabbitTemplate.convertSendAndReceive(RabbitMQConfiguration.STUDY_NAME_UPDATE_QUEUE, objectMapper.writeValueAsString(study)); diff --git a/shanoir-ng-studies/src/test/java/org/shanoir/ng/study/service/StudyServiceTest.java b/shanoir-ng-studies/src/test/java/org/shanoir/ng/study/service/StudyServiceTest.java index 0db2423dac..72a509ab7a 100644 --- a/shanoir-ng-studies/src/test/java/org/shanoir/ng/study/service/StudyServiceTest.java +++ b/shanoir-ng-studies/src/test/java/org/shanoir/ng/study/service/StudyServiceTest.java @@ -43,6 +43,7 @@ import org.shanoir.ng.shared.exception.MicroServiceCommunicationException; import org.shanoir.ng.shared.exception.ShanoirException; import org.shanoir.ng.shared.security.rights.StudyUserRight; +import org.shanoir.ng.study.dto.StudyDTO; import org.shanoir.ng.study.dto.mapper.StudyMapper; import org.shanoir.ng.study.dua.DataUserAgreementService; import org.shanoir.ng.study.model.Study; @@ -178,6 +179,7 @@ public void updateTest() throws ShanoirException, IOException { updatedStudy.setId(1L); given(studyRepository.save(Mockito.any(Study.class))).willReturn(updatedStudy); + given(studyService.updateStudyName(Mockito.any(StudyDTO.class))).willReturn(true); final Study returnedStudy = studyService.update(updatedStudy); Assertions.assertNotNull(returnedStudy); @@ -209,6 +211,7 @@ public void updateStudyUsersTest() throws ShanoirException { List in = new ArrayList<>(); in.add(updated.getStudyUserList().get(1)); List out = new ArrayList<>(); out.add(createStudyUsers(4L, 3L, updated, true, StudyUserRight.CAN_SEE_ALL)); given(studyUserRepository.saveAll(in)).willReturn(out); + given(studyService.updateStudyName(Mockito.any(StudyDTO.class))).willReturn(true); studyService.update(updated); } From d385b0fca4a2cc28c81b9c054504d681200d06fd Mon Sep 17 00:00:00 2001 From: Youenn Merel Date: Tue, 28 May 2024 16:53:29 +0200 Subject: [PATCH 35/80] [fli-iam#2194] Correct SQL script --- .../datasets/0049_create_tables_tags.sql | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/docker-compose/database/db-changes/datasets/0049_create_tables_tags.sql b/docker-compose/database/db-changes/datasets/0049_create_tables_tags.sql index 3adee9c14d..2ec482e5ad 100644 --- a/docker-compose/database/db-changes/datasets/0049_create_tables_tags.sql +++ b/docker-compose/database/db-changes/datasets/0049_create_tables_tags.sql @@ -1,13 +1,4 @@ -CREATE TABLE `dataset_tag` ( - `dataset_id` bigint(20) NOT NULL, - `study_tag_id` bigint(20) NOT NULL, - KEY `FKkh92b0ddi9nxrevqkdmvqpcm3` (`study_tag_id`), - KEY `FKd0dkfmqchgw18bxirml5an8ex` (`dataset_id`), - CONSTRAINT `FKd0dkfmqchgw18bxirml5an8ex` FOREIGN KEY (`dataset_id`) REFERENCES `dataset` (`id`), - CONSTRAINT `FKkh92b0ddi9nxrevqkdmvqpcm3` FOREIGN KEY (`study_tag_id`) REFERENCES `study_tag` (`id`) -); - - CREATE TABLE `study_tag` ( +CREATE TABLE `study_tag` ( `id` bigint(20) NOT NULL, `color` varchar(255) DEFAULT NULL, `name` varchar(255) DEFAULT NULL, @@ -16,3 +7,11 @@ CREATE TABLE `dataset_tag` ( KEY `FKboew1v3lqqa0afxnigq4fxhf3` (`study_id`), CONSTRAINT `FKboew1v3lqqa0afxnigq4fxhf3` FOREIGN KEY (`study_id`) REFERENCES `study` (`id`) ); + CREATE TABLE `dataset_tag` ( + `dataset_id` bigint(20) NOT NULL, + `study_tag_id` bigint(20) NOT NULL, + KEY `FKkh92b0ddi9nxrevqkdmvqpcm3` (`study_tag_id`), + KEY `FKd0dkfmqchgw18bxirml5an8ex` (`dataset_id`), + CONSTRAINT `FKd0dkfmqchgw18bxirml5an8ex` FOREIGN KEY (`dataset_id`) REFERENCES `dataset` (`id`), + CONSTRAINT `FKkh92b0ddi9nxrevqkdmvqpcm3` FOREIGN KEY (`study_tag_id`) REFERENCES `study_tag` (`id`) +); From b96f942d1f1483aa09e854521bc9edc3b7260ce5 Mon Sep 17 00:00:00 2001 From: Youenn Merel Date: Tue, 28 May 2024 17:21:03 +0200 Subject: [PATCH 36/80] [fli-iam#2194] Correct dataset tests --- .../oneshot-updates/tag_ofsep_organs.sql | 32 +++++++++++-------- .../ng/dataset/DatasetApiControllerTest.java | 4 +++ 2 files changed, 23 insertions(+), 13 deletions(-) diff --git a/docker-compose/database/oneshot-updates/tag_ofsep_organs.sql b/docker-compose/database/oneshot-updates/tag_ofsep_organs.sql index 4a73ebedee..afa3a7cfe4 100644 --- a/docker-compose/database/oneshot-updates/tag_ofsep_organs.sql +++ b/docker-compose/database/oneshot-updates/tag_ofsep_organs.sql @@ -1,41 +1,47 @@ --- Create study_tags for all relevant studies +-- Create study_tags in studies for all relevant studies -INSERT INTO study_tag (color, name, study_id) +INSERT INTO studies.study_tag (color, name, study_id) SELECT DISTINCT '#e74c3c', 'volume.organ:brain', proc.study_id -FROM dataset_property prop -INNER JOIN dataset_processing proc ON proc.id = prop.dataset_processing_id +FROM datasets.dataset_property prop +INNER JOIN datasets.dataset_processing proc ON proc.id = prop.dataset_processing_id WHERE prop.name = 'volume.organ' AND prop.value = 'brain'; -INSERT INTO study_tag (color, name, study_id) +INSERT INTO studies.study_tag (color, name, study_id) SELECT DISTINCT '#27ae60', 'volume.organ:spine', proc.study_id -FROM dataset_property prop -INNER JOIN dataset_processing proc ON proc.id = prop.dataset_processing_id +FROM datasets.dataset_property prop +INNER JOIN datasets.dataset_processing proc ON proc.id = prop.dataset_processing_id WHERE prop.name = 'volume.organ' AND prop.value = 'spine'; -INSERT INTO study_tag (color, name, study_id) +INSERT INTO studies.study_tag (color, name, study_id) SELECT DISTINCT '#198bda', 'volume.organ:null', proc.study_id -FROM dataset_property prop -INNER JOIN dataset_processing proc ON proc.id = prop.dataset_processing_id +FROM datasets.dataset_property prop +INNER JOIN datasets.dataset_processing proc ON proc.id = prop.dataset_processing_id WHERE prop.name = 'volume.organ' AND prop.value = 'null'; +-- Copy studies study_tags to datasets study_tags + +INSERT INTO datasets.study_tag (id, color, name, study_id) +SELECT id, color, name, study_id +FROM studies.study_tag; + -- Create dataset_tag for all relevant dataset INSERT INTO dataset_tag (dataset_id, study_tag_id) -SELECT DISTINCT prop.dataset_id, proc.study_id +SELECT DISTINCT prop.dataset_id, tag.id FROM dataset_property prop INNER JOIN dataset_processing proc ON proc.id = prop.dataset_processing_id INNER JOIN study_tag tag ON tag.study_id = proc.study_id AND tag.name = 'volume.organ:brain' WHERE prop.name = 'volume.organ' AND prop.value = 'brain'; INSERT INTO dataset_tag (dataset_id, study_tag_id) -SELECT DISTINCT prop.dataset_id, proc.study_id +SELECT DISTINCT prop.dataset_id, tag.id FROM dataset_property prop INNER JOIN dataset_processing proc ON proc.id = prop.dataset_processing_id INNER JOIN study_tag tag ON tag.study_id = proc.study_id AND tag.name = 'volume.organ:spine' WHERE prop.name = 'volume.organ' AND prop.value = 'spine'; INSERT INTO dataset_tag (dataset_id, study_tag_id) -SELECT DISTINCT prop.dataset_id, proc.study_id +SELECT DISTINCT prop.dataset_id, tag.id FROM dataset_property prop INNER JOIN dataset_processing proc ON proc.id = prop.dataset_processing_id INNER JOIN study_tag tag ON tag.study_id = proc.study_id AND tag.name = 'volume.organ:null' diff --git a/shanoir-ng-datasets/src/test/java/org/shanoir/ng/dataset/DatasetApiControllerTest.java b/shanoir-ng-datasets/src/test/java/org/shanoir/ng/dataset/DatasetApiControllerTest.java index b163d485f6..0a62a58c68 100644 --- a/shanoir-ng-datasets/src/test/java/org/shanoir/ng/dataset/DatasetApiControllerTest.java +++ b/shanoir-ng-datasets/src/test/java/org/shanoir/ng/dataset/DatasetApiControllerTest.java @@ -29,6 +29,7 @@ import org.shanoir.ng.dataset.modality.MrDatasetMapper; import org.shanoir.ng.dataset.model.Dataset; import org.shanoir.ng.dataset.model.DatasetMetadata; +import org.shanoir.ng.dataset.repository.DatasetRepository; import org.shanoir.ng.dataset.security.DatasetSecurityService; import org.shanoir.ng.dataset.service.DatasetDownloaderServiceImpl; import org.shanoir.ng.dataset.service.DatasetService; @@ -94,6 +95,9 @@ public class DatasetApiControllerTest { @MockBean private DatasetService datasetServiceMock; + @MockBean + private DatasetRepository datasetRepositoryMock; + @MockBean private StudyTagService studyTagServiceMock; From f51bf62b60fb2cfd8ea7d719ef4c01b5d51d9846 Mon Sep 17 00:00:00 2001 From: jcomedouteau Date: Mon, 3 Jun 2024 10:52:03 +0200 Subject: [PATCH 37/80] 2233-processed-dataset-creation - Avoid NPE while creating processed dataset --- .../shanoir/ng/dataset/service/DatasetServiceImpl.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/dataset/service/DatasetServiceImpl.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/dataset/service/DatasetServiceImpl.java index 5365e7d122..a74a269718 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/dataset/service/DatasetServiceImpl.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/dataset/service/DatasetServiceImpl.java @@ -190,7 +190,13 @@ public List findByIdIn(List ids) { @Override public Dataset create(final Dataset dataset) throws SolrServerException, IOException { Dataset ds = repository.save(dataset); - Long studyId = ds.getDatasetAcquisition().getExamination().getStudyId(); + Long studyId; + if (ds.getDatasetAcquisition() != null) { + studyId = ds.getDatasetAcquisition().getExamination().getStudyId(); + } else { + // We have a processed dataset -> acquisition is null but study id is set. + studyId = ds.getStudyId(); + } shanoirEventService.publishEvent(new ShanoirEvent(ShanoirEventType.CREATE_DATASET_EVENT, ds.getId().toString(), KeycloakUtil.getTokenUserId(), "", ShanoirEvent.SUCCESS, ds.getStudyId())); rabbitTemplate.convertAndSend(RabbitMQConfiguration.RELOAD_BIDS, objectMapper.writeValueAsString(studyId)); From 666e9ec11ce386d322c41739561ada52c621432e Mon Sep 17 00:00:00 2001 From: Youenn Merel Date: Tue, 4 Jun 2024 15:03:10 +0200 Subject: [PATCH 38/80] [fli-iam#2194] Display dataset tags in tree --- .../shared/components/tree/tree-node.component.html | 2 +- .../app/shared/components/tree/tree-node.component.ts | 10 +++++----- .../src/app/subjects/tree/subject-node.component.ts | 8 ++++++-- shanoir-ng-front/src/app/tree/tree.model.ts | 3 +-- 4 files changed, 13 insertions(+), 10 deletions(-) diff --git a/shanoir-ng-front/src/app/shared/components/tree/tree-node.component.html b/shanoir-ng-front/src/app/shared/components/tree/tree-node.component.html index 80f2aef443..65ac2bcd48 100644 --- a/shanoir-ng-front/src/app/shared/components/tree/tree-node.component.html +++ b/shanoir-ng-front/src/app/shared/components/tree/tree-node.component.html @@ -51,4 +51,4 @@
      -
      \ No newline at end of file + diff --git a/shanoir-ng-front/src/app/shared/components/tree/tree-node.component.ts b/shanoir-ng-front/src/app/shared/components/tree/tree-node.component.ts index 160e7756e5..8cb8057974 100644 --- a/shanoir-ng-front/src/app/shared/components/tree/tree-node.component.ts +++ b/shanoir-ng-front/src/app/shared/components/tree/tree-node.component.ts @@ -2,12 +2,12 @@ * Shanoir NG - Import, manage and share neuroimaging data * Copyright (C) 2009-2019 Inria - https://www.inria.fr/ * Contact us on https://project.inria.fr/shanoir/ - * + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * You should have received a copy of the GNU General Public License * along with this program. If not, see https://www.gnu.org/licenses/gpl-3.0.html */ @@ -120,7 +120,7 @@ export class TreeNodeComponent implements ControlValueAccessor, OnChanges { public toggle() { if (this.isOpen) this.close(); - else { + else { this.open(); } } @@ -135,7 +135,7 @@ export class TreeNodeComponent implements ControlValueAccessor, OnChanges { this.onChangeCallback(value); } } - + getFontColor(colorInp: string): boolean { return isDarkColor(colorInp); } @@ -156,4 +156,4 @@ export class TreeNodeComponent implements ControlValueAccessor, OnChanges { registerOnTouched(fn: any) { this.onTouchedCallback = fn; } -} \ No newline at end of file +} diff --git a/shanoir-ng-front/src/app/subjects/tree/subject-node.component.ts b/shanoir-ng-front/src/app/subjects/tree/subject-node.component.ts index 9611ca7d3b..94d4bea61d 100644 --- a/shanoir-ng-front/src/app/subjects/tree/subject-node.component.ts +++ b/shanoir-ng-front/src/app/subjects/tree/subject-node.component.ts @@ -35,6 +35,7 @@ import { import { Subject } from '../shared/subject.model'; import { SubjectService } from "../shared/subject.service"; import { TaskState } from "../../async-tasks/task.model"; +import {ConsoleService} from "../../shared/console/console.service"; @Component({ selector: 'subject-node', @@ -56,6 +57,7 @@ export class SubjectNodeComponent implements OnChanges { public downloadState: TaskState = new TaskState(); constructor( + private consoleService: ConsoleService, private examinationService: ExaminationService, private subjectService: SubjectService, private router: Router, @@ -106,8 +108,10 @@ export class SubjectNodeComponent implements OnChanges { } this.loading = false; this.node.open = true; - }).catch(() => { - this.loading = false; + }).catch(e => { + this.consoleService.log('error', e.toString()); + console.log(e) + this.loading = false; }); } } diff --git a/shanoir-ng-front/src/app/tree/tree.model.ts b/shanoir-ng-front/src/app/tree/tree.model.ts index 3727c7ba31..29930d99eb 100644 --- a/shanoir-ng-front/src/app/tree/tree.model.ts +++ b/shanoir-ng-front/src/app/tree/tree.model.ts @@ -135,8 +135,7 @@ export class DatasetNode implements ShanoirNode { public processed: boolean, public canDelete: boolean ) { - if (!tags) tags = []; - else tags = tags.map(t => t.clone()); + this.tags = !tags ? [] : tags; if(processed){ this.title = "processed-dataset"; this.awesome = "fas fa-camera-rotate"; From f85250b9b187a3dd6d5c9cd726be732499fa3a9b Mon Sep 17 00:00:00 2001 From: Youenn Merel Date: Tue, 4 Jun 2024 15:11:17 +0200 Subject: [PATCH 39/80] [fli-iam#2194] Add debug logs --- .../shanoir/ng/solr/service/SolrServiceImpl.java | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/solr/service/SolrServiceImpl.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/solr/service/SolrServiceImpl.java index 0cee0f7134..465ee59b35 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/solr/service/SolrServiceImpl.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/solr/service/SolrServiceImpl.java @@ -47,6 +47,9 @@ import org.shanoir.ng.tag.repository.StudyTagRepository; import org.shanoir.ng.utils.KeycloakUtil; import org.shanoir.ng.utils.Utils; +import org.shanoir.ng.vip.resulthandler.DefaultHandler; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; @@ -85,6 +88,8 @@ public class SolrServiceImpl implements SolrService { @Autowired private DatasetRepository dsRepository; + private static final Logger LOG = LoggerFactory.getLogger(SolrServiceImpl.class); + public void addToIndex (final ShanoirSolrDocument document) throws SolrServerException, IOException { solrJWrapper.addToIndex(document); } @@ -111,9 +116,11 @@ public void deleteAll() throws SolrServerException, IOException { public void indexAll() throws SolrServerException, IOException { // 1. delete all deleteAll(); + LOG.error("[SOLR] index cleaned"); // DEBUG // 2. get all datasets List documents = shanoirMetadataRepository.findAllAsSolrDoc(); - indexDocumentsInSolr(documents); + LOG.error("[SOLR] [{}] shanoir metadata doc found.", documents.size()); // DEBUG + this.indexDocumentsInSolr(documents); } @Transactional @@ -169,13 +176,19 @@ private void indexDocumentsInSolr(List metadatas) throws SolrSe List solrDocuments = new ArrayList<>(); + int cpt = 1; //DEBUG + while (docIt.hasNext()) { ShanoirMetadata shanoirMetadata = docIt.next(); ShanoirSolrDocument doc = this.getShanoirSolrDocument(shanoirMetadata); doc.setTags(this.getTagsAsStrings(shanoirMetadata)); + LOG.error("[SOLR] [{}] tags", doc.getTags().size()); //DEBUG solrDocuments.add(doc); + LOG.error("[SOLR] [{}] doc processed", cpt); //DEBUG + cpt++; //DEBUG } solrJWrapper.addAllToIndex(solrDocuments); + LOG.error("[{}] docs added to index", cpt); //DEBUG } private ShanoirSolrDocument getShanoirSolrDocument(ShanoirMetadata shanoirMetadata) { From d049bfbe5d379c05caff8f6c86e1bb09e83b932c Mon Sep 17 00:00:00 2001 From: Youenn Merel Date: Tue, 4 Jun 2024 17:35:00 +0200 Subject: [PATCH 40/80] [fli-iam#2194] Index into Solr as job --- .../amqp/RabbitMQDatasetsService.java | 1 + .../ng/solr/controler/SolrApiController.java | 21 ++++- .../shanoir/ng/solr/service/SolrService.java | 5 ++ .../ng/solr/service/SolrServiceImpl.java | 78 ++++++++++++++----- .../org/shanoir/ng/tag/model/StudyTag.java | 1 - .../src/app/async-tasks/task.model.ts | 2 + .../shared/side-menu/side-menu.component.html | 3 + shanoir-ng-front/src/app/solr/solr.service.ts | 4 +- .../ng/shared/event/ShanoirEventType.java | 5 ++ .../ng/events/ShanoirEventsService.java | 1 + .../ng/tasks/AsyncTaskApiController.java | 2 +- 11 files changed, 97 insertions(+), 26 deletions(-) diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/configuration/amqp/RabbitMQDatasetsService.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/configuration/amqp/RabbitMQDatasetsService.java index 4893941f34..bbd68ee39e 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/configuration/amqp/RabbitMQDatasetsService.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/configuration/amqp/RabbitMQDatasetsService.java @@ -123,6 +123,7 @@ public class RabbitMQDatasetsService { @Autowired private ObjectMapper objectMapper; + @Autowired EntityManager entityManager; diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/solr/controler/SolrApiController.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/solr/controler/SolrApiController.java index cf8ab1801b..f061a6efbc 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/solr/controler/SolrApiController.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/solr/controler/SolrApiController.java @@ -22,10 +22,14 @@ import io.swagger.v3.oas.annotations.Parameter; import jakarta.validation.Valid; import org.apache.solr.client.solrj.SolrServerException; +import org.shanoir.ng.shared.event.ShanoirEvent; +import org.shanoir.ng.shared.event.ShanoirEventService; +import org.shanoir.ng.shared.event.ShanoirEventType; import org.shanoir.ng.shared.exception.RestServiceException; import org.shanoir.ng.solr.model.ShanoirSolrDocument; import org.shanoir.ng.solr.model.ShanoirSolrQuery; import org.shanoir.ng.solr.service.SolrService; +import org.shanoir.ng.utils.KeycloakUtil; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; @@ -47,10 +51,21 @@ public class SolrApiController implements SolrApi { @Autowired private SolrService solrService; - + + @Autowired + private ShanoirEventService eventService; + @Override - public ResponseEntity indexAll() throws RestServiceException, SolrServerException, IOException { - solrService.indexAll(); + public ResponseEntity indexAll() throws SolrServerException, IOException { + ShanoirEvent event = new ShanoirEvent( + ShanoirEventType.SOLR_INDEX_ALL_EVENT, + null, + KeycloakUtil.getTokenUserId(), + "Indexing all datasets...", + ShanoirEvent.IN_PROGRESS, + 0f); + eventService.publishEvent(event); + solrService.indexAll(event); return new ResponseEntity<>(HttpStatus.OK); } diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/solr/service/SolrService.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/solr/service/SolrService.java index eae5c42f24..a370e84b71 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/solr/service/SolrService.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/solr/service/SolrService.java @@ -20,12 +20,14 @@ package org.shanoir.ng.solr.service; import org.apache.solr.client.solrj.SolrServerException; +import org.shanoir.ng.shared.event.ShanoirEvent; import org.shanoir.ng.shared.exception.RestServiceException; import org.shanoir.ng.solr.model.ShanoirSolrDocument; import org.shanoir.ng.solr.model.ShanoirSolrQuery; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.solr.core.query.result.SolrResultPage; +import org.springframework.scheduling.annotation.Scheduled; import org.springframework.security.access.prepost.PreAuthorize; import java.io.IOException; @@ -42,8 +44,11 @@ public interface SolrService { void addAllToIndex(List documents) throws SolrServerException, IOException; + @Scheduled(cron = "0 0 6 * * *", zone="Europe/Paris") void indexAll() throws SolrServerException, IOException; + void indexAll(ShanoirEvent event) throws SolrServerException, IOException; + void indexDataset(Long datasetId) throws SolrServerException, IOException; void indexDatasets(List datasetIds) throws SolrServerException, IOException; diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/solr/service/SolrServiceImpl.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/solr/service/SolrServiceImpl.java index 465ee59b35..e9a5b5a7a9 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/solr/service/SolrServiceImpl.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/solr/service/SolrServiceImpl.java @@ -26,8 +26,9 @@ import org.apache.solr.client.solrj.SolrServerException; import org.shanoir.ng.dataset.model.Dataset; import org.shanoir.ng.dataset.repository.DatasetRepository; -import org.shanoir.ng.dataset.service.DatasetService; import org.shanoir.ng.shared.dateTime.DateTimeUtils; +import org.shanoir.ng.shared.event.ShanoirEvent; +import org.shanoir.ng.shared.event.ShanoirEventService; import org.shanoir.ng.shared.exception.RestServiceException; import org.shanoir.ng.shared.model.Center; import org.shanoir.ng.shared.model.SubjectStudy; @@ -44,10 +45,8 @@ import org.shanoir.ng.solr.solrj.SolrJWrapper; import org.shanoir.ng.study.rights.StudyUser; import org.shanoir.ng.study.rights.StudyUserRightsRepository; -import org.shanoir.ng.tag.repository.StudyTagRepository; import org.shanoir.ng.utils.KeycloakUtil; import org.shanoir.ng.utils.Utils; -import org.shanoir.ng.vip.resulthandler.DefaultHandler; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -56,6 +55,7 @@ import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; import org.springframework.data.solr.core.query.result.SolrResultPage; +import org.springframework.scheduling.annotation.Async; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Isolation; @@ -88,6 +88,9 @@ public class SolrServiceImpl implements SolrService { @Autowired private DatasetRepository dsRepository; + @Autowired + private ShanoirEventService eventService; + private static final Logger LOG = LoggerFactory.getLogger(SolrServiceImpl.class); public void addToIndex (final ShanoirSolrDocument document) throws SolrServerException, IOException { @@ -109,26 +112,52 @@ public void deleteFromIndex(List datasetIds) throws SolrServerException, I public void deleteAll() throws SolrServerException, IOException { solrJWrapper.deleteAll(); } - - @Transactional + @Override @Scheduled(cron = "0 0 6 * * *", zone="Europe/Paris") public void indexAll() throws SolrServerException, IOException { - // 1. delete all - deleteAll(); - LOG.error("[SOLR] index cleaned"); // DEBUG - // 2. get all datasets + this.deleteAll(); List documents = shanoirMetadataRepository.findAllAsSolrDoc(); - LOG.error("[SOLR] [{}] shanoir metadata doc found.", documents.size()); // DEBUG - this.indexDocumentsInSolr(documents); + this.indexDocumentsInSolr(documents, null); } + + @Transactional + @Override + @Async + public void indexAll(ShanoirEvent event) { + event.setMessage("Cleaning Solr index..."); + eventService.publishEvent(event); + try { + deleteAll(); + } catch (SolrServerException | IOException e) { + LOG.error("Error while cleaning Solr index.", e); + event.setStatus(ShanoirEvent.ERROR); + event.setProgress(-1f); + event.setMessage("Error while cleaning Solr index : " + e.getMessage()); + return; + } + event.setProgress(0.1f); + event.setMessage("Fetching data to index..."); + eventService.publishEvent(event); + List documents = shanoirMetadataRepository.findAllAsSolrDoc(); + event.setProgress(0.2f); + try { + this.indexDocumentsInSolr(documents, event); + } catch (SolrServerException | IOException e) { + LOG.error("Error indexing datasets into Solr.", e); + event.setStatus(ShanoirEvent.ERROR); + event.setProgress(-1f); + event.setMessage("Error indexing datasets into Solr : " + e.getMessage()); + } + } + @Transactional @Override public void indexDatasets(List datasetIds) throws SolrServerException, IOException { // Get all associated datasets and index them to solr List metadatas = shanoirMetadataRepository.findSolrDocs(datasetIds); - this.indexDocumentsInSolr(metadatas); + this.indexDocumentsInSolr(metadatas, null); } @Override @@ -171,24 +200,35 @@ private List getTagsAsStrings(ShanoirMetadata metadata){ return tags; } - private void indexDocumentsInSolr(List metadatas) throws SolrServerException, IOException { + private void indexDocumentsInSolr(List metadatas, ShanoirEvent event) throws SolrServerException, IOException { + + int docNb = metadatas.size(); + Float progress = 0.8f / metadatas.size(); + Iterator docIt = metadatas.iterator(); List solrDocuments = new ArrayList<>(); - int cpt = 1; //DEBUG - + int cpt = 0; while (docIt.hasNext()) { + cpt++; + if(event != null){ + event.setMessage("Indexing [" + cpt + "/" + docNb + "] datasets..."); + event.setProgress(event.getProgress() + progress); + eventService.publishEvent(event); + } ShanoirMetadata shanoirMetadata = docIt.next(); ShanoirSolrDocument doc = this.getShanoirSolrDocument(shanoirMetadata); doc.setTags(this.getTagsAsStrings(shanoirMetadata)); - LOG.error("[SOLR] [{}] tags", doc.getTags().size()); //DEBUG solrDocuments.add(doc); - LOG.error("[SOLR] [{}] doc processed", cpt); //DEBUG - cpt++; //DEBUG } solrJWrapper.addAllToIndex(solrDocuments); - LOG.error("[{}] docs added to index", cpt); //DEBUG + if(event != null){ + event.setMessage("Indexed [" + cpt + "/" + docNb + "] datasets."); + event.setProgress(1f); + event.setStatus(ShanoirEvent.SUCCESS); + eventService.publishEvent(event); + } } private ShanoirSolrDocument getShanoirSolrDocument(ShanoirMetadata shanoirMetadata) { diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/tag/model/StudyTag.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/tag/model/StudyTag.java index 9f004ba9bf..9702de94e9 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/tag/model/StudyTag.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/tag/model/StudyTag.java @@ -75,5 +75,4 @@ public Study getStudy() { public void setStudy(Study study) { this.study = study; } - } diff --git a/shanoir-ng-front/src/app/async-tasks/task.model.ts b/shanoir-ng-front/src/app/async-tasks/task.model.ts index 512b6cdf29..d0ce2c6ec9 100644 --- a/shanoir-ng-front/src/app/async-tasks/task.model.ts +++ b/shanoir-ng-front/src/app/async-tasks/task.model.ts @@ -101,6 +101,8 @@ export class Task extends Entity { } } else if (this.eventType === 'executionMonitoring.event' && this.status != -1) { return '/dataset-processing/details/' + this.objectId + } else if (this.eventType === 'solrIndexAll.event' && this.status != -1) { + return '/solr-search'; } else if (this.eventType === 'copyDataset.event' && this.status != -1 && this.message.lastIndexOf('study [') != -1) { return '/study/details/' + this.message.slice(this.message.lastIndexOf("[") + 1, this.message.lastIndexOf("]")); } diff --git a/shanoir-ng-front/src/app/shared/side-menu/side-menu.component.html b/shanoir-ng-front/src/app/shared/side-menu/side-menu.component.html index 839a4c2660..e6f62112c0 100644 --- a/shanoir-ng-front/src/app/shared/side-menu/side-menu.component.html +++ b/shanoir-ng-front/src/app/shared/side-menu/side-menu.component.html @@ -84,6 +84,9 @@ + + + diff --git a/shanoir-ng-front/src/app/solr/solr.service.ts b/shanoir-ng-front/src/app/solr/solr.service.ts index 94ee7419c5..683a9ffd43 100644 --- a/shanoir-ng-front/src/app/solr/solr.service.ts +++ b/shanoir-ng-front/src/app/solr/solr.service.ts @@ -27,9 +27,9 @@ export class SolrService { } - public indexAll(): Promise { + public indexAll() { if (this.keycloakService.isUserAdmin()) { - return this.http.post(AppUtils.BACKEND_API_SOLR_INDEX_URL, {}).toPromise(); + return this.http.post(AppUtils.BACKEND_API_SOLR_INDEX_URL, {}, {reportProgress: true, observe: 'events'}).toPromise(); } } diff --git a/shanoir-ng-ms-common/src/main/java/org/shanoir/ng/shared/event/ShanoirEventType.java b/shanoir-ng-ms-common/src/main/java/org/shanoir/ng/shared/event/ShanoirEventType.java index a47deec8aa..bae3778217 100644 --- a/shanoir-ng-ms-common/src/main/java/org/shanoir/ng/shared/event/ShanoirEventType.java +++ b/shanoir-ng-ms-common/src/main/java/org/shanoir/ng/shared/event/ShanoirEventType.java @@ -91,4 +91,9 @@ public class ShanoirEventType { public static final String USER_ADD_TO_STUDY_EVENT = "userAddToStudy.event"; public static final String CHECK_QUALITY_EVENT = "checkQuality.event"; + + /** + * Index all datasets in solr + */ + public static final String SOLR_INDEX_ALL_EVENT = "solrIndexAll.event"; } diff --git a/shanoir-ng-users/src/main/java/org/shanoir/ng/events/ShanoirEventsService.java b/shanoir-ng-users/src/main/java/org/shanoir/ng/events/ShanoirEventsService.java index 92dd17f5ba..7bf3f5da50 100644 --- a/shanoir-ng-users/src/main/java/org/shanoir/ng/events/ShanoirEventsService.java +++ b/shanoir-ng-users/src/main/java/org/shanoir/ng/events/ShanoirEventsService.java @@ -41,6 +41,7 @@ public void addEvent(ShanoirEvent event) { // Push notification to UI if (ShanoirEventType.IMPORT_DATASET_EVENT.equals(event.getEventType()) || ShanoirEventType.EXECUTION_MONITORING_EVENT.equals(event.getEventType()) + || ShanoirEventType.SOLR_INDEX_ALL_EVENT.equals(event.getEventType()) || ShanoirEventType.COPY_DATASET_EVENT.equals(event.getEventType()) || ShanoirEventType.CHECK_QUALITY_EVENT.equals(event.getEventType())) { sendSseEventsToUI(saved); diff --git a/shanoir-ng-users/src/main/java/org/shanoir/ng/tasks/AsyncTaskApiController.java b/shanoir-ng-users/src/main/java/org/shanoir/ng/tasks/AsyncTaskApiController.java index 7fdf972e83..d00df59075 100644 --- a/shanoir-ng-users/src/main/java/org/shanoir/ng/tasks/AsyncTaskApiController.java +++ b/shanoir-ng-users/src/main/java/org/shanoir/ng/tasks/AsyncTaskApiController.java @@ -37,7 +37,7 @@ public class AsyncTaskApiController implements AsyncTaskApi { public ResponseEntity> findTasks() { Long userId = KeycloakUtil.getTokenUserId(); - List taskList = taskService.getEventsByUserAndType(userId, ShanoirEventType.IMPORT_DATASET_EVENT, ShanoirEventType.COPY_DATASET_EVENT, ShanoirEventType.EXECUTION_MONITORING_EVENT, ShanoirEventType.CHECK_QUALITY_EVENT); + List taskList = taskService.getEventsByUserAndType(userId, ShanoirEventType.IMPORT_DATASET_EVENT, ShanoirEventType.COPY_DATASET_EVENT, ShanoirEventType.EXECUTION_MONITORING_EVENT, ShanoirEventType.CHECK_QUALITY_EVENT, ShanoirEventType.SOLR_INDEX_ALL_EVENT); // Get only event with last updates < 7 days Date now = new Date(); From 942c40a85a45792767b70e19c4fe7c24df60aa0b Mon Sep 17 00:00:00 2001 From: Anthony Baire Date: Tue, 4 Jun 2024 19:37:31 +0200 Subject: [PATCH 41/80] docker workflow for the qualif instances updates: - allow the workflow can be triggered by workflow_call (used for the development bulids: we do a 'workflow_call' from the deploy repository) - in development, we build an arbitrary commit id given in 'inputs.sha' - disable the maven cache: - on all production builds (for security reasons) - on request for development builds (when 'inputs.no_cache' is set) - cache the docker image builds in the container registry - the cache is saved (cache_to) only on development builds when building 'heads/develop' - the cache is loaded (cache_from) only on development builds when 'inputs.no_cache' is not set - build the docker images with 'docker buildx bake', the list of images is derived from the docker-compose.yml --- .github/workflows/docker.yml | 192 ++++++++++++++++++++++------------- 1 file changed, 119 insertions(+), 73 deletions(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 433f786057..09939e2181 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -2,99 +2,145 @@ name: Publish Docker images env: - REGISTRY: ghcr.io - IMAGE_NAME: ghcr.io/${{ github.repository }} - TAG: ${{ github.ref_name }} + DOCKER_REPOSITORY: ghcr.io/${{ github.repository }} + GIT_REPO_URL: https://github.com/${{ github.repository }} + + # production builds + # - triggered by pushing a tag + # - builds the current commit + # - cache is disabled + # development builds + # - triggered by workflow_call (in the deploy-qualif workflow) + # - builds the commit provided in the 'sha' input variable + # - cache is enabled + PRODUCTION: ${{ github.event_name == 'push' && '1' || '' }} + + # id of the git commit to be built + SHA: ${{ github.event_name == 'push' && github.sha || inputs.sha }} + + # tag of the resulting docker image + # - name of the git tag (production builds) + # - commit id (develpment builds) + IMAGE_TAG: ${{ github.event_name == 'push' && github.ref_name || inputs.sha }} + + # boolean: force rebuilding the docker images from scratch + NO_CACHE: ${{ inputs.no_cache && '1' || '' }} + on: - workflow_dispatch: push: tags: - '*' - + workflow_call: + inputs: + sha: + description: 'git commit to be built' + type: string + required: true + no_cache: + description: 'force rebuilding the docker images from scratch' + type: boolean + default: false + jobs: - push_to_registry: - name: Push Docker images to Container registry + build_images: runs-on: ubuntu-latest permissions: packages: write contents: read steps: + - name: Check out the repo + uses: actions/checkout@v4 + with: + repository: ${{ env.PRODUCTION && github.repository || 'fli-iam/shanoir-ng' }} + ref: ${{ env.IMAGE_TAG }} + - name: Set up JDK 17 - uses: actions/setup-java@v1 + uses: actions/setup-java@v4.2.0 with: java-version: 17 + distribution: oracle - - name: Check out the repo - uses: actions/checkout@v3 + # maven cache only enabled on development builds + - name: Set up Maven cache + uses: actions/cache@v4 + if: "${{ ! env.production }}" + with: + path: ~/.m2/repository + key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} + restore-keys: ${{ runner.os }}-maven- + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3.1.0 - - name: Create directory /shanoir-ng-logs - run: sudo mkdir -m777 /var/log/shanoir-ng-logs - - - name: Maven build - run: mvn -f ./shanoir-ng-parent/pom.xml install -DskipTests - env: - SHANOIR_URL_HOST: localhost - SHANOIR_URL_SCHEME: http - SHANOIR_PREFIX: github - SHANOIR_ADMIN_EMAIL: nobody@inria.fr - SHANOIR_KEYCLOAK_USER: admin - SHANOIR_KEYCLOAK_PASSWORD: '&a1A&a1A' - - name: Login to GitHub Container Registry - uses: docker/login-action@v2 + uses: docker/login-action@v3.0.0 with: - registry: ${{ env.REGISTRY }} + registry: ${{ env.DOCKER_REPOSITORY }} username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v1 - - - name: USERS - Build & push Docker image - uses: docker/build-push-action@v2 - with: - context: ./docker-compose/users - builder: ${{ steps.buildx.outputs.name }} - push: true - tags: ${{ env.IMAGE_NAME }}/users:latest, ${{ env.IMAGE_NAME }}/users:${{ env.TAG }} - - - name: STUDIES - Build & push Docker image - uses: docker/build-push-action@v2 - with: - context: ./docker-compose/studies - builder: ${{ steps.buildx.outputs.name }} - push: true - tags: ${{ env.IMAGE_NAME }}/studies:latest, ${{ env.IMAGE_NAME }}/studies:${{ env.TAG }} - - - name: IMPORT - Build & push Docker image - uses: docker/build-push-action@v2 - with: - context: ./docker-compose/import - builder: ${{ steps.buildx.outputs.name }} - push: true - tags: ${{ env.IMAGE_NAME }}/import:latest, ${{ env.IMAGE_NAME }}/import:${{ env.TAG }} - - - name: DATASETS - Build & push Docker image - uses: docker/build-push-action@v2 - with: - context: ./docker-compose/datasets - builder: ${{ steps.buildx.outputs.name }} - push: true - tags: ${{ env.IMAGE_NAME }}/datasets:latest, ${{ env.IMAGE_NAME }}/datasets:${{ env.TAG }} - - name: PRECLINICAL - Build & push Docker image - uses: docker/build-push-action@v2 - with: - context: ./docker-compose/preclinical - builder: ${{ steps.buildx.outputs.name }} - push: true - tags: ${{ env.IMAGE_NAME }}/preclinical:latest, ${{ env.IMAGE_NAME }}/preclinical:${{ env.TAG }} - - - name: NGINX - Build & push Docker image - uses: docker/build-push-action@v2 + - name: Generate the docker bake config + shell: python + run: | + import json, os, pathlib, requests, subprocess + env = os.environ + + # query the sha of the head of the main branch + MAIN_BRANCH = "develop" + main_branch_sha = requests.get( + f"${{ github.api_url }}/repos/${{ github.repository }}/branches/{MAIN_BRANCH}", + headers={"Authorization": "Bearer ${{ github.token }}"}, + ).json().get("sha") + + # cache configuration + # + # We do not use 'type=gha' because the github actions cache is not + # able to reliably reuse an existing cache for all images in every + # build, thus rendering the cache ineffective (this is probably + # caused by the size limits). + # + # Instead we explicitely store the cache in container repository of + # the project ('type=registry'), under the tag 'cache' + # - we pull the cache (cache_from) before development builds of any + # branch when inputs.no_cache is false + # - we push the cache (cache_to) after development builds of the + # main branch only + # - we never use the cache on production builds + enable_cache_from = not env['PRODUCTION'] and not env['NO_CACHE'] + enable_cache_to = not env['PRODUCTION'] and ( + main_branch_sha == env['IMAGE_TAG'] == env['SHA']) + print(f"{enable_cache_from = }\n{enable_cache_to = }\n") + + def cache_arg(enabled: bool, image: str, extra=""): + return [f"type=registry,ref={env['DOCKER_REPOSITORY']}/{image}:cache{extra}" + ] if enabled else [] + + # extract the list of images to be built from the docker-compose config + images = json.loads(subprocess.check_output( + ["docker", "buildx", "bake", "--print", "-f", "docker-compose.yml"]) + )["group"]["default"]["targets"] + + # override the bake config (add cache config + set tags & labels) + pathlib.Path("docker-bake.json").write_text(json.dumps({ + #"group": { "default": { "targets": ["database"] }}, # TODO: remove + "target": { + image: { + "cache-from": cache_arg(enable_cache_from, image), + "cache-to": cache_arg(enable_cache_to, image, ",mode=max"), + "tags": [f"{env['DOCKER_REPOSITORY']}/{image}:{env['IMAGE_TAG']}"], + "labels": { + "org.opencontainers.image.url": env['GIT_REPO_URL'], + "org.opencontainers.image.source": env['GIT_REPO_URL'], + "org.opencontainers.image.version": env['IMAGE_TAG'], + "org.opencontainers.image.revision": env['SHA'], + }, + } for image in images }})) + + - name: Maven build + run: mvn -f ./shanoir-ng-parent/pom.xml install -DskipTests + + - name: Build docker images + uses: docker/bake-action@v4 with: - context: ./docker-compose/nginx - builder: ${{ steps.buildx.outputs.name }} push: true - tags: ${{ env.IMAGE_NAME }}/nginx:latest, ${{ env.IMAGE_NAME }}/nginx:${{ env.TAG }} From a7883c8bcabf413da81ad43bc8bc96a90b25cdc2 Mon Sep 17 00:00:00 2001 From: Youenn Merel Date: Wed, 5 Jun 2024 11:18:22 +0200 Subject: [PATCH 42/80] [fli-iam#2194] Optimize solr indexation --- .../ShanoirMetadataRepositoryCustom.java | 3 ++ .../ShanoirMetadataRepositoryImpl.java | 45 +++++++++++++++-- .../ng/solr/service/SolrServiceImpl.java | 50 +++++-------------- 3 files changed, 57 insertions(+), 41 deletions(-) diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/solr/repository/ShanoirMetadataRepositoryCustom.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/solr/repository/ShanoirMetadataRepositoryCustom.java index cd6dd5863d..6415229ee9 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/solr/repository/ShanoirMetadataRepositoryCustom.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/solr/repository/ShanoirMetadataRepositoryCustom.java @@ -20,6 +20,7 @@ package org.shanoir.ng.solr.repository; import java.util.List; +import java.util.Map; import org.shanoir.ng.solr.model.ShanoirMetadata; @@ -38,4 +39,6 @@ public interface ShanoirMetadataRepositoryCustom { ShanoirMetadata findOneSolrDoc(Long datasetId); List findSolrDocs(List datasetIds); + + Map> findAllTags(List datasetIds); } diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/solr/repository/ShanoirMetadataRepositoryImpl.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/solr/repository/ShanoirMetadataRepositoryImpl.java index de5c91a0ae..859090d8f3 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/solr/repository/ShanoirMetadataRepositoryImpl.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/solr/repository/ShanoirMetadataRepositoryImpl.java @@ -28,9 +28,7 @@ import org.springframework.stereotype.Component; import org.springframework.util.CollectionUtils; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; +import java.util.*; import java.util.stream.Collectors; /** @@ -304,6 +302,17 @@ public class ShanoirMetadataRepositoryImpl implements ShanoirMetadataRepositoryC + " WHERE d.updated_metadata_id = dm.id AND cd.id = d.id"; public static final String RESULTSET_MAPPING = "SolrResult"; + public static final String SUBJECT_TAG_QUERY = "SELECT d.id AS dataset_id, tag.name AS tag" + + " FROM dataset d" + + " LEFT JOIN subject_study substu ON d.subject_id = substu.subject_id" + + " LEFT JOIN subject_study_tag substutag ON substu.id = substutag.subject_study_id" + + " LEFT JOIN tag ON substutag.tags_id = tag.id"; + + public static final String STUDY_TAG_QUERY = "SELECT d.id AS dataset_id, tag.name AS tag" + + " FROM dataset d " + + " LEFT JOIN dataset_tag dstag ON d.id = dstag.dataset_id " + + " LEFT JOIN study_tag tag ON dstag.study_tag_id = tag.id"; + @PersistenceContext private EntityManager em; @@ -386,4 +395,34 @@ private List findSolrProcessed(String clause){ Query processedQuery = em.createNativeQuery(PROCESSED_QUERY + clause, RESULTSET_MAPPING); return processedQuery.getResultList(); } + + @Override + public Map> findAllTags(List datasetIds){ + + List result = new ArrayList<>(); + + String clause = ""; + + if(datasetIds != null && !datasetIds.isEmpty()){ + String ids = datasetIds.stream().map(Object::toString).collect(Collectors.joining(",")); + clause = " AND d.id IN (" + ids + ")"; + } + + Query subjectTagQuery = em.createNativeQuery(SUBJECT_TAG_QUERY + clause); + result.addAll(subjectTagQuery.getResultList()); + + Query studyTagQuery = em.createNativeQuery(STUDY_TAG_QUERY + clause); + result.addAll(studyTagQuery.getResultList()); + + Map> tags = new HashMap<>(); + + for(Object[] row : result){ + Long id = (Long) row[0]; + tags.putIfAbsent(id, new ArrayList<>()); + tags.get(id).add((String) row[1]); + } + + return tags; + + } } \ No newline at end of file diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/solr/service/SolrServiceImpl.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/solr/service/SolrServiceImpl.java index e9a5b5a7a9..ff2affdb55 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/solr/service/SolrServiceImpl.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/solr/service/SolrServiceImpl.java @@ -118,7 +118,8 @@ public void deleteAll() throws SolrServerException, IOException { public void indexAll() throws SolrServerException, IOException { this.deleteAll(); List documents = shanoirMetadataRepository.findAllAsSolrDoc(); - this.indexDocumentsInSolr(documents, null); + Map> tags = shanoirMetadataRepository.findAllTags(null); + this.indexDocumentsInSolr(documents, tags,null); } @@ -141,9 +142,10 @@ public void indexAll(ShanoirEvent event) { event.setMessage("Fetching data to index..."); eventService.publishEvent(event); List documents = shanoirMetadataRepository.findAllAsSolrDoc(); - event.setProgress(0.2f); + Map> tags = shanoirMetadataRepository.findAllTags(null); + event.setProgress(0.3f); try { - this.indexDocumentsInSolr(documents, event); + this.indexDocumentsInSolr(documents, tags, event); } catch (SolrServerException | IOException e) { LOG.error("Error indexing datasets into Solr.", e); event.setStatus(ShanoirEvent.ERROR); @@ -157,7 +159,8 @@ public void indexAll(ShanoirEvent event) { public void indexDatasets(List datasetIds) throws SolrServerException, IOException { // Get all associated datasets and index them to solr List metadatas = shanoirMetadataRepository.findSolrDocs(datasetIds); - this.indexDocumentsInSolr(metadatas, null); + Map> tags = shanoirMetadataRepository.findAllTags(datasetIds); + this.indexDocumentsInSolr(metadatas, tags, null); } @Override @@ -166,44 +169,15 @@ public void indexDataset(Long datasetId) throws SolrServerException, IOException ShanoirMetadata shanoirMetadata = shanoirMetadataRepository.findOneSolrDoc(datasetId); if (shanoirMetadata == null) throw new IllegalStateException("shanoir metadata with id " + datasetId + " query failed to return any result"); ShanoirSolrDocument doc = getShanoirSolrDocument(shanoirMetadata); - doc.setTags(this.getTagsAsStrings(shanoirMetadata)); + Map> tags = shanoirMetadataRepository.findAllTags(Collections.singletonList(datasetId)); + doc.setTags(tags.get(datasetId)); solrJWrapper.addToIndex(doc); } - private List getTagsAsStrings(ShanoirMetadata metadata){ - - List tags = new ArrayList<>(); - - // SubjectStudy tags - List subjectStudies = subjectStudyRepo.findByStudy_IdInAndSubjectIdIn( - Collections.singletonList(metadata.getStudyId()), - Collections.singletonList(metadata.getSubjectId()) - ); - - for (SubjectStudy subStu : subjectStudies) { - if (subStu.getTags() != null) { - for (Tag tag : subStu.getTags()) { - tags.add(tag.getName()); - } - } - } - - // Dataset tags - Optional ds = dsRepository.findById(metadata.getDatasetId()); - if(ds.isEmpty()){ - return tags; - } - for(StudyTag tag : ds.get().getTags()){ - tags.add(tag.getName()); - } - - return tags; - } - - private void indexDocumentsInSolr(List metadatas, ShanoirEvent event) throws SolrServerException, IOException { + private void indexDocumentsInSolr(List metadatas, Map> tags, ShanoirEvent event) throws SolrServerException, IOException { int docNb = metadatas.size(); - Float progress = 0.8f / metadatas.size(); + Float progress = 0.7f / metadatas.size(); Iterator docIt = metadatas.iterator(); @@ -219,7 +193,7 @@ private void indexDocumentsInSolr(List metadatas, ShanoirEvent } ShanoirMetadata shanoirMetadata = docIt.next(); ShanoirSolrDocument doc = this.getShanoirSolrDocument(shanoirMetadata); - doc.setTags(this.getTagsAsStrings(shanoirMetadata)); + doc.setTags(tags.get(shanoirMetadata.getDatasetId())); solrDocuments.add(doc); } solrJWrapper.addAllToIndex(solrDocuments); From 0646ee444d87dfa51a848a145a5232011350a1e0 Mon Sep 17 00:00:00 2001 From: Youenn Merel Date: Wed, 5 Jun 2024 14:09:13 +0200 Subject: [PATCH 43/80] [fli-iam#2194] Limit job updates --- .../ng/solr/service/SolrServiceImpl.java | 41 ++++++++++++++----- 1 file changed, 31 insertions(+), 10 deletions(-) diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/solr/service/SolrServiceImpl.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/solr/service/SolrServiceImpl.java index ff2affdb55..0d89b9cb59 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/solr/service/SolrServiceImpl.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/solr/service/SolrServiceImpl.java @@ -127,6 +127,7 @@ public void indexAll() throws SolrServerException, IOException { @Override @Async public void indexAll(ShanoirEvent event) { + event.setMessage("Cleaning Solr index..."); eventService.publishEvent(event); try { @@ -138,13 +139,25 @@ public void indexAll(ShanoirEvent event) { event.setMessage("Error while cleaning Solr index : " + e.getMessage()); return; } - event.setProgress(0.1f); + event.setProgress(0.05f); event.setMessage("Fetching data to index..."); eventService.publishEvent(event); - List documents = shanoirMetadataRepository.findAllAsSolrDoc(); - Map> tags = shanoirMetadataRepository.findAllTags(null); - event.setProgress(0.3f); - try { + + List documents; + Map> tags; + try { + documents = shanoirMetadataRepository.findAllAsSolrDoc(); + tags = shanoirMetadataRepository.findAllTags(null); + } catch(Exception e){ + LOG.error("Error while fetching data to index.", e); + event.setStatus(ShanoirEvent.ERROR); + event.setProgress(-1f); + event.setMessage("Error while fetching data to index : " + e.getMessage()); + return; + } + event.setProgress(0.2f); + + try { this.indexDocumentsInSolr(documents, tags, event); } catch (SolrServerException | IOException e) { LOG.error("Error indexing datasets into Solr.", e); @@ -177,21 +190,29 @@ public void indexDataset(Long datasetId) throws SolrServerException, IOException private void indexDocumentsInSolr(List metadatas, Map> tags, ShanoirEvent event) throws SolrServerException, IOException { int docNb = metadatas.size(); - Float progress = 0.7f / metadatas.size(); + Float progress = 0.8f / metadatas.size(); Iterator docIt = metadatas.iterator(); List solrDocuments = new ArrayList<>(); int cpt = 0; + long lastTime = System.currentTimeMillis(); while (docIt.hasNext()) { cpt++; + ShanoirMetadata shanoirMetadata = docIt.next(); + if(event != null){ - event.setMessage("Indexing [" + cpt + "/" + docNb + "] datasets..."); - event.setProgress(event.getProgress() + progress); - eventService.publishEvent(event); + // send job update only every 3 seconds + long currentTime = System.currentTimeMillis(); + if(currentTime - lastTime >= 3000){ + lastTime = currentTime; + event.setMessage("Indexing [" + cpt + "/" + docNb + "] datasets..."); + event.setProgress(event.getProgress() + progress); + eventService.publishEvent(event); + } } - ShanoirMetadata shanoirMetadata = docIt.next(); + ShanoirSolrDocument doc = this.getShanoirSolrDocument(shanoirMetadata); doc.setTags(tags.get(shanoirMetadata.getDatasetId())); solrDocuments.add(doc); From fe88dd3ea96c44b6caef4a7c5505d2772a05154e Mon Sep 17 00:00:00 2001 From: Laurent Paul Vallet Date: Wed, 5 Jun 2024 17:04:54 +0200 Subject: [PATCH 44/80] Update jdk link in README.md --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index edadff35f4..420a321d9d 100644 --- a/README.md +++ b/README.md @@ -57,8 +57,10 @@ Then the shanoir-downloader project can be simply managed as a normal git repo ( - Delete, if needed, %appData%/Docker/settings.json (Docker will create another one, see https://forums.docker.com/t/solved-docker-failed-to-start-docker-desktop-for-windows/106976/6) * Install Java 17 - - Download and install : https://www.oracle.com/fr/java/technologies/javase/jdk11-archive-downloads.html - - Add enviromnent variable : JAVA_HOME = C:\Program Files\Java\jdk-11.0.16 + - Download and install : https://www.oracle.com/java/technologies/javase/jdk17-archive-downloads.html + - Add enviromnent variable : + On Windows (as environment variable): JAVA_HOME = C:\Program Files\Java\jdk-17.0.10 + On Mac (in your .bashrc or .zshrc file): export JAVA_HOME=$(/usr/libexec/java_home) * Install Maven - Download : https://maven.apache.org/download.cgi From c3c33281deaf1296f34bba9e6a295732d979d654 Mon Sep 17 00:00:00 2001 From: Anthony Baire Date: Thu, 23 May 2024 17:57:06 +0200 Subject: [PATCH 45/80] migration to java 21 temurin --- bootstrap.sh | 8 ++------ docker-compose/Dockerfile | 38 ++++++++++++++++++++++++++++++-------- 2 files changed, 32 insertions(+), 14 deletions(-) diff --git a/bootstrap.sh b/bootstrap.sh index 5fa2cafcf0..ece2bfface 100755 --- a/bootstrap.sh +++ b/bootstrap.sh @@ -90,12 +90,8 @@ if [ -n "$build" ] ; then # 1. build a docker image with the java toolchain DEV_IMG=shanoir-ng-dev - docker build -t "$DEV_IMG" - <> /etc/apt/sources.list \ - && apt-get update -qq && apt-get install -qqy --no-install-recommends openjdk-17-jdk-headless maven bzip2 git -EOF + docker build -t "$DEV_IMG" --target=jdk docker-compose + # 2. run the maven build mkdir -p /tmp/home docker run --rm -t -i -v "$PWD:/src" -u "`id -u`:`id -g`" -e HOME="/src/tmp/home" \ diff --git a/docker-compose/Dockerfile b/docker-compose/Dockerfile index 3af0255641..5b060b5ffa 100644 --- a/docker-compose/Dockerfile +++ b/docker-compose/Dockerfile @@ -24,15 +24,36 @@ RUN rm /etc/apt/apt.conf.d/docker-clean \ ################ common image for the java microservices ################### +# - we use the temurin packages because they are more up-to-date that the +# debian packages +# - we use the system ssl trust store at /etc/ssl/certs/java/cacerts +# (rather that temurin's default /etc/ssl/certs/adoptium/cacerts) +# to keep the location independent of the implementation (in case users need +# to mount it from an external volume) +FROM base-debian as adoptium-key +RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ + apt-get update -qq && apt-get install -qqy wget gnupg +RUN wget -qO - https://packages.adoptium.net/artifactory/api/gpg/key/public \ + | gpg --dearmor > /adoptium.gpg + +FROM base-debian as base-debian-with-adoptium +COPY --link --from=adoptium-key /adoptium.gpg /etc/apt/keyrings/ +RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ + apt-get update -qq && apt install -qqy apt-transport-https ca-certificates \ + && echo "deb [signed-by=/etc/apt/keyrings/adoptium.gpg]" \ + "https://packages.adoptium.net/artifactory/deb" \ + "$(awk -F= '/^VERSION_CODENAME/{print$2}' /etc/os-release) main" \ + | tee /etc/apt/sources.list.d/adoptium.list -FROM base-debian as base-microservice +FROM base-debian-with-adoptium as jdk +RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ + apt-get update -qq && apt-get install -qqy temurin-21-jdk maven bzip2 git -# - NOTE: using bookworm-proposed-updates because of -# https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1039472 +FROM base-debian-with-adoptium as base-microservice RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ - echo "deb http://deb.debian.org/debian bookworm-proposed-updates main" >> /etc/apt/sources.list \ - && apt-get update -qq \ - && apt-get install -qqy openjdk-17-jre-headless ca-certificates-java + apt-get update -qq \ + && apt-get install -qqy temurin-21-jre ca-certificates-java \ + && update-ca-certificates RUN mkdir -pv /var/log/shanoir-ng-logs @@ -41,8 +62,9 @@ COPY --link \ common/oneshot \ /usr/bin/ -ENTRYPOINT ["/bin/entrypoint", "java", "-Djava.security.egd=file:/dev/urandom", "-Djavax.net.ssl.trustStorePassword=changeit"] - +ENTRYPOINT ["/bin/entrypoint", "java", "-Djava.security.egd=file:/dev/urandom", \ + "-Djavax.net.ssl.trustStore=/etc/ssl/certs/java/cacerts", \ + "-Djavax.net.ssl.trustStorePassword=changeit"] ################ datasets ################################################## From 56f4c2d6165230985070cbec73778e85bdd65f8d Mon Sep 17 00:00:00 2001 From: Anthony Baire Date: Thu, 6 Jun 2024 11:46:44 +0200 Subject: [PATCH 46/80] upgrade to keycloak 24.0.5 --- docker-compose/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-compose/Dockerfile b/docker-compose/Dockerfile index 3af0255641..086492a2e0 100644 --- a/docker-compose/Dockerfile +++ b/docker-compose/Dockerfile @@ -243,7 +243,7 @@ CMD ["-jar", "/shanoir-ng-users.jar"] ################ keycloak ################################################## -FROM quay.io/keycloak/keycloak:23.0.5 as keycloak-base +FROM quay.io/keycloak/keycloak:24.0.5 as keycloak-base # keycloak options (https://www.keycloak.org/server/all-config) # From 3c31f59b00717d71da3e9d114cb75b799c0a8cdc Mon Sep 17 00:00:00 2001 From: Youenn Merel Date: Fri, 7 Jun 2024 11:26:57 +0200 Subject: [PATCH 47/80] [fli-iam#2194] Manage deletion of study tag linked to datasets --- .../amqp/RabbitMQDatasetsService.java | 141 ++++-------------- .../dataset/repository/DatasetRepository.java | 3 + .../ng/shared/service/StudyService.java | 6 + .../ng/shared/service/StudyServiceImpl.java | 99 +++++++++++- .../shanoir/ng/solr/service/SolrService.java | 2 + .../ng/solr/service/SolrServiceImpl.java | 57 ++++--- .../org/shanoir/ng/tag/model/StudyTag.java | 20 ++- .../ng/tag/repository/StudyTagRepository.java | 1 + .../src/app/studies/study/study.component.ts | 9 +- .../study/controler/StudyApiController.java | 10 +- .../ng/study/service/StudyService.java | 6 +- .../ng/study/service/StudyServiceImpl.java | 20 +-- 12 files changed, 221 insertions(+), 153 deletions(-) diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/configuration/amqp/RabbitMQDatasetsService.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/configuration/amqp/RabbitMQDatasetsService.java index bbd68ee39e..734bfd88b3 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/configuration/amqp/RabbitMQDatasetsService.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/configuration/amqp/RabbitMQDatasetsService.java @@ -37,14 +37,13 @@ import org.shanoir.ng.shared.event.ShanoirEventType; import org.shanoir.ng.shared.model.*; import org.shanoir.ng.shared.repository.*; +import org.shanoir.ng.shared.service.StudyService; import org.shanoir.ng.shared.subjectstudy.SubjectStudyDTO; import org.shanoir.ng.shared.subjectstudy.SubjectType; import org.shanoir.ng.solr.service.SolrService; import org.shanoir.ng.study.rights.ampq.RabbitMqStudyUserService; import org.shanoir.ng.studycard.model.StudyCard; import org.shanoir.ng.studycard.repository.StudyCardRepository; -import org.shanoir.ng.tag.model.StudyTag; -import org.shanoir.ng.tag.model.Tag; import org.shanoir.ng.utils.SecurityContextUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -53,7 +52,6 @@ import org.springframework.amqp.rabbit.annotation.Queue; import org.springframework.amqp.rabbit.annotation.*; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.dao.DataIntegrityViolationException; import org.springframework.data.repository.CrudRepository; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Component; @@ -64,6 +62,7 @@ import java.io.IOException; import java.util.*; +import java.util.stream.Collectors; /** * RabbitMQ configuration. @@ -107,7 +106,7 @@ public class RabbitMQDatasetsService { private ExaminationService examinationService; @Autowired - ShanoirEventService eventService; + public ShanoirEventService eventService; @Autowired private ExaminationRepository examinationRepository; @@ -124,6 +123,9 @@ public class RabbitMQDatasetsService { @Autowired private ObjectMapper objectMapper; + @Autowired + private StudyService studyService; + @Autowired EntityManager entityManager; @@ -165,103 +167,40 @@ private SubjectStudy dtoToSubjectStudy(SubjectStudyDTO dto) { return subjectStudy; } - @Transactional @RabbitListener(queues = RabbitMQConfiguration.STUDY_NAME_UPDATE_QUEUE, containerFactory = "singleConsumerFactory") @RabbitHandler - public boolean receiveStudyNameUpdate(final String studyStr) { - - try { - - Study received = objectMapper.readValue(studyStr, Study.class); - - bidsService.deleteBidsFolder(received.getId(), null); - Study stud = receiveAndUpdateIdNameEntity(studyStr, Study.class, studyRepository); - - // TAGS - if (stud.getTags() != null) { - stud.getTags().clear(); - } else { - stud.setTags(new ArrayList<>()); - } - if (received.getTags() != null) { - stud.getTags().addAll(received.getTags()); - } - for (Tag tag : stud.getTags()) { - tag.setStudy(stud); - } - - // STUDY TAGS - if (stud.getStudyTags() != null) { - stud.getStudyTags().clear(); - } else { - stud.setTags(new ArrayList<>()); - } - if (received.getStudyTags() != null) { - stud.getStudyTags().addAll(received.getStudyTags()); - } - for (StudyTag tag : stud.getStudyTags()) { - tag.setStudy(stud); - } - - if (stud.getId() == null) - throw new IllegalStateException("The entity should must have an id ! Received string : \"" + studyStr + "\""); - - Study studyDb; - try { - studyDb = this.studyRepository.save(stud); - } catch (Exception ex){ - LOG.error("Data integrity error while saving study [{}].", stud.getId(), ex); - return false; - } + public String receiveStudyUpdate(final String studyAsString) { + try { + + Study updated = objectMapper.readValue(studyAsString, Study.class); - // SUBJECT_STUDY - if (stud.getSubjectStudyList() != null) { - stud.getSubjectStudyList().clear(); - } else { - stud.setSubjectStudyList(new ArrayList<>()); - } - if (received.getSubjectStudyList() != null) { - stud.getSubjectStudyList().addAll(received.getSubjectStudyList()); - } - for (SubjectStudy sustu : stud.getSubjectStudyList()) { - sustu.setStudy(stud); - for (Tag tag : sustu.getTags()) { - if (tag.getId() == null) { - Tag dbTag = studyDb.getTags().stream().filter(upTag -> - upTag.getColor().equals(tag.getColor()) && upTag.getName().equals(tag.getName()) - ).findFirst().orElse(null); - if (dbTag != null) { - tag.setId(dbTag.getId()); - } else { - throw new IllegalStateException("Cannot link a new tag to a subject-study, this tag does not exist in the study"); - } - } - } - } - if (stud.getId() == null) { - throw new IllegalStateException("The entity should must have an id ! Received string : \"" + studyStr + "\""); - } - - try { - this.studyRepository.save(stud); - } catch (Exception ex){ - LOG.error("Data integrity error while saving study [{}].", stud.getId(), ex); - return false; + bidsService.deleteBidsFolder(updated.getId(), null); + + Study current = this.receiveAndUpdateIdNameEntity(studyAsString, Study.class, studyRepository); + + List errors = studyService.validate(updated, current); + + if(!errors.isEmpty()){ + return errors.get(0); } - List subjectIds = new ArrayList<>(); - stud.getSubjectStudyList().forEach(subStu -> subjectIds.add(subStu.getSubject().getId())); + studyService.updateStudy(updated, current); - updateSolr(subjectIds); + List subjectIds = current.getSubjectStudyList() + .stream().map(subStu -> + subStu.getSubject().getId() + ).collect(Collectors.toList() + ); - return true; + solrService.updateSubjects(subjectIds); - } catch (Exception ex) { + } catch (Exception ex) { LOG.error("An error occured while processing study update", ex); - throw new AmqpRejectAndDontRequeueException(RABBIT_MQ_ERROR, ex); - } + return ex.getMessage(); + } - } + return ""; + } @Transactional @RabbitListener(queues = RabbitMQConfiguration.SUBJECT_NAME_UPDATE_QUEUE, containerFactory = "singleConsumerFactory") @@ -290,7 +229,7 @@ public boolean receiveSubjectNameUpdate(final String subjectStr) { // Update solr references List subjectIdList = new ArrayList(); subjectIdList.add(su.getId()); - updateSolr(subjectIdList); + solrService.updateSubjects(subjectIdList); // Update BIDS Set studyIds = new HashSet<>(); @@ -307,24 +246,6 @@ public boolean receiveSubjectNameUpdate(final String subjectStr) { } } - /** - * Updates all the solr references for this subject. - * @param subjectIds the subject ID updated - */ - private void updateSolr(final List subjectIds) throws SolrServerException, IOException { - Set datasetsToUpdate = new HashSet<>(); - for (Examination exam : examinationRepository.findBySubjectIdIn(subjectIds)) { - for (DatasetAcquisition acq : exam.getDatasetAcquisitions()) { - for (Dataset ds : acq.getDatasets()) { - datasetsToUpdate.add(ds.getId()); - } - } - } - if (!CollectionUtils.isEmpty(datasetsToUpdate)) { - this.solrService.indexDatasets(new ArrayList<>(datasetsToUpdate)); - } - } - @Transactional @RabbitListener(queues = RabbitMQConfiguration.ACQUISITION_EQUIPEMENT_UPDATE_QUEUE, containerFactory = "singleConsumerFactory") @RabbitHandler diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/dataset/repository/DatasetRepository.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/dataset/repository/DatasetRepository.java index 2c3e8344e1..ea712204c1 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/dataset/repository/DatasetRepository.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/dataset/repository/DatasetRepository.java @@ -15,6 +15,7 @@ package org.shanoir.ng.dataset.repository; import org.shanoir.ng.dataset.model.Dataset; +import org.shanoir.ng.tag.model.StudyTag; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; @@ -66,4 +67,6 @@ public interface DatasetRepository extends PagingAndSortingRepository findExpressionSizesTotalByStudyIdGroupByFormat(List studyIds); List deleteByDatasetProcessingId(Long id); + + boolean existsByTagsContains(StudyTag tag); } \ No newline at end of file diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/shared/service/StudyService.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/shared/service/StudyService.java index e72af8f0d4..bba5121858 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/shared/service/StudyService.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/shared/service/StudyService.java @@ -19,6 +19,8 @@ import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.stereotype.Service; +import java.util.List; + /** * Study service. * @@ -35,4 +37,8 @@ public interface StudyService { */ @PreAuthorize("hasAnyRole('USER', 'ADMIN', 'EXPERT') and (@datasetSecurityService.hasRightOnStudy(#id, 'CAN_SEE_ALL') or @datasetSecurityService.hasRightOnStudy(#id, 'CAN_ADMINISTRATE'))") Study findById(Long id); + + void updateStudy(Study updated, Study current); + + List validate(Study updated, Study current); } diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/shared/service/StudyServiceImpl.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/shared/service/StudyServiceImpl.java index e5618e5aca..6d2a68faf4 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/shared/service/StudyServiceImpl.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/shared/service/StudyServiceImpl.java @@ -14,20 +14,115 @@ package org.shanoir.ng.shared.service; +import org.shanoir.ng.dataset.repository.DatasetRepository; import org.shanoir.ng.shared.model.Study; +import org.shanoir.ng.shared.model.SubjectStudy; import org.shanoir.ng.shared.repository.StudyRepository; +import org.shanoir.ng.tag.model.StudyTag; +import org.shanoir.ng.tag.model.Tag; +import org.shanoir.ng.tag.repository.StudyTagRepository; +import org.shanoir.ng.vip.resulthandler.ResultHandlerService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.List; @Component public class StudyServiceImpl implements StudyService { @Autowired - private StudyRepository studyRepository; + private StudyRepository repository; + + @Autowired + private DatasetRepository dsRepository; @Override public Study findById(final Long id) { - return studyRepository.findById(id).orElse(null); + return repository.findById(id).orElse(null); + } + + @Transactional + public void updateStudy(Study updated, Study current) { + + // TAGS + if (current.getTags() != null) { + current.getTags().clear(); + } else { + current.setTags(new ArrayList<>()); + } + if (updated.getTags() != null) { + current.getTags().addAll(updated.getTags()); + } + for (Tag tag : current.getTags()) { + tag.setStudy(current); + } + + // STUDY TAGS + if (current.getStudyTags() != null) { + current.getStudyTags().clear(); + } else { + current.setTags(new ArrayList<>()); + } + if (updated.getStudyTags() != null) { + current.getStudyTags().addAll(updated.getStudyTags()); + } + for (StudyTag tag : current.getStudyTags()) { + tag.setStudy(current); + } + + if (current.getId() == null) + throw new IllegalStateException("The entity should have an id."); + + Study studyDb = this.repository.save(current); + + // SUBJECT_STUDY + if (current.getSubjectStudyList() != null) { + current.getSubjectStudyList().clear(); + } else { + current.setSubjectStudyList(new ArrayList<>()); + } + if (updated.getSubjectStudyList() != null) { + current.getSubjectStudyList().addAll(updated.getSubjectStudyList()); + } + + for (SubjectStudy sustu : current.getSubjectStudyList()) { + sustu.setStudy(current); + for (Tag tag : sustu.getTags()) { + if (tag.getId() == null) { + Tag dbTag = studyDb.getTags().stream().filter(upTag -> + upTag.getColor().equals(tag.getColor()) && upTag.getName().equals(tag.getName()) + ).findFirst().orElse(null); + if (dbTag != null) { + tag.setId(dbTag.getId()); + } else { + throw new IllegalStateException("Cannot link a new tag to a subject-study, this tag does not exist in the study"); + } + } + } + } + if (current.getId() == null) { + throw new IllegalStateException("The entity should have an id."); + } + this.repository.save(current); + } + + @Override + public List validate(Study updated, Study current){ + + List errors = new ArrayList<>(); + + for(StudyTag tag : current.getStudyTags()){ + if (!updated.getStudyTags().contains(tag) + && this.dsRepository.existsByTagsContains(tag)) { + errors.add("Study tag [" + tag.getName() + "] can't be removed because it's linked to at least one dataset."); + } + } + + return errors; } } diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/solr/service/SolrService.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/solr/service/SolrService.java index a370e84b71..79fb145e39 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/solr/service/SolrService.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/solr/service/SolrService.java @@ -63,4 +63,6 @@ public interface SolrService { void updateDatasets(List datasetIds) throws SolrServerException, IOException; + void updateSubjects(List subjectIds) throws SolrServerException, IOException; + } diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/solr/service/SolrServiceImpl.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/solr/service/SolrServiceImpl.java index 0d89b9cb59..ed106b208e 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/solr/service/SolrServiceImpl.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/solr/service/SolrServiceImpl.java @@ -26,14 +26,14 @@ import org.apache.solr.client.solrj.SolrServerException; import org.shanoir.ng.dataset.model.Dataset; import org.shanoir.ng.dataset.repository.DatasetRepository; +import org.shanoir.ng.datasetacquisition.model.DatasetAcquisition; +import org.shanoir.ng.examination.model.Examination; +import org.shanoir.ng.examination.repository.ExaminationRepository; import org.shanoir.ng.shared.dateTime.DateTimeUtils; import org.shanoir.ng.shared.event.ShanoirEvent; import org.shanoir.ng.shared.event.ShanoirEventService; import org.shanoir.ng.shared.exception.RestServiceException; import org.shanoir.ng.shared.model.Center; -import org.shanoir.ng.shared.model.SubjectStudy; -import org.shanoir.ng.tag.model.StudyTag; -import org.shanoir.ng.tag.model.Tag; import org.shanoir.ng.shared.paging.PageImpl; import org.shanoir.ng.shared.repository.CenterRepository; import org.shanoir.ng.shared.repository.SubjectStudyRepository; @@ -85,6 +85,9 @@ public class SolrServiceImpl implements SolrService { @Autowired private CenterRepository centerRepository; + @Autowired + private ExaminationRepository examRepository; + @Autowired private DatasetRepository dsRepository; @@ -139,7 +142,7 @@ public void indexAll(ShanoirEvent event) { event.setMessage("Error while cleaning Solr index : " + e.getMessage()); return; } - event.setProgress(0.05f); + event.setProgress(0.25f); event.setMessage("Fetching data to index..."); eventService.publishEvent(event); @@ -147,7 +150,13 @@ public void indexAll(ShanoirEvent event) { Map> tags; try { documents = shanoirMetadataRepository.findAllAsSolrDoc(); + event.setProgress(0.5f); + event.setMessage("Fetching data to index..."); + eventService.publishEvent(event); tags = shanoirMetadataRepository.findAllTags(null); + event.setProgress(0.75f); + event.setMessage("Fetching data to index..."); + eventService.publishEvent(event); } catch(Exception e){ LOG.error("Error while fetching data to index.", e); event.setStatus(ShanoirEvent.ERROR); @@ -155,7 +164,6 @@ public void indexAll(ShanoirEvent event) { event.setMessage("Error while fetching data to index : " + e.getMessage()); return; } - event.setProgress(0.2f); try { this.indexDocumentsInSolr(documents, tags, event); @@ -190,36 +198,27 @@ public void indexDataset(Long datasetId) throws SolrServerException, IOException private void indexDocumentsInSolr(List metadatas, Map> tags, ShanoirEvent event) throws SolrServerException, IOException { int docNb = metadatas.size(); - Float progress = 0.8f / metadatas.size(); + + if(event != null){ + event.setMessage("Indexed [0/" + docNb + "] datasets."); + event.setStatus(ShanoirEvent.IN_PROGRESS); + eventService.publishEvent(event); + } Iterator docIt = metadatas.iterator(); List solrDocuments = new ArrayList<>(); - int cpt = 0; - long lastTime = System.currentTimeMillis(); while (docIt.hasNext()) { - cpt++; ShanoirMetadata shanoirMetadata = docIt.next(); - if(event != null){ - // send job update only every 3 seconds - long currentTime = System.currentTimeMillis(); - if(currentTime - lastTime >= 3000){ - lastTime = currentTime; - event.setMessage("Indexing [" + cpt + "/" + docNb + "] datasets..."); - event.setProgress(event.getProgress() + progress); - eventService.publishEvent(event); - } - } - ShanoirSolrDocument doc = this.getShanoirSolrDocument(shanoirMetadata); doc.setTags(tags.get(shanoirMetadata.getDatasetId())); solrDocuments.add(doc); } solrJWrapper.addAllToIndex(solrDocuments); if(event != null){ - event.setMessage("Indexed [" + cpt + "/" + docNb + "] datasets."); + event.setMessage("Indexed [" + docNb + "/" + docNb + "] datasets."); event.setProgress(1f); event.setStatus(ShanoirEvent.SUCCESS); eventService.publishEvent(event); @@ -313,4 +312,20 @@ public void updateDatasets(List datasetIds) throws SolrServerException, IO this.indexDatasets(datasetIds); } + @Override + @Transactional + public void updateSubjects(List subjectIds) throws SolrServerException, IOException { + Set datasetsToUpdate = new HashSet<>(); + for (Examination exam : examRepository.findBySubjectIdIn(subjectIds)) { + for (DatasetAcquisition acq : exam.getDatasetAcquisitions()) { + for (Dataset ds : acq.getDatasets()) { + datasetsToUpdate.add(ds.getId()); + } + } + } + if (!CollectionUtils.isEmpty(datasetsToUpdate)) { + this.indexDatasets(new ArrayList<>(datasetsToUpdate)); + } + } + } diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/tag/model/StudyTag.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/tag/model/StudyTag.java index 9702de94e9..184fb592c4 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/tag/model/StudyTag.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/tag/model/StudyTag.java @@ -1,11 +1,16 @@ package org.shanoir.ng.tag.model; import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import jakarta.persistence.*; +import org.shanoir.ng.shared.core.model.IdName; import org.shanoir.ng.shared.model.Study; +import java.util.Objects; + @Entity -public class StudyTag { +@JsonIgnoreProperties(ignoreUnknown = true) +public class StudyTag extends IdName { @Id private Long id; @@ -75,4 +80,17 @@ public Study getStudy() { public void setStudy(Study study) { this.study = study; } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + StudyTag studyTag = (StudyTag) o; + return Objects.equals(id, studyTag.id); + } + + @Override + public int hashCode() { + return Objects.hash(id); + } } diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/tag/repository/StudyTagRepository.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/tag/repository/StudyTagRepository.java index 4e25206947..dd7d011e1b 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/tag/repository/StudyTagRepository.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/tag/repository/StudyTagRepository.java @@ -15,6 +15,7 @@ import org.shanoir.ng.shared.core.repository.ReadOnlyRepository; import org.shanoir.ng.tag.model.StudyTag; +import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.CrudRepository; /** diff --git a/shanoir-ng-front/src/app/studies/study/study.component.ts b/shanoir-ng-front/src/app/studies/study/study.component.ts index 0a0ba326f6..dbd5ca5152 100644 --- a/shanoir-ng-front/src/app/studies/study/study.component.ts +++ b/shanoir-ng-front/src/app/studies/study/study.component.ts @@ -48,6 +48,7 @@ import { MassDownloadService } from 'src/app/shared/mass-download/mass-download. import { DatasetExpressionFormat } from "../../enum/dataset-expression-format.enum"; import { KeyValue } from "@angular/common"; import { TaskState } from 'src/app/async-tasks/task.model'; +import {ShanoirError} from "../../shared/models/error.model"; @Component({ selector: 'study-detail', @@ -552,8 +553,14 @@ export class StudyComponent extends EntityComponent { this.dataUserAgreement = null; })); } - return Promise.all(uploads).then(() => null); + return Promise.all(uploads).then(); }).then(study => { + if(!study){ + if(this.saveError){ + this.consoleService.log('warn', this.saveError.message); + } + return; + } this.studyCardService.getAllForStudy(study.id).then(studyCards => { if (!studyCards || studyCards.length == 0) { this.confirmDialogService.confirm('Create a Study Card', diff --git a/shanoir-ng-studies/src/main/java/org/shanoir/ng/study/controler/StudyApiController.java b/shanoir-ng-studies/src/main/java/org/shanoir/ng/study/controler/StudyApiController.java index 6056704885..a978a43f0c 100644 --- a/shanoir-ng-studies/src/main/java/org/shanoir/ng/study/controler/StudyApiController.java +++ b/shanoir-ng-studies/src/main/java/org/shanoir/ng/study/controler/StudyApiController.java @@ -20,6 +20,7 @@ import jakarta.validation.Valid; import org.apache.commons.io.FileUtils; import org.shanoir.ng.shared.core.model.IdName; +import org.shanoir.ng.shared.error.FieldError; import org.shanoir.ng.shared.error.FieldErrorMap; import org.shanoir.ng.shared.event.ShanoirEvent; import org.shanoir.ng.shared.event.ShanoirEventService; @@ -282,14 +283,15 @@ public ResponseEntity updateStudy(@PathVariable("studyId") final Long stud try { studyService.update(study); - eventService.publishEvent(new ShanoirEvent(ShanoirEventType.UPDATE_STUDY_EVENT, studyId.toString(), - KeycloakUtil.getTokenUserId(), "", ShanoirEvent.SUCCESS, studyId)); } catch (EntityNotFoundException e) { return new ResponseEntity<>(HttpStatus.NOT_FOUND); } catch (ShanoirException e) { - throw new RestServiceException( - new ErrorModel(HttpStatus.UNPROCESSABLE_ENTITY.value(), "Study [" + studyId + "] couldn't be updated.", e)); + throw new RestServiceException(new ErrorModel(HttpStatus.UNPROCESSABLE_ENTITY.value(), e.getMessage(), e)); } + + eventService.publishEvent(new ShanoirEvent(ShanoirEventType.UPDATE_STUDY_EVENT, studyId.toString(), + KeycloakUtil.getTokenUserId(), "", ShanoirEvent.SUCCESS, studyId)); + return new ResponseEntity<>(HttpStatus.NO_CONTENT); } diff --git a/shanoir-ng-studies/src/main/java/org/shanoir/ng/study/service/StudyService.java b/shanoir-ng-studies/src/main/java/org/shanoir/ng/study/service/StudyService.java index d7eab0b89a..bec18da547 100644 --- a/shanoir-ng-studies/src/main/java/org/shanoir/ng/study/service/StudyService.java +++ b/shanoir-ng-studies/src/main/java/org/shanoir/ng/study/service/StudyService.java @@ -17,10 +17,7 @@ import java.util.List; import java.util.Map; -import org.shanoir.ng.shared.exception.AccessDeniedException; -import org.shanoir.ng.shared.exception.EntityNotFoundException; -import org.shanoir.ng.shared.exception.MicroServiceCommunicationException; -import org.shanoir.ng.shared.exception.ShanoirException; +import org.shanoir.ng.shared.exception.*; import org.shanoir.ng.study.dto.StudyStatisticsDTO; import org.shanoir.ng.study.dto.StudyStorageVolumeDTO; import org.shanoir.ng.study.model.Study; @@ -93,7 +90,6 @@ public interface StudyService { * * @param study * @return updated study - * @throws ShanoirStudiesException * @throws EntityNotFoundException * @throws MicroServiceCommunicationException * @throws AccessDeniedException diff --git a/shanoir-ng-studies/src/main/java/org/shanoir/ng/study/service/StudyServiceImpl.java b/shanoir-ng-studies/src/main/java/org/shanoir/ng/study/service/StudyServiceImpl.java index dc492fd470..a3b7cc8c0d 100644 --- a/shanoir-ng-studies/src/main/java/org/shanoir/ng/study/service/StudyServiceImpl.java +++ b/shanoir-ng-studies/src/main/java/org/shanoir/ng/study/service/StudyServiceImpl.java @@ -17,6 +17,7 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; +import jakarta.persistence.Tuple; import jakarta.transaction.Transactional; import org.apache.commons.io.FileUtils; import org.shanoir.ng.center.model.Center; @@ -24,9 +25,7 @@ import org.shanoir.ng.messaging.StudyUserUpdateBroadcastService; import org.shanoir.ng.shared.configuration.RabbitMQConfiguration; import org.shanoir.ng.shared.email.EmailStudyUsersAdded; -import org.shanoir.ng.shared.exception.EntityNotFoundException; -import org.shanoir.ng.shared.exception.MicroServiceCommunicationException; -import org.shanoir.ng.shared.exception.ShanoirException; +import org.shanoir.ng.shared.exception.*; import org.shanoir.ng.shared.security.rights.StudyUserRight; import org.shanoir.ng.study.dto.StudyDTO; import org.shanoir.ng.study.dto.StudyStatisticsDTO; @@ -59,6 +58,8 @@ import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; +import org.springframework.data.util.Pair; +import org.springframework.http.HttpStatus; import org.springframework.stereotype.Component; import org.springframework.util.CollectionUtils; @@ -241,6 +242,7 @@ public Study create(final Study study) throws MicroServiceCommunicationException } @Override + @Transactional(rollbackOn = {ShanoirException.class}) public Study update(Study study) throws ShanoirException { Study studyDb = studyRepository.findById(study.getId()).orElse(null); @@ -354,10 +356,11 @@ public Study update(Study study) throws ShanoirException { studyDb = studyRepository.save(studyDb); } - boolean synchro = this.updateStudyName(studyMapper.studyToStudyDTO(studyDb)); + String error = this.updateStudyName(studyMapper.studyToStudyDTO(studyDb)); - if(!synchro){ - throw new ShanoirException("Study [" + studyDb.getId() + "] couldn't be sync with other microservices. This entity and dependencies may be linked to others."); + if(error != null && !error.isEmpty()){ + LOG.error("Study [" + studyDb.getId() + "] couldn't be sync with datasets microservice : {}", error); + throw new ShanoirException(error); } return studyDb; @@ -646,11 +649,10 @@ public void removeStudyUserFromStudy(Long studyId, Long userId) { } } - public boolean updateStudyName(StudyDTO study) throws MicroServiceCommunicationException { + public String updateStudyName(StudyDTO study) throws MicroServiceCommunicationException { try { - Boolean result = (Boolean) rabbitTemplate.convertSendAndReceive(RabbitMQConfiguration.STUDY_NAME_UPDATE_QUEUE, + return (String) rabbitTemplate.convertSendAndReceive(RabbitMQConfiguration.STUDY_NAME_UPDATE_QUEUE, objectMapper.writeValueAsString(study)); - return result != null && result; } catch (AmqpException | JsonProcessingException e) { throw new MicroServiceCommunicationException( "Error while communicating with datasets MS to update study name.", e); From 149a85bb309ff40a03d237c04b0d70dd8d6123f7 Mon Sep 17 00:00:00 2001 From: Youenn Merel Date: Fri, 7 Jun 2024 11:34:29 +0200 Subject: [PATCH 48/80] [fli-iam#2194] Correct TUs --- .../java/org/shanoir/ng/study/service/StudyServiceTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/shanoir-ng-studies/src/test/java/org/shanoir/ng/study/service/StudyServiceTest.java b/shanoir-ng-studies/src/test/java/org/shanoir/ng/study/service/StudyServiceTest.java index 72a509ab7a..cf3f063dc6 100644 --- a/shanoir-ng-studies/src/test/java/org/shanoir/ng/study/service/StudyServiceTest.java +++ b/shanoir-ng-studies/src/test/java/org/shanoir/ng/study/service/StudyServiceTest.java @@ -179,7 +179,7 @@ public void updateTest() throws ShanoirException, IOException { updatedStudy.setId(1L); given(studyRepository.save(Mockito.any(Study.class))).willReturn(updatedStudy); - given(studyService.updateStudyName(Mockito.any(StudyDTO.class))).willReturn(true); + given(studyService.updateStudyName(Mockito.any(StudyDTO.class))).willReturn(""); final Study returnedStudy = studyService.update(updatedStudy); Assertions.assertNotNull(returnedStudy); @@ -211,7 +211,7 @@ public void updateStudyUsersTest() throws ShanoirException { List in = new ArrayList<>(); in.add(updated.getStudyUserList().get(1)); List out = new ArrayList<>(); out.add(createStudyUsers(4L, 3L, updated, true, StudyUserRight.CAN_SEE_ALL)); given(studyUserRepository.saveAll(in)).willReturn(out); - given(studyService.updateStudyName(Mockito.any(StudyDTO.class))).willReturn(true); + given(studyService.updateStudyName(Mockito.any(StudyDTO.class))).willReturn(""); studyService.update(updated); } From 7e7e0b7780eef3716e4984c52249bb66223cff97 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 10 Jun 2024 12:59:37 +0000 Subject: [PATCH 49/80] build(deps): bump shanoir-downloader from `f1a3254` to `2e4097d` Bumps [shanoir-downloader](https://github.com/Inria-Empenn/shanoir_downloader) from `f1a3254` to `2e4097d`. - [Commits](https://github.com/Inria-Empenn/shanoir_downloader/compare/f1a32543fd5be9d6620a9a67b498d67a65c87d96...2e4097d938b9a9775d117acc7cab79360cb5b770) --- updated-dependencies: - dependency-name: shanoir-downloader dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- shanoir-downloader | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shanoir-downloader b/shanoir-downloader index f1a32543fd..2e4097d938 160000 --- a/shanoir-downloader +++ b/shanoir-downloader @@ -1 +1 @@ -Subproject commit f1a32543fd5be9d6620a9a67b498d67a65c87d96 +Subproject commit 2e4097d938b9a9775d117acc7cab79360cb5b770 From a88c9f115fa74244868760eadd799151371aa40c Mon Sep 17 00:00:00 2001 From: Youenn Merel Date: Mon, 10 Jun 2024 16:53:08 +0200 Subject: [PATCH 50/80] [swagger] configure parameters + security --- .../shanoir/ng/ShanoirDatasetApplication.java | 10 ++- .../shanoir/ng/bids/controller/BidsApi.java | 14 ++-- .../ng/bids/controller/BidsApiController.java | 14 ++-- .../ng/configuration/security/BearerAuth.java | 13 ++++ .../ng/configuration/security/OAuth2Auth.java | 16 +++++ .../security/SecurityConfiguration.java | 2 + .../ng/dataset/controler/DatasetApi.java | 64 +++++++++---------- .../controler/DatasetApiController.java | 64 +++++++++---------- .../controler/DatasetAcquisitionApi.java | 16 ++--- .../DatasetAcquisitionApiController.java | 17 +++-- .../org/shanoir/ng/dicom/web/DICOMWebApi.java | 26 ++++---- .../examination/controler/ExaminationApi.java | 34 +++++----- .../controler/ExaminationApiController.java | 34 +++++----- .../controler/DatasetProcessingApi.java | 14 ++-- .../DatasetProcessingApiController.java | 14 ++-- .../shanoir/ng/solr/controler/SolrApi.java | 4 +- .../ng/solr/controler/SolrApiController.java | 4 +- .../studycard/controler/QualityCardApi.java | 22 +++---- .../controler/QualityCardApiController.java | 22 +++---- .../ng/studycard/controler/StudyCardApi.java | 18 +++--- .../controler/StudyCardApiController.java | 18 +++--- .../ng/vip/controller/ExecutionApi.java | 10 +-- .../controller/ExecutionApiController.java | 6 +- .../shanoir/ng/vip/controller/PathApi.java | 4 +- .../ng/vip/controller/PathApiController.java | 4 +- .../ng/vip/controller/PipelineApi.java | 2 +- .../controller/ExecutionMonitoringApi.java | 2 +- 27 files changed, 253 insertions(+), 215 deletions(-) create mode 100644 shanoir-ng-datasets/src/main/java/org/shanoir/ng/configuration/security/BearerAuth.java create mode 100644 shanoir-ng-datasets/src/main/java/org/shanoir/ng/configuration/security/OAuth2Auth.java diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/ShanoirDatasetApplication.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/ShanoirDatasetApplication.java index ffe0f9000a..413b6d9dcb 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/ShanoirDatasetApplication.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/ShanoirDatasetApplication.java @@ -18,6 +18,9 @@ import com.fasterxml.jackson.databind.Module; import com.fasterxml.jackson.databind.module.SimpleModule; import io.swagger.v3.oas.annotations.OpenAPIDefinition; +import io.swagger.v3.oas.annotations.info.Info; +import io.swagger.v3.oas.annotations.security.SecurityRequirement; +import io.swagger.v3.oas.annotations.servers.Server; import org.shanoir.ng.shared.paging.PageSerializer; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @@ -32,7 +35,12 @@ @SpringBootApplication @EnableSpringDataWebSupport @EnableScheduling -@OpenAPIDefinition +@OpenAPIDefinition( + info = @Info(title = "Shanoir datasets API", version = "1.0"), + servers = @Server(url = "/shanoir-ng/datasets", description = "Datasets"), + security = { @SecurityRequirement(name = "BearerAuth"), @SecurityRequirement(name = "OAuth2Auth") } +) + public class ShanoirDatasetApplication { public static void main(String[] args) { diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/bids/controller/BidsApi.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/bids/controller/BidsApi.java index 7b6e65bc6b..3bde5836c1 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/bids/controller/BidsApi.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/bids/controller/BidsApi.java @@ -32,8 +32,8 @@ public interface BidsApi { @ApiResponse(responseCode = "500", description = "unexpected error") }) @GetMapping(value = "/studyId/{studyId}/studyName/{studyName}") ResponseEntity generateBIDSByStudyId( - @Parameter(name = "id of the study", required=true) @PathVariable("studyId") Long studyId, - @Parameter(name = "name of the study", required=true) @PathVariable("studyName") String studyName) throws RestServiceException, IOException; + @Parameter(description = "id of the study", required=true) @PathVariable("studyId") Long studyId, + @Parameter(description = "name of the study", required=true) @PathVariable("studyName") String studyName) throws RestServiceException, IOException; @Operation(summary = "refreshBids", description = "Refresh the BIDS structure for a given study ID and study name") @ApiResponses(value = { @@ -44,8 +44,8 @@ ResponseEntity generateBIDSByStudyId( @ApiResponse(responseCode = "500", description = "unexpected error") }) @GetMapping(value = "refreshBids/studyId/{studyId}/studyName/{studyName}") ResponseEntity refreshBIDSByStudyId( - @Parameter(name = "id of the study", required=true) @PathVariable("studyId") Long studyId, - @Parameter(name = "name of the study", required=true) @PathVariable("studyName") String studyName) throws RestServiceException, IOException; + @Parameter(description = "id of the study", required=true) @PathVariable("studyId") Long studyId, + @Parameter(description = "name of the study", required=true) @PathVariable("studyName") String studyName) throws RestServiceException, IOException; @Operation(summary = "exportBIDSBySubjectId", description = "If exists, returns a zip file of the BIDS structure corresponding to the given study id and path") @ApiResponses(value = { @@ -57,8 +57,8 @@ ResponseEntity refreshBIDSByStudyId( @GetMapping(value = "/exportBIDS/studyId/{studyId}") @PreAuthorize("hasRole('ADMIN') or (hasAnyRole('EXPERT', 'USER') and @datasetSecurityService.hasRightOnStudy(#studyId, 'CAN_DOWNLOAD'))") void exportBIDSFile( - @Parameter(name = "Id of the study", required=true) @PathVariable("studyId") Long studyId, - @Parameter(name = "file path") @Valid @RequestParam(value = "filePath", required = true) String filePath, HttpServletResponse response) throws RestServiceException, IOException; + @Parameter(description = "Id of the study", required=true) @PathVariable("studyId") Long studyId, + @Parameter(description = "file path") @Valid @RequestParam(value = "filePath", required = true) String filePath, HttpServletResponse response) throws RestServiceException, IOException; @Operation(summary = "getBids", description = "If exists, returns a BIDSElement structure corresponding to the given study id") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "BidsElement"), @@ -68,7 +68,7 @@ void exportBIDSFile( @ApiResponse(responseCode = "500", description = "unexpected error") }) @GetMapping(value = "/bidsStructure/studyId/{studyId}", produces = { "application/json" }) ResponseEntity getBIDSStructureByStudyId( - @Parameter(name = "id of the study", required = true) @PathVariable("studyId") Long studyId) + @Parameter(description = "id of the study", required = true) @PathVariable("studyId") Long studyId) throws RestServiceException, IOException; } diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/bids/controller/BidsApiController.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/bids/controller/BidsApiController.java index b91f320bed..0312ed97e6 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/bids/controller/BidsApiController.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/bids/controller/BidsApiController.java @@ -62,23 +62,23 @@ public BidsApiController(final HttpServletRequest request) { @Override public ResponseEntity generateBIDSByStudyId( - @Parameter(name = "id of the study", required=true) @PathVariable("studyId") Long studyId, - @Parameter(name = "name of the study", required=true) @PathVariable("studyName") String studyName) throws RestServiceException, IOException { + @Parameter(description = "id of the study", required=true) @PathVariable("studyId") Long studyId, + @Parameter(description = "name of the study", required=true) @PathVariable("studyName") String studyName) throws RestServiceException, IOException { bidsService.exportAsBids(studyId, studyName); return ResponseEntity.ok().build(); } public ResponseEntity refreshBIDSByStudyId( - @Parameter(name = "id of the study", required=true) @PathVariable("studyId") Long studyId, - @Parameter(name = "name of the study", required=true) @PathVariable("studyName") String studyName) throws RestServiceException, IOException { + @Parameter(description = "id of the study", required=true) @PathVariable("studyId") Long studyId, + @Parameter(description = "name of the study", required=true) @PathVariable("studyName") String studyName) throws RestServiceException, IOException { this.bidsService.deleteBidsFolder(studyId, studyName); return this.getBIDSStructureByStudyId(studyId); } @Override public void exportBIDSFile( - @Parameter(name = "Id of the study", required=true) @PathVariable("studyId") Long studyId, - @Parameter(name = "file path") @Valid @RequestParam(value = "filePath", required = true) String filePath, HttpServletResponse response) throws RestServiceException, IOException { + @Parameter(description = "Id of the study", required=true) @PathVariable("studyId") Long studyId, + @Parameter(description = "file path") @Valid @RequestParam(value = "filePath", required = true) String filePath, HttpServletResponse response) throws RestServiceException, IOException { // Check filePath too // /var/datasets-data/bids-data/stud-1_NATIVE if (!filePath.startsWith("/var/datasets-data/bids-data/stud-" + studyId)) { @@ -141,7 +141,7 @@ public static File getUserDir(String importDir) { @Override public ResponseEntity getBIDSStructureByStudyId( - @Parameter(name = "id of the study", required = true) @PathVariable("studyId") Long studyId) + @Parameter(description = "id of the study", required = true) @PathVariable("studyId") Long studyId) throws RestServiceException, IOException { BidsElement studyBidsElement = new BidsFolder("Error while retrieving the study bids structure, please reload the page"); diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/configuration/security/BearerAuth.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/configuration/security/BearerAuth.java new file mode 100644 index 0000000000..3dd7543242 --- /dev/null +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/configuration/security/BearerAuth.java @@ -0,0 +1,13 @@ +package org.shanoir.ng.configuration.security; + +import io.swagger.v3.oas.annotations.enums.SecuritySchemeType; +import io.swagger.v3.oas.annotations.security.SecurityScheme; + +@SecurityScheme( + type = SecuritySchemeType.HTTP, + scheme = "bearer", + bearerFormat = "JWT", + name = "BearerAuth", + openIdConnectUrl = "https://localhost/auth") +public class BearerAuth { +} diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/configuration/security/OAuth2Auth.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/configuration/security/OAuth2Auth.java new file mode 100644 index 0000000000..593ae9ea43 --- /dev/null +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/configuration/security/OAuth2Auth.java @@ -0,0 +1,16 @@ +package org.shanoir.ng.configuration.security; + +import io.swagger.v3.oas.annotations.enums.SecuritySchemeType; +import io.swagger.v3.oas.annotations.security.OAuthFlow; +import io.swagger.v3.oas.annotations.security.OAuthFlows; +import io.swagger.v3.oas.annotations.security.SecurityScheme; + +@SecurityScheme( + type = SecuritySchemeType.OAUTH2, + name = "OAuth2Auth", + flows = @OAuthFlows(password = @OAuthFlow( + authorizationUrl = "https://shanoir-ofsep-qualif.irisa.fr/auth/", + tokenUrl = "https://shanoir-ofsep-qualif.irisa.fr/auth/realms/shanoir-ng/protocol/openid-connect/token", + refreshUrl = ""))) +public class OAuth2Auth { +} diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/configuration/security/SecurityConfiguration.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/configuration/security/SecurityConfiguration.java index 0eb53e3d4a..416b65eecf 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/configuration/security/SecurityConfiguration.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/configuration/security/SecurityConfiguration.java @@ -14,6 +14,8 @@ package org.shanoir.ng.configuration.security; +import io.swagger.v3.oas.annotations.enums.SecuritySchemeType; +import io.swagger.v3.oas.annotations.security.SecurityScheme; import org.shanoir.ng.dicom.web.StowRSMultipartRelatedRequestFilter; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/dataset/controler/DatasetApi.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/dataset/controler/DatasetApi.java index 75b372426e..e7d9997ca6 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/dataset/controler/DatasetApi.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/dataset/controler/DatasetApi.java @@ -54,7 +54,7 @@ public interface DatasetApi { @DeleteMapping(value = "/{datasetId}", produces = { "application/json" }) @PreAuthorize("hasRole('ADMIN') or (hasRole('EXPERT') and @datasetSecurityService.hasRightOnDataset(#datasetId, 'CAN_ADMINISTRATE'))") ResponseEntity deleteDataset( - @Parameter(name = "id of the dataset", required = true) @PathVariable("datasetId") Long datasetId) + @Parameter(description = "id of the dataset", required = true) @PathVariable("datasetId") Long datasetId) throws RestServiceException, EntityNotFoundException; @Operation(summary = "", description = "Deletes several datasets") @@ -66,7 +66,7 @@ ResponseEntity deleteDataset( @DeleteMapping(value = "/delete", produces = { "application/json" }) @PreAuthorize("hasRole('ADMIN') or (hasRole('EXPERT') and @datasetSecurityService.hasRightOnEveryDataset(#datasetIds, 'CAN_ADMINISTRATE'))") ResponseEntity deleteDatasets( - @Parameter(name = "ids of the datasets", required=true) @Valid + @Parameter(description = "ids of the datasets", required=true) @Valid @RequestBody(required = true) List datasetIds) throws RestServiceException; @@ -79,7 +79,7 @@ ResponseEntity deleteDatasets( @GetMapping(value = "/{datasetId}", produces = { "application/json" }) @PreAuthorize("hasRole('ADMIN') or (hasAnyRole('EXPERT', 'USER') and @datasetSecurityService.hasRightOnDataset(#datasetId, 'CAN_SEE_ALL'))") ResponseEntity findDatasetById( - @Parameter(name = "id of the dataset", required = true) @PathVariable("datasetId") Long datasetId); + @Parameter(description = "id of the dataset", required = true) @PathVariable("datasetId") Long datasetId); @Operation(summary = "", description = "Updates a dataset") @ApiResponses(value = { @ApiResponse(responseCode = "204", description = "dataset updated"), @@ -91,8 +91,8 @@ ResponseEntity findDatasetById( "application/json" }) @PreAuthorize("@controlerSecurityService.idMatches(#datasetId, #dataset) and hasRole('ADMIN') or (hasRole('EXPERT') and @datasetSecurityService.hasUpdateRightOnDataset(#dataset, 'CAN_ADMINISTRATE'))") ResponseEntity updateDataset( - @Parameter(name = "id of the dataset", required = true) @PathVariable("datasetId") Long datasetId, - @Parameter(name = "dataset to update", required = true) @Valid @RequestBody Dataset dataset, + @Parameter(description = "id of the dataset", required = true) @PathVariable("datasetId") Long datasetId, + @Parameter(description = "dataset to update", required = true) @Valid @RequestBody Dataset dataset, BindingResult result) throws RestServiceException; @Operation(summary = "", description = "Returns a datasets page") @@ -114,7 +114,7 @@ ResponseEntity updateDataset( @ApiResponse(responseCode = "500", description = "unexpected error") }) @GetMapping(value = "/examination/{examinationId}", produces = { "application/json" }) @PreAuthorize("hasRole('ADMIN') or (hasAnyRole('EXPERT', 'USER') and @datasetSecurityService.hasRightOnExamination(#examinationId, 'CAN_SEE_ALL'))") - ResponseEntity> findDatasetsByExaminationId(@Parameter(name = "id of the examination", required = true) @PathVariable("examinationId") Long examinationId); + ResponseEntity> findDatasetsByExaminationId(@Parameter(description = "id of the examination", required = true) @PathVariable("examinationId") Long examinationId); @Operation(summary = "", description = "Returns a dataset list") @@ -125,7 +125,7 @@ ResponseEntity updateDataset( @ApiResponse(responseCode = "500", description = "unexpected error") }) @GetMapping(value = "/acquisition/{acquisitionId}", produces = { "application/json" }) @PreAuthorize("hasRole('ADMIN') or (hasAnyRole('EXPERT', 'USER') and @datasetSecurityService.hasRightOnDatasetAcquisition(#acquisitionId, 'CAN_SEE_ALL'))") - ResponseEntity> findDatasetsByAcquisitionId(@Parameter(name = "id of the acquisition", required = true) @PathVariable("acquisitionId") Long acquisitionId); + ResponseEntity> findDatasetsByAcquisitionId(@Parameter(description = "id of the acquisition", required = true) @PathVariable("acquisitionId") Long acquisitionId); @Operation(summary = "", description = "Returns a dataset list") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "found datasets"), @@ -135,7 +135,7 @@ ResponseEntity updateDataset( @ApiResponse(responseCode = "500", description = "unexpected error") }) @GetMapping(value = "/studycard/{studycardId}", produces = { "application/json" }) @PostAuthorize("hasRole('ADMIN') or @datasetSecurityService.filterDatasetDTOList(returnObject.getBody(), 'CAN_SEE_ALL')") - ResponseEntity> findDatasetsByStudycardId(@Parameter(name = "id of the studycard", required = true) @PathVariable("studycardId") Long studycardId); + ResponseEntity> findDatasetsByStudycardId(@Parameter(description = "id of the studycard", required = true) @PathVariable("studycardId") Long studycardId); @Operation(summary = "", description = "Returns the list of dataset id by study id") @ApiResponses(value = { @@ -148,7 +148,7 @@ ResponseEntity updateDataset( @PreAuthorize("hasRole('ADMIN') or (hasAnyRole('EXPERT', 'USER') and @datasetSecurityService.hasRightOnStudy(#studyId, 'CAN_SEE_ALL'))") @PostAuthorize("hasRole('ADMIN') or @datasetSecurityService.filterDatasetDTOList(returnObject.getBody(), 'CAN_SEE_ALL')") ResponseEntity> findDatasetByStudyId( - @Parameter(name = "id of the study", required = true) @PathVariable("studyId") Long studyId); + @Parameter(description = "id of the study", required = true) @PathVariable("studyId") Long studyId); @Operation(summary = "", description = "Returns the number of datasets by study id") @ApiResponses(value = { @@ -160,7 +160,7 @@ ResponseEntity> findDatasetByStudyId( @RequestMapping(value = "/study/nb-datasets/{studyId}", produces = { "application/json" }, method = RequestMethod.GET) @PreAuthorize("hasRole('ADMIN') or (hasAnyRole('EXPERT', 'USER') and @datasetSecurityService.hasRightOnStudy(#studyId, 'CAN_SEE_ALL'))") ResponseEntity findNbDatasetByStudyId( - @Parameter(name = "id of the study", required = true) @PathVariable("studyId") Long studyId); + @Parameter(description = "id of the study", required = true) @PathVariable("studyId") Long studyId); @Operation(summary = "", description = "Returns the list of dataset id by subject id and study id") @ApiResponses(value = { @@ -172,8 +172,8 @@ ResponseEntity findNbDatasetByStudyId( @RequestMapping(value = "/subject/{subjectId}/study/{studyId}", produces = { "application/json" }, method = RequestMethod.GET) @PreAuthorize("hasRole('ADMIN') or (hasAnyRole('EXPERT', 'USER') and @datasetSecurityService.hasRightOnStudy(#studyId, 'CAN_SEE_ALL'))") ResponseEntity> findDatasetIdsBySubjectIdStudyId( - @Parameter(name = "id of the subject", required = true) @PathVariable("subjectId") Long subjectId, - @Parameter(name = "id of the study", required = true) @PathVariable("studyId") Long studyId); + @Parameter(description = "id of the subject", required = true) @PathVariable("subjectId") Long subjectId, + @Parameter(description = "id of the study", required = true) @PathVariable("studyId") Long studyId); @Operation(summary = "", description = "Returns the list of dataset by subject id and study id") @ApiResponses(value = { @@ -186,8 +186,8 @@ ResponseEntity> findDatasetIdsBySubjectIdStudyId( @PreAuthorize("hasRole('ADMIN') or (hasAnyRole('EXPERT', 'USER') and @datasetSecurityService.hasRightOnStudy(#studyId, 'CAN_SEE_ALL'))") @PostAuthorize("hasRole('ADMIN') or @datasetSecurityService.filterDatasetDTOList(returnObject.getBody(), 'CAN_SEE_ALL')") ResponseEntity> findDatasetsBySubjectIdStudyId( - @Parameter(name = "id of the subject", required = true) @PathVariable("subjectId") Long subjectId, - @Parameter(name = "id of the study", required = true) @PathVariable("studyId") Long studyId); + @Parameter(description = "id of the subject", required = true) @PathVariable("subjectId") Long subjectId, + @Parameter(description = "id of the study", required = true) @PathVariable("studyId") Long studyId); @Operation(summary = "downloadDatasetById", description = "If exists, returns a zip file of the dataset corresponding to the given id") @ApiResponses(value = { @@ -199,9 +199,9 @@ ResponseEntity> findDatasetsBySubjectIdStudyId( @GetMapping(value = "/download/{datasetId}") @PreAuthorize("hasRole('ADMIN') or (hasAnyRole('EXPERT', 'USER') and @datasetSecurityService.hasRightOnDataset(#datasetId, 'CAN_DOWNLOAD'))") void downloadDatasetById( - @Parameter(name = "id of the dataset", required=true) @PathVariable("datasetId") Long datasetId, - @Parameter(name = "Dowloading nifti, decide the nifti converter id") Long converterId, - @Parameter(name = "Decide if you want to download dicom (dcm) or nifti (nii) files.") + @Parameter(description = "id of the dataset", required=true) @PathVariable("datasetId") Long datasetId, + @Parameter(description = "Dowloading nifti, decide the nifti converter id") Long converterId, + @Parameter(description = "Decide if you want to download dicom (dcm) or nifti (nii) files.") @Valid @RequestParam(value = "format", required = false, defaultValue="dcm") String format, HttpServletResponse response) throws RestServiceException, MalformedURLException, IOException, EntityNotFoundException; @@ -216,7 +216,7 @@ void downloadDatasetById( @GetMapping(value = "/dicom-metadata/{datasetId}") @PreAuthorize("hasRole('ADMIN') or (hasAnyRole('EXPERT', 'USER') and @datasetSecurityService.hasRightOnDataset(#datasetId, 'CAN_DOWNLOAD'))") ResponseEntity getDicomMetadataByDatasetId( - @Parameter(name = "id of the dataset", required=true) @PathVariable("datasetId") Long datasetId) throws MalformedURLException, IOException, MessagingException; + @Parameter(description = "id of the dataset", required=true) @PathVariable("datasetId") Long datasetId) throws MalformedURLException, IOException, MessagingException; @Operation(summary = "", description = "Creates a processed dataset") @ApiResponses(value = { @@ -230,7 +230,7 @@ ResponseEntity getDicomMetadataByDatasetId( consumes = { "application/json" }, method = RequestMethod.POST) @PreAuthorize("hasRole('ADMIN') or (hasAnyRole('EXPERT', 'USER') and @datasetSecurityService.hasRightOnStudy(#importJob.getStudyId(), 'CAN_IMPORT'))") - ResponseEntity createProcessedDataset(@Parameter(name = "co to create" ,required=true ) @Valid @RequestBody ProcessedDatasetImportJob importJob) throws RestServiceException, IOException, Exception; + ResponseEntity createProcessedDataset(@Parameter(description = "co to create" ,required=true ) @Valid @RequestBody ProcessedDatasetImportJob importJob) throws RestServiceException, IOException, Exception; @Operation(summary = "massiveDownloadDatasetsByIds", description = "If exists, returns a zip file of the datasets corresponding to the given ids") @ApiResponses(value = { @@ -242,11 +242,11 @@ ResponseEntity getDicomMetadataByDatasetId( @PostMapping(value = "/massiveDownload") @PreAuthorize("hasRole('ADMIN') or (hasAnyRole('EXPERT', 'USER') and @datasetSecurityService.hasRightOnEveryDataset(#datasetIds, 'CAN_DOWNLOAD'))") void massiveDownloadByDatasetIds( - @Parameter(name = "ids of the datasets", required=true) @Valid + @Parameter(description = "ids of the datasets", required=true) @Valid @RequestParam(value = "datasetIds", required = true) List datasetIds, - @Parameter(name = "Decide if you want to download dicom (dcm) or nifti (nii) files.") @Valid + @Parameter(description = "Decide if you want to download dicom (dcm) or nifti (nii) files.") @Valid @RequestParam(value = "format", required = false, defaultValue="dcm") String format, - @Parameter(name = "If nifti, decide converter to use") @Valid + @Parameter(description = "If nifti, decide converter to use") @Valid @RequestParam(value = "converterId", required = false) Long converterId, HttpServletResponse response) throws RestServiceException, EntityNotFoundException, MalformedURLException, IOException; @@ -260,9 +260,9 @@ void massiveDownloadByDatasetIds( @GetMapping(value = "/massiveDownloadByStudy") @PreAuthorize("hasRole('ADMIN') or (hasAnyRole('EXPERT', 'USER') and @datasetSecurityService.hasRightOnStudy(#studyId, 'CAN_DOWNLOAD'))") void massiveDownloadByStudyId( - @Parameter(name = "id of the study", required=true) @Valid + @Parameter(description = "id of the study", required=true) @Valid @RequestParam(value = "studyId", required = true) Long studyId, - @Parameter(name = "Decide if you want to download dicom (dcm) or nifti (nii) files.") @Valid + @Parameter(description = "Decide if you want to download dicom (dcm) or nifti (nii) files.") @Valid @RequestParam(value = "format", required = false, defaultValue="dcm") String format, HttpServletResponse response) throws RestServiceException, EntityNotFoundException, IOException; @Operation(summary = "massiveDownloadDatasetsByExaminationId", description = "If exists, returns a zip file of the datasets corresponding to the given examination ID") @@ -275,9 +275,9 @@ void massiveDownloadByStudyId( @GetMapping(value = "/massiveDownloadByExamination") @PreAuthorize("hasRole('ADMIN') or (hasAnyRole('EXPERT', 'USER') and @datasetSecurityService.hasRightOnExamination(#examinationId, 'CAN_DOWNLOAD'))") void massiveDownloadByExaminationId( - @Parameter(name = "id of the examination", required=true) @Valid + @Parameter(description = "id of the examination", required=true) @Valid @RequestParam(value = "examinationId", required = true) Long examinationId, - @Parameter(name = "Decide if you want to download dicom (dcm) or nifti (nii) files.") @Valid + @Parameter(description = "Decide if you want to download dicom (dcm) or nifti (nii) files.") @Valid @RequestParam(value = "format", required = false, defaultValue="dcm") String format, HttpServletResponse response) throws RestServiceException, EntityNotFoundException, IOException; @Operation(summary = "massiveDownloadDatasetsByAcquisitionId", description = "If exists, returns a zip file of the datasets corresponding to the given acquisition ID") @@ -290,9 +290,9 @@ void massiveDownloadByExaminationId( @GetMapping(value = "/massiveDownloadByAcquisition") @PreAuthorize("hasRole('ADMIN') or (hasAnyRole('EXPERT', 'USER') and @datasetSecurityService.hasRightOnDatasetAcquisition(#acquisitionId, 'CAN_DOWNLOAD'))") void massiveDownloadByAcquisitionId( - @Parameter(name = "id of the acquisition", required=true) @Valid + @Parameter(description = "id of the acquisition", required=true) @Valid @RequestParam(value = "acquisitionId", required = true) Long acquisitionId, - @Parameter(name = "Decide if you want to download dicom (dcm) or nifti (nii) files.") @Valid + @Parameter(description = "Decide if you want to download dicom (dcm) or nifti (nii) files.") @Valid @RequestParam(value = "format", required = false, defaultValue="dcm") String format, HttpServletResponse response) throws RestServiceException, EntityNotFoundException, IOException; @Operation(summary = "downloadStatistics", description = "Download statistics from the entire database") @@ -305,13 +305,13 @@ void massiveDownloadByAcquisitionId( @GetMapping(value = "/downloadStatistics", produces = { "application/zip" }) @PreAuthorize("hasRole('ADMIN')") ResponseEntity downloadStatistics( - @Parameter(name = "Study name including regular expression", required=false) @Valid + @Parameter(description = "Study name including regular expression", required=false) @Valid @RequestParam(value = "studyNameInRegExp", required = false) String studyNameInRegExp, - @Parameter(name = "Study name excluding regular expression", required=false) @Valid + @Parameter(description = "Study name excluding regular expression", required=false) @Valid @RequestParam(value = "studyNameOutRegExp", required = false) String studyNameOutRegExp, - @Parameter(name = "Subject name including regular expression", required=false) @Valid + @Parameter(description = "Subject name including regular expression", required=false) @Valid @RequestParam(value = "subjectNameInRegExp", required = false) String subjectNameInRegExp, - @Parameter(name = "Subject name excluding regular expression", required=false) @Valid + @Parameter(description = "Subject name excluding regular expression", required=false) @Valid @RequestParam(value = "subjectNameOutRegExp", required = false) String subjectNameOutRegExp) throws RestServiceException, IOException; @Operation(summary = "", description = "If exists, returns the datasets corresponding to the given ids") diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/dataset/controler/DatasetApiController.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/dataset/controler/DatasetApiController.java index 76f2169ce7..f1a4cfa347 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/dataset/controler/DatasetApiController.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/dataset/controler/DatasetApiController.java @@ -140,7 +140,7 @@ public class DatasetApiController implements DatasetApi { @Override public ResponseEntity deleteDataset( - @Parameter(name = "id of the dataset", required = true) @PathVariable("datasetId") final Long datasetId) throws EntityNotFoundException, RestServiceException { + final Long datasetId) throws EntityNotFoundException, RestServiceException { try { Long studyId = datasetService.findById(datasetId).getDatasetAcquisition().getExamination().getStudyId(); @@ -159,7 +159,7 @@ public ResponseEntity deleteDataset( @Override public ResponseEntity deleteDatasets( - @Parameter(name = "ids of the datasets", required=true) @Valid + @Parameter(description = "ids of the datasets", required=true) @Valid @RequestBody List datasetIds) throws RestServiceException { try { @@ -176,7 +176,7 @@ public ResponseEntity deleteDatasets( @Override public ResponseEntity findDatasetById( - @Parameter(name = "id of the dataset", required = true) @PathVariable("datasetId") final Long datasetId) { + final Long datasetId) { final Dataset dataset = datasetService.findById(datasetId); @@ -196,8 +196,8 @@ else if (dataset instanceof EegDataset) { @Override public ResponseEntity updateDataset( - @Parameter(name = "id of the dataset", required = true) @PathVariable("datasetId") final Long datasetId, - @Parameter(name = "study to update", required = true) @Valid @RequestBody final Dataset dataset, + final Long datasetId, + @Parameter(description = "study to update", required = true) @Valid @RequestBody final Dataset dataset, final BindingResult result) throws RestServiceException { validate(result); @@ -243,7 +243,7 @@ public ResponseEntity> findDatasetsByIds } @Override - public ResponseEntity> findDatasetsByExaminationId(@Parameter(name = "id of the examination", required = true) @PathVariable("examinationId") Long examinationId) { + public ResponseEntity> findDatasetsByExaminationId(Long examinationId) { List datasets = datasetService.findByExaminationId(examinationId); if (datasets.isEmpty()) { return new ResponseEntity<>(HttpStatus.NO_CONTENT); @@ -253,7 +253,7 @@ public ResponseEntity> findDatasetsByExaminationId(@Parameter(n } @Override - public ResponseEntity> findDatasetsByAcquisitionId(@Parameter(name = "id of the acquisition", required = true) @PathVariable("acquisitionId") Long acquisitionId) { + public ResponseEntity> findDatasetsByAcquisitionId(Long acquisitionId) { List datasets = datasetService.findByAcquisition(acquisitionId); if (datasets.isEmpty()) { return new ResponseEntity<>(HttpStatus.NO_CONTENT); @@ -263,7 +263,7 @@ public ResponseEntity> findDatasetsByAcquisitionId(@Parameter(n } @Override - public ResponseEntity> findDatasetsByStudycardId(@Parameter(name = "id of the studycard", required = true) @PathVariable("studycardId") Long studycardId) { + public ResponseEntity> findDatasetsByStudycardId(Long studycardId) { List datasets = datasetService.findByStudycard(studycardId); if (datasets.isEmpty()) { return new ResponseEntity<>(HttpStatus.NO_CONTENT); @@ -274,7 +274,7 @@ public ResponseEntity> findDatasetsByStudycardId(@Parameter(nam @Override public ResponseEntity> findDatasetByStudyId( - @Parameter(name = "id of the study", required = true) @PathVariable("studyId") Long studyId) { + Long studyId) { final List examinations = examinationService.findByStudyId(studyId); if (examinations.isEmpty()) { @@ -294,7 +294,7 @@ public ResponseEntity> findDatasetByStudyId( @Override public ResponseEntity findNbDatasetByStudyId( - @Parameter(name = "id of the study", required = true) @PathVariable("studyId") Long studyId) { + Long studyId) { final int nbDatasets = datasetService.countByStudyId(studyId); return new ResponseEntity(nbDatasets, HttpStatus.OK); @@ -302,15 +302,15 @@ public ResponseEntity findNbDatasetByStudyId( @Override public ResponseEntity> findDatasetIdsBySubjectIdStudyId( - @Parameter(name = "id of the subject", required = true) @PathVariable("subjectId") Long subjectId, - @Parameter(name = "id of the study", required = true) @PathVariable("studyId") Long studyId) { + Long subjectId, + Long studyId) { List datasets = getBySubjectStudy(subjectId, studyId); return new ResponseEntity<>(datasets.stream().map(Dataset::getId).collect(Collectors.toList()), HttpStatus.OK); } public ResponseEntity> findDatasetsBySubjectIdStudyId( - @Parameter(name = "id of the subject", required = true) @PathVariable("subjectId") Long subjectId, - @Parameter(name = "id of the study", required = true) @PathVariable("studyId") Long studyId) { + Long subjectId, + Long studyId) { List datasets = getBySubjectStudy(subjectId, studyId); return new ResponseEntity>(datasetMapper.datasetToDatasetDTO(datasets), HttpStatus.OK); } @@ -332,9 +332,9 @@ private List getBySubjectStudy(Long subjectId, Long studyId) { @Override public void downloadDatasetById( - @Parameter(name = "id of the dataset", required = true) @PathVariable("datasetId") final Long datasetId, - @Parameter(name = "Dowloading nifti, decide the nifti converter id") final Long converterId, - @Parameter(name = "Decide if you want to download dicom (dcm) or nifti (nii) files.") + final Long datasetId, + @Parameter(description = "Dowloading nifti, decide the nifti converter id") final Long converterId, + @Parameter(description = "Decide if you want to download dicom (dcm) or nifti (nii) files.") @Valid @RequestParam(value = "format", required = false, defaultValue = DCM) final String format, HttpServletResponse response) throws RestServiceException, EntityNotFoundException { Dataset dataset = this.datasetService.findById(datasetId); if (dataset == null) { @@ -346,7 +346,7 @@ public void downloadDatasetById( @Override public ResponseEntity getDicomMetadataByDatasetId( - @Parameter(name = "id of the dataset", required=true) @PathVariable("datasetId") Long datasetId) throws IOException, MessagingException { + Long datasetId) throws IOException, MessagingException { final Dataset dataset = datasetService.findById(datasetId); DatasetDownloadError result = new DatasetDownloadError(); List pathURLs = new ArrayList<>(); @@ -358,7 +358,7 @@ public ResponseEntity getDicomMetadataByDatasetId( } } - public ResponseEntity createProcessedDataset(@Parameter(name = "ProcessedDataset to create" ,required=true ) @Valid @RequestBody ProcessedDatasetImportJob importJob) throws IOException, Exception { + public ResponseEntity createProcessedDataset(@Parameter(description = "ProcessedDataset to create" ,required=true ) @Valid @RequestBody ProcessedDatasetImportJob importJob) throws IOException, Exception { importerService.createProcessedDataset(importJob); File originalNiftiName = new File(importJob.getProcessedDatasetFilePath()); importerService.cleanTempFiles(originalNiftiName.getParent()); @@ -367,11 +367,11 @@ public ResponseEntity createProcessedDataset(@Parameter(name = "ProcessedD @Override public void massiveDownloadByDatasetIds( - @Parameter(name = "ids of the datasets", required=true) @Valid + @Parameter(description = "ids of the datasets", required=true) @Valid @RequestParam(value = "datasetIds", required = true) List datasetIds, - @Parameter(name = "Decide if you want to download dicom (dcm) or nifti (nii) files.") @Valid + @Parameter(description = "Decide if you want to download dicom (dcm) or nifti (nii) files.") @Valid @RequestParam(value = "format", required = false, defaultValue=DCM) String format, - @Parameter(name = "If nifti, decide converter to use") @Valid + @Parameter(description = "If nifti, decide converter to use") @Valid @RequestParam(value = "converterId", required = false) Long converterId, HttpServletResponse response) throws RestServiceException, EntityNotFoundException, MalformedURLException, IOException { // STEP 0: Check data integrity @@ -394,9 +394,9 @@ public void massiveDownloadByDatasetIds( @Override public void massiveDownloadByStudyId( - @Parameter(name = "id of the study", required=true) @Valid + @Parameter(description = "id of the study", required=true) @Valid @RequestParam(value = "studyId", required = true) Long studyId, - @Parameter(name = "Decide if you want to download dicom (dcm) or nifti (nii) files.") @Valid + @Parameter(description = "Decide if you want to download dicom (dcm) or nifti (nii) files.") @Valid @RequestParam(value = "format", required = false, defaultValue=DCM) String format, HttpServletResponse response) throws RestServiceException, EntityNotFoundException, IOException { // STEP 0: Check data integrity if (studyId == null) { @@ -417,9 +417,9 @@ public void massiveDownloadByStudyId( @Override public void massiveDownloadByExaminationId( - @Parameter(name = "id of the examination", required=true) @Valid + @Parameter(description = "id of the examination", required=true) @Valid @RequestParam(value = "examinationId", required = true) Long examinationId, - @Parameter(name = "Decide if you want to download dicom (dcm) or nifti (nii) files.") @Valid + @Parameter(description = "Decide if you want to download dicom (dcm) or nifti (nii) files.") @Valid @RequestParam(value = "format", required = false, defaultValue=DCM) String format, HttpServletResponse response) throws RestServiceException, EntityNotFoundException, IOException { // STEP 0: Check data integrity if (examinationId == null) { @@ -441,9 +441,9 @@ public void massiveDownloadByExaminationId( @Override public void massiveDownloadByAcquisitionId( - @Parameter(name = "id of the acquisition", required=true) @Valid + @Parameter(description = "id of the acquisition", required=true) @Valid @RequestParam(value = "acquisitionId", required = true) Long acquisitionId, - @Parameter(name = "Decide if you want to download dicom (dcm) or nifti (nii) files.") @Valid + @Parameter(description = "Decide if you want to download dicom (dcm) or nifti (nii) files.") @Valid @RequestParam(value = "format", required = false, defaultValue="dcm") String format, HttpServletResponse response) throws RestServiceException, EntityNotFoundException, IOException { // STEP 0: Check data integrity @@ -569,13 +569,13 @@ public String getUnit() { @Override public ResponseEntity downloadStatistics( - @Parameter(name = "Study name including regular expression", required=false) @Valid + @Parameter(description = "Study name including regular expression", required=false) @Valid @RequestParam(value = "studyNameInRegExp", required = false) String studyNameInRegExp, - @Parameter(name = "Study name excluding regular expression", required=false) @Valid + @Parameter(description = "Study name excluding regular expression", required=false) @Valid @RequestParam(value = "studyNameOutRegExp", required = false) String studyNameOutRegExp, - @Parameter(name = "Subject name including regular expression", required=false) @Valid + @Parameter(description = "Subject name including regular expression", required=false) @Valid @RequestParam(value = "subjectNameInRegExp", required = false) String subjectNameInRegExp, - @Parameter(name = "Subject name excluding regular expression", required=false) @Valid + @Parameter(description = "Subject name excluding regular expression", required=false) @Valid @RequestParam(value = "subjectNameOutRegExp", required = false) String subjectNameOutRegExp ) throws RestServiceException, IOException { String tmpDir = System.getProperty(JAVA_IO_TMPDIR); diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/datasetacquisition/controler/DatasetAcquisitionApi.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/datasetacquisition/controler/DatasetAcquisitionApi.java index 816827e08f..1af687de70 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/datasetacquisition/controler/DatasetAcquisitionApi.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/datasetacquisition/controler/DatasetAcquisitionApi.java @@ -50,7 +50,7 @@ public interface DatasetAcquisitionApi { consumes = { "application/json" }, method = RequestMethod.POST) @PreAuthorize("hasRole('ADMIN') or (hasAnyRole('EXPERT', 'USER') and @datasetSecurityService.hasRightOnExamination(#importJob.getExaminationId(), 'CAN_IMPORT'))") - ResponseEntity createNewDatasetAcquisition(@Parameter(name = "DatasetAcquisition to create" ,required=true ) @Valid @RequestBody ImportJob importJob) throws RestServiceException; + ResponseEntity createNewDatasetAcquisition(@Parameter(description = "DatasetAcquisition to create" ,required=true ) @Valid @RequestBody ImportJob importJob) throws RestServiceException; @Operation(summary = "", description = "If exists, returns the dataset acquisitions corresponding to the given study card") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "found dataset acquisitions"), @@ -62,7 +62,7 @@ public interface DatasetAcquisitionApi { @PreAuthorize("hasAnyRole('ADMIN', 'EXPERT', 'USER')") @PostAuthorize("hasRole('ADMIN') or @datasetSecurityService.filterDatasetAcquisitionDTOList(returnObject.getBody(), 'CAN_SEE_ALL')") ResponseEntity> findByStudyCard( - @Parameter(name = "id of the study card", required = true) @PathVariable("studyCardId") Long studyCardId); + @Parameter(description = "id of the study card", required = true) @PathVariable("studyCardId") Long studyCardId); @Operation(summary = "", description = "Deletes a datasetAcquisition") @ApiResponses(value = { @ApiResponse(responseCode = "204", description = "datasetAcquisition deleted"), @@ -73,7 +73,7 @@ ResponseEntity> findByStudyCard( @RequestMapping(value = "/datasetacquisition/{datasetAcquisitionId}", produces = { "application/json" }, method = RequestMethod.DELETE) @PreAuthorize("hasAnyRole('ADMIN', 'EXPERT') and @datasetSecurityService.hasRightOnDatasetAcquisition(#datasetAcquisitionId, 'CAN_ADMINISTRATE')") ResponseEntity deleteDatasetAcquisition( - @Parameter(name = "id of the datasetAcquisition", required = true) @PathVariable("datasetAcquisitionId") Long datasetAcquisitionId) + @Parameter(description = "id of the datasetAcquisition", required = true) @PathVariable("datasetAcquisitionId") Long datasetAcquisitionId) throws RestServiceException; @Operation(summary = "", description = "If exists, returns the datasetAcquisition corresponding to the given id") @@ -86,7 +86,7 @@ ResponseEntity deleteDatasetAcquisition( @PreAuthorize("hasAnyRole('ADMIN', 'EXPERT', 'USER')") @PostAuthorize("hasRole('ADMIN') or returnObject == null or returnObject.getBody() == null or @datasetSecurityService.hasRightOnTrustedExaminationDTO(returnObject.getBody().getExamination(), 'CAN_SEE_ALL')") ResponseEntity findDatasetAcquisitionById( - @Parameter(name = "id of the datasetAcquisition", required = true) @PathVariable("datasetAcquisitionId") Long datasetAcquisitionId); + @Parameter(description = "id of the datasetAcquisition", required = true) @PathVariable("datasetAcquisitionId") Long datasetAcquisitionId); @Operation(summary = "", description = "If exists, returns the datasetAcquisitions corresponding to the given examination id") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "found datasetAcquisition"), @@ -98,7 +98,7 @@ ResponseEntity findDatasetAcquisitionById( @PreAuthorize("hasRole('ADMIN') or (hasAnyRole('EXPERT', 'USER') and @datasetSecurityService.hasRightOnExamination(#examinationId, 'CAN_SEE_ALL'))") @PostAuthorize("hasRole('ADMIN') or @datasetSecurityService.filterExaminationDatasetAcquisitionDTOList(returnObject.getBody(), 'CAN_SEE_ALL')") ResponseEntity> findDatasetAcquisitionByExaminationId( - @Parameter(name = "id of the examination", required = true) @PathVariable("examinationId") Long examinationId); + @Parameter(description = "id of the examination", required = true) @PathVariable("examinationId") Long examinationId); @Operation(summary = "", description = "If exists, returns the datasetAcquisitions corresponding to the given dataset ids") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "found datasetAcquisition"), @@ -110,7 +110,7 @@ ResponseEntity> findDatasetAcquisitionByE @PreAuthorize("hasAnyRole('ADMIN', 'EXPERT', 'USER')") @PostAuthorize("hasRole('ADMIN') or @datasetSecurityService.filterDatasetAcquisitionDTOList(returnObject.getBody(), 'CAN_SEE_ALL')") ResponseEntity> findDatasetAcquisitionByDatasetIds( - @Parameter(name = "ids of the datasets", required = true) @RequestBody Long[] datasetIds); + @Parameter(description = "ids of the datasets", required = true) @RequestBody Long[] datasetIds); @Operation(summary = "", description = "Returns a dataset acquisitions page") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "found dataset acquisitions"), @@ -133,8 +133,8 @@ ResponseEntity> findDatasetAcquisitionByData "application/json" }, method = RequestMethod.PUT) @PreAuthorize("hasAnyRole('ADMIN', 'EXPERT') and #datasetAcquisitionId == #datasetAcquisition.getId() and @datasetSecurityService.hasRightOnExamination(#datasetAcquisition.examination.id, 'CAN_ADMINISTRATE')") ResponseEntity updateDatasetAcquisition( - @Parameter(name = "id of the datasetAcquisition", required = true) @PathVariable("datasetAcquisitionId") Long datasetAcquisitionId, - @Parameter(name = "datasetAcquisition to update", required = true) @Valid @RequestBody DatasetAcquisitionDTO datasetAcquisition, BindingResult result) + @Parameter(description = "id of the datasetAcquisition", required = true) @PathVariable("datasetAcquisitionId") Long datasetAcquisitionId, + @Parameter(description = "datasetAcquisition to update", required = true) @Valid @RequestBody DatasetAcquisitionDTO datasetAcquisition, BindingResult result) throws RestServiceException; } diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/datasetacquisition/controler/DatasetAcquisitionApiController.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/datasetacquisition/controler/DatasetAcquisitionApiController.java index e719415423..f6f1b05db9 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/datasetacquisition/controler/DatasetAcquisitionApiController.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/datasetacquisition/controler/DatasetAcquisitionApiController.java @@ -101,7 +101,7 @@ public class DatasetAcquisitionApiController implements DatasetAcquisitionApi { @Override public ResponseEntity createNewDatasetAcquisition( - @Parameter(name = "DatasetAcquisition to create", required = true) @Valid @RequestBody ImportJob importJob) throws RestServiceException { + @Parameter(description = "DatasetAcquisition to create", required = true) @Valid @RequestBody ImportJob importJob) throws RestServiceException { try { importerService.createAllDatasetAcquisition(importJob, KeycloakUtil.getTokenUserId()); } catch (ShanoirException e) { @@ -155,7 +155,7 @@ private void createAllDatasetAcquisitions(ImportJob importJob, Long userId) thro @Override public ResponseEntity> findByStudyCard( - @Parameter(name = "id of the study card", required = true) @PathVariable("studyCardId") Long studyCardId) { + Long studyCardId) { List daList = datasetAcquisitionService.findByStudyCard(studyCardId); if (daList == null || daList.isEmpty()) { return new ResponseEntity<>(HttpStatus.NO_CONTENT); @@ -165,8 +165,7 @@ public ResponseEntity> findByStudyCard( } @Override - public ResponseEntity> findDatasetAcquisitionByExaminationId( - @Parameter(name = "id of the examination", required = true) @PathVariable("examinationId") Long examinationId) { + public ResponseEntity> findDatasetAcquisitionByExaminationId(Long examinationId) { List daList = datasetAcquisitionService.findByExamination(examinationId); daList.sort(new Comparator() { @@ -186,7 +185,7 @@ public int compare(DatasetAcquisition o1, DatasetAcquisition o2) { @Override public ResponseEntity> findDatasetAcquisitionByDatasetIds( - @Parameter(name = "ids of the datasets", required = true) @RequestBody Long[] datasetIds) { + @Parameter(description = "ids of the datasets", required = true) @RequestBody Long[] datasetIds) { List daList = datasetAcquisitionService.findByDatasetId(datasetIds); @@ -207,7 +206,7 @@ public int compare(DatasetAcquisition o1, DatasetAcquisition o2) { @Override public ResponseEntity deleteDatasetAcquisition( - @Parameter(name = "id of the datasetAcquisition", required = true) @PathVariable("datasetAcquisitionId") Long datasetAcquisitionId) + Long datasetAcquisitionId) throws RestServiceException { try { Long studyId = datasetAcquisitionService.findById(datasetAcquisitionId).getExamination().getStudyId(); @@ -227,7 +226,7 @@ public ResponseEntity deleteDatasetAcquisition( @Override public ResponseEntity findDatasetAcquisitionById( - @Parameter(name = "id of the datasetAcquisition", required = true) @PathVariable("datasetAcquisitionId") Long datasetAcquisitionId) { + Long datasetAcquisitionId) { final DatasetAcquisition datasetAcquisition = datasetAcquisitionService.findById(datasetAcquisitionId); if (datasetAcquisition == null) { @@ -249,8 +248,8 @@ public ResponseEntity> findDatasetAcquisitions(final @Override public ResponseEntity updateDatasetAcquisition( - @Parameter(name = "id of the datasetAcquisition", required = true) @PathVariable("datasetAcquisitionId") Long datasetAcquisitionId, - @Parameter(name = "datasetAcquisition to update", required = true) @Valid @RequestBody DatasetAcquisitionDTO datasetAcquisition, + Long datasetAcquisitionId, + @Parameter(description = "datasetAcquisition to update", required = true) @Valid @RequestBody DatasetAcquisitionDTO datasetAcquisition, final BindingResult result) throws RestServiceException { validate(result); diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/dicom/web/DICOMWebApi.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/dicom/web/DICOMWebApi.java index 67f463e349..5fae8a1cf7 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/dicom/web/DICOMWebApi.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/dicom/web/DICOMWebApi.java @@ -72,7 +72,7 @@ public interface DICOMWebApi { @GetMapping(value = "/studies/{examinationUID}/series", produces = { "application/dicom+json" }) @PreAuthorize("hasRole('ADMIN') or (hasAnyRole('EXPERT', 'USER') and @datasetSecurityService.hasRightOnExamination(#examinationUID, 'CAN_SEE_ALL'))") ResponseEntity findSeriesOfStudy( - @Parameter(name = "examinationUID", required = true) @PathVariable("examinationUID") String examinationUID + @Parameter(description = "examinationUID", required = true) @PathVariable("examinationUID") String examinationUID ) throws RestServiceException, JsonMappingException, JsonProcessingException; @Operation(summary = "", description = "Returns the metadata of a DICOM serie/acquisition of an examination") @@ -85,8 +85,8 @@ ResponseEntity findSeriesOfStudy( @GetMapping(value = "/studies/{examinationUID}/series/{serieInstanceUID}/metadata", produces = { "application/dicom+json" }) @PreAuthorize("hasRole('ADMIN') or (hasAnyRole('EXPERT', 'USER') and @datasetSecurityService.hasRightOnExamination(#examinationUID, 'CAN_SEE_ALL'))") ResponseEntity findSerieMetadataOfStudy( - @Parameter(name = "examinationUID", required = true) @PathVariable("examinationUID") String examinationUID, - @Parameter(name = "serieInstanceUID", required = true) @PathVariable("serieInstanceUID") String serieInstanceUID + @Parameter(description = "examinationUID", required = true) @PathVariable("examinationUID") String examinationUID, + @Parameter(description = "serieInstanceUID", required = true) @PathVariable("serieInstanceUID") String serieInstanceUID ) throws RestServiceException, JsonMappingException, JsonProcessingException; @Operation(summary = "", description = "Returns all DICOM instances/datasets of a study and serie") @@ -99,8 +99,8 @@ ResponseEntity findSerieMetadataOfStudy( @GetMapping(value = "/studies/{examinationUID}/series/{serieInstanceUID}/instances", produces = { "application/dicom+json" }) @PreAuthorize("hasRole('ADMIN') or (hasAnyRole('EXPERT', 'USER') and @datasetSecurityService.hasRightOnExamination(#examinationUID, 'CAN_SEE_ALL'))") ResponseEntity findInstancesOfStudyOfSerie( - @Parameter(name = "examinationUID", required = true) @PathVariable("examinationUID") String examinationUID, - @Parameter(name = "serieInstanceUID", required = true) @PathVariable("serieInstanceUID") String serieInstanceUID + @Parameter(description = "examinationUID", required = true) @PathVariable("examinationUID") String examinationUID, + @Parameter(description = "serieInstanceUID", required = true) @PathVariable("serieInstanceUID") String serieInstanceUID ) throws RestServiceException; @Operation(summary = "", description = "Returns a DICOM instance") @@ -113,9 +113,9 @@ ResponseEntity findInstancesOfStudyOfSerie( @GetMapping(value = "/studies/{examinationUID}/series/{serieInstanceUID}/instances/{sopInstanceUID}") @PreAuthorize("hasRole('ADMIN') or (hasAnyRole('EXPERT', 'USER') and @datasetSecurityService.hasRightOnExamination(#examinationUID, 'CAN_SEE_ALL'))") ResponseEntity findInstance( - @Parameter(name = "examinationUID", required = true) @PathVariable("examinationUID") String examinationUID, - @Parameter(name = "serieInstanceUID", required = true) @PathVariable("serieInstanceUID") String serieInstanceUID, - @Parameter(name = "sopInstanceUID", required = true) @PathVariable("sopInstanceUID") String sopInstanceUID + @Parameter(description = "examinationUID", required = true) @PathVariable("examinationUID") String examinationUID, + @Parameter(description = "serieInstanceUID", required = true) @PathVariable("serieInstanceUID") String serieInstanceUID, + @Parameter(description = "sopInstanceUID", required = true) @PathVariable("sopInstanceUID") String sopInstanceUID ) throws RestServiceException; @Operation(summary = "", description = "Returns a frame of a DICOM instance/dataset, of a study and serie") @@ -128,10 +128,10 @@ ResponseEntity findInstance( @GetMapping(value = "/studies/{examinationUID}/series/{serieInstanceUID}/instances/{sopInstanceUID}/frames/{frame}") @PreAuthorize("hasRole('ADMIN') or (hasAnyRole('EXPERT', 'USER') and @datasetSecurityService.hasRightOnExamination(#examinationUID, 'CAN_SEE_ALL'))") ResponseEntity findFrameOfStudyOfSerieOfInstance( - @Parameter(name = "examinationUID", required = true) @PathVariable("examinationUID") String examinationUID, - @Parameter(name = "serieInstanceUID", required = true) @PathVariable("serieInstanceUID") String serieInstanceUID, - @Parameter(name = "sopInstanceUID", required = true) @PathVariable("sopInstanceUID") String sopInstanceUID, - @Parameter(name = "frame", required = true) @PathVariable("frame") String frame + @Parameter(description = "examinationUID", required = true) @PathVariable("examinationUID") String examinationUID, + @Parameter(description = "serieInstanceUID", required = true) @PathVariable("serieInstanceUID") String serieInstanceUID, + @Parameter(description = "sopInstanceUID", required = true) @PathVariable("sopInstanceUID") String sopInstanceUID, + @Parameter(description = "frame", required = true) @PathVariable("frame") String frame ) throws RestServiceException; @Operation(summary = "", description = "Returns all DICOM instances/datasets") @@ -155,7 +155,7 @@ ResponseEntity findFrameOfStudyOfSerieOfInstance( @GetMapping(value = "/studies/{studyInstanceUID}/instances", produces = { "application/dicom+json" }) @PreAuthorize("hasAnyRole('ADMIN', 'EXPERT', 'USER')") ResponseEntity findInstancesOfStudy( - @Parameter(name = "studyInstanceUID", required = true) @PathVariable("studyInstanceUID") String studyInstanceUID + @Parameter(description = "studyInstanceUID", required = true) @PathVariable("studyInstanceUID") String studyInstanceUID ) throws RestServiceException; @Operation(summary = "", description = "STOW-RS") diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/examination/controler/ExaminationApi.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/examination/controler/ExaminationApi.java index 5537af2adb..cfff83779a 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/examination/controler/ExaminationApi.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/examination/controler/ExaminationApi.java @@ -56,7 +56,7 @@ public interface ExaminationApi { @DeleteMapping(value = "/{examinationId}", produces = { "application/json" }) @PreAuthorize("hasRole('ADMIN') or (hasRole('EXPERT') and @datasetSecurityService.hasRightOnExamination(#examinationId, 'CAN_ADMINISTRATE'))") ResponseEntity deleteExamination( - @Parameter(name = "id of the examination", required = true) @PathVariable("examinationId") Long examinationId) + @Parameter(description = "id of the examination", required = true) @PathVariable("examinationId") Long examinationId) throws RestServiceException; @Operation(summary = "", description = "If exists, returns the examination corresponding to the given id") @@ -69,7 +69,7 @@ ResponseEntity deleteExamination( @PreAuthorize("hasAnyRole('ADMIN', 'EXPERT', 'USER')") @PostAuthorize("hasRole('ADMIN') or @datasetSecurityService.hasRightOnExamination(#examinationId, 'CAN_SEE_ALL')") ResponseEntity findExaminationById( - @Parameter(name = "id of the examination", required = true) @PathVariable("examinationId") Long examinationId) + @Parameter(description = "id of the examination", required = true) @PathVariable("examinationId") Long examinationId) throws RestServiceException; @Operation(summary = "", description = "Returns all the examinations") @@ -94,7 +94,7 @@ ResponseEntity findExaminationById( @GetMapping(value = "/preclinical/{isPreclinical}", produces = { "application/json" }) @PostAuthorize("hasRole('ADMIN') or @datasetSecurityService.filterExaminationDTOPage(returnObject.getBody(), 'CAN_SEE_ALL')") ResponseEntity> findPreclinicalExaminations( - @Parameter(name = "preclinical", required = true) @PathVariable("isPreclinical") Boolean isPreclinical, Pageable pageable); + @Parameter(description = "preclinical", required = true) @PathVariable("isPreclinical") Boolean isPreclinical, Pageable pageable); @Operation(summary = "", description = "Returns the list of examinations by subject id and study id") @ApiResponses(value = { @@ -107,8 +107,8 @@ ResponseEntity> findPreclinicalExaminations( @PreAuthorize("hasRole('ADMIN') or (hasAnyRole('EXPERT', 'USER') and @datasetSecurityService.hasRightOnStudy(#studyId, 'CAN_SEE_ALL'))") @PostAuthorize("hasRole('ADMIN') or @datasetSecurityService.filterSubjectExaminationDTOList(returnObject.getBody(), 'CAN_SEE_ALL')") ResponseEntity> findExaminationsBySubjectIdStudyId( - @Parameter(name = "id of the subject", required = true) @PathVariable("subjectId") Long subjectId, - @Parameter(name = "id of the study", required = true) @PathVariable("studyId") Long studyId); + @Parameter(description = "id of the subject", required = true) @PathVariable("subjectId") Long subjectId, + @Parameter(description = "id of the study", required = true) @PathVariable("studyId") Long studyId); @Operation(summary = "", description = "Returns the list of examinations by study id") @ApiResponses(value = { @@ -120,7 +120,7 @@ ResponseEntity> findExaminationsBySubjectIdStudyId( @GetMapping(value = "/study/{studyId}", produces = { "application/json" }) @PreAuthorize("hasRole('ADMIN') or (hasAnyRole('EXPERT', 'USER') and @datasetSecurityService.hasRightOnStudy(#studyId, 'CAN_SEE_ALL'))") ResponseEntity> findExaminationsByStudyId( - @Parameter(name = "id of the study", required = true) @PathVariable("studyId") Long studyId); + @Parameter(description = "id of the study", required = true) @PathVariable("studyId") Long studyId); @Operation(summary = "", description = "Returns the list of examinations by subject id") @ApiResponses(value = { @@ -133,7 +133,7 @@ ResponseEntity> findExaminationsByStudyId( @PreAuthorize("hasAnyRole('ADMIN', 'EXPERT', 'USER')") @PostAuthorize("hasRole('ADMIN') or @datasetSecurityService.filterExaminationDTOList(returnObject.getBody(), 'CAN_SEE_ALL')") ResponseEntity> findExaminationsBySubjectId( - @Parameter(name = "id of the subject", required = true) @PathVariable("subjectId") Long subjectId); + @Parameter(description = "id of the subject", required = true) @PathVariable("subjectId") Long subjectId); // Attention: this method is used by ShanoirUploader!!! @Operation(summary = "", description = "Saves a new examination") @@ -146,7 +146,7 @@ ResponseEntity> findExaminationsBySubjectId( "application/json" }) @PreAuthorize("hasRole('ADMIN') or (hasAnyRole('EXPERT', 'USER') and @datasetSecurityService.hasRightOnStudyCenter(#examinationDTO.getCenterId(), #examinationDTO.getStudyId(), 'CAN_IMPORT'))") ResponseEntity saveNewExamination( - @Parameter(name = "examination to create", required = true) @Valid @RequestBody ExaminationDTO examinationDTO, + @Parameter(description = "examination to create", required = true) @Valid @RequestBody ExaminationDTO examinationDTO, final BindingResult result) throws RestServiceException; @Operation(summary = "", description = "Updates an examination") @@ -159,8 +159,8 @@ ResponseEntity saveNewExamination( "application/json" }) @PreAuthorize("hasRole('ADMIN') or (hasAnyRole('EXPERT', 'USER') and #examination.getId().equals(#examinationId) and @datasetSecurityService.hasRightOnExamination(#examination.getId(), 'CAN_IMPORT'))") ResponseEntity updateExamination( - @Parameter(name = "id of the examination", required = true) @PathVariable("examinationId") Long examinationId, - @Parameter(name = "examination to update", required = true) @Valid @RequestBody ExaminationDTO examination, + @Parameter(description = "id of the examination", required = true) @PathVariable("examinationId") Long examinationId, + @Parameter(description = "examination to update", required = true) @Valid @RequestBody ExaminationDTO examination, final BindingResult result) throws RestServiceException; @Operation(summary = "", description = "Add extra data to an examination") @@ -174,8 +174,8 @@ ResponseEntity updateExamination( consumes = { "multipart/form-data" }) @PreAuthorize("hasRole('ADMIN') or (hasAnyRole('EXPERT', 'USER') and @datasetSecurityService.hasRightOnExamination(#examinationId, 'CAN_IMPORT'))") ResponseEntity addExtraData( - @Parameter(name = "id of the examination", required = true) @PathVariable("examinationId") Long examinationId, - @Parameter(name = "file to upload", required = true) @Valid @RequestBody MultipartFile file) throws RestServiceException; + @Parameter(description = "id of the examination", required = true) @PathVariable("examinationId") Long examinationId, + @Parameter(description = "file to upload", required = true) @Valid @RequestBody MultipartFile file) throws RestServiceException; @Operation(summary = "", description = "Create an examination and add extra data") @ApiResponses(value = { @ApiResponse(responseCode = "204", description = "examination created and data added"), @@ -188,9 +188,9 @@ ResponseEntity addExtraData( consumes = { "multipart/form-data" }) @PreAuthorize("hasRole('ADMIN') or (hasAnyRole('EXPERT', 'USER') and @datasetSecurityService.hasRightOnSubjectName(#subjectName, 'CAN_IMPORT'))") ResponseEntity createExaminationAndAddExtraData( - @Parameter(name = "name of the subject", required = true) @PathVariable("subjectName") String subjectName, - @Parameter(name = "id of the center", required = true) @PathVariable("centerId") Long centerId, - @Parameter(name = "file to upload", required = true) @Valid @RequestBody MultipartFile file) throws RestServiceException; + @Parameter(description = "name of the subject", required = true) @PathVariable("subjectName") String subjectName, + @Parameter(description = "id of the center", required = true) @PathVariable("centerId") Long centerId, + @Parameter(description = "file to upload", required = true) @Valid @RequestBody MultipartFile file) throws RestServiceException; @Operation(summary = "", description = "Download extra data from an examination", tags = {}) @ApiResponses(value = { @@ -202,7 +202,7 @@ ResponseEntity createExaminationAndAddExtraData( @GetMapping(value = "extra-data-download/{examinationId}/{fileName:.+}/") @PreAuthorize("hasRole('ADMIN') or (hasAnyRole('EXPERT', 'USER') and @datasetSecurityService.hasRightOnExamination(#examinationId, 'CAN_SEE_ALL'))") void downloadExtraData( - @Parameter(name = "id of the examination", required = true) @PathVariable("examinationId") Long examinationId, - @Parameter(name = "file to download", required = true) @PathVariable("fileName") String fileName, HttpServletResponse response) throws RestServiceException, IOException; + @Parameter(description = "id of the examination", required = true) @PathVariable("examinationId") Long examinationId, + @Parameter(description = "file to download", required = true) @PathVariable("fileName") String fileName, HttpServletResponse response) throws RestServiceException, IOException; } diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/examination/controler/ExaminationApiController.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/examination/controler/ExaminationApiController.java index 3c0c3cac00..46e44ef1ef 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/examination/controler/ExaminationApiController.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/examination/controler/ExaminationApiController.java @@ -98,7 +98,7 @@ public ExaminationApiController(final HttpServletRequest request) { @Override public ResponseEntity deleteExamination( - @Parameter(name = "id of the examination", required = true) @PathVariable("examinationId") final Long examinationId) + @Parameter(description = "id of the examination", required = true) @PathVariable("examinationId") final Long examinationId) throws RestServiceException { try { // Delete extra data @@ -128,7 +128,7 @@ public ResponseEntity deleteExamination( @Override public ResponseEntity findExaminationById( - @Parameter(name = "id of the examination", required = true) @PathVariable("examinationId") final Long examinationId) + @Parameter(description = "id of the examination", required = true) @PathVariable("examinationId") final Long examinationId) throws RestServiceException { Examination examination = examinationService.findById(examinationId); orderDatasetAcquisitions(examination); @@ -150,7 +150,7 @@ public ResponseEntity> findExaminations(final Pageable page @Override public ResponseEntity> findPreclinicalExaminations( - @Parameter(name = "preclinical", required = true) @PathVariable("isPreclinical") Boolean isPreclinical, Pageable pageable) { + @Parameter(description = "preclinical", required = true) @PathVariable("isPreclinical") Boolean isPreclinical, Pageable pageable) { Page examinations; // Get examinations reachable by connected user @@ -163,8 +163,8 @@ public ResponseEntity> findPreclinicalExaminations( @Override public ResponseEntity> findExaminationsBySubjectIdStudyId( - @Parameter(name = "id of the subject", required = true) @PathVariable("subjectId") Long subjectId, - @Parameter(name = "id of the study", required = true) @PathVariable("studyId") Long studyId) { + @Parameter(description = "id of the subject", required = true) @PathVariable("subjectId") Long subjectId, + @Parameter(description = "id of the study", required = true) @PathVariable("studyId") Long studyId) { final List examinations = examinationService.findBySubjectIdStudyId(subjectId, studyId); for (Examination exam : examinations) { orderDatasetAcquisitions(exam); @@ -177,7 +177,7 @@ public ResponseEntity> findExaminationsBySubjectIdSt @Override public ResponseEntity> findExaminationsByStudyId( - @Parameter(name = "id of the study", required = true) @PathVariable("studyId") Long studyId) { + @Parameter(description = "id of the study", required = true) @PathVariable("studyId") Long studyId) { final List examinations = examinationService.findIdsByStudyId(studyId); if (examinations.isEmpty()) { return new ResponseEntity<>(HttpStatus.NO_CONTENT); @@ -188,7 +188,7 @@ public ResponseEntity> findExaminationsByStudyId( // Attention: this method is used by ShanoirUploader!!! @Override public ResponseEntity saveNewExamination( - @Parameter(name = "the examination to create", required = true) @RequestBody @Valid final ExaminationDTO examinationDTO, + @Parameter(description = "the examination to create", required = true) @RequestBody @Valid final ExaminationDTO examinationDTO, final BindingResult result) throws RestServiceException { validate(result); final Examination createdExamination = examinationService.save(examinationMapper.examinationDTOToExamination(examinationDTO)); @@ -199,8 +199,8 @@ public ResponseEntity saveNewExamination( @Override public ResponseEntity updateExamination( - @Parameter(name = "id of the examination", required = true) @PathVariable("examinationId") final Long examinationId, - @Parameter(name = "the examination to update", required = true) @RequestBody @Valid final ExaminationDTO examination, + @Parameter(description = "id of the examination", required = true) @PathVariable("examinationId") final Long examinationId, + @Parameter(description = "the examination to update", required = true) @RequestBody @Valid final ExaminationDTO examination, final BindingResult result) throws RestServiceException { /* Update examination in db. */ try { @@ -216,7 +216,7 @@ public ResponseEntity updateExamination( } @Override - public ResponseEntity> findExaminationsBySubjectId(@Parameter(name = "id of the subject", required = true) @PathVariable("subjectId") Long subjectId) { + public ResponseEntity> findExaminationsBySubjectId(@Parameter(description = "id of the subject", required = true) @PathVariable("subjectId") Long subjectId) { final List examinations = examinationService.findBySubjectId(subjectId); if (examinations.isEmpty()) { return new ResponseEntity<>(HttpStatus.NO_CONTENT); @@ -227,8 +227,8 @@ public ResponseEntity> findExaminationsBySubjectId(@Paramet @Override public ResponseEntity addExtraData( - @Parameter(name = "id of the examination", required = true) @PathVariable("examinationId") Long examinationId, - @Parameter(name = "file to upload", required = true) @Valid @RequestBody MultipartFile file) throws RestServiceException { + @Parameter(description = "id of the examination", required = true) @PathVariable("examinationId") Long examinationId, + @Parameter(description = "file to upload", required = true) @Valid @RequestBody MultipartFile file) throws RestServiceException { if (examinationService.addExtraData(examinationId, file) != null) { return new ResponseEntity<>(HttpStatus.OK); } @@ -237,9 +237,9 @@ public ResponseEntity addExtraData( @Override public ResponseEntity createExaminationAndAddExtraData( - @Parameter(name = "name of the subject", required = true) @PathVariable("subjectName") String subjectName, - @Parameter(name = "id of the center", required = true) @PathVariable("centerId") Long centerId, - @Parameter(name = "file to upload", required = true) @Valid @RequestBody MultipartFile file) throws RestServiceException { + @Parameter(description = "name of the subject", required = true) @PathVariable("subjectName") String subjectName, + @Parameter(description = "id of the center", required = true) @PathVariable("centerId") Long centerId, + @Parameter(description = "file to upload", required = true) @Valid @RequestBody MultipartFile file) throws RestServiceException { Subject subject = subjectRepository.findByName(subjectName); if (subject == null) { @@ -275,8 +275,8 @@ public ResponseEntity createExaminationAndAddExtraData( @Override public void downloadExtraData( - @Parameter(name = "id of the examination", required = true) @PathVariable("examinationId") Long examinationId, - @Parameter(name = "file to download", required = true) @PathVariable("fileName") String fileName, HttpServletResponse response) throws RestServiceException, IOException { + @Parameter(description = "id of the examination", required = true) @PathVariable("examinationId") Long examinationId, + @Parameter(description = "file to download", required = true) @PathVariable("fileName") String fileName, HttpServletResponse response) throws RestServiceException, IOException { String filePath = this.examinationService.getExtraDataFilePath(examinationId, fileName); LOG.info("Retrieving file : {}", filePath); File fileToDownLoad = new File(filePath); diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/processing/controler/DatasetProcessingApi.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/processing/controler/DatasetProcessingApi.java index 1c2e76d5c6..89076bd69c 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/processing/controler/DatasetProcessingApi.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/processing/controler/DatasetProcessingApi.java @@ -47,7 +47,7 @@ public interface DatasetProcessingApi { @DeleteMapping(value = "/{datasetProcessingId}", produces = { "application/json" }) @PreAuthorize("hasAnyRole('ADMIN', 'EXPERT')") ResponseEntity deleteDatasetProcessing( - @Parameter(name = "id of the dataset processing", required = true) @PathVariable("datasetProcessingId") Long datasetProcessingId) + @Parameter(description = "id of the dataset processing", required = true) @PathVariable("datasetProcessingId") Long datasetProcessingId) throws RestServiceException, ShanoirException, SolrServerException, IOException; @Operation(summary = "", description = "If exists, returns the dataset processing corresponding to the given id") @@ -59,7 +59,7 @@ ResponseEntity deleteDatasetProcessing( @GetMapping(value = "/{datasetProcessingId}", produces = { "application/json" }) @PreAuthorize("hasAnyRole('ADMIN', 'EXPERT', 'USER')") ResponseEntity findDatasetProcessingById( - @Parameter(name = "id of the dataset processing", required = true) @PathVariable("datasetProcessingId") Long datasetProcessingId); + @Parameter(description = "id of the dataset processing", required = true) @PathVariable("datasetProcessingId") Long datasetProcessingId); @Operation(summary = "", description = "Returns the dataset processings with given study and subject") @ApiResponses(value = { @@ -81,7 +81,7 @@ ResponseEntity findDatasetProcessingById( @ApiResponse(responseCode = "500", description = "unexpected error") }) @GetMapping(value = "/{datasetProcessingId}/inputDatasets/", produces = { "application/json" }) @PreAuthorize("hasAnyRole('ADMIN', 'EXPERT', 'USER')") - ResponseEntity> getInputDatasets(@Parameter(name = "id of the dataset processing", required = true) @PathVariable("datasetProcessingId") Long datasetProcessingId); + ResponseEntity> getInputDatasets(@Parameter(description = "id of the dataset processing", required = true) @PathVariable("datasetProcessingId") Long datasetProcessingId); @Operation(summary = "", description = "Returns the output datasets of a processing") @ApiResponses(value = { @@ -92,7 +92,7 @@ ResponseEntity findDatasetProcessingById( @ApiResponse(responseCode = "500", description = "unexpected error") }) @GetMapping(value = "/{datasetProcessingId}/outputDatasets/", produces = { "application/json" }) @PreAuthorize("hasAnyRole('ADMIN', 'EXPERT', 'USER')") - ResponseEntity> getOutputDatasets(@Parameter(name = "id of the dataset processing", required = true) @PathVariable("datasetProcessingId") Long datasetProcessingId); + ResponseEntity> getOutputDatasets(@Parameter(description = "id of the dataset processing", required = true) @PathVariable("datasetProcessingId") Long datasetProcessingId); @Operation(summary = "", description = "Saves a new dataset processing") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "created dataset processing"), @@ -103,7 +103,7 @@ ResponseEntity findDatasetProcessingById( @PostMapping(value = "", produces = { "application/json" }, consumes = { "application/json" }) @PreAuthorize("hasAnyRole('ADMIN', 'EXPERT', 'USER')") - ResponseEntity saveNewDatasetProcessing(@Parameter(name = "dataset processing to create", required = true) @Valid @RequestBody DatasetProcessing datasetProcessing, + ResponseEntity saveNewDatasetProcessing(@Parameter(description = "dataset processing to create", required = true) @Valid @RequestBody DatasetProcessing datasetProcessing, BindingResult result) throws RestServiceException; @Operation(summary = "", description = "Updates a dataset processing") @@ -116,8 +116,8 @@ ResponseEntity saveNewDatasetProcessing(@Parameter(name = "application/json" }) @PreAuthorize("hasAnyRole('ADMIN', 'EXPERT', 'USER') and @controlerSecurityService.idMatches(#datasetProcessingId, #datasetProcessing)") ResponseEntity updateDatasetProcessing( - @Parameter(name = "id of the dataset processing", required = true) @PathVariable("datasetProcessingId") Long datasetProcessingId, - @Parameter(name = "dataset processing to update", required = true) @Valid @RequestBody DatasetProcessing datasetProcessing, BindingResult result) + @Parameter(description = "id of the dataset processing", required = true) @PathVariable("datasetProcessingId") Long datasetProcessingId, + @Parameter(description = "dataset processing to update", required = true) @Valid @RequestBody DatasetProcessing datasetProcessing, BindingResult result) throws RestServiceException; } diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/processing/controler/DatasetProcessingApiController.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/processing/controler/DatasetProcessingApiController.java index 3db01ee081..3c2e448725 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/processing/controler/DatasetProcessingApiController.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/processing/controler/DatasetProcessingApiController.java @@ -59,7 +59,7 @@ public class DatasetProcessingApiController implements DatasetProcessingApi { @Override public ResponseEntity deleteDatasetProcessing( - @Parameter(name = "id of the dataset processing", required = true) @PathVariable("datasetProcessingId") Long datasetProcessingId) + @Parameter(description = "id of the dataset processing", required = true) @PathVariable("datasetProcessingId") Long datasetProcessingId) throws RestServiceException { try { @@ -74,7 +74,7 @@ public ResponseEntity deleteDatasetProcessing( @Override public ResponseEntity findDatasetProcessingById( - @Parameter(name = "id of the dataset processing", required = true) @PathVariable("datasetProcessingId") Long datasetProcessingId) { + @Parameter(description = "id of the dataset processing", required = true) @PathVariable("datasetProcessingId") Long datasetProcessingId) { final Optional datasetProcessing = datasetProcessingService.findById(datasetProcessingId); if (!datasetProcessing.isPresent()) { @@ -94,7 +94,7 @@ public ResponseEntity> findDatasetProcessings() { @Override public ResponseEntity> getInputDatasets( - @Parameter(name = "id of the dataset processing", required = true) @PathVariable("datasetProcessingId") Long datasetProcessingId) { + @Parameter(description = "id of the dataset processing", required = true) @PathVariable("datasetProcessingId") Long datasetProcessingId) { final Optional datasetProcessing = datasetProcessingService.findById(datasetProcessingId); List inputDatasets = datasetProcessing.get().getInputDatasets(); return new ResponseEntity<>(datasetMapper.datasetToDatasetDTO(inputDatasets), HttpStatus.OK); @@ -102,7 +102,7 @@ public ResponseEntity> getInputDatasets( @Override public ResponseEntity> getOutputDatasets( - @Parameter(name = "id of the dataset processing", required = true) @PathVariable("datasetProcessingId") Long datasetProcessingId) { + @Parameter(description = "id of the dataset processing", required = true) @PathVariable("datasetProcessingId") Long datasetProcessingId) { final Optional datasetProcessing = datasetProcessingService.findById(datasetProcessingId); List outputDatasets = datasetProcessing.get().getOutputDatasets(); return new ResponseEntity<>(datasetMapper.datasetToDatasetDTO(outputDatasets), HttpStatus.OK); @@ -110,7 +110,7 @@ public ResponseEntity> getOutputDatasets( @Override public ResponseEntity saveNewDatasetProcessing( - @Parameter(name = "dataset processing to create", required = true) @Valid @RequestBody DatasetProcessing datasetProcessing, + @Parameter(description = "dataset processing to create", required = true) @Valid @RequestBody DatasetProcessing datasetProcessing, final BindingResult result) throws RestServiceException { /* Validation */ @@ -123,8 +123,8 @@ public ResponseEntity saveNewDatasetProcessing( @Override public ResponseEntity updateDatasetProcessing( - @Parameter(name = "id of the dataset processing", required = true) @PathVariable("datasetProcessingId") Long datasetProcessingId, - @Parameter(name = "dataset processing to update", required = true) @Valid @RequestBody DatasetProcessing datasetProcessing, + @Parameter(description = "id of the dataset processing", required = true) @PathVariable("datasetProcessingId") Long datasetProcessingId, + @Parameter(description = "dataset processing to update", required = true) @Valid @RequestBody DatasetProcessing datasetProcessing, final BindingResult result) throws RestServiceException { validate(result); diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/solr/controler/SolrApi.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/solr/controler/SolrApi.java index 9fc136d84e..fd015727ee 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/solr/controler/SolrApi.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/solr/controler/SolrApi.java @@ -68,7 +68,7 @@ public interface SolrApi { @ApiResponse(responseCode = "500", description = "unexpected error") }) @RequestMapping(value = "", produces = { "application/json" }, consumes = { "application/json" }, method = RequestMethod.POST) - ResponseEntity> facetSearch(@Parameter(name = "facets", required = true) @Valid @RequestBody ShanoirSolrQuery query, Pageable pageable) throws RestServiceException; + ResponseEntity> facetSearch(@Parameter(description = "facets", required = true) @Valid @RequestBody ShanoirSolrQuery query, Pageable pageable) throws RestServiceException; @Operation(summary = "", description = "Returns solr documents matching the given dataset ids") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "found documents"), @@ -77,6 +77,6 @@ public interface SolrApi { @ApiResponse(responseCode = "403", description = "forbidden"), @ApiResponse(responseCode = "500", description = "unexpected error") }) @RequestMapping(value = "/byIds", consumes = {"application/json" }, produces = { "application/json" }, method = RequestMethod.POST) - ResponseEntity> findByIdIn(@Parameter(name = "dataset ids", required = true) @Valid @RequestBody List datasetIds, Pageable pageable) throws RestServiceException; + ResponseEntity> findByIdIn(@Parameter(description = "dataset ids", required = true) @Valid @RequestBody List datasetIds, Pageable pageable) throws RestServiceException; } diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/solr/controler/SolrApiController.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/solr/controler/SolrApiController.java index cf8ab1801b..63aaaf0d30 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/solr/controler/SolrApiController.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/solr/controler/SolrApiController.java @@ -56,13 +56,13 @@ public ResponseEntity indexAll() throws RestServiceException, SolrServerEx @Override public ResponseEntity> facetSearch( - @Parameter(name = "facets", required = true) @Valid @RequestBody ShanoirSolrQuery facet, Pageable pageable) throws RestServiceException { + @Parameter(description = "facets", required = true) @Valid @RequestBody ShanoirSolrQuery facet, Pageable pageable) throws RestServiceException { SolrResultPage documents = solrService.facetSearch(facet, pageable); return new ResponseEntity<>(documents, HttpStatus.OK); } @Override - public ResponseEntity> findByIdIn(@Parameter(name = "dataset ids", required = true) @Valid @RequestBody List datasetIds, Pageable pageable) throws RestServiceException { + public ResponseEntity> findByIdIn(@Parameter(description = "dataset ids", required = true) @Valid @RequestBody List datasetIds, Pageable pageable) throws RestServiceException { Page documents = solrService.getByIdIn(datasetIds, pageable); if (documents.getContent().isEmpty()) { return new ResponseEntity<>(HttpStatus.NO_CONTENT); diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/studycard/controler/QualityCardApi.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/studycard/controler/QualityCardApi.java index 0f5c58f2de..7091ed80af 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/studycard/controler/QualityCardApi.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/studycard/controler/QualityCardApi.java @@ -47,7 +47,7 @@ public interface QualityCardApi { @RequestMapping(value = "/{qualityCardId}", produces = { "application/json" }, method = RequestMethod.DELETE) @PreAuthorize("hasRole('ADMIN') or (hasRole('EXPERT') and @datasetSecurityService.hasRightOnQualityCard(#qualityCardId, 'CAN_ADMINISTRATE'))") ResponseEntity deleteQualityCard( - @Parameter(name = "id of the quality card", required = true) @PathVariable("qualityCardId") Long qualityCardId) throws RestServiceException; + @Parameter(description = "id of the quality card", required = true) @PathVariable("qualityCardId") Long qualityCardId) throws RestServiceException; @Operation(summary = "", description = "If exists, returns the quality card corresponding to the given id") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "found quality card"), @@ -59,7 +59,7 @@ ResponseEntity deleteQualityCard( @PreAuthorize("hasAnyRole('ADMIN', 'EXPERT', 'USER')") @PostAuthorize("hasRole('ADMIN') or @datasetSecurityService.hasRightOnStudy(returnObject.getBody().getStudyId(), 'CAN_SEE_ALL')") ResponseEntity findQualityCardById( - @Parameter(name = "id of the quality card", required = true) @PathVariable("qualityCardId") Long qualityCardId); + @Parameter(description = "id of the quality card", required = true) @PathVariable("qualityCardId") Long qualityCardId); @Operation(summary = "", description = "If exists, returns the quality cards corresponding to the given study id") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "found quality cards"), @@ -71,7 +71,7 @@ ResponseEntity findQualityCardById( @PreAuthorize("hasAnyRole('ADMIN', 'EXPERT', 'USER')") @PostAuthorize("hasRole('ADMIN') or @datasetSecurityService.filterCardList(returnObject.getBody(), 'CAN_SEE_ALL')") ResponseEntity> findQualityCardByStudyId( - @Parameter(name = "id of the study", required = true) @PathVariable("studyId") Long studyId); + @Parameter(description = "id of the study", required = true) @PathVariable("studyId") Long studyId); @Operation(summary = "", description = "Returns all the quality Cards") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "found quality cards"), @@ -94,7 +94,7 @@ ResponseEntity> findQualityCardByStudyId( "application/json" }, method = RequestMethod.POST) @PreAuthorize("hasRole('ADMIN') or (hasRole('EXPERT') and @datasetSecurityService.hasRightOnStudy(#qualityCard.getStudyId(), 'CAN_ADMINISTRATE'))") ResponseEntity saveNewQualityCard( - @Parameter(name = "Quality Card to create", required = true) @RequestBody QualityCard QualityCard, + @Parameter(description = "Quality Card to create", required = true) @RequestBody QualityCard QualityCard, final BindingResult result) throws RestServiceException; @Operation(summary = "", description = "Updates a quality card") @@ -107,8 +107,8 @@ ResponseEntity saveNewQualityCard( "application/json" }, method = RequestMethod.PUT) @PreAuthorize("hasRole('ADMIN') or ( hasRole('EXPERT') and #qualityCardId == #qualityCard.getId() and @datasetSecurityService.hasUpdateRightOnQualityCard(#qualityCard, 'CAN_ADMINISTRATE'))") ResponseEntity updateQualityCard( - @Parameter(name = "id of the quality card", required = true) @PathVariable("qualityCardId") Long qualityCardId, - @Parameter(name = "quality card to update", required = true) @RequestBody QualityCard qualityCard, + @Parameter(description = "id of the quality card", required = true) @PathVariable("qualityCardId") Long qualityCardId, + @Parameter(description = "quality card to update", required = true) @RequestBody QualityCard qualityCard, final BindingResult result) throws RestServiceException; @Operation(summary = "", description = "Apply a quality card on a study for quality control") @@ -122,7 +122,7 @@ ResponseEntity updateQualityCard( @RequestMapping(value = "/apply/{qualityCardId}", method = RequestMethod.GET) @PreAuthorize("hasRole('ADMIN') or (hasRole('EXPERT') and @datasetSecurityService.hasRightOnQualityCard(#qualityCardId, 'CAN_ADMINISTRATE'))") ResponseEntity applyQualityCardOnStudy( - @Parameter(name = "id of the quality card", required = true) @PathVariable("qualityCardId") Long qualityCardId) throws RestServiceException, MicroServiceCommunicationException; + @Parameter(description = "id of the quality card", required = true) @PathVariable("qualityCardId") Long qualityCardId) throws RestServiceException, MicroServiceCommunicationException; @Operation(summary = "", description = "Test a quality card on a study for quality control") @ApiResponses(value = { @@ -135,7 +135,7 @@ ResponseEntity applyQualityCardOnStudy( @RequestMapping(value = "/test/{qualityCardId}", method = RequestMethod.GET) @PreAuthorize("hasRole('ADMIN') or (hasRole('EXPERT') and @datasetSecurityService.hasRightOnQualityCard(#qualityCardId, 'CAN_ADMINISTRATE'))") ResponseEntity testQualityCardOnStudy( - @Parameter(name = "id of the quality card", required = true) @PathVariable("qualityCardId") Long qualityCardId) throws RestServiceException, MicroServiceCommunicationException; + @Parameter(description = "id of the quality card", required = true) @PathVariable("qualityCardId") Long qualityCardId) throws RestServiceException, MicroServiceCommunicationException; @Operation(summary = "", description = "Test a quality card on a study for quality control") @ApiResponses(value = { @@ -149,7 +149,7 @@ ResponseEntity testQualityCardOnStudy( @RequestMapping(value = "/test/{qualityCardId}/{start}/{stop}", method = RequestMethod.GET) @PreAuthorize("hasRole('ADMIN') or (hasRole('EXPERT') and @datasetSecurityService.hasRightOnQualityCard(#qualityCardId, 'CAN_ADMINISTRATE'))") ResponseEntity testQualityCardOnStudy( - @Parameter(name = "id of the quality card", required = true) @PathVariable("qualityCardId") Long qualityCardId, - @Parameter(name = "examination number start ", required = true) @PathVariable("start") int start, - @Parameter(name = "examination number stop", required = true) @PathVariable("stop") int stop) throws RestServiceException, MicroServiceCommunicationException; + @Parameter(description = "id of the quality card", required = true) @PathVariable("qualityCardId") Long qualityCardId, + @Parameter(description = "examination number start ", required = true) @PathVariable("start") int start, + @Parameter(description = "examination number stop", required = true) @PathVariable("stop") int stop) throws RestServiceException, MicroServiceCommunicationException; } \ No newline at end of file diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/studycard/controler/QualityCardApiController.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/studycard/controler/QualityCardApiController.java index 096913b881..719219bdb1 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/studycard/controler/QualityCardApiController.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/studycard/controler/QualityCardApiController.java @@ -58,7 +58,7 @@ public class QualityCardApiController implements QualityCardApi { @Override public ResponseEntity deleteQualityCard( - @Parameter(name = "id of the quality card", required = true) @PathVariable("qualityCardId") Long qualityCardId) throws RestServiceException { + Long qualityCardId) throws RestServiceException { try { qualityCardService.deleteById(qualityCardId); } catch (EntityNotFoundException e) { @@ -72,7 +72,7 @@ public ResponseEntity deleteQualityCard( @Override public ResponseEntity findQualityCardById( - @Parameter(name = "id of the study card", required = true) @PathVariable("qualityCardId") Long qualityCardId) { + Long qualityCardId) { final QualityCard qualityCard = qualityCardService.findById(qualityCardId); if (qualityCard == null) { return new ResponseEntity<>(HttpStatus.NOT_FOUND); @@ -83,7 +83,7 @@ public ResponseEntity findQualityCardById( @Override public ResponseEntity> findQualityCardByStudyId( - @Parameter(name = "id of the study", required = true) @PathVariable("studyId") Long studyId) { + Long studyId) { final List qualityCards = qualityCardService.findByStudy(studyId); if (qualityCards.isEmpty()) { return new ResponseEntity<>(HttpStatus.NO_CONTENT); @@ -102,7 +102,7 @@ public ResponseEntity> findQualityCards() { @Override public ResponseEntity saveNewQualityCard( - @Parameter(name = "Quality Card to create", required = true) @RequestBody QualityCard qualityCard, + @Parameter(description = "Quality Card to create", required = true) @RequestBody QualityCard qualityCard, final BindingResult result) throws RestServiceException { validate(qualityCard, result); QualityCard createdQualityCard; @@ -117,8 +117,8 @@ public ResponseEntity saveNewQualityCard( @Override public ResponseEntity updateQualityCard( - @Parameter(name = "id of the quality card", required = true) @PathVariable("qualityCardId") Long qualityCardId, - @Parameter(name = "quality card to update", required = true) @RequestBody QualityCard qualityCard, + Long qualityCardId, + @Parameter(description = "quality card to update", required = true) @RequestBody QualityCard qualityCard, final BindingResult result) throws RestServiceException { validate(qualityCard, result); @@ -152,7 +152,7 @@ protected void validate(QualityCard qualityCard, BindingResult result) throws Re @Override public ResponseEntity applyQualityCardOnStudy( - @Parameter(name = "id of the quality card", required = true) @PathVariable("qualityCardId") Long qualityCardId) throws RestServiceException, MicroServiceCommunicationException { + Long qualityCardId) throws RestServiceException, MicroServiceCommunicationException { final QualityCard qualityCard = qualityCardService.findById(qualityCardId); if (qualityCard == null) { @@ -165,7 +165,7 @@ public ResponseEntity applyQualityCardOnStudy( @Override public ResponseEntity testQualityCardOnStudy( - @Parameter(name = "id of the quality card", required = true) @PathVariable("qualityCardId") Long qualityCardId) throws RestServiceException, MicroServiceCommunicationException { + Long qualityCardId) throws RestServiceException, MicroServiceCommunicationException { final QualityCard qualityCard = qualityCardService.findById(qualityCardId); if (qualityCard == null) { @@ -178,9 +178,9 @@ public ResponseEntity testQualityCardOnStudy( @Override public ResponseEntity testQualityCardOnStudy( - @Parameter(name = "id of the quality card", required = true) @PathVariable("qualityCardId") Long qualityCardId, - @Parameter(name = "examination number start ", required = true) @PathVariable("start") int start, - @Parameter(name = "examination number stop", required = true) @PathVariable("stop") int stop) throws RestServiceException, MicroServiceCommunicationException { + Long qualityCardId, + int start, + int stop) throws RestServiceException, MicroServiceCommunicationException { final QualityCard qualityCard = qualityCardService.findById(qualityCardId); if (qualityCard == null) { diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/studycard/controler/StudyCardApi.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/studycard/controler/StudyCardApi.java index 69504d1507..703dcba6e7 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/studycard/controler/StudyCardApi.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/studycard/controler/StudyCardApi.java @@ -51,7 +51,7 @@ public interface StudyCardApi { @RequestMapping(value = "/{studyCardId}", produces = { "application/json" }, method = RequestMethod.DELETE) @PreAuthorize("hasRole('ADMIN') or (hasRole('EXPERT') and @datasetSecurityService.hasRightOnStudyCard(#studyCardId, 'CAN_ADMINISTRATE'))") ResponseEntity deleteStudyCard( - @Parameter(name = "id of the study card", required = true) @PathVariable("studyCardId") Long studyCardId) throws RestServiceException; + @Parameter(description = "id of the study card", required = true) @PathVariable("studyCardId") Long studyCardId) throws RestServiceException; @Operation(summary = "", description = "If exists, returns the study card corresponding to the given id") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "found study card"), @@ -63,7 +63,7 @@ ResponseEntity deleteStudyCard( @PreAuthorize("hasAnyRole('ADMIN', 'EXPERT', 'USER')") @PostAuthorize("hasRole('ADMIN') or @datasetSecurityService.hasRightOnStudy(returnObject.getBody().getStudyId(), 'CAN_SEE_ALL')") ResponseEntity findStudyCardById( - @Parameter(name = "id of the study card", required = true) @PathVariable("studyCardId") Long studyCardId); + @Parameter(description = "id of the study card", required = true) @PathVariable("studyCardId") Long studyCardId); @Operation(summary = "", description = "If exists, returns the study cards corresponding to the given study id") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "found study cards"), @@ -75,7 +75,7 @@ ResponseEntity findStudyCardById( @PreAuthorize("hasAnyRole('ADMIN', 'EXPERT', 'USER')") @PostAuthorize("hasRole('ADMIN') or @datasetSecurityService.filterCardList(returnObject.getBody(), 'CAN_SEE_ALL')") ResponseEntity> findStudyCardByStudyId( - @Parameter(name = "id of the study", required = true) @PathVariable("studyId") Long studyId); + @Parameter(description = "id of the study", required = true) @PathVariable("studyId") Long studyId); @Operation(summary = "", description = "If exists, returns the study cards corresponding to the given equipment id") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "found study cards"), @@ -87,7 +87,7 @@ ResponseEntity> findStudyCardByStudyId( @PreAuthorize("hasAnyRole('ADMIN', 'EXPERT', 'USER')") @PostAuthorize("hasRole('ADMIN') or @datasetSecurityService.filterCardList(returnObject.getBody(), 'CAN_SEE_ALL')") ResponseEntity> findStudyCardByAcqEqId( - @Parameter(name = "id of the acquisition equipment", required = true) @PathVariable("acqEqId") Long acqEqId); + @Parameter(description = "id of the acquisition equipment", required = true) @PathVariable("acqEqId") Long acqEqId); @Operation(summary = "", description = "Returns all the study Cards") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "found study cards"), @@ -110,7 +110,7 @@ ResponseEntity> findStudyCardByAcqEqId( "application/json" }, method = RequestMethod.POST) @PreAuthorize("hasRole('ADMIN') or (hasRole('EXPERT') and @datasetSecurityService.hasRightOnStudy(#studyCard.getStudyId(), 'CAN_ADMINISTRATE'))") ResponseEntity saveNewStudyCard( - @Parameter(name = "study Card to create", required = true) @RequestBody StudyCard studyCard, + @Parameter(description = "study Card to create", required = true) @RequestBody StudyCard studyCard, final BindingResult result) throws RestServiceException; // Attention: used by ShanoirUploader! @@ -125,7 +125,7 @@ ResponseEntity saveNewStudyCard( @PreAuthorize("hasAnyRole('ADMIN', 'EXPERT', 'USER')") @PostAuthorize("hasRole('ADMIN') or ( hasAnyRole('EXPERT', 'USER') and @datasetSecurityService.filterCardList(returnObject.getBody(), 'CAN_SEE_ALL') )") ResponseEntity> searchStudyCards( - @Parameter(name = "study ids", required = true) @RequestBody IdList studyIds); + @Parameter(description = "study ids", required = true) @RequestBody IdList studyIds); @Operation(summary = "", description = "Updates a study card") @ApiResponses(value = { @ApiResponse(responseCode = "204", description = "study card updated"), @@ -137,8 +137,8 @@ ResponseEntity> searchStudyCards( "application/json" }, method = RequestMethod.PUT) @PreAuthorize("hasRole('ADMIN') or ( hasRole('EXPERT') and #studyCardId == #studyCard.getId() and @datasetSecurityService.hasUpdateRightOnStudyCard(#studyCard, 'CAN_ADMINISTRATE'))") ResponseEntity updateStudyCard( - @Parameter(name = "id of the study card", required = true) @PathVariable("studyCardId") Long studyCardId, - @Parameter(name = "study card to update", required = true) @RequestBody StudyCard studyCard, + @Parameter(description = "id of the study card", required = true) @PathVariable("studyCardId") Long studyCardId, + @Parameter(description = "study card to update", required = true) @RequestBody StudyCard studyCard, final BindingResult result) throws RestServiceException; @Operation(summary = "", description = "Returns all the dicom tags") @@ -158,6 +158,6 @@ ResponseEntity updateStudyCard( "application/json" }, method = RequestMethod.POST) @PreAuthorize("hasRole('ADMIN') or (hasRole('EXPERT') and @datasetSecurityService.hasRightOnEveryDatasetAcquisition(#studyCardApplyObject.datasetAcquisitionIds, 'CAN_ADMINISTRATE'))") ResponseEntity applyStudyCard( - @Parameter(name = "study card id and acquisition ids", required = true) @RequestBody StudyCardApply studyCardApplyObject) throws RestServiceException, PacsException, SolrServerException, IOException; + @Parameter(description = "study card id and acquisition ids", required = true) @RequestBody StudyCardApply studyCardApplyObject) throws RestServiceException, PacsException, SolrServerException, IOException; } diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/studycard/controler/StudyCardApiController.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/studycard/controler/StudyCardApiController.java index ad9b70db12..322cd62717 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/studycard/controler/StudyCardApiController.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/studycard/controler/StudyCardApiController.java @@ -73,7 +73,7 @@ public class StudyCardApiController implements StudyCardApi { @Override public ResponseEntity deleteStudyCard( - @Parameter(name = "id of the study card", required = true) @PathVariable("studyCardId") Long studyCardId) throws RestServiceException { + @Parameter(description = "id of the study card", required = true) @PathVariable("studyCardId") Long studyCardId) throws RestServiceException { try { if (datasetAcquisitionService.existsByStudyCardId(studyCardId)) { throw new RestServiceException( @@ -94,7 +94,7 @@ public ResponseEntity deleteStudyCard( @Override public ResponseEntity findStudyCardById( - @Parameter(name = "id of the study card", required = true) @PathVariable("studyCardId") Long studyCardId) { + @Parameter(description = "id of the study card", required = true) @PathVariable("studyCardId") Long studyCardId) { final StudyCard studyCard = studyCardService.findById(studyCardId); if (studyCard == null) { return new ResponseEntity<>(HttpStatus.NOT_FOUND); @@ -104,7 +104,7 @@ public ResponseEntity findStudyCardById( @Override public ResponseEntity> findStudyCardByStudyId( - @Parameter(name = "id of the study", required = true) @PathVariable("studyId") Long studyId) { + @Parameter(description = "id of the study", required = true) @PathVariable("studyId") Long studyId) { final List studyCards = studyCardService.findByStudy(studyId); if (studyCards.isEmpty()) { return new ResponseEntity<>(HttpStatus.NO_CONTENT); @@ -114,7 +114,7 @@ public ResponseEntity> findStudyCardByStudyId( @Override public ResponseEntity> findStudyCardByAcqEqId( - @Parameter(name = "id of the acquisition equipment", required = true) @PathVariable("acqEqId") Long acqEqId) { + @Parameter(description = "id of the acquisition equipment", required = true) @PathVariable("acqEqId") Long acqEqId) { final List studyCards = studyCardService.findStudyCardsByAcqEq(acqEqId); if (studyCards.isEmpty()) { return new ResponseEntity<>(HttpStatus.NO_CONTENT); @@ -133,7 +133,7 @@ public ResponseEntity> findStudyCards() { @Override public ResponseEntity saveNewStudyCard( - @Parameter(name = "study Card to create", required = true) @RequestBody StudyCard studyCard, + @Parameter(description = "study Card to create", required = true) @RequestBody StudyCard studyCard, final BindingResult result) throws RestServiceException { validate(studyCard, result); StudyCard createdStudyCard; @@ -149,7 +149,7 @@ public ResponseEntity saveNewStudyCard( // Attention: used by ShanoirUploader! @Override public ResponseEntity> searchStudyCards( - @Parameter(name = "study ids", required = true) @RequestBody final IdList studyIds) { + @Parameter(description = "study ids", required = true) @RequestBody final IdList studyIds) { final List studyCards = studyCardService.search(studyIds.getIdList()); if (studyCards.isEmpty()) { return new ResponseEntity<>(HttpStatus.NO_CONTENT); @@ -159,8 +159,8 @@ public ResponseEntity> searchStudyCards( @Override public ResponseEntity updateStudyCard( - @Parameter(name = "id of the study card", required = true) @PathVariable("studyCardId") Long studyCardId, - @Parameter(name = "study card to update", required = true) @RequestBody StudyCard studyCard, + @Parameter(description = "id of the study card", required = true) @PathVariable("studyCardId") Long studyCardId, + @Parameter(description = "study card to update", required = true) @RequestBody StudyCard studyCard, final BindingResult result) throws RestServiceException { validate(studyCard, result); try { @@ -228,7 +228,7 @@ protected void validate(StudyCard studyCard, BindingResult result) throws RestSe @Override public ResponseEntity applyStudyCard( - @Parameter(name = "study card id and dataset ids", required = true) @RequestBody StudyCardApply studyCardApplyObject) throws PacsException, SolrServerException, IOException { + @Parameter(description = "study card id and dataset ids", required = true) @RequestBody StudyCardApply studyCardApplyObject) throws PacsException, SolrServerException, IOException { if (studyCardApplyObject == null || studyCardApplyObject.getDatasetAcquisitionIds() == null || studyCardApplyObject.getDatasetAcquisitionIds().isEmpty() diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/vip/controller/ExecutionApi.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/vip/controller/ExecutionApi.java index ad90f85835..8b8e9fc228 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/vip/controller/ExecutionApi.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/vip/controller/ExecutionApi.java @@ -53,7 +53,7 @@ public interface ExecutionApi { consumes = {"application/json"}, method = RequestMethod.POST) ResponseEntity createExecution( - @Parameter(name = "execution", required = true) @RequestBody final ExecutionCandidateDTO candidate) throws EntityNotFoundException, SecurityException, RestServiceException; + @Parameter(description = "execution", required = true) @RequestBody final ExecutionCandidateDTO candidate) throws EntityNotFoundException, SecurityException, RestServiceException; @Operation(summary = "Get VIP execution for the given identifier", description = "Returns the VIP execution that has the given identifier in parameter.", tags={ }) @ApiResponses(value = { @@ -64,7 +64,7 @@ ResponseEntity createExecution( @RequestMapping(value = "/{identifier}", produces = { "application/json", "application/octet-stream" }, method = RequestMethod.GET) - ResponseEntity getExecution(@Parameter(name = "The execution identifier", required=true) @PathVariable("identifier") String identifier) throws IOException, RestServiceException, EntityNotFoundException, SecurityException; + ResponseEntity getExecution(@Parameter(description = "The execution identifier", required=true) @PathVariable("identifier") String identifier) throws IOException, RestServiceException, EntityNotFoundException, SecurityException; @Operation(summary = "Get stderr logs for the given VIP execution identifier", description = "Returns the stderr logs of the VIP execution that has the given identifier in parameter.", tags={ }) @ApiResponses(value = { @@ -75,7 +75,7 @@ ResponseEntity createExecution( @RequestMapping(value = "/{identifier}/stderr", produces = { "application/json", "application/octet-stream" }, method = RequestMethod.GET) - ResponseEntity getExecutionStderr(@Parameter(name = "The execution identifier", required=true) @PathVariable("identifier") String identifier) throws IOException, RestServiceException, EntityNotFoundException, SecurityException; + ResponseEntity getExecutionStderr(@Parameter(description = "The execution identifier", required=true) @PathVariable("identifier") String identifier) throws IOException, RestServiceException, EntityNotFoundException, SecurityException; @Operation(summary = "Get stdout logs for the given VIP execution identifier", description = "Returns the stdout logs of the VIP execution that has the given identifier in parameter.", tags={ }) @ApiResponses(value = { @@ -86,7 +86,7 @@ ResponseEntity createExecution( @RequestMapping(value = "/{identifier}/stdout", produces = { "application/json", "application/octet-stream" }, method = RequestMethod.GET) - ResponseEntity getExecutionStdout(@Parameter(name = "The execution identifier", required=true) @PathVariable("identifier") String identifier) throws IOException, RestServiceException, EntityNotFoundException, SecurityException; + ResponseEntity getExecutionStdout(@Parameter(description = "The execution identifier", required=true) @PathVariable("identifier") String identifier) throws IOException, RestServiceException, EntityNotFoundException, SecurityException; @Operation(summary = "Get status for the given VIP execution identifier", description = "Returns the status of the VIP execution that has the given identifier in parameter.", tags={ }) @@ -98,6 +98,6 @@ ResponseEntity createExecution( @RequestMapping(value = "/{identifier}/status", produces = { "application/json", "application/octet-stream" }, method = RequestMethod.GET) - ResponseEntity getExecutionStatus(@Parameter(name = "The execution identifier", required=true) @PathVariable("identifier") String identifier) throws IOException, RestServiceException, EntityNotFoundException, SecurityException; + ResponseEntity getExecutionStatus(@Parameter(description = "The execution identifier", required=true) @PathVariable("identifier") String identifier) throws IOException, RestServiceException, EntityNotFoundException, SecurityException; } diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/vip/controller/ExecutionApiController.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/vip/controller/ExecutionApiController.java index a9c34bc7f8..266937ac88 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/vip/controller/ExecutionApiController.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/vip/controller/ExecutionApiController.java @@ -87,7 +87,7 @@ public class ExecutionApiController implements ExecutionApi { */ @Override public ResponseEntity createExecution( - @Parameter(name = "execution", required = true) @RequestBody final ExecutionCandidateDTO candidate) throws EntityNotFoundException, SecurityException, RestServiceException { + @Parameter(description = "execution", required = true) @RequestBody final ExecutionCandidateDTO candidate) throws EntityNotFoundException, SecurityException, RestServiceException { // 1: Get dataset and check rights List inputDatasets = this.getDatasetsFromParams(candidate.getDatasetParameters()); @@ -231,13 +231,13 @@ private ExecutionMonitoring createExecutionMonitoring(ExecutionCandidateDTO exec } @Override - public ResponseEntity getExecution(@Parameter(name = "The execution identifier", required=true) @PathVariable("identifier") String identifier) { + public ResponseEntity getExecution(@Parameter(description = "The execution identifier", required=true) @PathVariable("identifier") String identifier) { return ResponseEntity.ok(vipClient.getExecution(identifier).block()); } @Override - public ResponseEntity getExecutionStatus(@Parameter(name = "The execution identifier", required=true) @PathVariable("identifier") String identifier) { + public ResponseEntity getExecutionStatus(@Parameter(description = "The execution identifier", required=true) @PathVariable("identifier") String identifier) { return ResponseEntity.ok(vipClient.getExecution(identifier).map(VipExecutionDTO::getStatus).block()); } diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/vip/controller/PathApi.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/vip/controller/PathApi.java index d7f6b50dd6..d3bf512ecc 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/vip/controller/PathApi.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/vip/controller/PathApi.java @@ -33,8 +33,8 @@ public interface PathApi { produces = { "application/json", "application/octet-stream" }, method = RequestMethod.GET) ResponseEntity getPath( - @Parameter(name = "the complete path on which to request information. It can contain non-encoded slashes. Except for the \"exists\" action, any request on a non-existing path should return an error", required=true) @PathVariable("completePath") String completePath, - @NotNull @Parameter(name = "The \"content\" action downloads the raw file. If the path points to a directory, a tarball of this directory is returned. The \"exists\" action returns a BooleanResponse object (see definition) indicating if the path exists or not. The \"properties\" action returns a Path object (see definition) with the path properties. The \"list\" action returns a DirectoryList object (see definition) with the properties of all the files of the directory (if the path is not a directory an error must be returned). The \"md5\" action is optional and returns a PathMd5 object (see definition)." ,required=true + @Parameter(description = "the complete path on which to request information. It can contain non-encoded slashes. Except for the \"exists\" action, any request on a non-existing path should return an error", required=true) @PathVariable("completePath") String completePath, + @NotNull @Parameter(description = "The \"content\" action downloads the raw file. If the path points to a directory, a tarball of this directory is returned. The \"exists\" action returns a BooleanResponse object (see definition) indicating if the path exists or not. The \"properties\" action returns a Path object (see definition) with the path properties. The \"list\" action returns a DirectoryList object (see definition) with the properties of all the files of the directory (if the path is not a directory an error must be returned). The \"md5\" action is optional and returns a PathMd5 object (see definition)." ,required=true ) @Valid @RequestParam(value = "action", required = true) String action, @Valid @RequestParam(value = "format", required = false, defaultValue = "dcm") final String format, @Valid @RequestParam(value = "converter", required = false) Long converter, diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/vip/controller/PathApiController.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/vip/controller/PathApiController.java index 1a575c5d72..c25a4d0765 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/vip/controller/PathApiController.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/vip/controller/PathApiController.java @@ -36,8 +36,8 @@ public class PathApiController implements PathApi { @Override public ResponseEntity getPath( - @Parameter(name = "the complete path on which to request information. It can contain non-encoded slashes. Except for the \"exists\" action, any request on a non-existing path should return an error", required = true) @PathVariable("completePath") String completePath, - @NotNull @Parameter(name = "The \"content\" action downloads the raw file. If the path points to a directory, a tarball of this directory is returned. The \"exists\" action returns a BooleanResponse object (see definition) indicating if the path exists or not. The \"properties\" action returns a Path object (see definition) with the path properties. The \"list\" action returns a DirectoryList object (see definition) with the properties of all the files of the directory (if the path is not a directory an error must be returned). The \"md5\" action is optional and returns a PathMd5 object (see definition).", required = true) @Valid @RequestParam(value = "action", required = true, defaultValue = "content") String action, + @Parameter(description = "the complete path on which to request information. It can contain non-encoded slashes. Except for the \"exists\" action, any request on a non-existing path should return an error", required = true) @PathVariable("completePath") String completePath, + @NotNull @Parameter(description = "The \"content\" action downloads the raw file. If the path points to a directory, a tarball of this directory is returned. The \"exists\" action returns a BooleanResponse object (see definition) indicating if the path exists or not. The \"properties\" action returns a Path object (see definition) with the path properties. The \"list\" action returns a DirectoryList object (see definition) with the properties of all the files of the directory (if the path is not a directory an error must be returned). The \"md5\" action is optional and returns a PathMd5 object (see definition).", required = true) @Valid @RequestParam(value = "action", required = true, defaultValue = "content") String action, @Valid @RequestParam(value = "format", required = false, defaultValue = DCM) final String format, @Valid @RequestParam(value = "converter", required = false) Long converter, HttpServletResponse response) throws IOException, RestServiceException, EntityNotFoundException { diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/vip/controller/PipelineApi.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/vip/controller/PipelineApi.java index 34c067f7cb..14f911e266 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/vip/controller/PipelineApi.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/vip/controller/PipelineApi.java @@ -60,7 +60,7 @@ public interface PipelineApi { @RequestMapping(value = "/{identifier}/{version}", produces = { "application/json", "application/octet-stream" }, method = RequestMethod.GET) - ResponseEntity getPipeline(@Parameter(name = "The pipeline identifier", required=true) @PathVariable("identifier") String identifier, @Parameter(name = "The pipeline version", required=true) @PathVariable("version") String version) throws SecurityException; + ResponseEntity getPipeline(@Parameter(description = "The pipeline identifier", required=true) @PathVariable("identifier") String identifier, @Parameter(description = "The pipeline version", required=true) @PathVariable("version") String version) throws SecurityException; } diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/vip/monitoring/controller/ExecutionMonitoringApi.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/vip/monitoring/controller/ExecutionMonitoringApi.java index 05355001b3..4138d2c65f 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/vip/monitoring/controller/ExecutionMonitoringApi.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/vip/monitoring/controller/ExecutionMonitoringApi.java @@ -31,7 +31,7 @@ public interface ExecutionMonitoringApi { @GetMapping(value = "/{id}", produces = { "application/json" }) @PreAuthorize("hasAnyRole('ADMIN', 'EXPERT', 'USER')") ResponseEntity findExecutionMonitoringById( - @Parameter(name = "id of the execution monitoring", required = true) @PathVariable("id") Long id); + @Parameter(description = "id of the execution monitoring", required = true) @PathVariable("id") Long id); @Operation(summary = "", description = "Return all execution monitorings") @ApiResponses(value = { From 78befd8ac5edae369e981f05946d8ca1ebbe01fc Mon Sep 17 00:00:00 2001 From: Youenn Merel Date: Tue, 11 Jun 2024 09:46:16 +0200 Subject: [PATCH 51/80] [fli-iam#2194] Correct some review comments --- .../org/shanoir/ng/shared/service/StudyServiceImpl.java | 9 +++------ .../org/shanoir/ng/solr/controler/SolrApiController.java | 8 -------- .../org/shanoir/ng/solr/service/SolrServiceImpl.java | 1 + .../src/main/java/org/shanoir/ng/tag/model/Tag.java | 5 ++++- .../src/app/subjects/tree/subject-node.component.ts | 1 - .../java/org/shanoir/ng/tag/service/StudyTagService.java | 1 - 6 files changed, 8 insertions(+), 17 deletions(-) diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/shared/service/StudyServiceImpl.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/shared/service/StudyServiceImpl.java index 6d2a68faf4..03c0d0010b 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/shared/service/StudyServiceImpl.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/shared/service/StudyServiceImpl.java @@ -49,6 +49,9 @@ public Study findById(final Long id) { @Transactional public void updateStudy(Study updated, Study current) { + if (current.getId() == null) + throw new IllegalStateException("The entity should have an id."); + // TAGS if (current.getTags() != null) { current.getTags().clear(); @@ -75,9 +78,6 @@ public void updateStudy(Study updated, Study current) { tag.setStudy(current); } - if (current.getId() == null) - throw new IllegalStateException("The entity should have an id."); - Study studyDb = this.repository.save(current); // SUBJECT_STUDY @@ -105,9 +105,6 @@ public void updateStudy(Study updated, Study current) { } } } - if (current.getId() == null) { - throw new IllegalStateException("The entity should have an id."); - } this.repository.save(current); } diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/solr/controler/SolrApiController.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/solr/controler/SolrApiController.java index f061a6efbc..f2cfe27ae4 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/solr/controler/SolrApiController.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/solr/controler/SolrApiController.java @@ -57,14 +57,6 @@ public class SolrApiController implements SolrApi { @Override public ResponseEntity indexAll() throws SolrServerException, IOException { - ShanoirEvent event = new ShanoirEvent( - ShanoirEventType.SOLR_INDEX_ALL_EVENT, - null, - KeycloakUtil.getTokenUserId(), - "Indexing all datasets...", - ShanoirEvent.IN_PROGRESS, - 0f); - eventService.publishEvent(event); solrService.indexAll(event); return new ResponseEntity<>(HttpStatus.OK); } diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/solr/service/SolrServiceImpl.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/solr/service/SolrServiceImpl.java index ed106b208e..08c5bf37bb 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/solr/service/SolrServiceImpl.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/solr/service/SolrServiceImpl.java @@ -116,6 +116,7 @@ public void deleteAll() throws SolrServerException, IOException { solrJWrapper.deleteAll(); } + @Transactional @Override @Scheduled(cron = "0 0 6 * * *", zone="Europe/Paris") public void indexAll() throws SolrServerException, IOException { diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/tag/model/Tag.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/tag/model/Tag.java index 278e7a887b..826be3faac 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/tag/model/Tag.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/tag/model/Tag.java @@ -1,7 +1,10 @@ package org.shanoir.ng.tag.model; import com.fasterxml.jackson.annotation.JsonIgnore; -import jakarta.persistence.*; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; import org.shanoir.ng.shared.model.Study; @Entity diff --git a/shanoir-ng-front/src/app/subjects/tree/subject-node.component.ts b/shanoir-ng-front/src/app/subjects/tree/subject-node.component.ts index 94d4bea61d..743737fa20 100644 --- a/shanoir-ng-front/src/app/subjects/tree/subject-node.component.ts +++ b/shanoir-ng-front/src/app/subjects/tree/subject-node.component.ts @@ -110,7 +110,6 @@ export class SubjectNodeComponent implements OnChanges { this.node.open = true; }).catch(e => { this.consoleService.log('error', e.toString()); - console.log(e) this.loading = false; }); } diff --git a/shanoir-ng-studies/src/main/java/org/shanoir/ng/tag/service/StudyTagService.java b/shanoir-ng-studies/src/main/java/org/shanoir/ng/tag/service/StudyTagService.java index 1451127f27..3db8a214a3 100644 --- a/shanoir-ng-studies/src/main/java/org/shanoir/ng/tag/service/StudyTagService.java +++ b/shanoir-ng-studies/src/main/java/org/shanoir/ng/tag/service/StudyTagService.java @@ -11,7 +11,6 @@ public interface StudyTagService { @PreAuthorize("hasAnyRole('ADMIN', 'EXPERT') and @studySecurityService.hasRightOnStudy(#study.id, 'CAN_ADMINISTRATE') and @studySecurityService.studyUsersMatchStudy(#study)") StudyTag create(Study study, StudyTagDTO dto); - void update(StudyTagDTO dto) throws EntityNotFoundException; void delete(Long id); From 35ee69f4880ac0a45642d7ad696635693643e970 Mon Sep 17 00:00:00 2001 From: Youenn Merel Date: Tue, 11 Jun 2024 14:50:39 +0200 Subject: [PATCH 52/80] [fli-iam#2194] Correct some review comments --- .../ng/solr/controler/SolrApiController.java | 2 +- .../shanoir/ng/solr/service/SolrService.java | 3 - .../ng/solr/service/SolrServiceImpl.java | 59 +++++++------------ .../ng/shared/event/ShanoirEventService.java | 19 ++++++ 4 files changed, 40 insertions(+), 43 deletions(-) diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/solr/controler/SolrApiController.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/solr/controler/SolrApiController.java index f2cfe27ae4..e31829c025 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/solr/controler/SolrApiController.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/solr/controler/SolrApiController.java @@ -57,7 +57,7 @@ public class SolrApiController implements SolrApi { @Override public ResponseEntity indexAll() throws SolrServerException, IOException { - solrService.indexAll(event); + solrService.indexAll(); return new ResponseEntity<>(HttpStatus.OK); } diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/solr/service/SolrService.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/solr/service/SolrService.java index 79fb145e39..a9572d4bc6 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/solr/service/SolrService.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/solr/service/SolrService.java @@ -44,11 +44,8 @@ public interface SolrService { void addAllToIndex(List documents) throws SolrServerException, IOException; - @Scheduled(cron = "0 0 6 * * *", zone="Europe/Paris") void indexAll() throws SolrServerException, IOException; - void indexAll(ShanoirEvent event) throws SolrServerException, IOException; - void indexDataset(Long datasetId) throws SolrServerException, IOException; void indexDatasets(List datasetIds) throws SolrServerException, IOException; diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/solr/service/SolrServiceImpl.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/solr/service/SolrServiceImpl.java index 08c5bf37bb..948f1c02c4 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/solr/service/SolrServiceImpl.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/solr/service/SolrServiceImpl.java @@ -32,6 +32,7 @@ import org.shanoir.ng.shared.dateTime.DateTimeUtils; import org.shanoir.ng.shared.event.ShanoirEvent; import org.shanoir.ng.shared.event.ShanoirEventService; +import org.shanoir.ng.shared.event.ShanoirEventType; import org.shanoir.ng.shared.exception.RestServiceException; import org.shanoir.ng.shared.model.Center; import org.shanoir.ng.shared.paging.PageImpl; @@ -116,53 +117,40 @@ public void deleteAll() throws SolrServerException, IOException { solrJWrapper.deleteAll(); } - @Transactional - @Override - @Scheduled(cron = "0 0 6 * * *", zone="Europe/Paris") - public void indexAll() throws SolrServerException, IOException { - this.deleteAll(); - List documents = shanoirMetadataRepository.findAllAsSolrDoc(); - Map> tags = shanoirMetadataRepository.findAllTags(null); - this.indexDocumentsInSolr(documents, tags,null); - } - - @Transactional @Override @Async - public void indexAll(ShanoirEvent event) { - - event.setMessage("Cleaning Solr index..."); + @Transactional + @Scheduled(cron = "0 0 6 * * *", zone="Europe/Paris") + public void indexAll() { + + ShanoirEvent event = new ShanoirEvent( + ShanoirEventType.SOLR_INDEX_ALL_EVENT, + null, + KeycloakUtil.getTokenUserId(), + "Cleaning Solr index...", + ShanoirEvent.IN_PROGRESS, + 0f); eventService.publishEvent(event); try { deleteAll(); } catch (SolrServerException | IOException e) { LOG.error("Error while cleaning Solr index.", e); - event.setStatus(ShanoirEvent.ERROR); - event.setProgress(-1f); - event.setMessage("Error while cleaning Solr index : " + e.getMessage()); + eventService.publishErrorEvent(event, "Error while cleaning Solr index : " + e.getMessage()); return; } - event.setProgress(0.25f); - event.setMessage("Fetching data to index..."); - eventService.publishEvent(event); + eventService.publishEvent(event, "Fetching data to index...", 0.25f); List documents; Map> tags; try { documents = shanoirMetadataRepository.findAllAsSolrDoc(); - event.setProgress(0.5f); - event.setMessage("Fetching data to index..."); - eventService.publishEvent(event); + eventService.publishEvent(event, "Fetching data to index...", 0.5f); tags = shanoirMetadataRepository.findAllTags(null); - event.setProgress(0.75f); - event.setMessage("Fetching data to index..."); - eventService.publishEvent(event); + eventService.publishEvent(event, "Fetching data to index...", 0.75f); } catch(Exception e){ LOG.error("Error while fetching data to index.", e); - event.setStatus(ShanoirEvent.ERROR); - event.setProgress(-1f); - event.setMessage("Error while fetching data to index : " + e.getMessage()); + eventService.publishErrorEvent(event, "Error while fetching data to index : " + e.getMessage()); return; } @@ -170,9 +158,7 @@ public void indexAll(ShanoirEvent event) { this.indexDocumentsInSolr(documents, tags, event); } catch (SolrServerException | IOException e) { LOG.error("Error indexing datasets into Solr.", e); - event.setStatus(ShanoirEvent.ERROR); - event.setProgress(-1f); - event.setMessage("Error indexing datasets into Solr : " + e.getMessage()); + eventService.publishErrorEvent(event, "Error indexing datasets into Solr : " + e.getMessage()); } } @@ -201,9 +187,7 @@ private void indexDocumentsInSolr(List metadatas, Map docIt = metadatas.iterator(); @@ -219,10 +203,7 @@ private void indexDocumentsInSolr(List metadatas, Map Date: Tue, 11 Jun 2024 15:17:27 +0200 Subject: [PATCH 53/80] [fli-iam#2194] Correct last review comments --- .../ng/dataset/controler/DatasetApiController.java | 12 +++++------- .../ng/study/security/StudySecurityService.java | 14 ++++++++++++++ .../shanoir/ng/tag/service/StudyTagService.java | 2 ++ .../ng/tag/service/StudyTagServiceImpl.java | 4 +++- 4 files changed, 24 insertions(+), 8 deletions(-) diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/dataset/controler/DatasetApiController.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/dataset/controler/DatasetApiController.java index f0e3d031e2..510442b52c 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/dataset/controler/DatasetApiController.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/dataset/controler/DatasetApiController.java @@ -249,21 +249,19 @@ public ResponseEntity> findDatasetsByIds } @Override - public ResponseEntity updateDatasetTags(Long datasetId, List studyTagIds, BindingResult result) throws RestServiceException, EntityNotFoundException, SolrServerException, IOException { + public ResponseEntity updateDatasetTags(Long datasetId, List studyTagIds, BindingResult result) throws EntityNotFoundException, SolrServerException, IOException { Dataset ds = datasetService.findById(datasetId); if (ds == null) { throw new EntityNotFoundException(Dataset.class, datasetId); } - for(Long id : studyTagIds){ - if(!studyTagService.existsById(id)){ - throw new RestServiceException( - new ErrorModel(HttpStatus.NOT_FOUND.value(), "Study tag [" + id + "] does not exists.")); - } - } + List tags = studyTagService.findByIds(studyTagIds); ds.setTags(tags); + datasetRepository.save(ds); + solrService.indexDataset(datasetId); + return new ResponseEntity<>(HttpStatus.OK); } diff --git a/shanoir-ng-studies/src/main/java/org/shanoir/ng/study/security/StudySecurityService.java b/shanoir-ng-studies/src/main/java/org/shanoir/ng/study/security/StudySecurityService.java index 8246390912..04f5057c5a 100644 --- a/shanoir-ng-studies/src/main/java/org/shanoir/ng/study/security/StudySecurityService.java +++ b/shanoir-ng-studies/src/main/java/org/shanoir/ng/study/security/StudySecurityService.java @@ -33,6 +33,8 @@ import org.shanoir.ng.subjectstudy.dto.SubjectStudyDTO; import org.shanoir.ng.subjectstudy.model.SubjectStudy; import org.shanoir.ng.subjectstudy.repository.SubjectStudyRepository; +import org.shanoir.ng.tag.model.StudyTag; +import org.shanoir.ng.tag.repository.StudyTagRepository; import org.shanoir.ng.utils.KeycloakUtil; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -56,6 +58,9 @@ public class StudySecurityService { @Autowired DataUserAgreementRepository dataUserAgreementRepository; + @Autowired + StudyTagRepository studyTagRepository; + /** * Check that the connected user has the given right for the given study. * @@ -75,6 +80,15 @@ public boolean hasRightOnStudy(Long studyId, String rightStr) throws EntityNotFo return hasPrivilege(study, right); } + public boolean hasRightOnStudyTag(Long id, String rightStr) throws EntityNotFoundException { + StudyTag tag = studyTagRepository.findById(id) + .orElseThrow(() -> + new EntityNotFoundException("Cannot find study tag with id [" + id + "]")); + return this.hasRightOnStudy(tag.getStudy().getId(), rightStr) && + this.studyUsersMatchStudy(tag.getStudy()); + + } + public boolean filterVolumesHasRightOnStudies(List studyIds, String rightStr) throws EntityNotFoundException { List invalidStudyIds = new ArrayList<>(); diff --git a/shanoir-ng-studies/src/main/java/org/shanoir/ng/tag/service/StudyTagService.java b/shanoir-ng-studies/src/main/java/org/shanoir/ng/tag/service/StudyTagService.java index 3db8a214a3..835e457010 100644 --- a/shanoir-ng-studies/src/main/java/org/shanoir/ng/tag/service/StudyTagService.java +++ b/shanoir-ng-studies/src/main/java/org/shanoir/ng/tag/service/StudyTagService.java @@ -11,7 +11,9 @@ public interface StudyTagService { @PreAuthorize("hasAnyRole('ADMIN', 'EXPERT') and @studySecurityService.hasRightOnStudy(#study.id, 'CAN_ADMINISTRATE') and @studySecurityService.studyUsersMatchStudy(#study)") StudyTag create(Study study, StudyTagDTO dto); + @PreAuthorize("hasAnyRole('ADMIN', 'EXPERT') and @studySecurityService.hasRightOnStudy(#dto.id, 'CAN_ADMINISTRATE')") void update(StudyTagDTO dto) throws EntityNotFoundException; + @PreAuthorize("hasAnyRole('ADMIN', 'EXPERT') and @studySecurityService.hasRightOnStudy(#id, 'CAN_ADMINISTRATE')") void delete(Long id); } diff --git a/shanoir-ng-studies/src/main/java/org/shanoir/ng/tag/service/StudyTagServiceImpl.java b/shanoir-ng-studies/src/main/java/org/shanoir/ng/tag/service/StudyTagServiceImpl.java index 6127ab238e..90aa2733a5 100644 --- a/shanoir-ng-studies/src/main/java/org/shanoir/ng/tag/service/StudyTagServiceImpl.java +++ b/shanoir-ng-studies/src/main/java/org/shanoir/ng/tag/service/StudyTagServiceImpl.java @@ -1,7 +1,9 @@ package org.shanoir.ng.tag.service; import org.shanoir.ng.shared.exception.EntityNotFoundException; +import org.shanoir.ng.shared.security.rights.StudyUserRight; import org.shanoir.ng.study.model.Study; +import org.shanoir.ng.study.security.StudySecurityService; import org.shanoir.ng.tag.model.StudyTag; import org.shanoir.ng.tag.model.StudyTagDTO; import org.shanoir.ng.tag.repository.StudyTagRepository; @@ -14,7 +16,6 @@ public class StudyTagServiceImpl implements StudyTagService { @Autowired private StudyTagRepository repository; - @Override public StudyTag create(Study study, StudyTagDTO dto) { StudyTag tag = new StudyTag(); tag.setStudy(study); @@ -39,4 +40,5 @@ public void delete(Long id){ } + } From fa25314725ece908ea9e5487c1627029f2b97874 Mon Sep 17 00:00:00 2001 From: Youenn Merel Date: Tue, 11 Jun 2024 15:46:04 +0200 Subject: [PATCH 54/80] [fli-iam#2194] Correct secu check --- .../main/java/org/shanoir/ng/tag/service/StudyTagService.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/shanoir-ng-studies/src/main/java/org/shanoir/ng/tag/service/StudyTagService.java b/shanoir-ng-studies/src/main/java/org/shanoir/ng/tag/service/StudyTagService.java index 835e457010..097e9dba51 100644 --- a/shanoir-ng-studies/src/main/java/org/shanoir/ng/tag/service/StudyTagService.java +++ b/shanoir-ng-studies/src/main/java/org/shanoir/ng/tag/service/StudyTagService.java @@ -11,9 +11,9 @@ public interface StudyTagService { @PreAuthorize("hasAnyRole('ADMIN', 'EXPERT') and @studySecurityService.hasRightOnStudy(#study.id, 'CAN_ADMINISTRATE') and @studySecurityService.studyUsersMatchStudy(#study)") StudyTag create(Study study, StudyTagDTO dto); - @PreAuthorize("hasAnyRole('ADMIN', 'EXPERT') and @studySecurityService.hasRightOnStudy(#dto.id, 'CAN_ADMINISTRATE')") + @PreAuthorize("hasAnyRole('ADMIN', 'EXPERT') and @studySecurityService.hasRightOnStudyTag(#dto.id, 'CAN_ADMINISTRATE')") void update(StudyTagDTO dto) throws EntityNotFoundException; - @PreAuthorize("hasAnyRole('ADMIN', 'EXPERT') and @studySecurityService.hasRightOnStudy(#id, 'CAN_ADMINISTRATE')") + @PreAuthorize("hasAnyRole('ADMIN', 'EXPERT') and @studySecurityService.hasRightOnStudyTag(#id, 'CAN_ADMINISTRATE')") void delete(Long id); } From a806a8ff235d9a13d1dd8ec34d785960062977db Mon Sep 17 00:00:00 2001 From: Anthony Baire Date: Wed, 12 Jun 2024 11:00:42 +0200 Subject: [PATCH 55/80] migrate ci workflows to java 21 temurin --- .github/workflows/docker.yml | 7 ++++--- .github/workflows/maven.yml | 7 ++++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 433f786057..5bfc85c897 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -20,10 +20,11 @@ jobs: contents: read steps: - - name: Set up JDK 17 - uses: actions/setup-java@v1 + - name: Set up JDK 21 + uses: actions/setup-java@v4 with: - java-version: 17 + distribution: temurin + java-version: 21 - name: Check out the repo uses: actions/checkout@v3 diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index e791a9233d..81b6edc06e 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -20,10 +20,11 @@ jobs: run: git fetch --no-tags --no-recurse-submodules --depth=1 origin +refs/heads/master:refs/remotes/origin/master - name: check migration names run: cd docker-compose/database && ./check_migration_names.py - - name: Set up JDK 17 - uses: actions/setup-java@v1 + - name: Set up JDK 21 + uses: actions/setup-java@v4 with: - java-version: 17 + distribution: temurin + java-version: 21 - name: Create directory /shanoir-ng-logs run: sudo mkdir -m777 /var/log/shanoir-ng-logs - name: Maven cache From 875626e286364d56c2503dd06ba8fc042729d04a Mon Sep 17 00:00:00 2001 From: Youenn Merel Date: Wed, 12 Jun 2024 11:44:21 +0200 Subject: [PATCH 56/80] Config auth methods in swagger --- .../shanoir/ng/configuration/security/BearerAuth.java | 2 +- .../shanoir/ng/configuration/security/OAuth2Auth.java | 9 ++++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/configuration/security/BearerAuth.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/configuration/security/BearerAuth.java index 3dd7543242..e7fe92f916 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/configuration/security/BearerAuth.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/configuration/security/BearerAuth.java @@ -8,6 +8,6 @@ scheme = "bearer", bearerFormat = "JWT", name = "BearerAuth", - openIdConnectUrl = "https://localhost/auth") + description = "Authentication using 'Bearer' JWT token") public class BearerAuth { } diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/configuration/security/OAuth2Auth.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/configuration/security/OAuth2Auth.java index 593ae9ea43..8ab48a1eed 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/configuration/security/OAuth2Auth.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/configuration/security/OAuth2Auth.java @@ -1,6 +1,8 @@ package org.shanoir.ng.configuration.security; import io.swagger.v3.oas.annotations.enums.SecuritySchemeType; +import io.swagger.v3.oas.annotations.extensions.Extension; +import io.swagger.v3.oas.annotations.extensions.ExtensionProperty; import io.swagger.v3.oas.annotations.security.OAuthFlow; import io.swagger.v3.oas.annotations.security.OAuthFlows; import io.swagger.v3.oas.annotations.security.SecurityScheme; @@ -9,8 +11,9 @@ type = SecuritySchemeType.OAUTH2, name = "OAuth2Auth", flows = @OAuthFlows(password = @OAuthFlow( - authorizationUrl = "https://shanoir-ofsep-qualif.irisa.fr/auth/", - tokenUrl = "https://shanoir-ofsep-qualif.irisa.fr/auth/realms/shanoir-ng/protocol/openid-connect/token", - refreshUrl = ""))) + authorizationUrl = "${SHANOIR_URL_SCHEME}://${SHANOIR_URL_HOST}/auth/realms/shanoir-ng/protocol/openid-connect/auth/", + tokenUrl = "${SHANOIR_URL_SCHEME}://${SHANOIR_URL_HOST}/auth/realms/shanoir-ng/protocol/openid-connect/token", + refreshUrl = "${SHANOIR_URL_SCHEME}://${SHANOIR_URL_HOST}/auth/realms/shanoir-ng/protocol/openid-connect/token" + ))) public class OAuth2Auth { } From b8329da408cf3c2de7950822c65ef61fe4870472 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 12 Jun 2024 09:58:38 +0000 Subject: [PATCH 57/80] build(deps): bump org.keycloak:keycloak-core Bumps [org.keycloak:keycloak-core](https://github.com/keycloak/keycloak) from 23.0.5 to 23.0.6. - [Release notes](https://github.com/keycloak/keycloak/releases) - [Commits](https://github.com/keycloak/keycloak/compare/23.0.5...23.0.6) --- updated-dependencies: - dependency-name: org.keycloak:keycloak-core dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- shanoir-ng-keycloak-auth/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shanoir-ng-keycloak-auth/pom.xml b/shanoir-ng-keycloak-auth/pom.xml index ea5cff19b5..946f209209 100644 --- a/shanoir-ng-keycloak-auth/pom.xml +++ b/shanoir-ng-keycloak-auth/pom.xml @@ -28,7 +28,7 @@ along with this program. If not, see https://www.gnu.org/licenses/gpl-3.0.html UTF-8 11 3.4.1.Final - 23.0.5 + 23.0.6 From 404973c26908740af95601e44996f1308ede9c8d Mon Sep 17 00:00:00 2001 From: Youenn Merel Date: Wed, 12 Jun 2024 15:35:10 +0200 Subject: [PATCH 58/80] use shanoir-swagger client --- .../keycloak/cfg/shanoir-ng-realm.json | 55 +++++++++++++++++++ .../src/main/resources/application.yml | 2 + 2 files changed, 57 insertions(+) diff --git a/docker-compose/keycloak/cfg/shanoir-ng-realm.json b/docker-compose/keycloak/cfg/shanoir-ng-realm.json index e996e54b3f..5b10c710cf 100644 --- a/docker-compose/keycloak/cfg/shanoir-ng-realm.json +++ b/docker-compose/keycloak/cfg/shanoir-ng-realm.json @@ -1004,6 +1004,61 @@ "microprofile-jwt" ] }, + { + "clientId": "shanoir-swagger", + "name": "shanoir-swagger", + "description": "", + "rootUrl": "", + "adminUrl": "", + "baseUrl": "", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "redirectUris": [ + "/*" + ], + "webOrigins": [ + "/*" + ], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": true, + "serviceAccountsEnabled": false, + "publicClient": true, + "frontchannelLogout": true, + "protocol": "openid-connect", + "attributes": { + "oidc.ciba.grant.enabled": "false", + "oauth2.device.authorization.grant.enabled": "false", + "backchannel.logout.session.required": "true", + "backchannel.logout.revoke.offline.tokens": "false" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": true, + "nodeReRegistrationTimeout": -1, + "defaultClientScopes": [ + "web-origins", + "acr", + "roles", + "profile", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ], + "access": { + "view": true, + "configure": true, + "manage": true + } + }, { "id": "a219a3a0-14a6-4f9a-8cc5-264f4f64c94a", "clientId": "ohif-viewer", diff --git a/shanoir-ng-datasets/src/main/resources/application.yml b/shanoir-ng-datasets/src/main/resources/application.yml index 8a15c53d3a..71bd6bac32 100644 --- a/shanoir-ng-datasets/src/main/resources/application.yml +++ b/shanoir-ng-datasets/src/main/resources/application.yml @@ -86,6 +86,8 @@ springdoc: config-url: /shanoir-ng/datasets/api-docs/swagger-config enabled: true disable-swagger-default-url: true + oauth: + client-id: shanoir-swagger front.server: address: ${SHANOIR_URL_SCHEME}://${SHANOIR_URL_HOST}/shanoir-ng/ From 91ae16f55fcb4fbb258b57fe194600311a599789 Mon Sep 17 00:00:00 2001 From: Youenn Merel Date: Wed, 12 Jun 2024 16:36:11 +0200 Subject: [PATCH 59/80] correct swagger client conf --- .../keycloak/cfg/shanoir-ng-realm.json | 141 +++++++++++------- 1 file changed, 88 insertions(+), 53 deletions(-) diff --git a/docker-compose/keycloak/cfg/shanoir-ng-realm.json b/docker-compose/keycloak/cfg/shanoir-ng-realm.json index 5b10c710cf..33f2899d08 100644 --- a/docker-compose/keycloak/cfg/shanoir-ng-realm.json +++ b/docker-compose/keycloak/cfg/shanoir-ng-realm.json @@ -1005,60 +1005,95 @@ ] }, { - "clientId": "shanoir-swagger", - "name": "shanoir-swagger", - "description": "", - "rootUrl": "", - "adminUrl": "", - "baseUrl": "", - "surrogateAuthRequired": false, - "enabled": true, - "alwaysDisplayInConsole": false, - "clientAuthenticatorType": "client-secret", - "redirectUris": [ - "/*" - ], - "webOrigins": [ - "/*" - ], - "notBefore": 0, - "bearerOnly": false, - "consentRequired": false, - "standardFlowEnabled": true, - "implicitFlowEnabled": false, - "directAccessGrantsEnabled": true, - "serviceAccountsEnabled": false, - "publicClient": true, - "frontchannelLogout": true, - "protocol": "openid-connect", - "attributes": { - "oidc.ciba.grant.enabled": "false", - "oauth2.device.authorization.grant.enabled": "false", - "backchannel.logout.session.required": "true", - "backchannel.logout.revoke.offline.tokens": "false" + "clientId": "shanoir-swagger", + "name": "shanoir-swagger", + "description": "", + "rootUrl": "", + "adminUrl": "", + "baseUrl": "", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "redirectUris": [ + "http://localhost*" + ], + "webOrigins": [ + "/*" + ], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": true, + "serviceAccountsEnabled": false, + "publicClient": true, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": { + "oidc.ciba.grant.enabled": "false", + "backchannel.logout.session.required": "true", + "post.logout.redirect.uris": "+", + "display.on.consent.screen": "false", + "oauth2.device.authorization.grant.enabled": "false", + "backchannel.logout.revoke.offline.tokens": "false" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": true, + "nodeReRegistrationTimeout": -1, + "protocolMappers": [ + { + "name": "canImportFromPACS", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "false", + "user.attribute": "canImportFromPACS", + "id.token.claim": "false", + "lightweight.claim": "false", + "access.token.claim": "true", + "claim.name": "canImportFromPACS", + "jsonType.label": "boolean" + } + }, + { + "name": "userId", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "false", + "user.attribute": "userId", + "id.token.claim": "false", + "lightweight.claim": "false", + "access.token.claim": "true", + "claim.name": "userId", + "jsonType.label": "long" + } + } + ], + "defaultClientScopes": [ + "web-origins", + "roles", + "profile", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ], + "access": { + "view": true, + "configure": true, + "manage": true + } }, - "authenticationFlowBindingOverrides": {}, - "fullScopeAllowed": true, - "nodeReRegistrationTimeout": -1, - "defaultClientScopes": [ - "web-origins", - "acr", - "roles", - "profile", - "email" - ], - "optionalClientScopes": [ - "address", - "phone", - "offline_access", - "microprofile-jwt" - ], - "access": { - "view": true, - "configure": true, - "manage": true - } - }, { "id": "a219a3a0-14a6-4f9a-8cc5-264f4f64c94a", "clientId": "ohif-viewer", From a3c96935ecf688bef17edb03e603f94675ff8bea Mon Sep 17 00:00:00 2001 From: Youenn Merel Date: Thu, 13 Jun 2024 15:36:00 +0200 Subject: [PATCH 60/80] [fli-iam#2194] remove update study tags endpoint --- .../shanoir/ng/study/controler/StudyApi.java | 46 +++++-------------- .../study/controler/StudyApiController.java | 29 ------------ 2 files changed, 12 insertions(+), 63 deletions(-) diff --git a/shanoir-ng-studies/src/main/java/org/shanoir/ng/study/controler/StudyApi.java b/shanoir-ng-studies/src/main/java/org/shanoir/ng/study/controler/StudyApi.java index a02b184d56..f2d4b710a9 100644 --- a/shanoir-ng-studies/src/main/java/org/shanoir/ng/study/controler/StudyApi.java +++ b/shanoir-ng-studies/src/main/java/org/shanoir/ng/study/controler/StudyApi.java @@ -14,25 +14,22 @@ package org.shanoir.ng.study.controler; -import java.io.IOException; -import java.util.List; -import java.util.Map; - +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.Valid; import org.shanoir.ng.shared.core.model.IdName; -import org.shanoir.ng.shared.exception.EntityNotFoundException; import org.shanoir.ng.shared.exception.MicroServiceCommunicationException; import org.shanoir.ng.shared.exception.RestServiceException; import org.shanoir.ng.shared.exception.ShanoirException; import org.shanoir.ng.shared.security.rights.StudyUserRight; -import org.shanoir.ng.study.dto.IdNameCenterStudyDTO; -import org.shanoir.ng.study.dto.PublicStudyDTO; -import org.shanoir.ng.study.dto.StudyDTO; -import org.shanoir.ng.study.dto.StudyStorageVolumeDTO; -import org.shanoir.ng.study.dto.StudyStatisticsDTO; +import org.shanoir.ng.study.dto.*; import org.shanoir.ng.study.dua.DataUserAgreement; import org.shanoir.ng.study.model.Study; import org.shanoir.ng.study.model.StudyUser; -import org.shanoir.ng.tag.model.StudyTagDTO; import org.springframework.http.ResponseEntity; import org.springframework.security.access.prepost.PostAuthorize; import org.springframework.security.access.prepost.PreAuthorize; @@ -40,13 +37,9 @@ import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.responses.ApiResponse; -import io.swagger.v3.oas.annotations.responses.ApiResponses; -import io.swagger.v3.oas.annotations.tags.Tag; -import jakarta.servlet.http.HttpServletResponse; -import jakarta.validation.Valid; +import java.io.IOException; +import java.util.List; +import java.util.Map; @Tag(name = "studies", description = "the studies API") @@ -366,20 +359,5 @@ ResponseEntity deleteStudyUser( @PreAuthorize("hasRole('ADMIN') or hasRole('EXPERT')") ResponseEntity> getStudyStatistics( @Parameter(name = "id of the study", required = true) @PathVariable("studyId") Long studyId) throws RestServiceException, IOException; - - @Operation(summary = "", description = "Updates study tags") - @ApiResponses(value = { @ApiResponse(responseCode = "204", description = "study tags updated"), - @ApiResponse(responseCode = "401", description = "unauthorized"), - @ApiResponse(responseCode = "403", description = "forbidden"), - @ApiResponse(responseCode = "404", description = "study does not exists"), - @ApiResponse(responseCode = "422", description = "bad parameters"), - @ApiResponse(responseCode = "500", description = "unexpected error") }) - @PutMapping(value = "/{studyId}/tags", produces = { "application/json" }, consumes = { - "application/json" }) - @PreAuthorize("hasAnyRole('ADMIN', 'EXPERT') and @studySecurityService.hasRightOnStudy(#studyId, 'CAN_ADMINISTRATE')") - ResponseEntity updateStudyTags( - @Parameter(description = "id of the study", required = true) @PathVariable("studyId") Long studyId, - @io.swagger.v3.oas.annotations.parameters.RequestBody(description = "array of study tags", required = true) @RequestBody List studyTags, - BindingResult result) throws RestServiceException, EntityNotFoundException, MicroServiceCommunicationException; - + } \ No newline at end of file diff --git a/shanoir-ng-studies/src/main/java/org/shanoir/ng/study/controler/StudyApiController.java b/shanoir-ng-studies/src/main/java/org/shanoir/ng/study/controler/StudyApiController.java index a978a43f0c..e5f4e8bcfb 100644 --- a/shanoir-ng-studies/src/main/java/org/shanoir/ng/study/controler/StudyApiController.java +++ b/shanoir-ng-studies/src/main/java/org/shanoir/ng/study/controler/StudyApiController.java @@ -20,7 +20,6 @@ import jakarta.validation.Valid; import org.apache.commons.io.FileUtils; import org.shanoir.ng.shared.core.model.IdName; -import org.shanoir.ng.shared.error.FieldError; import org.shanoir.ng.shared.error.FieldErrorMap; import org.shanoir.ng.shared.event.ShanoirEvent; import org.shanoir.ng.shared.event.ShanoirEventService; @@ -38,8 +37,6 @@ import org.shanoir.ng.study.service.StudyService; import org.shanoir.ng.study.service.StudyUniqueConstraintManager; import org.shanoir.ng.study.service.StudyUserService; -import org.shanoir.ng.tag.model.StudyTag; -import org.shanoir.ng.tag.model.StudyTagDTO; import org.shanoir.ng.tag.model.StudyTagMapper; import org.shanoir.ng.utils.KeycloakUtil; import org.slf4j.Logger; @@ -558,30 +555,4 @@ public ResponseEntity> getStudyStatistics(@Parameter(na } } - @Override - public ResponseEntity updateStudyTags(Long studyId, List studyTags, BindingResult result) throws MicroServiceCommunicationException, RestServiceException { - Study study = studyService.findById(studyId); - - if(study == null){ - return new ResponseEntity<>(HttpStatus.NOT_FOUND); - } - - List tags = studyTagMapper.studyTagDTOListToStudyTagList(studyTags); - for(StudyTag tag : tags){ - tag.setStudy(study); - } - - study.setStudyTags(studyTagMapper.studyTagDTOListToStudyTagList(studyTags)); - - try { - studyService.update(study); - } catch (EntityNotFoundException e) { - throw new RestServiceException(new ErrorModel(HttpStatus.NOT_FOUND.value(), "Study [" + studyId + "] not found.", e)); - } catch (ShanoirException e) { - throw new RestServiceException(new ErrorModel(HttpStatus.UNPROCESSABLE_ENTITY.value(), "Study [" + studyId + "] tags couldn't be updated.", e)); - } - - return new ResponseEntity<>(HttpStatus.OK); - } - } From 963d86bdf2b76f18f546cddd09393d970c7e648e Mon Sep 17 00:00:00 2001 From: Youenn Merel Date: Thu, 13 Jun 2024 15:41:31 +0200 Subject: [PATCH 61/80] Correct redirectUris --- docker-compose/keycloak/cfg/shanoir-ng-realm.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-compose/keycloak/cfg/shanoir-ng-realm.json b/docker-compose/keycloak/cfg/shanoir-ng-realm.json index 33f2899d08..db9566b86b 100644 --- a/docker-compose/keycloak/cfg/shanoir-ng-realm.json +++ b/docker-compose/keycloak/cfg/shanoir-ng-realm.json @@ -1016,7 +1016,7 @@ "alwaysDisplayInConsole": false, "clientAuthenticatorType": "client-secret", "redirectUris": [ - "http://localhost*" + "SHANOIR_URL_SCHEME://SHANOIR_URL_HOST/*" ], "webOrigins": [ "/*" From be5a86375b4bfa236b70ca1281cf458adc3108c6 Mon Sep 17 00:00:00 2001 From: Julien Louis Date: Fri, 14 Jun 2024 09:26:52 +0200 Subject: [PATCH 62/80] dicom vr ds & di better vm management --- .../studycard/controler/StudyCardApiController.java | 12 +++++++----- .../org/shanoir/ng/studycard/model/DicomTagType.java | 8 +++++--- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/studycard/controler/StudyCardApiController.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/studycard/controler/StudyCardApiController.java index 322cd62717..5414c57c5f 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/studycard/controler/StudyCardApiController.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/studycard/controler/StudyCardApiController.java @@ -184,8 +184,8 @@ public ResponseEntity> findDicomTags() throws RestServiceExceptio if (field.getType().getName() == "int") { int tagCode = field.getInt(null); VR tagVr = StandardElementDictionary.INSTANCE.vrOf(tagCode); - DicomTagType tagType = DicomTagType.valueOf(tagVr); VM tagVm = VM.of(tagCode); + DicomTagType tagType = DicomTagType.valueOf(tagVr, tagVm); dicomTags.add(new DicomTag(tagCode, field.getName(), tagType, tagVm)); } // longs actually code a date and a time, see Tag.class @@ -196,10 +196,12 @@ else if (field.getType().getName() == "long") { String timeStr = hexStr.substring(8); int dateTagCode = Integer.parseInt(dateStr, 16); int timeTagCode = Integer.parseInt(timeStr, 16); - DicomTagType dateTagType = DicomTagType.valueOf(StandardElementDictionary.INSTANCE.vrOf(dateTagCode)); - DicomTagType timeTagType = DicomTagType.valueOf(StandardElementDictionary.INSTANCE.vrOf(timeTagCode)); - dicomTags.add(new DicomTag(dateTagCode, name + "Date", dateTagType, VM.of(dateTagCode))); - dicomTags.add(new DicomTag(timeTagCode, name + "Time", timeTagType, VM.of(timeTagCode))); + VM dateVm = VM.of(dateTagCode); + VM timeVm = VM.of(timeTagCode); + DicomTagType dateTagType = DicomTagType.valueOf(StandardElementDictionary.INSTANCE.vrOf(dateTagCode), dateVm); + DicomTagType timeTagType = DicomTagType.valueOf(StandardElementDictionary.INSTANCE.vrOf(timeTagCode), timeVm); + dicomTags.add(new DicomTag(dateTagCode, name + "Date", dateTagType, dateVm)); + dicomTags.add(new DicomTag(timeTagCode, name + "Time", timeTagType, timeVm)); } } } diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/studycard/model/DicomTagType.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/studycard/model/DicomTagType.java index 16645dacb3..e58c2e2269 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/studycard/model/DicomTagType.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/studycard/model/DicomTagType.java @@ -24,7 +24,7 @@ public enum DicomTagType { */ String, Long, Float, Double, Integer, Binary, Date, FloatArray, IntArray; - public static DicomTagType valueOf(VR vr) { + public static DicomTagType valueOf(VR vr, VM vm) { if ( vr.equals(VR.AE) || vr.equals(VR.AS) // Age special format || vr.equals(VR.CS) @@ -53,7 +53,8 @@ public static DicomTagType valueOf(VR vr) { return DicomTagType.Date; } else if (vr.equals(VR.FL) - || vr.equals(VR.OF)) { + || vr.equals(VR.OF) + || (vr.equals(VR.DS) && Cardinality.ONE.equals(vm.getMax()))) { return DicomTagType.Float; } else if (vr.equals(VR.FD) @@ -61,7 +62,8 @@ public static DicomTagType valueOf(VR vr) { return DicomTagType.Double; } else if (vr.equals(VR.SS) - || vr.equals(VR.US)) { + || vr.equals(VR.US) + || (vr.equals(VR.IS) && Cardinality.ONE.equals(vm.getMax()))) { return DicomTagType.Integer; } else if (vr.equals(VR.OL) From 4736cf5112c2826cb5600427b00ea0ebbfe94e39 Mon Sep 17 00:00:00 2001 From: Julien Louis Date: Fri, 14 Jun 2024 10:05:36 +0200 Subject: [PATCH 63/80] fix --- .../model/condition/StudyCardDICOMConditionOnDatasets.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/studycard/model/condition/StudyCardDICOMConditionOnDatasets.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/studycard/model/condition/StudyCardDICOMConditionOnDatasets.java index 68f724b707..9b73e752f3 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/studycard/model/condition/StudyCardDICOMConditionOnDatasets.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/studycard/model/condition/StudyCardDICOMConditionOnDatasets.java @@ -33,6 +33,7 @@ import org.shanoir.ng.shared.exception.PacsException; import org.shanoir.ng.studycard.model.DicomTagType; import org.shanoir.ng.studycard.model.Operation; +import org.shanoir.ng.studycard.model.VM; import org.shanoir.ng.utils.Utils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -171,7 +172,8 @@ private Boolean fulfilled(Attributes dicomAttributes, StringBuffer errorMsg, Obj throw new IllegalArgumentException("dicomAttributes can't be null"); } VR tagVr = StandardElementDictionary.INSTANCE.vrOf(dicomTag); - DicomTagType tagType = DicomTagType.valueOf(tagVr); + VM tagVm = VM.of(dicomTag); + DicomTagType tagType = DicomTagType.valueOf(tagVr, tagVm); if (!this.getOperation().compatibleWith(tagType)) { if (errorMsg != null) errorMsg.append("\ncondition [" + toString() + "] failed on dataset " + datasetId + " because the operation " + this.getOperation() + " is not compatible with dicom tag " From 1f5b621bd03af6d970014752d200f003921a4237 Mon Sep 17 00:00:00 2001 From: Julien Louis Date: Fri, 14 Jun 2024 10:12:08 +0200 Subject: [PATCH 64/80] forgot that file --- .../main/java/org/shanoir/ng/studycard/model/Cardinality.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/studycard/model/Cardinality.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/studycard/model/Cardinality.java index 5ec227cc81..0fa09ffefc 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/studycard/model/Cardinality.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/studycard/model/Cardinality.java @@ -26,6 +26,8 @@ public class Cardinality { boolean isMultiplier; + public static Cardinality ONE = new Cardinality(1); + Cardinality(String str) { String strCopy = new String(str); if ("n".equals(str)) { From 7f6ae3fbbda647fbfdce72eda0eaec0f58e577c9 Mon Sep 17 00:00:00 2001 From: Julien Louis Date: Fri, 14 Jun 2024 11:21:11 +0200 Subject: [PATCH 65/80] implement equals --- .../studycard/controler/StudyCardApiController.java | 1 + .../org/shanoir/ng/studycard/model/Cardinality.java | 12 ++++++++++++ .../main/java/org/shanoir/ng/studycard/model/VM.java | 5 +++++ 3 files changed, 18 insertions(+) diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/studycard/controler/StudyCardApiController.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/studycard/controler/StudyCardApiController.java index 5414c57c5f..9b40e20f85 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/studycard/controler/StudyCardApiController.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/studycard/controler/StudyCardApiController.java @@ -27,6 +27,7 @@ import org.shanoir.ng.shared.exception.*; import org.shanoir.ng.solr.service.SolrService; import org.shanoir.ng.studycard.dto.DicomTag; +import org.shanoir.ng.studycard.model.Cardinality; import org.shanoir.ng.studycard.model.DicomTagType; import org.shanoir.ng.studycard.model.StudyCard; import org.shanoir.ng.studycard.model.StudyCardApply; diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/studycard/model/Cardinality.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/studycard/model/Cardinality.java index 0fa09ffefc..64a3c1ba7e 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/studycard/model/Cardinality.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/studycard/model/Cardinality.java @@ -65,6 +65,18 @@ public void setMultiplier(boolean isMultiplier) { this.isMultiplier = isMultiplier; } + @Override + public boolean equals(Object obj) { + return obj != null && obj instanceof Cardinality + && ((Cardinality) obj).isMultiplier() == this.isMultiplier() + && ((Cardinality) obj).getNumber() == this.getNumber(); + } + + @Override + public String toString() { + return getNumber() + (isMultiplier() ? "N" : ""); + } + } diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/studycard/model/VM.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/studycard/model/VM.java index 654062bad5..e26e050d9a 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/studycard/model/VM.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/studycard/model/VM.java @@ -57,6 +57,11 @@ public Cardinality getMax() { public void setMax(Cardinality max) { this.max = max; } + + @Override + public String toString() { + return getMin() + "-" + getMax().toString(); + } public static VM of(int tag) { if (Tag.OffendingElement == tag) return new VM("1-n"); From 14a34b1a93d077a7bdfeb2a32f931060c634fa84 Mon Sep 17 00:00:00 2001 From: jcomedouteau <7604176+jcomedouteau@users.noreply.github.com> Date: Fri, 14 Jun 2024 14:35:01 +0200 Subject: [PATCH 66/80] [fli-am#2251] Fix tags in Solr view - Make tags clearer with multiple tags - Correct youenn's PR discretly (do not add tag on datasets not in study) --- .../ng/dataset/controler/DatasetApiController.java | 9 ++++++++- .../java/org/shanoir/ng/solr/solrj/SolrJWrapperImpl.java | 5 +++++ shanoir-ng-front/src/app/solr/solr.document.model.ts | 2 +- shanoir-ng-front/src/app/solr/solr.search.component.ts | 4 +++- 4 files changed, 17 insertions(+), 3 deletions(-) diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/dataset/controler/DatasetApiController.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/dataset/controler/DatasetApiController.java index 4c1021ebc1..47002637fe 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/dataset/controler/DatasetApiController.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/dataset/controler/DatasetApiController.java @@ -256,7 +256,14 @@ public ResponseEntity updateDatasetTags(Long datasetId, List studyTa } List tags = studyTagService.findByIds(studyTagIds); - ds.setTags(tags); + + ds.setTags(new ArrayList<>()); + + for(StudyTag tag : tags){ + if(tag.getStudy().getId().equals(ds.getStudyId())){ + ds.getTags().add(tag); + } + } datasetRepository.save(ds); diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/solr/solrj/SolrJWrapperImpl.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/solr/solrj/SolrJWrapperImpl.java index c6c3d483ef..0dce0e7750 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/solr/solrj/SolrJWrapperImpl.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/solr/solrj/SolrJWrapperImpl.java @@ -400,6 +400,11 @@ private SolrResultPage buildShanoirSolrPage(QueryResponse r solrDoc.setAcquisitionEquipmentName((String) document.getFirstValue("acquisitionEquipmentName")); solrDoc.setSubjectName((String) document.getFirstValue("subjectName")); solrDoc.setSubjectId((Long) document.getFirstValue("subjectId")); + if (document.getFieldValues("tags") != null) { + solrDoc.setTags(document.getFieldValues("tags").stream() + .map(object -> Objects.toString(object, null)) + .toList()); + } solrDoc.setStudyName((String) document.getFirstValue("studyName")); solrDoc.setSubjectType((String) document.getFirstValue("subjectType")); solrDoc.setStudyId((Long) document.getFirstValue("studyId")); diff --git a/shanoir-ng-front/src/app/solr/solr.document.model.ts b/shanoir-ng-front/src/app/solr/solr.document.model.ts index 6726d42aff..c9de5f7227 100644 --- a/shanoir-ng-front/src/app/solr/solr.document.model.ts +++ b/shanoir-ng-front/src/app/solr/solr.document.model.ts @@ -26,7 +26,7 @@ export class SolrDocument { examinationComment: string; centerName: string; centerId: string; - tags: string; + tags: string[]; examinationDate: Date; subjectName: string; subjectType: string; diff --git a/shanoir-ng-front/src/app/solr/solr.search.component.ts b/shanoir-ng-front/src/app/solr/solr.search.component.ts index a0b8e77d34..61018e3f4a 100644 --- a/shanoir-ng-front/src/app/solr/solr.search.component.ts +++ b/shanoir-ng-front/src/app/solr/solr.search.component.ts @@ -426,7 +426,9 @@ export class SolrSearchComponent implements AfterViewChecked, AfterContentInit { {headerName: "Admin", type: "boolean", cellRenderer: row => this.hasAdminRight(row.data.studyId), awesome: "fa-solid fa-shield", color: "goldenrod", disableSorting: true}, {headerName: "", type: "boolean", cellRenderer: row => row.data.processed, awesome: "fa-solid fa-gears", color: "dimgrey", disableSorting: true, tip: item => { return item.processed ? "processed dataset" : "" }}, {headerName: "Name", field: "datasetName"}, - {headerName: "Tags", field: "tags"}, + {headerName: "Tags", field: "tags", cellRenderer: (params: any) => { + return params?.data?.tags ? params.data.tags.join(', ') : ''; + }}, {headerName: "Type", field: "datasetType"}, {headerName: "Nature", field: "datasetNature"}, {headerName: "Creation", field: "datasetCreationDate", type: "date", hidden: true, cellRenderer: (params: any) => dateRenderer(params.data.datasetCreationDate)}, From d8d2583cb55ff0b177cf0b6625f273c32b3d9185 Mon Sep 17 00:00:00 2001 From: Youenn Merel Date: Fri, 14 Jun 2024 15:18:44 +0200 Subject: [PATCH 67/80] [swagger] Fix studies swagger UI --- .../shanoir/ng/ShanoirStudiesApplication.java | 9 ++- .../controler/AcquisitionEquipmentApi.java | 18 +++--- .../AcquisitionEquipmentApiController.java | 18 +++--- .../ng/center/controler/CenterApi.java | 14 ++--- .../center/controler/CenterApiController.java | 14 ++--- .../shanoir/ng/coil/controler/CoilApi.java | 10 ++-- .../ng/coil/controler/CoilApiController.java | 10 ++-- .../ng/configuration/security/BearerAuth.java | 13 ++++ .../ng/configuration/security/OAuth2Auth.java | 17 ++++++ .../controler/ManufacturerApi.java | 10 ++-- .../controler/ManufacturerModelApi.java | 10 ++-- .../shanoir/ng/shared/common/CommonApi.java | 2 +- .../shanoir/ng/study/controler/StudyApi.java | 60 +++++++++---------- .../study/controler/StudyApiController.java | 34 +++++------ .../ng/subject/controler/SubjectApi.java | 22 +++---- .../controler/SubjectApiController.java | 14 ++--- .../controler/SubjectStudyApi.java | 4 +- .../controler/SubjectStudyApiController.java | 4 +- .../src/main/resources/application.yml | 2 + 19 files changed, 162 insertions(+), 123 deletions(-) create mode 100644 shanoir-ng-studies/src/main/java/org/shanoir/ng/configuration/security/BearerAuth.java create mode 100644 shanoir-ng-studies/src/main/java/org/shanoir/ng/configuration/security/OAuth2Auth.java diff --git a/shanoir-ng-studies/src/main/java/org/shanoir/ng/ShanoirStudiesApplication.java b/shanoir-ng-studies/src/main/java/org/shanoir/ng/ShanoirStudiesApplication.java index c9e312c2d9..938df629cb 100644 --- a/shanoir-ng-studies/src/main/java/org/shanoir/ng/ShanoirStudiesApplication.java +++ b/shanoir-ng-studies/src/main/java/org/shanoir/ng/ShanoirStudiesApplication.java @@ -14,6 +14,9 @@ package org.shanoir.ng; +import io.swagger.v3.oas.annotations.info.Info; +import io.swagger.v3.oas.annotations.security.SecurityRequirement; +import io.swagger.v3.oas.annotations.servers.Server; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @@ -23,7 +26,11 @@ * Shanoir-NG microservice studies application. */ @SpringBootApplication -@OpenAPIDefinition +@OpenAPIDefinition( + info = @Info(title = "Shanoir studies API", version = "1.0"), + servers = @Server(url = "/shanoir-ng/studies", description = "Studies"), + security = { @SecurityRequirement(name = "BearerAuth"), @SecurityRequirement(name = "OAuth2Auth") } +) public class ShanoirStudiesApplication { public static void main(String[] args) { diff --git a/shanoir-ng-studies/src/main/java/org/shanoir/ng/acquisitionequipment/controler/AcquisitionEquipmentApi.java b/shanoir-ng-studies/src/main/java/org/shanoir/ng/acquisitionequipment/controler/AcquisitionEquipmentApi.java index 0b1f1c4707..2cde219f13 100644 --- a/shanoir-ng-studies/src/main/java/org/shanoir/ng/acquisitionequipment/controler/AcquisitionEquipmentApi.java +++ b/shanoir-ng-studies/src/main/java/org/shanoir/ng/acquisitionequipment/controler/AcquisitionEquipmentApi.java @@ -48,7 +48,7 @@ public interface AcquisitionEquipmentApi { "application/json" }, method = RequestMethod.DELETE) @PreAuthorize("hasAnyRole('ADMIN', 'EXPERT')") ResponseEntity deleteAcquisitionEquipment( - @Parameter(name = "id of the acquisition equipment", required = true) @PathVariable("acquisitionEquipmentId") Long acquisitionEquipmentId); + @Parameter(description = "id of the acquisition equipment", required = true) @PathVariable("acquisitionEquipmentId") Long acquisitionEquipmentId); @Operation(summary = "", description = "If exists, returns the acquisition equipment corresponding to the given id") @ApiResponses(value = { @@ -60,7 +60,7 @@ ResponseEntity deleteAcquisitionEquipment( @RequestMapping(value = "/{acquisitionEquipmentId}", produces = { "application/json" }, method = RequestMethod.GET) @PreAuthorize("hasAnyRole('USER', 'ADMIN', 'EXPERT')") ResponseEntity findAcquisitionEquipmentById( - @Parameter(name = "id of the acquisition equipment", required = true) @PathVariable("acquisitionEquipmentId") Long acquisitionEquipmentId); + @Parameter(description = "id of the acquisition equipment", required = true) @PathVariable("acquisitionEquipmentId") Long acquisitionEquipmentId); @Operation(summary = "", description = "If exists, returns the acquisition equipments corresponding to the given serial number") @ApiResponses(value = { @@ -72,7 +72,7 @@ ResponseEntity findAcquisitionEquipmentById( @RequestMapping(value = "/bySerialNumber/{serialNumber}", produces = { "application/json" }, method = RequestMethod.GET) @PreAuthorize("hasAnyRole('USER', 'ADMIN', 'EXPERT')") ResponseEntity> findAcquisitionEquipmentsBySerialNumber( - @Parameter(name = "serial number of the acquisition equipment", required = true) @PathVariable("serialNumber") String serialNumber); + @Parameter(description = "serial number of the acquisition equipment", required = true) @PathVariable("serialNumber") String serialNumber); @Operation(summary = "", description = "If exists, returns the acquisition equipment(s) corresponding to the equipment dicom or creates a new one") @ApiResponses(value = { @@ -84,7 +84,7 @@ ResponseEntity> findAcquisitionEquipmentsBySerialN @RequestMapping(value = "/byDicom", produces = { "application/json" }, consumes = {"application/json" }, method = RequestMethod.POST) @PreAuthorize("hasAnyRole('USER', 'ADMIN', 'EXPERT')") ResponseEntity> findAcquisitionEquipmentsOrCreateOneByEquipmentDicom( - @Parameter(name = "equipment dicom to find or create an equipment", required = true) @RequestBody EquipmentDicom equipmentDicom, + @Parameter(description = "equipment dicom to find or create an equipment", required = true) @RequestBody EquipmentDicom equipmentDicom, BindingResult result); @Operation(summary = "", description = "Returns all the acquisition equipments for a center") @@ -96,7 +96,7 @@ ResponseEntity> findAcquisitionEquipmentsOrCreateO @ApiResponse(responseCode = "500", description = "unexpected error") }) @RequestMapping(value = "/byCenter/{centerId}", produces = { "application/json" }, method = RequestMethod.GET) @PreAuthorize("hasAnyRole('USER', 'ADMIN', 'EXPERT')") - ResponseEntity> findAcquisitionEquipmentsByCenter(@Parameter(name = "id of the center", required = true) @PathVariable("centerId") Long centerId); + ResponseEntity> findAcquisitionEquipmentsByCenter(@Parameter(description = "id of the center", required = true) @PathVariable("centerId") Long centerId); @Operation(summary = "", description = "Returns all the acquisition equipments for a study") @ApiResponses(value = { @@ -107,7 +107,7 @@ ResponseEntity> findAcquisitionEquipmentsOrCreateO @ApiResponse(responseCode = "500", description = "unexpected error") }) @RequestMapping(value = "/byStudy/{studyId}", produces = { "application/json" }, method = RequestMethod.GET) @PreAuthorize("hasAnyRole('USER', 'ADMIN', 'EXPERT')") - ResponseEntity> findAcquisitionEquipmentsByStudy(@Parameter(name = "id of the study", required = true) @PathVariable("studyId") Long studyId); + ResponseEntity> findAcquisitionEquipmentsByStudy(@Parameter(description = "id of the study", required = true) @PathVariable("studyId") Long studyId); @Operation(summary = "", description = "Returns all the acquisition equipments") @ApiResponses(value = { @@ -131,7 +131,7 @@ ResponseEntity> findAcquisitionEquipmentsOrCreateO "application/json" }, method = RequestMethod.POST) @PreAuthorize("hasAnyRole('ADMIN', 'EXPERT')") ResponseEntity saveNewAcquisitionEquipment( - @Parameter(name = "acquisition equipment to create", required = true) @RequestBody AcquisitionEquipment acquisitionEquipment, + @Parameter(description = "acquisition equipment to create", required = true) @RequestBody AcquisitionEquipment acquisitionEquipment, BindingResult result) throws RestServiceException; @Operation(summary = "", description = "Updates a acquisition equipment") @@ -145,8 +145,8 @@ ResponseEntity saveNewAcquisitionEquipment( "application/json" }, method = RequestMethod.PUT) @PreAuthorize("hasAnyRole('ADMIN', 'EXPERT') and @controlerSecurityService.idMatches(#acquisitionEquipmentId, #acquisitionEquipment)") ResponseEntity updateAcquisitionEquipment( - @Parameter(name = "id of the acquisition equipment", required = true) @PathVariable("acquisitionEquipmentId") Long acquisitionEquipmentId, - @Parameter(name = "acquisition equipment to update", required = true) @RequestBody AcquisitionEquipment acquisitionEquipment, + @Parameter(description = "id of the acquisition equipment", required = true) @PathVariable("acquisitionEquipmentId") Long acquisitionEquipmentId, + @Parameter(description = "acquisition equipment to update", required = true) @RequestBody AcquisitionEquipment acquisitionEquipment, final BindingResult result) throws RestServiceException; } diff --git a/shanoir-ng-studies/src/main/java/org/shanoir/ng/acquisitionequipment/controler/AcquisitionEquipmentApiController.java b/shanoir-ng-studies/src/main/java/org/shanoir/ng/acquisitionequipment/controler/AcquisitionEquipmentApiController.java index 1fac92f5a4..35d78349ec 100644 --- a/shanoir-ng-studies/src/main/java/org/shanoir/ng/acquisitionequipment/controler/AcquisitionEquipmentApiController.java +++ b/shanoir-ng-studies/src/main/java/org/shanoir/ng/acquisitionequipment/controler/AcquisitionEquipmentApiController.java @@ -59,7 +59,7 @@ public class AcquisitionEquipmentApiController implements AcquisitionEquipmentAp @Override public ResponseEntity deleteAcquisitionEquipment( - @Parameter(name = "id of the acquisition equipment", required = true) @PathVariable("acquisitionEquipmentId") final Long acquisitionEquipmentId) { + @Parameter(description = "id of the acquisition equipment", required = true) @PathVariable("acquisitionEquipmentId") final Long acquisitionEquipmentId) { try { if (acquisitionEquipmentId.equals(0L)) { throw new EntityNotFoundException("Cannot update unknown equipment"); @@ -74,7 +74,7 @@ public ResponseEntity deleteAcquisitionEquipment( @Override public ResponseEntity findAcquisitionEquipmentById( - @Parameter(name = "id of the acquisition equipment", required = true) @PathVariable("acquisitionEquipmentId") final Long acquisitionEquipmentId) { + @Parameter(description = "id of the acquisition equipment", required = true) @PathVariable("acquisitionEquipmentId") final Long acquisitionEquipmentId) { final AcquisitionEquipment equipment = acquisitionEquipmentService.findById(acquisitionEquipmentId).orElse(null); if (equipment == null) { return new ResponseEntity<>(HttpStatus.NOT_FOUND); @@ -94,7 +94,7 @@ public ResponseEntity> findAcquisitionEquipments() acquisitionEquipmentMapper.acquisitionEquipmentsToAcquisitionEquipmentDTOs(equipments), HttpStatus.OK); } - public ResponseEntity> findAcquisitionEquipmentsByCenter(@Parameter(name = "id of the center", required = true) @PathVariable("centerId") Long centerId) { + public ResponseEntity> findAcquisitionEquipmentsByCenter(@Parameter(description = "id of the center", required = true) @PathVariable("centerId") Long centerId) { final List equipments = acquisitionEquipmentService.findAllByCenterId(centerId); if (equipments.isEmpty()) { return new ResponseEntity<>(HttpStatus.NOT_FOUND); @@ -103,7 +103,7 @@ public ResponseEntity> findAcquisitionEquipmentsBy acquisitionEquipmentMapper.acquisitionEquipmentsToAcquisitionEquipmentDTOs(equipments), HttpStatus.OK); } - public ResponseEntity> findAcquisitionEquipmentsByStudy(@Parameter(name = "id of the study", required = true) @PathVariable("studyId") Long studyId) { + public ResponseEntity> findAcquisitionEquipmentsByStudy(@Parameter(description = "id of the study", required = true) @PathVariable("studyId") Long studyId) { List equipments = acquisitionEquipmentService.findAllByStudyId(studyId); // Remove "unknown" equipment equipments = equipments.stream().filter(equipment -> equipment.getId() != 0).collect(Collectors.toList()); @@ -116,7 +116,7 @@ public ResponseEntity> findAcquisitionEquipmentsBy @Override public ResponseEntity saveNewAcquisitionEquipment( - @Parameter(name = "acquisition equipment to create", required = true) @RequestBody final AcquisitionEquipment acquisitionEquipment, + @Parameter(description = "acquisition equipment to create", required = true) @RequestBody final AcquisitionEquipment acquisitionEquipment, final BindingResult result) throws RestServiceException { validate(result); @@ -136,8 +136,8 @@ public ResponseEntity saveNewAcquisitionEquipment( @Override public ResponseEntity updateAcquisitionEquipment( - @Parameter(name = "id of the acquisition equipment", required = true) @PathVariable("acquisitionEquipmentId") final Long acquisitionEquipmentId, - @Parameter(name = "acquisition equipment to update", required = true) @RequestBody final AcquisitionEquipment acquisitionEquipment, + @Parameter(description = "id of the acquisition equipment", required = true) @PathVariable("acquisitionEquipmentId") final Long acquisitionEquipmentId, + @Parameter(description = "acquisition equipment to update", required = true) @RequestBody final AcquisitionEquipment acquisitionEquipment, final BindingResult result) throws RestServiceException { validate(result); @@ -183,7 +183,7 @@ private void checkDataIntegrityException(DataIntegrityViolationException e, Acqu @Override public ResponseEntity> findAcquisitionEquipmentsBySerialNumber( - @Parameter(name = "serial number of the acquisition equipment", required = true) @PathVariable("serialNumber") final String serialNumber) { + @Parameter(description = "serial number of the acquisition equipment", required = true) @PathVariable("serialNumber") final String serialNumber) { List equipments = acquisitionEquipmentService.findAllBySerialNumber(serialNumber); // Remove "unknown" equipment equipments = equipments.stream().filter(equipment -> equipment.getId() != 0).collect(Collectors.toList()); @@ -196,7 +196,7 @@ public ResponseEntity> findAcquisitionEquipmentsBy @Override public ResponseEntity> findAcquisitionEquipmentsOrCreateOneByEquipmentDicom( - @Parameter(name = "equipment dicom to find or create an equipment", required = true) @RequestBody final EquipmentDicom equipmentDicom, + @Parameter(description = "equipment dicom to find or create an equipment", required = true) @RequestBody final EquipmentDicom equipmentDicom, final BindingResult result) { List equipments = acquisitionEquipmentService.findAcquisitionEquipmentsOrCreateOneByEquipmentDicom(equipmentDicom); // Remove "unknown" equipment diff --git a/shanoir-ng-studies/src/main/java/org/shanoir/ng/center/controler/CenterApi.java b/shanoir-ng-studies/src/main/java/org/shanoir/ng/center/controler/CenterApi.java index 4e0f575051..34f59445f7 100644 --- a/shanoir-ng-studies/src/main/java/org/shanoir/ng/center/controler/CenterApi.java +++ b/shanoir-ng-studies/src/main/java/org/shanoir/ng/center/controler/CenterApi.java @@ -47,7 +47,7 @@ public interface CenterApi { @RequestMapping(value = "/{centerId}", produces = { "application/json" }, method = RequestMethod.DELETE) @PreAuthorize("hasAnyRole('ADMIN', 'EXPERT')") ResponseEntity deleteCenter( - @Parameter(name = "id of the center", required = true) @PathVariable("centerId") Long centerId) + @Parameter(description = "id of the center", required = true) @PathVariable("centerId") Long centerId) throws RestServiceException; @Operation(summary = "", description = "If exists, returns the center corresponding to the given id") @@ -59,7 +59,7 @@ ResponseEntity deleteCenter( @RequestMapping(value = "/{centerId}", produces = { "application/json" }, method = RequestMethod.GET) @PreAuthorize("hasAnyRole('ADMIN', 'EXPERT', 'USER')") ResponseEntity findCenterById( - @Parameter(name = "id of the center", required = true) @PathVariable("centerId") Long centerId); + @Parameter(description = "id of the center", required = true) @PathVariable("centerId") Long centerId); @Operation(summary = "", description = "Returns all the centers") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "found centers"), @@ -80,7 +80,7 @@ ResponseEntity findCenterById( @ApiResponse(responseCode = "500", description = "unexpected error") }) @RequestMapping(value = "/study/{studyId}", produces = { "application/json" }, method = RequestMethod.GET) public ResponseEntity> findCentersByStudy ( - @Parameter(name = "id of the study", required = true) @PathVariable("studyId") Long studyId); + @Parameter(description = "id of the study", required = true) @PathVariable("studyId") Long studyId); @Operation(summary = "", description = "Returns id and name for all the centers") @ApiResponses(value = { @@ -101,7 +101,7 @@ public ResponseEntity> findCentersByStudy ( @ApiResponse(responseCode = "500", description = "unexpected error") }) @RequestMapping(value = "/names/{studyId}", produces = { "application/json" }, method = RequestMethod.GET) ResponseEntity> findCentersNames( - @Parameter(name = "id of the study", required = true) @PathVariable("studyId") Long studyId); + @Parameter(description = "id of the study", required = true) @PathVariable("studyId") Long studyId); @Operation(summary = "", description = "Saves a new center") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "created center"), @@ -113,7 +113,7 @@ ResponseEntity> findCentersNames( "application/json" }, method = RequestMethod.POST) @PreAuthorize("hasAnyRole('ADMIN', 'EXPERT')") ResponseEntity saveNewCenter( - @Parameter(name = "center to create", required = true) @RequestBody Center center, BindingResult result) + @Parameter(description = "center to create", required = true) @RequestBody Center center, BindingResult result) throws RestServiceException; @Operation(summary = "", description = "Updates a center") @@ -126,8 +126,8 @@ ResponseEntity saveNewCenter( "application/json" }, method = RequestMethod.PUT) @PreAuthorize("hasAnyRole('ADMIN', 'EXPERT') and @controlerSecurityService.idMatches(#centerId, #center)") ResponseEntity updateCenter( - @Parameter(name = "id of the center", required = true) @PathVariable("centerId") Long centerId, - @Parameter(name = "center to update", required = true) @RequestBody Center center, BindingResult result) + @Parameter(description = "id of the center", required = true) @PathVariable("centerId") Long centerId, + @Parameter(description = "center to update", required = true) @RequestBody Center center, BindingResult result) throws RestServiceException; } diff --git a/shanoir-ng-studies/src/main/java/org/shanoir/ng/center/controler/CenterApiController.java b/shanoir-ng-studies/src/main/java/org/shanoir/ng/center/controler/CenterApiController.java index a4ecf43ee1..cbe921c273 100644 --- a/shanoir-ng-studies/src/main/java/org/shanoir/ng/center/controler/CenterApiController.java +++ b/shanoir-ng-studies/src/main/java/org/shanoir/ng/center/controler/CenterApiController.java @@ -67,7 +67,7 @@ public class CenterApiController implements CenterApi { @Override public ResponseEntity deleteCenter( - @Parameter(name = "id of the center", required = true) @PathVariable("centerId") final Long centerId) + @Parameter(description = "id of the center", required = true) @PathVariable("centerId") final Long centerId) throws RestServiceException { try { if (centerId.equals(0L)) { @@ -86,7 +86,7 @@ public ResponseEntity deleteCenter( @Override public ResponseEntity findCenterById( - @Parameter(name = "id of the center", required = true) @PathVariable("centerId") final Long centerId) { + @Parameter(description = "id of the center", required = true) @PathVariable("centerId") final Long centerId) { final Optional
      center = centerService.findById(centerId); if (center.isEmpty()) { return new ResponseEntity<>(HttpStatus.NOT_FOUND); @@ -107,7 +107,7 @@ public ResponseEntity> findCenters() { @Override public ResponseEntity> findCentersByStudy ( - @Parameter(name = "id of the study", required = true) @PathVariable("studyId") Long studyId) { + @Parameter(description = "id of the study", required = true) @PathVariable("studyId") Long studyId) { final List
      centers = centerService.findByStudy(studyId); if (centers.isEmpty()) { return new ResponseEntity<>(HttpStatus.NO_CONTENT); @@ -129,7 +129,7 @@ public ResponseEntity> findCentersNames() { @Override public ResponseEntity> findCentersNames( - @Parameter(name = "id of the study", required = true) @PathVariable("studyId") Long studyId) { + @Parameter(description = "id of the study", required = true) @PathVariable("studyId") Long studyId) { final List centers = centerService.findIdsAndNames(studyId); if (centers.isEmpty()) { return new ResponseEntity<>(HttpStatus.NO_CONTENT); @@ -139,7 +139,7 @@ public ResponseEntity> findCentersNames( @Override public ResponseEntity saveNewCenter( - @Parameter(name = "the center to create", required = true) @RequestBody @Valid final Center center, + @Parameter(description = "the center to create", required = true) @RequestBody @Valid final Center center, final BindingResult result) throws RestServiceException { forceCentersOfStudyCenterList(center); @@ -153,8 +153,8 @@ public ResponseEntity saveNewCenter( @Override public ResponseEntity updateCenter( - @Parameter(name = "id of the center", required = true) @PathVariable("centerId") final Long centerId, - @Parameter(name = "the center to update", required = true) @RequestBody @Valid final Center center, + @Parameter(description = "id of the center", required = true) @PathVariable("centerId") final Long centerId, + @Parameter(description = "the center to update", required = true) @RequestBody @Valid final Center center, final BindingResult result) throws RestServiceException { try { if (centerId.equals(0L)) { diff --git a/shanoir-ng-studies/src/main/java/org/shanoir/ng/coil/controler/CoilApi.java b/shanoir-ng-studies/src/main/java/org/shanoir/ng/coil/controler/CoilApi.java index fd7bb79878..ed18df5d9a 100644 --- a/shanoir-ng-studies/src/main/java/org/shanoir/ng/coil/controler/CoilApi.java +++ b/shanoir-ng-studies/src/main/java/org/shanoir/ng/coil/controler/CoilApi.java @@ -50,7 +50,7 @@ public interface CoilApi { @DeleteMapping(value = "/{coilId}", produces = { "application/json" }) @PreAuthorize("hasAnyRole('ADMIN', 'EXPERT')") ResponseEntity deleteCoil( - @Parameter(name = "id of the coil", required = true) @PathVariable("coilId") Long coilId) + @Parameter(description = "id of the coil", required = true) @PathVariable("coilId") Long coilId) throws RestServiceException; @Operation(summary = "", description = "If exists, returns the coil corresponding to the given id") @@ -62,7 +62,7 @@ ResponseEntity deleteCoil( @GetMapping(value = "/{coilId}", produces = { "application/json" }) @PreAuthorize("hasAnyRole('ADMIN', 'EXPERT', 'USER')") ResponseEntity findCoilById( - @Parameter(name = "id of the coil", required = true) @PathVariable("coilId") Long coilId); + @Parameter(description = "id of the coil", required = true) @PathVariable("coilId") Long coilId); @Operation(summary = "", description = "Returns all the coils") @ApiResponses(value = { @@ -84,7 +84,7 @@ ResponseEntity findCoilById( @PostMapping(value = "", produces = { "application/json" }, consumes = { "application/json" }) @PreAuthorize("hasAnyRole('ADMIN', 'EXPERT')") - ResponseEntity saveNewCoil(@Parameter(name = "coil to create", required = true) @Valid @RequestBody Coil coil, + ResponseEntity saveNewCoil(@Parameter(description = "coil to create", required = true) @Valid @RequestBody Coil coil, BindingResult result) throws RestServiceException; @Operation(summary = "", description = "Updates a coil") @@ -97,8 +97,8 @@ ResponseEntity saveNewCoil(@Parameter(name = "coil to create", required "application/json" }) @PreAuthorize("hasAnyRole('ADMIN', 'EXPERT') and @controlerSecurityService.idMatches(#coilId, #coil)") ResponseEntity updateCoil( - @Parameter(name = "id of the coil", required = true) @PathVariable("coilId") Long coilId, - @Parameter(name = "coil to update", required = true) @Valid @RequestBody Coil coil, BindingResult result) + @Parameter(description = "id of the coil", required = true) @PathVariable("coilId") Long coilId, + @Parameter(description = "coil to update", required = true) @Valid @RequestBody Coil coil, BindingResult result) throws RestServiceException; } diff --git a/shanoir-ng-studies/src/main/java/org/shanoir/ng/coil/controler/CoilApiController.java b/shanoir-ng-studies/src/main/java/org/shanoir/ng/coil/controler/CoilApiController.java index 391eb87c30..238e225d59 100644 --- a/shanoir-ng-studies/src/main/java/org/shanoir/ng/coil/controler/CoilApiController.java +++ b/shanoir-ng-studies/src/main/java/org/shanoir/ng/coil/controler/CoilApiController.java @@ -54,7 +54,7 @@ public class CoilApiController implements CoilApi { @Override public ResponseEntity deleteCoil( - @Parameter(name = "id of the coil", required = true) @PathVariable("coilId") Long coilId) + @Parameter(description = "id of the coil", required = true) @PathVariable("coilId") Long coilId) throws RestServiceException { try { @@ -69,7 +69,7 @@ public ResponseEntity deleteCoil( @Override public ResponseEntity findCoilById( - @Parameter(name = "id of the coil", required = true) @PathVariable("coilId") Long coilId) { + @Parameter(description = "id of the coil", required = true) @PathVariable("coilId") Long coilId) { final Coil coil = coilService.findById(coilId).orElse(null); if (coil == null) { return new ResponseEntity<>(HttpStatus.NOT_FOUND); @@ -88,7 +88,7 @@ public ResponseEntity> findCoils() { @Override public ResponseEntity saveNewCoil( - @Parameter(name = "coil to create", required = true) @Valid @RequestBody Coil coil, + @Parameter(description = "coil to create", required = true) @Valid @RequestBody Coil coil, final BindingResult result) throws RestServiceException { /* Validation */ @@ -102,8 +102,8 @@ public ResponseEntity saveNewCoil( @Override public ResponseEntity updateCoil( - @Parameter(name = "id of the coil", required = true) @PathVariable("coilId") Long coilId, - @Parameter(name = "coil to update", required = true) @Valid @RequestBody Coil coil, + @Parameter(description = "id of the coil", required = true) @PathVariable("coilId") Long coilId, + @Parameter(description = "coil to update", required = true) @Valid @RequestBody Coil coil, final BindingResult result) throws RestServiceException { validate(result); diff --git a/shanoir-ng-studies/src/main/java/org/shanoir/ng/configuration/security/BearerAuth.java b/shanoir-ng-studies/src/main/java/org/shanoir/ng/configuration/security/BearerAuth.java new file mode 100644 index 0000000000..e7fe92f916 --- /dev/null +++ b/shanoir-ng-studies/src/main/java/org/shanoir/ng/configuration/security/BearerAuth.java @@ -0,0 +1,13 @@ +package org.shanoir.ng.configuration.security; + +import io.swagger.v3.oas.annotations.enums.SecuritySchemeType; +import io.swagger.v3.oas.annotations.security.SecurityScheme; + +@SecurityScheme( + type = SecuritySchemeType.HTTP, + scheme = "bearer", + bearerFormat = "JWT", + name = "BearerAuth", + description = "Authentication using 'Bearer' JWT token") +public class BearerAuth { +} diff --git a/shanoir-ng-studies/src/main/java/org/shanoir/ng/configuration/security/OAuth2Auth.java b/shanoir-ng-studies/src/main/java/org/shanoir/ng/configuration/security/OAuth2Auth.java new file mode 100644 index 0000000000..0eb0a529df --- /dev/null +++ b/shanoir-ng-studies/src/main/java/org/shanoir/ng/configuration/security/OAuth2Auth.java @@ -0,0 +1,17 @@ +package org.shanoir.ng.configuration.security; + +import io.swagger.v3.oas.annotations.enums.SecuritySchemeType; +import io.swagger.v3.oas.annotations.security.OAuthFlow; +import io.swagger.v3.oas.annotations.security.OAuthFlows; +import io.swagger.v3.oas.annotations.security.SecurityScheme; + +@SecurityScheme( + type = SecuritySchemeType.OAUTH2, + name = "OAuth2Auth", + flows = @OAuthFlows(password = @OAuthFlow( + authorizationUrl = "${SHANOIR_URL_SCHEME}://${SHANOIR_URL_HOST}/auth/realms/shanoir-ng/protocol/openid-connect/auth/", + tokenUrl = "${SHANOIR_URL_SCHEME}://${SHANOIR_URL_HOST}/auth/realms/shanoir-ng/protocol/openid-connect/token", + refreshUrl = "${SHANOIR_URL_SCHEME}://${SHANOIR_URL_HOST}/auth/realms/shanoir-ng/protocol/openid-connect/token" + ))) +public class OAuth2Auth { +} diff --git a/shanoir-ng-studies/src/main/java/org/shanoir/ng/manufacturermodel/controler/ManufacturerApi.java b/shanoir-ng-studies/src/main/java/org/shanoir/ng/manufacturermodel/controler/ManufacturerApi.java index 3e7fee01b2..4e1cd7be6a 100644 --- a/shanoir-ng-studies/src/main/java/org/shanoir/ng/manufacturermodel/controler/ManufacturerApi.java +++ b/shanoir-ng-studies/src/main/java/org/shanoir/ng/manufacturermodel/controler/ManufacturerApi.java @@ -48,7 +48,7 @@ public interface ManufacturerApi { @GetMapping(value = "/{manufacturerId}", produces = { "application/json" }) @PreAuthorize("hasAnyRole('ADMIN', 'EXPERT', 'USER')") ResponseEntity findManufacturerById( - @Parameter(name = "id of the manufacturer", required = true) @PathVariable("manufacturerId") Long manufacturerId); + @Parameter(description = "id of the manufacturer", required = true) @PathVariable("manufacturerId") Long manufacturerId); @Operation(summary = "", description = "Returns all the manufacturers") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "found manufacturers"), @@ -70,7 +70,7 @@ ResponseEntity findManufacturerById( "application/json" }) @PreAuthorize("hasAnyRole('ADMIN', 'EXPERT')") ResponseEntity saveNewManufacturer( - @Parameter(name = "manufacturer to create", required = true) @RequestBody Manufacturer manufacturer, + @Parameter(description = "manufacturer to create", required = true) @RequestBody Manufacturer manufacturer, final BindingResult result) throws RestServiceException; @Operation(summary = "", description = "Updates a manufacturer") @@ -84,8 +84,8 @@ ResponseEntity saveNewManufacturer( "application/json" }) @PreAuthorize("hasAnyRole('ADMIN', 'EXPERT') and @controlerSecurityService.idMatches(#manufacturerId, #manufacturer)") ResponseEntity updateManufacturer( - @Parameter(name = "id of the manufacturer", required = true) @PathVariable("manufacturerId") Long manufacturerId, - @Parameter(name = "manufacturer to update", required = true) @RequestBody Manufacturer manufacturer, + @Parameter(description = "id of the manufacturer", required = true) @PathVariable("manufacturerId") Long manufacturerId, + @Parameter(description = "manufacturer to update", required = true) @RequestBody Manufacturer manufacturer, BindingResult result) throws RestServiceException; @Operation(summary = "", description = "Deletes a manufacturer") @@ -97,7 +97,7 @@ ResponseEntity updateManufacturer( @DeleteMapping(value = "/{manufacturerId}", produces = { "application/json" }) @PreAuthorize("hasAnyRole('ADMIN', 'EXPERT')") ResponseEntity deleteManufacturer( - @Parameter(name = "id of the manufacturer", required = true) @PathVariable("manufacturerId") Long manufacturerId) + @Parameter(description = "id of the manufacturer", required = true) @PathVariable("manufacturerId") Long manufacturerId) throws RestServiceException; } diff --git a/shanoir-ng-studies/src/main/java/org/shanoir/ng/manufacturermodel/controler/ManufacturerModelApi.java b/shanoir-ng-studies/src/main/java/org/shanoir/ng/manufacturermodel/controler/ManufacturerModelApi.java index b2458ede6f..3607303415 100644 --- a/shanoir-ng-studies/src/main/java/org/shanoir/ng/manufacturermodel/controler/ManufacturerModelApi.java +++ b/shanoir-ng-studies/src/main/java/org/shanoir/ng/manufacturermodel/controler/ManufacturerModelApi.java @@ -44,7 +44,7 @@ public interface ManufacturerModelApi { @GetMapping(value = "/{manufacturerModelId}", produces = { "application/json" }) @PreAuthorize("hasAnyRole('ADMIN', 'EXPERT', 'USER')") ResponseEntity findManufacturerModelById( - @Parameter(name = "id of the manufacturer model", required = true) @PathVariable("manufacturerModelId") Long manufacturerModelId); + @Parameter(description = "id of the manufacturer model", required = true) @PathVariable("manufacturerModelId") Long manufacturerModelId); @Operation(summary = "", description = "Returns id and name of all the manufacturer models") @ApiResponses(value = { @@ -66,7 +66,7 @@ ResponseEntity findManufacturerModelById( @ApiResponse(responseCode = "500", description = "unexpected error") }) @GetMapping(value = "/centerManuModelsNames/{centerId}", produces = { "application/json" }) @PreAuthorize("hasAnyRole('ADMIN', 'EXPERT', 'USER')") - ResponseEntity> findCenterManufacturerModelsNames(@Parameter(name = "id of the center", required = true) @PathVariable("centerId") Long centerId); + ResponseEntity> findCenterManufacturerModelsNames(@Parameter(description = "id of the center", required = true) @PathVariable("centerId") Long centerId); @Operation(summary = "", description = "Returns all the manufacturer models") @ApiResponses(value = { @@ -90,7 +90,7 @@ ResponseEntity findManufacturerModelById( "application/json" }) @PreAuthorize("hasAnyRole('ADMIN', 'EXPERT')") ResponseEntity saveNewManufacturerModel( - @Parameter(name = "manufacturer model to create", required = true) @RequestBody ManufacturerModel manufacturerModel, + @Parameter(description = "manufacturer model to create", required = true) @RequestBody ManufacturerModel manufacturerModel, final BindingResult result) throws RestServiceException; @Operation(summary = "", description = "Updates a manufacturer model") @@ -104,7 +104,7 @@ ResponseEntity saveNewManufacturerModel( "application/json" }) @PreAuthorize("hasAnyRole('ADMIN', 'EXPERT') and @controlerSecurityService.idMatches(#manufacturerModelId, #manufacturerModel)") ResponseEntity updateManufacturerModel( - @Parameter(name = "id of the manufacturer model", required = true) @PathVariable("manufacturerModelId") Long manufacturerModelId, - @Parameter(name = "manufacturer model to update", required = true) @RequestBody ManufacturerModel manufacturerModel, + @Parameter(description = "id of the manufacturer model", required = true) @PathVariable("manufacturerModelId") Long manufacturerModelId, + @Parameter(description = "manufacturer model to update", required = true) @RequestBody ManufacturerModel manufacturerModel, final BindingResult result) throws RestServiceException; } diff --git a/shanoir-ng-studies/src/main/java/org/shanoir/ng/shared/common/CommonApi.java b/shanoir-ng-studies/src/main/java/org/shanoir/ng/shared/common/CommonApi.java index 8bfedfe8cc..140d6473eb 100644 --- a/shanoir-ng-studies/src/main/java/org/shanoir/ng/shared/common/CommonApi.java +++ b/shanoir-ng-studies/src/main/java/org/shanoir/ng/shared/common/CommonApi.java @@ -37,6 +37,6 @@ public interface CommonApi { @PostMapping(value = "", produces = { "application/json" }, consumes = { "application/json" }) ResponseEntity findStudySubjectCenterNamesByIds( - @Parameter(name = "study to update", required = true) @RequestBody CommonIdsDTO commonIdDTO); + @Parameter(description = "study to update", required = true) @RequestBody CommonIdsDTO commonIdDTO); } diff --git a/shanoir-ng-studies/src/main/java/org/shanoir/ng/study/controler/StudyApi.java b/shanoir-ng-studies/src/main/java/org/shanoir/ng/study/controler/StudyApi.java index f2d4b710a9..66c39704e2 100644 --- a/shanoir-ng-studies/src/main/java/org/shanoir/ng/study/controler/StudyApi.java +++ b/shanoir-ng-studies/src/main/java/org/shanoir/ng/study/controler/StudyApi.java @@ -55,7 +55,7 @@ public interface StudyApi { @RequestMapping(value = "/{studyId}", produces = { "application/json" }, method = RequestMethod.DELETE) @PreAuthorize("hasAnyRole('ADMIN', 'EXPERT') and @studySecurityService.hasRightOnStudy(#studyId, 'CAN_ADMINISTRATE')") ResponseEntity deleteStudy( - @Parameter(name = "id of the study", required = true) @PathVariable("studyId") Long studyId); + @Parameter(description = "id of the study", required = true) @PathVariable("studyId") Long studyId); @Operation(summary = "", description = "If exists, returns the studies that the user is allowed to see") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "found studies"), @@ -111,8 +111,8 @@ ResponseEntity deleteStudy( @PreAuthorize("hasAnyRole('ADMIN', 'EXPERT', 'USER')") @PostAuthorize("@studySecurityService.hasRightOnTrustedStudyDTO(returnObject.getBody(), 'CAN_SEE_ALL')") ResponseEntity findStudyById( - @Parameter(name = "id of the study", required = true) @PathVariable("studyId") Long studyId, - @Parameter(name = "Fetch detailed storage volume of study") + @Parameter(description = "id of the study", required = true) @PathVariable("studyId") Long studyId, + @Parameter(description = "Fetch detailed storage volume of study") @Valid @RequestParam(value = "withStorageVolume", required = false, defaultValue="false") boolean withStorageVolume); @@ -126,7 +126,7 @@ ResponseEntity findStudyById( "application/json" }, method = RequestMethod.POST) @PreAuthorize("hasAnyRole('ADMIN', 'EXPERT')") ResponseEntity saveNewStudy( - @Parameter(name = "study to create", required = true) @RequestBody Study study, BindingResult result) + @Parameter(description = "study to create", required = true) @RequestBody Study study, BindingResult result) throws RestServiceException; @Operation(summary = "", description = "Copy a list of dataset to a study") @@ -139,13 +139,13 @@ ResponseEntity saveNewStudy( @RequestMapping(value = "/copyDatasets", produces = { "application/json" }, method = RequestMethod.POST) @PreAuthorize("hasAnyRole('ADMIN', 'EXPERT')") ResponseEntity copyDatasetsToStudy( - @Parameter(name = "Dataset ids to copy", required = true) + @Parameter(description = "Dataset ids to copy", required = true) @RequestParam(value = "datasetIds", required = true) List datasetIds, - @Parameter(name = "Study id to copy in", required = true) + @Parameter(description = "Study id to copy in", required = true) @RequestParam(value = "studyId", required = true) String studyId, - @Parameter(name = "center id of datasets", required = true) + @Parameter(description = "center id of datasets", required = true) @RequestParam(value = "centerIds", required = true) List centerIds, - @Parameter(name = "subject id of datasets", required = true) + @Parameter(description = "subject id of datasets", required = true) @RequestParam(value = "subjectIds", required = true) List subjectIdStudyId); @@ -170,7 +170,7 @@ ResponseEntity copyDatasetsToStudy( @PostMapping(value = "/detailedStorageVolume", produces = { "application/json" }) @PreAuthorize("hasRole('ADMIN') or (hasAnyRole('EXPERT', 'USER') and @studySecurityService.filterVolumesHasRightOnStudies(#studyIds, 'CAN_SEE_ALL'))") ResponseEntity> getDetailedStorageVolumeByStudy( - @Parameter(name = "study ids") @RequestParam List studyIds + @Parameter(description = "study ids") @RequestParam List studyIds ) throws RestServiceException; @@ -184,8 +184,8 @@ ResponseEntity> getDetailedStorageVolumeByStudy "application/json" }, method = RequestMethod.PUT) @PreAuthorize("hasAnyRole('ADMIN', 'EXPERT') and @controlerSecurityService.idMatches(#studyId, #study) and @studySecurityService.hasRightOnStudy(#studyId, 'CAN_ADMINISTRATE')") ResponseEntity updateStudy( - @Parameter(name = "id of the study", required = true) @PathVariable("studyId") Long studyId, - @Parameter(name = "study to update", required = true) @RequestBody Study study, BindingResult result) + @Parameter(description = "id of the study", required = true) @PathVariable("studyId") Long studyId, + @Parameter(description = "study to update", required = true) @RequestBody Study study, BindingResult result) throws RestServiceException; @Operation(summary = "", description = "Get my rights on this study") @@ -197,7 +197,7 @@ ResponseEntity updateStudy( "application/json" }, method = RequestMethod.GET) @PreAuthorize("hasAnyRole('ADMIN', 'EXPERT', 'USER')") ResponseEntity> rights( - @Parameter(name = "id of the study", required = true) @PathVariable("studyId") Long studyId) + @Parameter(description = "id of the study", required = true) @PathVariable("studyId") Long studyId) throws RestServiceException; @Operation(summary = "", description = "Get my rights") @@ -231,8 +231,8 @@ ResponseEntity> rights( "multipart/form-data" }) @PreAuthorize("hasAnyRole('ADMIN', 'EXPERT') and @studySecurityService.hasRightOnStudy(#studyId, 'CAN_ADMINISTRATE')") ResponseEntity uploadProtocolFile( - @Parameter(name = "id of the study", required = true) @PathVariable("studyId") Long studyId, - @Parameter(name = "file to upload", required = true) @Valid @RequestBody MultipartFile file) + @Parameter(description = "id of the study", required = true) @PathVariable("studyId") Long studyId, + @Parameter(description = "file to upload", required = true) @Valid @RequestBody MultipartFile file) throws RestServiceException; @Operation(summary = "", description = "Download protocol file from a study") @@ -245,8 +245,8 @@ ResponseEntity uploadProtocolFile( @GetMapping(value = "protocol-file-download/{studyId}/{fileName:.+}/") @PreAuthorize("hasRole('ADMIN') or (hasAnyRole('EXPERT', 'USER') and @studySecurityService.hasRightOnStudy(#studyId, 'CAN_DOWNLOAD'))") void downloadProtocolFile( - @Parameter(name = "id of the study", required = true) @PathVariable("studyId") Long studyId, - @Parameter(name = "file to download", required = true) @PathVariable("fileName") String fileName, HttpServletResponse response) throws RestServiceException, IOException; + @Parameter(description = "id of the study", required = true) @PathVariable("studyId") Long studyId, + @Parameter(description = "file to download", required = true) @PathVariable("fileName") String fileName, HttpServletResponse response) throws RestServiceException, IOException; @Operation(summary = "", description = "If one or more exist, return a list of data user agreements (DUAs) waiting for the given user id") @ApiResponses(value = { @@ -270,7 +270,7 @@ ResponseEntity> getDataUserAgreements() @PutMapping(value = "/dua/{duaId}", produces = { "application/json" }, consumes = {"application/json" }) @PreAuthorize("hasAnyRole('ADMIN', 'EXPERT', 'USER') and @studySecurityService.checkUserOnDUA(#duaId)") ResponseEntity acceptDataUserAgreement( - @Parameter(name = "id of the dua", required = true) @PathVariable("duaId") Long duaId) + @Parameter(description = "id of the dua", required = true) @PathVariable("duaId") Long duaId) throws RestServiceException, MicroServiceCommunicationException; @Operation(summary = "", description = "Returns the data user agreement (DUA) of a specific study") @@ -283,7 +283,7 @@ ResponseEntity acceptDataUserAgreement( @GetMapping(value = "/dua/study/{studyId}", produces = { "application/json" }, consumes = {"application/json" }) @PreAuthorize("hasAnyRole('ADMIN', 'EXPERT', 'USER')") ResponseEntity hasDUAByStudyId( - @Parameter(name = "id of the study", required = true) @PathVariable("studyId") Long studyId) + @Parameter(description = "id of the study", required = true) @PathVariable("studyId") Long studyId) throws RestServiceException, ShanoirException; @@ -298,8 +298,8 @@ ResponseEntity hasDUAByStudyId( "multipart/form-data" }) @PreAuthorize("hasAnyRole('ADMIN', 'EXPERT') and @studySecurityService.hasRightOnStudy(#studyId, 'CAN_ADMINISTRATE')") ResponseEntity uploadDataUserAgreement( - @Parameter(name = "id of the study", required = true) @PathVariable("studyId") Long studyId, - @Parameter(name = "file to upload", required = true) @Valid @RequestBody MultipartFile file) + @Parameter(description = "id of the study", required = true) @PathVariable("studyId") Long studyId, + @Parameter(description = "file to upload", required = true) @Valid @RequestBody MultipartFile file) throws RestServiceException; @Operation(summary = "", description = "Download DUA of a study") @@ -312,21 +312,21 @@ ResponseEntity uploadDataUserAgreement( @GetMapping(value = "dua-download/{studyId}/{fileName:.+}/") @PreAuthorize("hasAnyRole('ADMIN', 'EXPERT', 'USER')") void downloadDataUserAgreement( - @Parameter(name = "id of the study", required = true) @PathVariable("studyId") Long studyId, - @Parameter(name = "file to download", required = true) @PathVariable("fileName") String fileName, HttpServletResponse response) throws RestServiceException, IOException; + @Parameter(description = "id of the study", required = true) @PathVariable("studyId") Long studyId, + @Parameter(description = "file to download", required = true) @PathVariable("fileName") String fileName, HttpServletResponse response) throws RestServiceException, IOException; @Operation(summary = "", description = "Deletes the user of a study") - @ApiResponses(value = { @ApiResponse(responseCode = " 204", description = "user removed from study"), - @ApiResponse(responseCode = " 401", description = "unauthorized"), - @ApiResponse(responseCode = " 403", description = "forbidden"), - @ApiResponse(responseCode = " 404", description = "no study or user found"), - @ApiResponse(responseCode = " 500", description = "unexpected error") }) + @ApiResponses(value = { @ApiResponse(responseCode = "204", description = "user removed from study"), + @ApiResponse(responseCode = "401", description = "unauthorized"), + @ApiResponse(responseCode = "403", description = "forbidden"), + @ApiResponse(responseCode = "404", description = "no study or user found"), + @ApiResponse(responseCode = "500", description = "unexpected error") }) @RequestMapping(value = "studyUser/{studyId}/{userId}", produces = { "application/json" }, method = RequestMethod.DELETE) @PreAuthorize("hasAnyRole('ADMIN', 'EXPERT') and @studySecurityService.hasRightOnStudy(#studyId, 'CAN_ADMINISTRATE')") ResponseEntity deleteStudyUser( - @Parameter(name = "id of the study", required = true) @PathVariable("studyId") Long studyId, - @Parameter(name = "id of the user", required = true) @PathVariable("userId") Long userId) + @Parameter(description = "id of the study", required = true) @PathVariable("studyId") Long studyId, + @Parameter(description = "id of the user", required = true) @PathVariable("userId") Long userId) throws IOException; @Operation(summary = "", description = "If exists, returns the studies that are publicly available for a given user") @@ -358,6 +358,6 @@ ResponseEntity deleteStudyUser( @GetMapping(value = "/statistics/{studyId}", produces = { "application/json" }) @PreAuthorize("hasRole('ADMIN') or hasRole('EXPERT')") ResponseEntity> getStudyStatistics( - @Parameter(name = "id of the study", required = true) @PathVariable("studyId") Long studyId) throws RestServiceException, IOException; + @Parameter(description = "id of the study", required = true) @PathVariable("studyId") Long studyId) throws RestServiceException, IOException; } \ No newline at end of file diff --git a/shanoir-ng-studies/src/main/java/org/shanoir/ng/study/controler/StudyApiController.java b/shanoir-ng-studies/src/main/java/org/shanoir/ng/study/controler/StudyApiController.java index e5f4e8bcfb..919fef2d64 100644 --- a/shanoir-ng-studies/src/main/java/org/shanoir/ng/study/controler/StudyApiController.java +++ b/shanoir-ng-studies/src/main/java/org/shanoir/ng/study/controler/StudyApiController.java @@ -233,13 +233,13 @@ private void addCurrentUserAsStudyUserIfEmptyStudyUsers(final Study study) { @Override public ResponseEntity copyDatasetsToStudy( - @Parameter(name = "Dataset ids to copy", required = true) + @Parameter(description = "Dataset ids to copy", required = true) @RequestParam(value = "datasetIds", required = true) List datasetIds, - @Parameter(name = "Study id to copy in", required = true) + @Parameter(description = "Study id to copy in", required = true) @RequestParam(value = "studyId", required = true) String studyIdAsStr, - @Parameter(name = "center id of datasets", required = true) + @Parameter(description = "center id of datasets", required = true) @RequestParam(value = "centerIds", required = true) List centerIds, - @Parameter(name = "subject id of datasets", required = true) + @Parameter(description = "subject id of datasets", required = true) @RequestParam(value = "subjectIdStudyId", required = true) List subjectIdStudyId) { String res = null; @@ -321,8 +321,8 @@ public ResponseEntity hasOneStudyToImport() throws RestServiceException @Override public void downloadProtocolFile( - @Parameter(name = "id of the study", required = true) @PathVariable("studyId") Long studyId, - @Parameter(name = "file to download", required = true) @PathVariable("fileName") String fileName, + @Parameter(description = "id of the study", required = true) @PathVariable("studyId") Long studyId, + @Parameter(description = "file to download", required = true) @PathVariable("fileName") String fileName, HttpServletResponse response) throws RestServiceException, IOException { String filePath = studyService.getStudyFilePath(studyId, fileName); LOG.info("Retrieving file : {}", filePath); @@ -342,8 +342,8 @@ public void downloadProtocolFile( @Override public ResponseEntity uploadProtocolFile( - @Parameter(name = "id of the study", required = true) @PathVariable("studyId") Long studyId, - @Parameter(name = "file to upload", required = true) @Valid @RequestBody MultipartFile file) + @Parameter(description = "id of the study", required = true) @PathVariable("studyId") Long studyId, + @Parameter(description = "file to upload", required = true) @Valid @RequestBody MultipartFile file) throws RestServiceException { try { String parentDir = dataDir + "/study-" + studyId; @@ -393,7 +393,7 @@ public ResponseEntity> getDataUserAgreements() throws Re @Override public ResponseEntity hasDUAByStudyId( - @Parameter(name = "id of the study", required = true) @PathVariable("studyId") Long studyId) throws ShanoirException { + @Parameter(description = "id of the study", required = true) @PathVariable("studyId") Long studyId) throws ShanoirException { DataUserAgreement dua = this.dataUserAgreementService.findDUAByUserIdAndStudyId(KeycloakUtil.getTokenUserId(), studyId); @@ -402,7 +402,7 @@ public ResponseEntity hasDUAByStudyId( @Override public ResponseEntity acceptDataUserAgreement( - @Parameter(name = "id of the dua", required = true) @PathVariable("duaId") Long duaId) + @Parameter(description = "id of the dua", required = true) @PathVariable("duaId") Long duaId) throws RestServiceException, MicroServiceCommunicationException { try { this.dataUserAgreementService.acceptDataUserAgreement(duaId); @@ -415,8 +415,8 @@ public ResponseEntity acceptDataUserAgreement( @Override public ResponseEntity uploadDataUserAgreement( - @Parameter(name = "id of the study", required = true) @PathVariable("studyId") Long studyId, - @Parameter(name = "dua to upload", required = true) @Valid @RequestBody MultipartFile file) throws RestServiceException { + @Parameter(description = "id of the study", required = true) @PathVariable("studyId") Long studyId, + @Parameter(description = "dua to upload", required = true) @Valid @RequestBody MultipartFile file) throws RestServiceException { try { if (!file.getOriginalFilename().endsWith(PDF_EXTENSION) || file.getSize() > 50000000) { LOG.error("Could not upload the file: {}", file.getOriginalFilename()); @@ -442,8 +442,8 @@ public ResponseEntity uploadDataUserAgreement( @Override public void downloadDataUserAgreement( - @Parameter(name = "id of the study", required = true) @PathVariable("studyId") Long studyId, - @Parameter(name = "file to download", required = true) @PathVariable("fileName") String fileName, HttpServletResponse response) throws RestServiceException, IOException { + @Parameter(description = "id of the study", required = true) @PathVariable("studyId") Long studyId, + @Parameter(description = "file to download", required = true) @PathVariable("fileName") String fileName, HttpServletResponse response) throws RestServiceException, IOException { String filePath = studyService.getStudyFilePath(studyId, fileName); LOG.info("Retrieving file : {}", filePath); File fileToDownLoad = new File(filePath); @@ -461,8 +461,8 @@ public void downloadDataUserAgreement( @Override public ResponseEntity deleteStudyUser ( - @Parameter(name = "id of the study", required = true) @PathVariable("studyId") Long studyId, - @Parameter(name = "id of the userId", required = true) @PathVariable("userId") Long userId) throws IOException { + @Parameter(description = "id of the study", required = true) @PathVariable("studyId") Long studyId, + @Parameter(description = "id of the userId", required = true) @PathVariable("userId") Long userId) throws IOException { studyService.removeStudyUserFromStudy(studyId, userId); List surList = studyUserService.getRightsForStudy(studyId); @@ -537,7 +537,7 @@ private List filterStudies(List studies, Long tokenUserId) { } @Override - public ResponseEntity> getStudyStatistics(@Parameter(name = "id of the study", required = true) @PathVariable("studyId") Long studyId) + public ResponseEntity> getStudyStatistics(@Parameter(description = "id of the study", required = true) @PathVariable("studyId") Long studyId) throws RestServiceException, IOException { try { List statistics = studyService.queryStudyStatistics(studyId); diff --git a/shanoir-ng-studies/src/main/java/org/shanoir/ng/subject/controler/SubjectApi.java b/shanoir-ng-studies/src/main/java/org/shanoir/ng/subject/controler/SubjectApi.java index fb85dd780f..cc90cba1a9 100644 --- a/shanoir-ng-studies/src/main/java/org/shanoir/ng/subject/controler/SubjectApi.java +++ b/shanoir-ng-studies/src/main/java/org/shanoir/ng/subject/controler/SubjectApi.java @@ -57,7 +57,7 @@ public interface SubjectApi { @DeleteMapping(value = "/{subjectId}", produces = { "application/json" }) @PreAuthorize("hasRole('ADMIN') or (hasRole('EXPERT') and @studySecurityService.hasRightOnSubjectForEveryStudy(#subjectId, 'CAN_ADMINISTRATE'))") ResponseEntity deleteSubject( - @Parameter(name = "id of the subject", required = true) @PathVariable("subjectId") Long subjectId); + @Parameter(description = "id of the subject", required = true) @PathVariable("subjectId") Long subjectId); @Operation(summary = "", description = "Returns all the subjects") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "found subjects"), @@ -69,9 +69,9 @@ ResponseEntity deleteSubject( @PreAuthorize("hasAnyRole('ADMIN', 'EXPERT', 'USER')") @PostAuthorize("hasRole('ADMIN') or @studySecurityService.filterSubjectDTOsHasRightInOneStudy(returnObject.getBody(), 'CAN_SEE_ALL')") ResponseEntity> findSubjects( - @Parameter(name = "Include preclinical subject") @Valid + @Parameter(description = "Include preclinical subject") @Valid @RequestParam(value = "preclinical", required = false, defaultValue = "true") boolean preclinical, - @Parameter(name = "Include non-preclinical subject") @Valid + @Parameter(description = "Include non-preclinical subject") @Valid @RequestParam(value = "clinical", required = false, defaultValue = "true") boolean clinical); @Operation(summary = "", description = "Returns the clinical subjects as Pageable with corresponding name") @@ -116,7 +116,7 @@ ResponseEntity> findSubjects( @PreAuthorize("hasAnyRole('ADMIN', 'EXPERT', 'USER')") @PostAuthorize("hasRole('ADMIN') or @studySecurityService.hasRightOnSubjectForEveryStudies(returnObject.getBody(), 'CAN_SEE_ALL')") ResponseEntity findSubjectById( - @Parameter(name = "id of the subject", required = true) @PathVariable("subjectId") Long subjectId); + @Parameter(description = "id of the subject", required = true) @PathVariable("subjectId") Long subjectId); // Attention: this method is used by ShanoirUploader!!! @Operation(summary = "", description = "Saves a new subject") @@ -129,8 +129,8 @@ ResponseEntity findSubjectById( "application/json" }) @PreAuthorize("hasRole('ADMIN') or (hasAnyRole('EXPERT', 'USER') and @studySecurityService.checkRightOnEverySubjectStudyList(#subject.getSubjectStudyList(), 'CAN_IMPORT'))") ResponseEntity saveNewSubject( - @Parameter(name = "subject to create", required = true) @RequestBody Subject subject, - @Parameter(name = "request param centerId as flag for auto-increment common name", required = false) @RequestParam(required = false) Long centerId, + @Parameter(description = "subject to create", required = true) @RequestBody Subject subject, + @Parameter(description = "request param centerId as flag for auto-increment common name", required = false) @RequestParam(required = false) Long centerId, final BindingResult result) throws RestServiceException; // Attention: this method is used by ShanoirUploader!!! @@ -144,8 +144,8 @@ ResponseEntity saveNewSubject( "application/json" }) @PreAuthorize("hasRole('ADMIN') or (hasAnyRole('EXPERT', 'USER') and @studySecurityService.checkRightOnEverySubjectStudyList(#subject.getSubjectStudyList(), 'CAN_IMPORT'))") ResponseEntity updateSubject( - @Parameter(name = "id of the subject", required = true) @PathVariable("subjectId") Long subjectId, - @Parameter(name = "subject to update", required = true) @RequestBody Subject subject, + @Parameter(description = "id of the subject", required = true) @PathVariable("subjectId") Long subjectId, + @Parameter(description = "subject to update", required = true) @RequestBody Subject subject, final BindingResult result) throws RestServiceException, MicroServiceCommunicationException; @Operation(summary = "", description = "If exists, returns the subjects of a study") @@ -159,8 +159,8 @@ ResponseEntity updateSubject( @PreAuthorize("hasAnyRole('ADMIN', 'EXPERT', 'USER')") @PostAuthorize("hasRole('ADMIN') or @studySecurityService.filterSimpleSubjectDTOsHasRightInOneStudy(returnObject.getBody(), 'CAN_SEE_ALL')") ResponseEntity> findSubjectsByStudyId( - @Parameter(name = "id of the study", required = true) @PathVariable("studyId") Long studyId, - @Parameter(name = "preclinical", required = false) @RequestParam(value="preclinical", required = false) String preclinical); + @Parameter(description = "id of the study", required = true) @PathVariable("studyId") Long studyId, + @Parameter(description = "preclinical", required = false) @RequestParam(value="preclinical", required = false) String preclinical); @Operation(summary = "", description = "If exists, returns the subject corresponding to the given identifier") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "found subject"), @@ -173,6 +173,6 @@ ResponseEntity> findSubjectsByStudyId( @PreAuthorize("hasAnyRole('ADMIN', 'EXPERT', 'USER')") @PostAuthorize("hasRole('ADMIN') or @studySecurityService.filterSubjectDTOsHasRightInOneStudy(returnObject.getBody(), 'CAN_SEE_ALL')") ResponseEntity findSubjectByIdentifier( - @Parameter(name = "identifier of the subject", required = true) @PathVariable("subjectIdentifier") String subjectIdentifier); + @Parameter(description = "identifier of the subject", required = true) @PathVariable("subjectIdentifier") String subjectIdentifier); } diff --git a/shanoir-ng-studies/src/main/java/org/shanoir/ng/subject/controler/SubjectApiController.java b/shanoir-ng-studies/src/main/java/org/shanoir/ng/subject/controler/SubjectApiController.java index 624428c796..67f8f79e8c 100644 --- a/shanoir-ng-studies/src/main/java/org/shanoir/ng/subject/controler/SubjectApiController.java +++ b/shanoir-ng-studies/src/main/java/org/shanoir/ng/subject/controler/SubjectApiController.java @@ -72,7 +72,7 @@ public class SubjectApiController implements SubjectApi { @Override public ResponseEntity deleteSubject( - @Parameter(name = "id of the subject", required = true) @PathVariable("subjectId") Long subjectId) { + @Parameter(description = "id of the subject", required = true) @PathVariable("subjectId") Long subjectId) { try { // Delete all associated bids folders subjectService.deleteById(subjectId); @@ -86,7 +86,7 @@ public ResponseEntity deleteSubject( @Override public ResponseEntity findSubjectById( - @Parameter(name = "id of the subject", required = true) @PathVariable("subjectId") Long subjectId) { + @Parameter(description = "id of the subject", required = true) @PathVariable("subjectId") Long subjectId) { final Subject subject = subjectService.findById(subjectId); if (subject == null) { return new ResponseEntity<>(HttpStatus.NOT_FOUND); @@ -154,8 +154,8 @@ public ResponseEntity saveNewSubject( // Attention: this method is used by ShanoirUploader!!! @Override public ResponseEntity updateSubject( - @Parameter(name = "id of the subject", required = true) @PathVariable("subjectId") Long subjectId, - @Parameter(name = "subject to update", required = true) @RequestBody Subject subject, + @Parameter(description = "id of the subject", required = true) @PathVariable("subjectId") Long subjectId, + @Parameter(description = "subject to update", required = true) @RequestBody Subject subject, final BindingResult result) throws RestServiceException, MicroServiceCommunicationException { validate(subject, result); try { @@ -172,8 +172,8 @@ public ResponseEntity updateSubject( @Override public ResponseEntity> findSubjectsByStudyId( - @Parameter(name = "id of the study", required = true) @PathVariable("studyId") Long studyId, - @Parameter(name="preclinical", required = false) @RequestParam(value="preclinical", required = false) String preclinical) { + @Parameter(description = "id of the study", required = true) @PathVariable("studyId") Long studyId, + @Parameter(description="preclinical", required = false) @RequestParam(value="preclinical", required = false) String preclinical) { final List simpleSubjectDTOList; if ("null".equals(preclinical)) { simpleSubjectDTOList = subjectService.findAllSubjectsOfStudy(studyId); @@ -196,7 +196,7 @@ public int compare(SimpleSubjectDTO o1, SimpleSubjectDTO o2) { @Override public ResponseEntity findSubjectByIdentifier( - @Parameter(name = "identifier of the subject", required = true) @PathVariable("subjectIdentifier") String subjectIdentifier) { + @Parameter(description = "identifier of the subject", required = true) @PathVariable("subjectIdentifier") String subjectIdentifier) { final Subject subject = subjectService.findByIdentifier(subjectIdentifier); if (subject == null) { return new ResponseEntity<>(HttpStatus.NO_CONTENT); diff --git a/shanoir-ng-studies/src/main/java/org/shanoir/ng/subjectstudy/controler/SubjectStudyApi.java b/shanoir-ng-studies/src/main/java/org/shanoir/ng/subjectstudy/controler/SubjectStudyApi.java index 91d3073d00..e70c0aaf96 100644 --- a/shanoir-ng-studies/src/main/java/org/shanoir/ng/subjectstudy/controler/SubjectStudyApi.java +++ b/shanoir-ng-studies/src/main/java/org/shanoir/ng/subjectstudy/controler/SubjectStudyApi.java @@ -55,7 +55,7 @@ public interface SubjectStudyApi { + " or @studySecurityService.hasRightOnStudy(#subjectStudy.getStudy(), 'CAN_ADMINISTRATE') )" + " )) and @controlerSecurityService.idMatches(#subjectStudyId, #subjectStudy)") ResponseEntity updateSubjectStudy( - @Parameter(name = "id of the subject study", required = true) @PathVariable("subjectStudyId") Long subjectStudyId, - @Parameter(name = "subject study to update", required = true) @RequestBody SubjectStudy subjectStudy, + @Parameter(description = "id of the subject study", required = true) @PathVariable("subjectStudyId") Long subjectStudyId, + @Parameter(description = "subject study to update", required = true) @RequestBody SubjectStudy subjectStudy, final BindingResult result) throws RestServiceException; } diff --git a/shanoir-ng-studies/src/main/java/org/shanoir/ng/subjectstudy/controler/SubjectStudyApiController.java b/shanoir-ng-studies/src/main/java/org/shanoir/ng/subjectstudy/controler/SubjectStudyApiController.java index e7ec3a0b39..908a6a70ba 100644 --- a/shanoir-ng-studies/src/main/java/org/shanoir/ng/subjectstudy/controler/SubjectStudyApiController.java +++ b/shanoir-ng-studies/src/main/java/org/shanoir/ng/subjectstudy/controler/SubjectStudyApiController.java @@ -53,8 +53,8 @@ public class SubjectStudyApiController implements SubjectStudyApi { @Override public ResponseEntity updateSubjectStudy( - @Parameter(name = "id of the subject study", required = true) @PathVariable("subjectStudyId") Long subjectStudyId, - @Parameter(name = "subject study to update", required = true) @RequestBody SubjectStudy subjectStudy, + @Parameter(description = "id of the subject study", required = true) @PathVariable("subjectStudyId") Long subjectStudyId, + @Parameter(description = "subject study to update", required = true) @RequestBody SubjectStudy subjectStudy, final BindingResult result) throws RestServiceException { final FieldErrorMap errors = new FieldErrorMap(result); diff --git a/shanoir-ng-studies/src/main/resources/application.yml b/shanoir-ng-studies/src/main/resources/application.yml index 3b499909c6..84afbb645b 100644 --- a/shanoir-ng-studies/src/main/resources/application.yml +++ b/shanoir-ng-studies/src/main/resources/application.yml @@ -86,6 +86,8 @@ springdoc: config-url: /shanoir-ng/studies/api-docs/swagger-config enabled: true disable-swagger-default-url: true + oauth: + client-id: shanoir-swagger front.server: address: ${SHANOIR_URL_SCHEME}://${SHANOIR_URL_HOST}/shanoir-ng/ From 1076f1915a5212ccb96318a9a62b7b02d1baebcc Mon Sep 17 00:00:00 2001 From: jcomedouteau Date: Tue, 18 Jun 2024 16:44:59 +0200 Subject: [PATCH 68/80] #2268-bugfix-processing-deletion --- .../ng/processing/service/DatasetProcessingServiceImpl.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/processing/service/DatasetProcessingServiceImpl.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/processing/service/DatasetProcessingServiceImpl.java index aa9199e621..c83abae5f2 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/processing/service/DatasetProcessingServiceImpl.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/processing/service/DatasetProcessingServiceImpl.java @@ -22,6 +22,7 @@ import org.shanoir.ng.utils.Utils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import java.util.ArrayList; import java.util.List; @@ -82,6 +83,7 @@ public DatasetProcessing update(final DatasetProcessing entity) throws EntityNot } @Override + @Transactional public void deleteById(final Long id) throws EntityNotFoundException { final Optional entity = repository.findById(id); entity.orElseThrow(() -> new EntityNotFoundException("Cannot find dataset processing [" + id + "]")); From 714b2d806eb0ca243a479f248fad0cb8a1571c5e Mon Sep 17 00:00:00 2001 From: Youenn Merel Date: Mon, 24 Jun 2024 10:43:52 +0200 Subject: [PATCH 69/80] Correct right check on update dataset tags endpoint --- shanoir-downloader | 2 +- .../main/java/org/shanoir/ng/dataset/controler/DatasetApi.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/shanoir-downloader b/shanoir-downloader index 2e4097d938..f1a32543fd 160000 --- a/shanoir-downloader +++ b/shanoir-downloader @@ -1 +1 @@ -Subproject commit 2e4097d938b9a9775d117acc7cab79360cb5b770 +Subproject commit f1a32543fd5be9d6620a9a67b498d67a65c87d96 diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/dataset/controler/DatasetApi.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/dataset/controler/DatasetApi.java index 142c893d7f..35fbbd6428 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/dataset/controler/DatasetApi.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/dataset/controler/DatasetApi.java @@ -335,7 +335,7 @@ ResponseEntity> findDatasetsByIds( @ApiResponse(responseCode = "500", description = "unexpected error") }) @PutMapping(value = "/{datasetId}/tags", produces = { "application/json" }, consumes = { "application/json" }) - @PreAuthorize("hasRole('ADMIN') or (hasRole('EXPERT') and @datasetSecurityService.hasUpdateRightOnDataset(#dataset, 'CAN_ADMINISTRATE'))") + @PreAuthorize("hasRole('ADMIN') or (hasRole('EXPERT') and @datasetSecurityService.hasRightOnDataset(#datasetId, 'CAN_ADMINISTRATE'))") ResponseEntity updateDatasetTags( @Parameter(description = "id of the dataset", required = true) @PathVariable("datasetId") Long datasetId, @io.swagger.v3.oas.annotations.parameters.RequestBody(description = "array of study tag ids", required = true) @RequestBody List studyTagIds, From 81af74260a1a0f3c2d54725354f230faaa9052de Mon Sep 17 00:00:00 2001 From: Youenn Merel Date: Mon, 24 Jun 2024 11:11:08 +0200 Subject: [PATCH 70/80] Correct OK return code --- .../main/java/org/shanoir/ng/dataset/controler/DatasetApi.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/dataset/controler/DatasetApi.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/dataset/controler/DatasetApi.java index 35fbbd6428..0dc5cec28c 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/dataset/controler/DatasetApi.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/dataset/controler/DatasetApi.java @@ -327,7 +327,7 @@ ResponseEntity> findDatasetsByIds( @RequestParam(value = "datasetIds", required = true) List datasetIds); @Operation(summary = "", description = "Updates the study tags of a dataset") - @ApiResponses(value = { @ApiResponse(responseCode = "204", description = "dataset study tags updated"), + @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "dataset study tags updated"), @ApiResponse(responseCode = "401", description = "unauthorized"), @ApiResponse(responseCode = "403", description = "forbidden"), @ApiResponse(responseCode = "404", description = "dataset does not exists"), From b5a2e1920b103bd5417c25ce2de098aa4aa9129e Mon Sep 17 00:00:00 2001 From: pierrehenri-dauvergne Date: Mon, 24 Jun 2024 11:41:52 +0200 Subject: [PATCH 71/80] shanoir-issue#2274: remove field --- .../src/app/studies/study/study.component.html | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/shanoir-ng-front/src/app/studies/study/study.component.html b/shanoir-ng-front/src/app/studies/study/study.component.html index 7992fb10d1..b3361f4908 100644 --- a/shanoir-ng-front/src/app/studies/study/study.component.html +++ b/shanoir-ng-front/src/app/studies/study/study.component.html @@ -271,20 +271,6 @@

      Display as public - - - - - - - - - -

    3. -

    Subject tags From d9c0541efb2760169c3a6a51b2438305c5513d52 Mon Sep 17 00:00:00 2001 From: jcomedouteau Date: Mon, 24 Jun 2024 12:03:10 +0200 Subject: [PATCH 72/80] #2275-processed-dataset-bug - Correctly download EEG processed dataset - Correctly display input dataset no processing detail - Correctly set processed dataset folder when downloading --- .../service/DatasetDownloaderServiceImpl.java | 35 ++++++++----------- .../processing/dto/DatasetProcessingDTO.java | 18 +++++++--- .../mapper/DatasetProcessingDecorator.java | 6 ++-- .../mass-download/mass-download.service.ts | 3 ++ 4 files changed, 34 insertions(+), 28 deletions(-) diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/dataset/service/DatasetDownloaderServiceImpl.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/dataset/service/DatasetDownloaderServiceImpl.java index 7f59cc0ad9..991e79eb92 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/dataset/service/DatasetDownloaderServiceImpl.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/dataset/service/DatasetDownloaderServiceImpl.java @@ -14,20 +14,7 @@ package org.shanoir.ng.dataset.service; -import java.io.File; -import java.io.IOException; -import java.net.URL; -import java.nio.file.Files; -import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.stream.Collectors; -import java.util.zip.ZipEntry; -import java.util.zip.ZipOutputStream; - +import com.fasterxml.jackson.databind.ObjectMapper; import jakarta.annotation.PostConstruct; import jakarta.servlet.http.HttpServletResponse; import org.apache.commons.io.FileUtils; @@ -58,7 +45,15 @@ import org.springframework.http.HttpStatus; import org.springframework.stereotype.Service; import org.springframework.util.StreamUtils; -import com.fasterxml.jackson.databind.ObjectMapper; + +import java.io.File; +import java.io.IOException; +import java.net.URL; +import java.text.SimpleDateFormat; +import java.util.*; +import java.util.stream.Collectors; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; @Service public class DatasetDownloaderServiceImpl { @@ -139,7 +134,11 @@ public void massiveDownload(String format, List datasets, HttpServletRe List pathURLs = new ArrayList<>(); - if (dataset instanceof EegDataset) { + if (dataset.getDatasetProcessing() != null) { + // DOWNLOAD PROCESSED DATASET + DatasetFileUtils.getDatasetFilePathURLs(dataset, pathURLs, DatasetExpressionFormat.NIFTI_SINGLE_FILE, downloadResult); + DatasetFileUtils.copyNiftiFilesForURLs(pathURLs, zipOutputStream, dataset, subjectName, true, datasetFilePath); + } else if (dataset instanceof EegDataset) { // DOWNLOAD EEG DatasetFileUtils.getDatasetFilePathURLs(dataset, pathURLs, DatasetExpressionFormat.EEG, downloadResult); DatasetFileUtils.copyNiftiFilesForURLs(pathURLs, zipOutputStream, dataset, subjectName, false, datasetFilePath); @@ -148,10 +147,6 @@ public void massiveDownload(String format, List datasets, HttpServletRe DatasetFileUtils.getDatasetFilePathURLs(dataset, pathURLs, DatasetExpressionFormat.BIDS, downloadResult); DatasetFileUtils.copyNiftiFilesForURLs(pathURLs, zipOutputStream, dataset, subjectName, true, datasetFilePath); // Manage errors here - } else if (dataset.getDatasetProcessing() != null) { - // DOWNLOAD PROCESSED DATASET - DatasetFileUtils.getDatasetFilePathURLs(dataset, pathURLs, DatasetExpressionFormat.NIFTI_SINGLE_FILE, downloadResult); - DatasetFileUtils.copyNiftiFilesForURLs(pathURLs, zipOutputStream, dataset, subjectName, true, datasetFilePath); } else if (DCM.equals(format)) { // DOWNLOAD DICOM DatasetFileUtils.getDatasetFilePathURLs(dataset, pathURLs, DatasetExpressionFormat.DICOM, downloadResult); diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/processing/dto/DatasetProcessingDTO.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/processing/dto/DatasetProcessingDTO.java index a2270cb171..b5d04a03d9 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/processing/dto/DatasetProcessingDTO.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/processing/dto/DatasetProcessingDTO.java @@ -14,12 +14,12 @@ package org.shanoir.ng.processing.dto; -import java.time.LocalDate; -import java.util.List; - import org.shanoir.ng.dataset.dto.DatasetDTO; import org.shanoir.ng.processing.model.DatasetProcessingType; +import java.time.LocalDate; +import java.util.List; + /** * DTO for dataset. @@ -36,7 +36,9 @@ public class DatasetProcessingDTO { private DatasetProcessingType datasetProcessingType; private List outputDatasets; - + + private List inputDatasets; + private LocalDate processingDate; private Long studyId; @@ -98,4 +100,12 @@ public Long getParentId() { public void setParentId(Long parentId) { this.parentId = parentId; } + + public List getInputDatasets() { + return inputDatasets; + } + + public void setInputDatasets(List inputDatasets) { + this.inputDatasets = inputDatasets; + } } diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/processing/dto/mapper/DatasetProcessingDecorator.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/processing/dto/mapper/DatasetProcessingDecorator.java index 6c4f929b9b..ff99b669bb 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/processing/dto/mapper/DatasetProcessingDecorator.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/processing/dto/mapper/DatasetProcessingDecorator.java @@ -1,11 +1,9 @@ package org.shanoir.ng.processing.dto.mapper; -import org.shanoir.ng.examination.dto.ExaminationDTO; import org.shanoir.ng.processing.dto.DatasetProcessingDTO; import org.shanoir.ng.processing.model.DatasetProcessing; import org.springframework.beans.factory.annotation.Autowired; -import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; @@ -21,7 +19,6 @@ public DatasetProcessingDTO datasetProcessingToDatasetProcessingDTO(DatasetProce return null; } - DatasetProcessingDTO dto = delegate.datasetProcessingToDatasetProcessingDTO(processing); if(processing.getParent() != null){ dto.setParentId(processing.getParent().getId()); @@ -31,6 +28,7 @@ public DatasetProcessingDTO datasetProcessingToDatasetProcessingDTO(DatasetProce @Override public List datasetProcessingsToDatasetProcessingDTOs(List processings) { - return processings.stream().map(this::datasetProcessingToDatasetProcessingDTO).collect(Collectors.toList()); + // When loading multiple processing, remove input datasets to avoid loading too much data #2121 + return processings.stream().map(this::datasetProcessingToDatasetProcessingDTO).peek(dto -> dto.setInputDatasets(null)).collect(Collectors.toList()); } } diff --git a/shanoir-ng-front/src/app/shared/mass-download/mass-download.service.ts b/shanoir-ng-front/src/app/shared/mass-download/mass-download.service.ts index 3399c5c742..050fc408d8 100644 --- a/shanoir-ng-front/src/app/shared/mass-download/mass-download.service.ts +++ b/shanoir-ng-front/src/app/shared/mass-download/mass-download.service.ts @@ -378,6 +378,9 @@ export class MassDownloadService { } private buildAcquisitionPath(dataset: Dataset): string { + if (dataset.datasetProcessing) { + return this.buildAcquisitionPath(dataset.datasetProcessing.inputDatasets[0]); + } return dataset.datasetAcquisition?.examination?.subject?.name + '_' + dataset.datasetAcquisition?.examination?.subject?.id + '/' From cad27f5b3e33bc1663d478d48853f38c9c35cbc5 Mon Sep 17 00:00:00 2001 From: pierrehenri-dauvergne Date: Mon, 24 Jun 2024 15:08:13 +0200 Subject: [PATCH 73/80] shanoir-issue#2276: add logs for debugging --- .../ng/study/service/RelatedDatasetServiceImpl.java | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/shanoir-ng-studies/src/main/java/org/shanoir/ng/study/service/RelatedDatasetServiceImpl.java b/shanoir-ng-studies/src/main/java/org/shanoir/ng/study/service/RelatedDatasetServiceImpl.java index 99c2872292..b0747b207c 100644 --- a/shanoir-ng-studies/src/main/java/org/shanoir/ng/study/service/RelatedDatasetServiceImpl.java +++ b/shanoir-ng-studies/src/main/java/org/shanoir/ng/study/service/RelatedDatasetServiceImpl.java @@ -84,6 +84,7 @@ public class RelatedDatasetServiceImpl implements RelatedDatasetService { public void addSubjectStudyToNewStudy(List subjectIdStudyId, Long studyId) { List subjectIds = new ArrayList<>(); List studySourceId = new ArrayList<>(); + System.out.println("subjectIdStudyId : " + subjectIdStudyId); for (String s : subjectIdStudyId) { subjectIds.add(Long.valueOf(s.substring(0, s.indexOf("/")))); studySourceId.add(Long.valueOf(s.substring(s.indexOf("/") + 1, s.length()))); @@ -92,20 +93,27 @@ public void addSubjectStudyToNewStudy(List subjectIdStudyId, Long studyI Study studyTarget = studyService.findById(Long.valueOf(studyId)); Boolean toAdd = true; Iterable subjects = subjectRepository.findAllById(subjectIds); + System.out.println("FOR"); for (Subject subject : subjects) { + System.out.println(" subject : " + subject); List subjectStudyList = studyTarget.getSubjectStudyList(); for (SubjectStudy subjectStudy : subjectStudyList) { + System.out.println(" subjectStudy : " + subjectStudy); if (subjectStudy.getSubject().equals(subject)) { + System.out.println(" subject already in study"); toAdd = false; break; } else { + System.out.println(" subject not in study"); toAdd = true; } } if (toAdd) { + System.out.println("toAdd"); SubjectStudy ssToAdd = new SubjectStudy(); for (int i = 0; i < subjectIds.size(); i++) { + System.out.println("subjectId = " + subjectIds.get(i) + " / subject.getId = " + subject.getId()); if (subjectIds.get(i).equals(subject.getId())) { SubjectStudy type = subjectStudyRepository.findByStudyIdAndSubjectId(studySourceId.get(i), subjectIds.get(i)); ssToAdd.setSubjectType(type.getSubjectType()); @@ -113,11 +121,12 @@ public void addSubjectStudyToNewStudy(List subjectIdStudyId, Long studyI } ssToAdd.setStudy(studyTarget); ssToAdd.setSubject(subject); + System.out.println("subjectStudy to add is set : " + ssToAdd.getId() + " / " + ssToAdd.getSubjectType() + " / " + ssToAdd.getSubject().getName()); subjectStudyList.add(ssToAdd); studyTarget.setSubjectStudyList(subjectStudyList); studyRepository.save(studyTarget); - + System.out.println("add subject to study and save study"); // then send it to dataset ms which has a duplicated table try { subjectStudyUpdateBroadcastService.send(subjectStudyList); From 9a8f53edc932416a55093ce30318d95fe6438b2f Mon Sep 17 00:00:00 2001 From: jcomedouteau Date: Mon, 24 Jun 2024 15:30:33 +0200 Subject: [PATCH 74/80] #2275-processed-dataset-bug - Correctly not load input datasets - Correctly set processed dataset folder when downloading --- .../ExaminationDatasetAcquisitionDecorator.java | 15 ++++++++++++--- .../shared/mass-download/mass-download.service.ts | 7 ++++++- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/datasetacquisition/dto/mapper/ExaminationDatasetAcquisitionDecorator.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/datasetacquisition/dto/mapper/ExaminationDatasetAcquisitionDecorator.java index 3ed7d2b962..478d1c0c65 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/datasetacquisition/dto/mapper/ExaminationDatasetAcquisitionDecorator.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/datasetacquisition/dto/mapper/ExaminationDatasetAcquisitionDecorator.java @@ -14,18 +14,19 @@ package org.shanoir.ng.datasetacquisition.dto.mapper; -import java.util.ArrayList; -import java.util.List; - import org.shanoir.ng.dataset.modality.BidsDataset; import org.shanoir.ng.dataset.model.Dataset; import org.shanoir.ng.datasetacquisition.dto.ExaminationDatasetAcquisitionDTO; import org.shanoir.ng.datasetacquisition.model.DatasetAcquisition; import org.shanoir.ng.datasetacquisition.model.bids.BidsDatasetAcquisition; +import org.shanoir.ng.processing.model.DatasetProcessing; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; +import java.util.ArrayList; +import java.util.List; + /** * Decorator for dataset acquisitions mapper. * @@ -45,6 +46,14 @@ public List datasetAcquisitionsToExaminationDa } final List datasetAcquisitionDTOs = new ArrayList<>(); for (DatasetAcquisition datasetAcquisition : datasetAcquisitions) { + // Remove dataset processing input childs here #2121 + for (Dataset datasetToBe : datasetAcquisition.getDatasets()) { + if (datasetToBe.getProcessings() != null) { + for (DatasetProcessing processing : datasetToBe.getProcessings()) { + processing.setInputDatasets(null); + } + } + } datasetAcquisitionDTOs.add(datasetAcquisitionToExaminationDatasetAcquisitionDTO(datasetAcquisition)); } return datasetAcquisitionDTOs; diff --git a/shanoir-ng-front/src/app/shared/mass-download/mass-download.service.ts b/shanoir-ng-front/src/app/shared/mass-download/mass-download.service.ts index 050fc408d8..9525d8cb54 100644 --- a/shanoir-ng-front/src/app/shared/mass-download/mass-download.service.ts +++ b/shanoir-ng-front/src/app/shared/mass-download/mass-download.service.ts @@ -379,7 +379,12 @@ export class MassDownloadService { private buildAcquisitionPath(dataset: Dataset): string { if (dataset.datasetProcessing) { - return this.buildAcquisitionPath(dataset.datasetProcessing.inputDatasets[0]); + return dataset.datasetProcessing.inputDatasets[0].subject?.name + + '_' + dataset.datasetProcessing.inputDatasets[0].subject?.id + + '/' + + dataset.datasetProcessing.inputDatasets[0].name + + "_" + + dataset.name; } return dataset.datasetAcquisition?.examination?.subject?.name + '_' + dataset.datasetAcquisition?.examination?.subject?.id From 3921c2447bd091dc5341e750462b2202d2ee6c6b Mon Sep 17 00:00:00 2001 From: jcomedouteau Date: Mon, 24 Jun 2024 12:03:10 +0200 Subject: [PATCH 75/80] #2275-processed-dataset-bug - Correctly download EEG processed dataset - Correctly display input dataset no processing detail - Correctly set processed dataset folder when downloading --- .../service/DatasetDownloaderServiceImpl.java | 35 ++++++++----------- .../processing/dto/DatasetProcessingDTO.java | 18 +++++++--- .../mapper/DatasetProcessingDecorator.java | 6 ++-- .../mass-download/mass-download.service.ts | 3 ++ 4 files changed, 34 insertions(+), 28 deletions(-) diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/dataset/service/DatasetDownloaderServiceImpl.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/dataset/service/DatasetDownloaderServiceImpl.java index 7f59cc0ad9..991e79eb92 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/dataset/service/DatasetDownloaderServiceImpl.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/dataset/service/DatasetDownloaderServiceImpl.java @@ -14,20 +14,7 @@ package org.shanoir.ng.dataset.service; -import java.io.File; -import java.io.IOException; -import java.net.URL; -import java.nio.file.Files; -import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.stream.Collectors; -import java.util.zip.ZipEntry; -import java.util.zip.ZipOutputStream; - +import com.fasterxml.jackson.databind.ObjectMapper; import jakarta.annotation.PostConstruct; import jakarta.servlet.http.HttpServletResponse; import org.apache.commons.io.FileUtils; @@ -58,7 +45,15 @@ import org.springframework.http.HttpStatus; import org.springframework.stereotype.Service; import org.springframework.util.StreamUtils; -import com.fasterxml.jackson.databind.ObjectMapper; + +import java.io.File; +import java.io.IOException; +import java.net.URL; +import java.text.SimpleDateFormat; +import java.util.*; +import java.util.stream.Collectors; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; @Service public class DatasetDownloaderServiceImpl { @@ -139,7 +134,11 @@ public void massiveDownload(String format, List datasets, HttpServletRe List pathURLs = new ArrayList<>(); - if (dataset instanceof EegDataset) { + if (dataset.getDatasetProcessing() != null) { + // DOWNLOAD PROCESSED DATASET + DatasetFileUtils.getDatasetFilePathURLs(dataset, pathURLs, DatasetExpressionFormat.NIFTI_SINGLE_FILE, downloadResult); + DatasetFileUtils.copyNiftiFilesForURLs(pathURLs, zipOutputStream, dataset, subjectName, true, datasetFilePath); + } else if (dataset instanceof EegDataset) { // DOWNLOAD EEG DatasetFileUtils.getDatasetFilePathURLs(dataset, pathURLs, DatasetExpressionFormat.EEG, downloadResult); DatasetFileUtils.copyNiftiFilesForURLs(pathURLs, zipOutputStream, dataset, subjectName, false, datasetFilePath); @@ -148,10 +147,6 @@ public void massiveDownload(String format, List datasets, HttpServletRe DatasetFileUtils.getDatasetFilePathURLs(dataset, pathURLs, DatasetExpressionFormat.BIDS, downloadResult); DatasetFileUtils.copyNiftiFilesForURLs(pathURLs, zipOutputStream, dataset, subjectName, true, datasetFilePath); // Manage errors here - } else if (dataset.getDatasetProcessing() != null) { - // DOWNLOAD PROCESSED DATASET - DatasetFileUtils.getDatasetFilePathURLs(dataset, pathURLs, DatasetExpressionFormat.NIFTI_SINGLE_FILE, downloadResult); - DatasetFileUtils.copyNiftiFilesForURLs(pathURLs, zipOutputStream, dataset, subjectName, true, datasetFilePath); } else if (DCM.equals(format)) { // DOWNLOAD DICOM DatasetFileUtils.getDatasetFilePathURLs(dataset, pathURLs, DatasetExpressionFormat.DICOM, downloadResult); diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/processing/dto/DatasetProcessingDTO.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/processing/dto/DatasetProcessingDTO.java index a2270cb171..b5d04a03d9 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/processing/dto/DatasetProcessingDTO.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/processing/dto/DatasetProcessingDTO.java @@ -14,12 +14,12 @@ package org.shanoir.ng.processing.dto; -import java.time.LocalDate; -import java.util.List; - import org.shanoir.ng.dataset.dto.DatasetDTO; import org.shanoir.ng.processing.model.DatasetProcessingType; +import java.time.LocalDate; +import java.util.List; + /** * DTO for dataset. @@ -36,7 +36,9 @@ public class DatasetProcessingDTO { private DatasetProcessingType datasetProcessingType; private List outputDatasets; - + + private List inputDatasets; + private LocalDate processingDate; private Long studyId; @@ -98,4 +100,12 @@ public Long getParentId() { public void setParentId(Long parentId) { this.parentId = parentId; } + + public List getInputDatasets() { + return inputDatasets; + } + + public void setInputDatasets(List inputDatasets) { + this.inputDatasets = inputDatasets; + } } diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/processing/dto/mapper/DatasetProcessingDecorator.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/processing/dto/mapper/DatasetProcessingDecorator.java index 6c4f929b9b..ff99b669bb 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/processing/dto/mapper/DatasetProcessingDecorator.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/processing/dto/mapper/DatasetProcessingDecorator.java @@ -1,11 +1,9 @@ package org.shanoir.ng.processing.dto.mapper; -import org.shanoir.ng.examination.dto.ExaminationDTO; import org.shanoir.ng.processing.dto.DatasetProcessingDTO; import org.shanoir.ng.processing.model.DatasetProcessing; import org.springframework.beans.factory.annotation.Autowired; -import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; @@ -21,7 +19,6 @@ public DatasetProcessingDTO datasetProcessingToDatasetProcessingDTO(DatasetProce return null; } - DatasetProcessingDTO dto = delegate.datasetProcessingToDatasetProcessingDTO(processing); if(processing.getParent() != null){ dto.setParentId(processing.getParent().getId()); @@ -31,6 +28,7 @@ public DatasetProcessingDTO datasetProcessingToDatasetProcessingDTO(DatasetProce @Override public List datasetProcessingsToDatasetProcessingDTOs(List processings) { - return processings.stream().map(this::datasetProcessingToDatasetProcessingDTO).collect(Collectors.toList()); + // When loading multiple processing, remove input datasets to avoid loading too much data #2121 + return processings.stream().map(this::datasetProcessingToDatasetProcessingDTO).peek(dto -> dto.setInputDatasets(null)).collect(Collectors.toList()); } } diff --git a/shanoir-ng-front/src/app/shared/mass-download/mass-download.service.ts b/shanoir-ng-front/src/app/shared/mass-download/mass-download.service.ts index 3399c5c742..050fc408d8 100644 --- a/shanoir-ng-front/src/app/shared/mass-download/mass-download.service.ts +++ b/shanoir-ng-front/src/app/shared/mass-download/mass-download.service.ts @@ -378,6 +378,9 @@ export class MassDownloadService { } private buildAcquisitionPath(dataset: Dataset): string { + if (dataset.datasetProcessing) { + return this.buildAcquisitionPath(dataset.datasetProcessing.inputDatasets[0]); + } return dataset.datasetAcquisition?.examination?.subject?.name + '_' + dataset.datasetAcquisition?.examination?.subject?.id + '/' From 301e49eee1c203fd2abb0f68889387341b5d740a Mon Sep 17 00:00:00 2001 From: jcomedouteau Date: Tue, 25 Jun 2024 14:17:55 +0200 Subject: [PATCH 76/80] #2268-delete-processing-bug -> Clean solr index --- .../service/DatasetProcessingService.java | 8 +++--- .../service/DatasetProcessingServiceImpl.java | 27 +++++++++++++++---- 2 files changed, 26 insertions(+), 9 deletions(-) diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/processing/service/DatasetProcessingService.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/processing/service/DatasetProcessingService.java index 14427da24d..17abfb2554 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/processing/service/DatasetProcessingService.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/processing/service/DatasetProcessingService.java @@ -15,6 +15,9 @@ package org.shanoir.ng.processing.service; import org.apache.solr.client.solrj.SolrServerException; +import org.shanoir.ng.processing.model.DatasetProcessing; +import org.shanoir.ng.shared.exception.EntityNotFoundException; +import org.shanoir.ng.shared.exception.MicroServiceCommunicationException; import org.shanoir.ng.shared.exception.RestServiceException; import org.shanoir.ng.shared.exception.ShanoirException; import org.springframework.security.access.prepost.PreAuthorize; @@ -22,9 +25,6 @@ import java.io.IOException; import java.util.List; import java.util.Optional; -import org.shanoir.ng.processing.model.DatasetProcessing; -import org.shanoir.ng.shared.exception.EntityNotFoundException; -import org.shanoir.ng.shared.exception.MicroServiceCommunicationException; /** * DatasetProcessing service. @@ -96,7 +96,7 @@ public interface DatasetProcessingService { * @param datasetId */ @PreAuthorize("hasAnyRole('ADMIN', 'EXPERT')") - void removeDatasetFromAllProcessingInput(Long datasetId) throws ShanoirException; + void removeDatasetFromAllProcessingInput(Long datasetId) throws ShanoirException, RestServiceException, SolrServerException, IOException; /** * Delete child processing of given processing diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/processing/service/DatasetProcessingServiceImpl.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/processing/service/DatasetProcessingServiceImpl.java index c83abae5f2..ee3fdd5c91 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/processing/service/DatasetProcessingServiceImpl.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/processing/service/DatasetProcessingServiceImpl.java @@ -14,16 +14,22 @@ package org.shanoir.ng.processing.service; +import org.apache.solr.client.solrj.SolrServerException; +import org.shanoir.ng.dataset.model.Dataset; +import org.shanoir.ng.dataset.service.DatasetService; import org.shanoir.ng.dataset.service.ProcessedDatasetService; import org.shanoir.ng.processing.model.DatasetProcessing; import org.shanoir.ng.processing.repository.DatasetProcessingRepository; import org.shanoir.ng.shared.exception.EntityNotFoundException; +import org.shanoir.ng.shared.exception.RestServiceException; import org.shanoir.ng.shared.exception.ShanoirException; +import org.shanoir.ng.solr.service.SolrServiceImpl; import org.shanoir.ng.utils.Utils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Optional; @@ -43,7 +49,13 @@ public class DatasetProcessingServiceImpl implements DatasetProcessingService { @Autowired private ProcessedDatasetService processedDatasetService; - protected DatasetProcessing updateValues(final DatasetProcessing from, final DatasetProcessing to) { + @Autowired + private DatasetService datasetService; + + @Autowired + private SolrServiceImpl solrService; + + protected DatasetProcessing updateValues(final DatasetProcessing from, final DatasetProcessing to) { to.setDatasetProcessingType(from.getDatasetProcessingType()); to.setComment(from.getComment()); to.setInputDatasets(from.getInputDatasets()); @@ -84,10 +96,15 @@ public DatasetProcessing update(final DatasetProcessing entity) throws EntityNot @Override @Transactional - public void deleteById(final Long id) throws EntityNotFoundException { + public void deleteById(final Long id) throws ShanoirException, RestServiceException, SolrServerException, IOException { final Optional entity = repository.findById(id); entity.orElseThrow(() -> new EntityNotFoundException("Cannot find dataset processing [" + id + "]")); - processedDatasetService.deleteByProcessingId(id); + + for (Dataset ds : entity.get().getOutputDatasets()) { + datasetService.deleteById(ds.getId()); + solrService.deleteFromIndex(ds.getId()); + } + this.deleteByParentId(id); repository.deleteById(id); } @@ -98,7 +115,7 @@ public void deleteById(final Long id) throws EntityNotFoundException { * @param datasetId */ @Override - public void removeDatasetFromAllProcessingInput(Long datasetId) throws ShanoirException { + public void removeDatasetFromAllProcessingInput(Long datasetId) throws ShanoirException, RestServiceException, SolrServerException, IOException { List processings = repository.findAllByInputDatasets_Id(datasetId); List toUpdate = new ArrayList<>(); List toDelete = new ArrayList<>(); @@ -119,7 +136,7 @@ public void removeDatasetFromAllProcessingInput(Long datasetId) throws ShanoirEx } @Override - public void deleteByParentId(Long id) throws EntityNotFoundException { + public void deleteByParentId(Long id) throws ShanoirException, RestServiceException, SolrServerException, IOException { List processings = repository.findAllByParentId(id); for(DatasetProcessing child : processings){ this.deleteById(child.getId()); From f75521b4bef3e3f7822d6e5c8176679b5296bba0 Mon Sep 17 00:00:00 2001 From: jcomedouteau Date: Tue, 25 Jun 2024 14:36:49 +0200 Subject: [PATCH 77/80] #2268-delete-processing-bug -> Correct tests --- .../processing/service/DatasetProcessingServiceImpl.java | 4 ++-- .../shanoir/ng/dataset/DatasetServiceSecurityTest.java | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/processing/service/DatasetProcessingServiceImpl.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/processing/service/DatasetProcessingServiceImpl.java index ee3fdd5c91..15021fb42a 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/processing/service/DatasetProcessingServiceImpl.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/processing/service/DatasetProcessingServiceImpl.java @@ -23,7 +23,7 @@ import org.shanoir.ng.shared.exception.EntityNotFoundException; import org.shanoir.ng.shared.exception.RestServiceException; import org.shanoir.ng.shared.exception.ShanoirException; -import org.shanoir.ng.solr.service.SolrServiceImpl; +import org.shanoir.ng.solr.service.SolrService; import org.shanoir.ng.utils.Utils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -53,7 +53,7 @@ public class DatasetProcessingServiceImpl implements DatasetProcessingService { private DatasetService datasetService; @Autowired - private SolrServiceImpl solrService; + private SolrService solrService; protected DatasetProcessing updateValues(final DatasetProcessing from, final DatasetProcessing to) { to.setDatasetProcessingType(from.getDatasetProcessingType()); diff --git a/shanoir-ng-datasets/src/test/java/org/shanoir/ng/dataset/DatasetServiceSecurityTest.java b/shanoir-ng-datasets/src/test/java/org/shanoir/ng/dataset/DatasetServiceSecurityTest.java index f4f678c903..c150ecac2c 100644 --- a/shanoir-ng-datasets/src/test/java/org/shanoir/ng/dataset/DatasetServiceSecurityTest.java +++ b/shanoir-ng-datasets/src/test/java/org/shanoir/ng/dataset/DatasetServiceSecurityTest.java @@ -72,6 +72,9 @@ public class DatasetServiceSecurityTest { @MockBean private DatasetRepository datasetRepository; + + @MockBean + private SolrService solrService; @MockBean private DatasetAcquisitionRepository datasetAcquisitionRepository; @@ -90,10 +93,7 @@ public class DatasetServiceSecurityTest { @MockBean private ShanoirEventService shanoirEventService; - - @MockBean - private SolrService solrService; - + @Test @WithAnonymousUser public void testAsAnonymous() throws ShanoirException { From ff2b60d9688e347dd0d52ab21cc03bf807231aac Mon Sep 17 00:00:00 2001 From: pierrehenri-dauvergne Date: Tue, 25 Jun 2024 16:15:56 +0200 Subject: [PATCH 78/80] shanoir-issue#2276: add logs and prevent NPE on getType --- docker-compose-dev.yml | 2 +- .../configuration/amqp/RabbitMQDatasetsService.java | 1 + .../messaging/SubjectStudyUpdateBroadcastService.java | 2 +- .../ng/study/controler/StudyApiController.java | 1 + .../ng/study/service/RelatedDatasetServiceImpl.java | 11 +---------- 5 files changed, 5 insertions(+), 12 deletions(-) diff --git a/docker-compose-dev.yml b/docker-compose-dev.yml index 9436aca5c1..7ed06e8d5a 100644 --- a/docker-compose-dev.yml +++ b/docker-compose-dev.yml @@ -26,7 +26,7 @@ services: target: front-dev volumes: - "./shanoir-ng-front:/app/" - command: ng serve --host 0.0.0.0 --disable-host-check + command: ng serve --host 0.0.0.0 --disable-host-check --poll 10000 ports: - "4200:4200" tty: true diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/configuration/amqp/RabbitMQDatasetsService.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/configuration/amqp/RabbitMQDatasetsService.java index 734bfd88b3..7c43af8f6c 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/configuration/amqp/RabbitMQDatasetsService.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/configuration/amqp/RabbitMQDatasetsService.java @@ -152,6 +152,7 @@ public void receiveSubjectStudies(String commandArrStr) { } subjectStudyRepository.saveAll(subjectStudies); } catch (Exception e) { + LOG.error("Error during copy of dataset : ", e); throw new AmqpRejectAndDontRequeueException(RABBIT_MQ_ERROR, e); } } diff --git a/shanoir-ng-studies/src/main/java/org/shanoir/ng/messaging/SubjectStudyUpdateBroadcastService.java b/shanoir-ng-studies/src/main/java/org/shanoir/ng/messaging/SubjectStudyUpdateBroadcastService.java index c0ce6a8c7c..0952001748 100644 --- a/shanoir-ng-studies/src/main/java/org/shanoir/ng/messaging/SubjectStudyUpdateBroadcastService.java +++ b/shanoir-ng-studies/src/main/java/org/shanoir/ng/messaging/SubjectStudyUpdateBroadcastService.java @@ -59,7 +59,7 @@ private List toDTO(List subjectStudies) { subjectStudy.getId(), subjectStudy.getStudy().getId(), subjectStudy.getSubject().getId(), - subjectStudy.getSubjectType().getId()); + (subjectStudy.getSubjectType() != null ? subjectStudy.getSubjectType().getId() : null)); dtos.add(dto); } return dtos; diff --git a/shanoir-ng-studies/src/main/java/org/shanoir/ng/study/controler/StudyApiController.java b/shanoir-ng-studies/src/main/java/org/shanoir/ng/study/controler/StudyApiController.java index 919fef2d64..1d92ad55c3 100644 --- a/shanoir-ng-studies/src/main/java/org/shanoir/ng/study/controler/StudyApiController.java +++ b/shanoir-ng-studies/src/main/java/org/shanoir/ng/study/controler/StudyApiController.java @@ -249,6 +249,7 @@ public ResponseEntity copyDatasetsToStudy( relatedDatasetService.addSubjectStudyToNewStudy(subjectIdStudyId, studyId); } catch (Exception e) { + LOG.error("Error during copy for datasetsIds : " + datasetIds + ", studyId : " + studyIdAsStr + ", centersId : " + centerIds + ". Error : ", e); return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); } return new ResponseEntity<>(res, HttpStatus.OK); diff --git a/shanoir-ng-studies/src/main/java/org/shanoir/ng/study/service/RelatedDatasetServiceImpl.java b/shanoir-ng-studies/src/main/java/org/shanoir/ng/study/service/RelatedDatasetServiceImpl.java index b0747b207c..dedc4b847c 100644 --- a/shanoir-ng-studies/src/main/java/org/shanoir/ng/study/service/RelatedDatasetServiceImpl.java +++ b/shanoir-ng-studies/src/main/java/org/shanoir/ng/study/service/RelatedDatasetServiceImpl.java @@ -84,7 +84,6 @@ public class RelatedDatasetServiceImpl implements RelatedDatasetService { public void addSubjectStudyToNewStudy(List subjectIdStudyId, Long studyId) { List subjectIds = new ArrayList<>(); List studySourceId = new ArrayList<>(); - System.out.println("subjectIdStudyId : " + subjectIdStudyId); for (String s : subjectIdStudyId) { subjectIds.add(Long.valueOf(s.substring(0, s.indexOf("/")))); studySourceId.add(Long.valueOf(s.substring(s.indexOf("/") + 1, s.length()))); @@ -93,27 +92,20 @@ public void addSubjectStudyToNewStudy(List subjectIdStudyId, Long studyI Study studyTarget = studyService.findById(Long.valueOf(studyId)); Boolean toAdd = true; Iterable subjects = subjectRepository.findAllById(subjectIds); - System.out.println("FOR"); for (Subject subject : subjects) { - System.out.println(" subject : " + subject); List subjectStudyList = studyTarget.getSubjectStudyList(); for (SubjectStudy subjectStudy : subjectStudyList) { - System.out.println(" subjectStudy : " + subjectStudy); if (subjectStudy.getSubject().equals(subject)) { - System.out.println(" subject already in study"); toAdd = false; break; } else { - System.out.println(" subject not in study"); toAdd = true; } } if (toAdd) { - System.out.println("toAdd"); SubjectStudy ssToAdd = new SubjectStudy(); for (int i = 0; i < subjectIds.size(); i++) { - System.out.println("subjectId = " + subjectIds.get(i) + " / subject.getId = " + subject.getId()); if (subjectIds.get(i).equals(subject.getId())) { SubjectStudy type = subjectStudyRepository.findByStudyIdAndSubjectId(studySourceId.get(i), subjectIds.get(i)); ssToAdd.setSubjectType(type.getSubjectType()); @@ -121,12 +113,11 @@ public void addSubjectStudyToNewStudy(List subjectIdStudyId, Long studyI } ssToAdd.setStudy(studyTarget); ssToAdd.setSubject(subject); - System.out.println("subjectStudy to add is set : " + ssToAdd.getId() + " / " + ssToAdd.getSubjectType() + " / " + ssToAdd.getSubject().getName()); subjectStudyList.add(ssToAdd); studyTarget.setSubjectStudyList(subjectStudyList); + studyRepository.save(studyTarget); - System.out.println("add subject to study and save study"); // then send it to dataset ms which has a duplicated table try { subjectStudyUpdateBroadcastService.send(subjectStudyList); From b6caabd5e90a422d1f3c33bfc2df0681523ea33f Mon Sep 17 00:00:00 2001 From: pierrehenri-dauvergne Date: Tue, 25 Jun 2024 16:17:21 +0200 Subject: [PATCH 79/80] shanoir-issue#2276: revert change on docker-compose-dev.yml --- docker-compose-dev.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-compose-dev.yml b/docker-compose-dev.yml index 7ed06e8d5a..9436aca5c1 100644 --- a/docker-compose-dev.yml +++ b/docker-compose-dev.yml @@ -26,7 +26,7 @@ services: target: front-dev volumes: - "./shanoir-ng-front:/app/" - command: ng serve --host 0.0.0.0 --disable-host-check --poll 10000 + command: ng serve --host 0.0.0.0 --disable-host-check ports: - "4200:4200" tty: true From 836111eb9f89068673f23a971c646ce7caf70b17 Mon Sep 17 00:00:00 2001 From: jcomedouteau <7604176+jcomedouteau@users.noreply.github.com> Date: Tue, 25 Jun 2024 16:53:45 +0200 Subject: [PATCH 80/80] #2256-missing-mr-sequence-variant (#2261) Manage unknown MR Sequence Variant --- .../ng/datasetacquisition/model/mr/MrSequenceVariant.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/datasetacquisition/model/mr/MrSequenceVariant.java b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/datasetacquisition/model/mr/MrSequenceVariant.java index e591fdf261..7efe274726 100644 --- a/shanoir-ng-datasets/src/main/java/org/shanoir/ng/datasetacquisition/model/mr/MrSequenceVariant.java +++ b/shanoir-ng-datasets/src/main/java/org/shanoir/ng/datasetacquisition/model/mr/MrSequenceVariant.java @@ -14,6 +14,8 @@ package org.shanoir.ng.datasetacquisition.model.mr; +import org.apache.commons.lang3.EnumUtils; + /** * Sequence Variant. * @@ -95,7 +97,7 @@ public static MrSequenceVariant getIdByType(final String type) { if (TOF.equals(type)) { // see GitHub issue #561 return MrSequenceVariant.SS; } - return MrSequenceVariant.valueOf(type); + return EnumUtils.isValidEnum(MrSequenceVariant.class, type) ? MrSequenceVariant.valueOf(type): UNKNOWN; } /**