Skip to content

Commit 5393ed2

Browse files
chrisknollrkboyce
andauthored
Filter cohorts and concepts by permission (#2245) (#2301)
The new property is called security.defaultGlobalReadPermissions which is true by default. Co-authored-by: Richard D Boyce, PhD <[email protected]>
1 parent c6be6ce commit 5393ed2

26 files changed

+319
-87
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
Makefile
12
WebAPIConfig/
23
*application.properties
34
.idea/
@@ -12,6 +13,7 @@ sandbox/
1213
/nbactions*.xml
1314
*~
1415
.DS_Store
16+
.factorypath
1517

1618
### Developer's personal properties ###
1719
**/resources/config/application*-dev-*.properties

Makefile

Lines changed: 10 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,56 +1,21 @@
11
compile:
2-
mvn clean
3-
mvn compile -Pwebapi-postgresql-laertes
2+
mvn clean compile -DskipUnitTests -DskipITtests -s WebAPIConfig/settings.xml -P webapi-postgresql
43

54
package: compile
6-
mvn package -Pwebapi-postgresql-laertes
5+
mvn package -DskipUnitTests -DskipITtests -s WebAPIConfig/settings.xml -P webapi-postgresql
76

8-
deploy: package
9-
sudo service tomcat7 stop
10-
sleep 4
11-
sudo rm -rf /var/lib/tomcat7/webapps/WebAPI*
12-
sudo cp -r target/WebAPI.war /var/lib/tomcat7/webapps/
13-
sudo chown tomcat7 /var/lib/tomcat7/webapps/WebAPI.war
14-
sudo chgrp tomcat7 /var/lib/tomcat7/webapps/WebAPI.war
15-
sudo service tomcat7 start
7+
deploy: package
8+
/home/ubuntu/Downloads/apache-tomcat-8.5.84-DEV/bin/shutdown.sh
9+
mv /home/ubuntu/Downloads/apache-tomcat-8.5.84-DEV/webapps/WebAPI /mnt/disk1/webapi-dev-tmp/WebAPI-FOLDER-`date +%m%d%H%S`
10+
mv /home/ubuntu/Downloads/apache-tomcat-8.5.84-DEV/webapps/WebAPI.war /mnt/disk1/webapi-dev-tmp/WebAPI.war-`date +%m%d%H%S`
11+
mv target/WebAPI.war /home/ubuntu/Downloads/apache-tomcat-8.5.84-DEV/webapps/
12+
echo "Now run /home/ubuntu/Downloads/apache-tomcat-8.5.84-DEV/bin/startup.sh"
1613

1714
git-push:
18-
git push myfork master
15+
git push
1916

2017
test:
21-
wget -O tests/test-general-evidence.json "http://localhost:8080/WebAPI/LAERTES_CDM/evidence/752061"
22-
wget -O tests/test-drug-hoi.json "http://localhost:8080/WebAPI/LAERTES_CDM/evidence/drughoi/752061-374013"
23-
wget -O tests/test-drug.json "http://localhost:8080/WebAPI/LAERTES_CDM/evidence/drug/752061"
24-
wget -O tests/test-hoi.json "http://localhost:8080/WebAPI/LAERTES_CDM/evidence/hoi/320073"
25-
wget -O tests/test-info.json "http://localhost:8080/WebAPI/LAERTES_CDM/evidence/info"
26-
wget -O tests/test-drug-hoi-eu-spc.json "http://localhost:8080/WebAPI/LAERTES_CDM/evidence/drughoi/904351-4190045"
27-
wget -O tests/test-drug-hoi-splicer.json "http://localhost:8080/WebAPI/LAERTES_CDM/evidence/drughoi/19133853-195588"
28-
wget -O tests/test-drug-hoi-faers-counts-and-signals.json "http://localhost:8080/WebAPI/LAERTES_CDM/evidence/drughoi/1154343-433031"
29-
wget -O tests/test-drug-hoi-pubmed-mesh-cr.json "http://localhost:8080/WebAPI/LAERTES_CDM/evidence/drughoi/1154343-433031"
30-
wget -O tests/test-drug-hoi-pubmed-mesh-clin-trial.json "http://localhost:8080/WebAPI/LAERTES_CDM/evidence/drughoi/789578-378144"
31-
wget -O tests/test-drug-hoi-pubmed-mesh-other.json "http://localhost:8080/WebAPI/LAERTES_CDM/evidence/drughoi/19010482-316866"
32-
wget -O tests/test-drug-hoi-semmed-cr.json "http://localhost:8080/WebAPI/LAERTES_CDM/evidence/drughoi/1112807-441202"
33-
wget -O tests/test-drug-hoi-semmed-clin-trial.json "http://localhost:8080/WebAPI/LAERTES_CDM/evidence/drughoi/19059744-381591"
34-
wget -O tests/test-drug-rollup-ingredient.json "http://localhost:8080/WebAPI/LAERTES_CDM/evidence/drugrollup/ingredient/1000632"
35-
wget -O tests/test-drug-rollup-clin-drug.json "http://localhost:8080/WebAPI/LAERTES_CDM/evidence/drugrollup/clinicaldrug/19074181"
36-
wget -O tests/test-drug-rollup-branded-drug.json "http://localhost:8080/WebAPI/LAERTES_CDM/evidence/drugrollup/brandeddrug/1000640"
37-
wget -O tests/test-rdf-evidencesummary.json "http://localhost:8080/WebAPI/LAERTES_CDM/evidence/evidencesummary?conditionID=139900&drugID=1115008&evidenceGroup=Literature"
38-
wget -O tests/test-rdf-evidencedetails.json "http://localhost:8080/WebAPI/LAERTES_CDM/evidence/evidencedetails?conditionID=24134&drugID=1115008&evidenceType=SPL_SPLICER_ADR"
18+
wget -O /tmp/tests/test-drug-rollup-branded-drug.json "http://api.ohdsi.org/WebAPI/CS1/evidence/drugrollup/brandeddrug/1000640"
3919

4020
test-public:
41-
wget -O tests/test-general-evidence.json "http://api.ohdsi.org/WebAPI/CS1/evidence/1000640"
42-
wget -O /tmp/tests/test-drug-hoi.json "http://api.ohdsi.org/WebAPI/CS1/evidence/drughoi/1000640-137682"
43-
wget -O /tmp/tests/test-drug.json "http://api.ohdsi.org/WebAPI/CS1/evidence/drug/1000640"
44-
wget -O /tmp/tests/test-hoi.json "http://api.ohdsi.org/WebAPI/CS1/evidence/hoi/320073"
45-
wget -O /tmp/tests/test-info.json "http://api.ohdsi.org/WebAPI/CS1/evidence/info"
46-
wget -O /tmp/tests/test-drug-hoi-eu-spc.json "http://api.ohdsi.org/WebAPI/CS1/evidence/drughoi/40239056-75053"
47-
wget -O /tmp/tests/test-drug-hoi-splicer.json "http://api.ohdsi.org/WebAPI/CS1/evidence/drughoi/19133853-195588"
48-
wget -O /tmp/tests/test-drug-hoi-faers-counts-and-signals.json "http://api.ohdsi.org/WebAPI/CS1/evidence/drughoi/1154343-433031"
49-
wget -O /tmp/tests/test-drug-hoi-pubmed-mesh-cr.json "http://api.ohdsi.org/WebAPI/CS1/evidence/drughoi/1154343-433031"
50-
wget -O /tmp/tests/test-drug-hoi-pubmed-mesh-clin-trial.json "http://api.ohdsi.org/WebAPI/CS1/evidence/drughoi/789578-378144"
51-
wget -O /tmp/tests/test-drug-hoi-pubmed-mesh-other.json "http://api.ohdsi.org/WebAPI/CS1/evidence/drughoi/19010482-316866"
52-
wget -O /tmp/tests/test-drug-hoi-semmed-cr.json "http://api.ohdsi.org/WebAPI/CS1/evidence/drughoi/1782521-45612000"
53-
wget -O /tmp/tests/test-drug-hoi-semmed-clin-trial.json "http://api.ohdsi.org/WebAPI/CS1/evidence/drughoi/1303425-45616736"
54-
wget -O /tmp/tests/test-drug-rollup-ingredient.json "http://api.ohdsi.org/WebAPI/CS1/evidence/drugrollup/ingredient/1000632"
55-
wget -O /tmp/tests/test-drug-rollup-clin-drug.json "http://api.ohdsi.org/WebAPI/CS1/evidence/drugrollup/clinicaldrug/19074181"
5621
wget -O /tmp/tests/test-drug-rollup-branded-drug.json "http://api.ohdsi.org/WebAPI/CS1/evidence/drugrollup/brandeddrug/1000640"

pom.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,11 @@
192192
<spring.datasource.hikari.register-mbeans>true</spring.datasource.hikari.register-mbeans>
193193
<spring.datasource.hikari.mbean-name>authDataSource</spring.datasource.hikari.mbean-name>
194194

195+
<!-- If defaultGlobalReadPermissions is set to true (default), then all users can see every artifact. -->
196+
<!-- If it is set to false, WebAPI will filter out the artifacts that a user does not explicitly have -->
197+
<!-- read permissions to -->
198+
<security.defaultGlobalReadPermissions>true</security.defaultGlobalReadPermissions>
199+
195200
<!-- EMBEDDED SERVER CONFIGURATION (ServerProperties) -->
196201
<server.port>8080</server.port>
197202
<server.ssl.key-store></server.ssl.key-store>

src/main/java/org/ohdsi/webapi/cohortcharacterization/CcController.java

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,11 @@
4242
import org.ohdsi.webapi.versioning.dto.VersionUpdateDTO;
4343
import org.springframework.core.convert.ConversionService;
4444
import org.springframework.data.domain.Page;
45+
import org.springframework.data.domain.PageImpl;
4546
import org.springframework.data.domain.Pageable;
4647
import org.springframework.stereotype.Controller;
4748
import org.springframework.transaction.annotation.Transactional;
49+
import org.springframework.beans.factory.annotation.Value;
4850
import org.springframework.web.bind.annotation.RequestBody;
4951

5052
import javax.ws.rs.Consumes;
@@ -63,6 +65,7 @@
6365
import java.io.ByteArrayOutputStream;
6466
import java.io.IOException;
6567
import java.io.StringWriter;
68+
import java.util.ArrayList;
6669
import java.util.Collections;
6770
import java.util.List;
6871
import java.util.Map;
@@ -151,11 +154,12 @@ public CohortCharacterizationDTO copy(@PathParam("id") final Long id) {
151154
@Produces(MediaType.APPLICATION_JSON)
152155
@Consumes(MediaType.APPLICATION_JSON)
153156
public Page<CcShortDTO> list(@Pagination Pageable pageable) {
154-
return service.getPage(pageable).map(entity -> {
155-
CcShortDTO dto = convertCcToShortDto(entity);
156-
permissionService.fillWriteAccess(entity, dto);
157-
return dto;
158-
});
157+
return service.getPage(pageable).map(entity -> {
158+
CcShortDTO dto = convertCcToShortDto(entity);
159+
permissionService.fillWriteAccess(entity, dto);
160+
permissionService.fillReadAccess(entity, dto);
161+
return dto;
162+
});
159163
}
160164

161165
/**
@@ -168,7 +172,12 @@ public Page<CcShortDTO> list(@Pagination Pageable pageable) {
168172
@Produces(MediaType.APPLICATION_JSON)
169173
@Consumes(MediaType.APPLICATION_JSON)
170174
public Page<CohortCharacterizationDTO> listDesign(@Pagination Pageable pageable) {
171-
return service.getPageWithLinkedEntities(pageable).map(this::convertCcToDto);
175+
return service.getPageWithLinkedEntities(pageable).map(entity -> {
176+
CohortCharacterizationDTO dto = convertCcToDto(entity);
177+
permissionService.fillWriteAccess(entity, dto);
178+
permissionService.fillReadAccess(entity, dto);
179+
return dto;
180+
});
172181
}
173182

174183
/**

src/main/java/org/ohdsi/webapi/cohortcharacterization/CcServiceImpl.java

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,9 @@
133133
import static org.ohdsi.webapi.Constants.Params.JOB_AUTHOR;
134134
import static org.ohdsi.webapi.Constants.Params.JOB_NAME;
135135
import static org.ohdsi.webapi.Constants.Params.SOURCE_ID;
136+
import org.ohdsi.webapi.security.PermissionService;
137+
import org.springframework.beans.factory.annotation.Value;
138+
import org.springframework.data.domain.PageImpl;
136139

137140
@Service
138141
@Transactional
@@ -207,7 +210,12 @@ public class CcServiceImpl extends AbstractDaoService implements CcService, Gene
207210
private final GenericConversionService genericConversionService;
208211
private final VocabularyService vocabularyService;
209212
private VersionService<CharacterizationVersion> versionService;
213+
214+
private PermissionService permissionService;
210215

216+
@Value("#{'${security.defaultGlobalReadPermissions}'.equals(false)}")
217+
private boolean defaultGlobalReadPermissions;
218+
211219
private final Environment env;
212220

213221
public CcServiceImpl(
@@ -231,6 +239,7 @@ public CcServiceImpl(
231239
final JobInvalidator jobInvalidator,
232240
final VocabularyService vocabularyService,
233241
final VersionService<CharacterizationVersion> versionService,
242+
final PermissionService permissionService,
234243
@Qualifier("conversionService") final GenericConversionService genericConversionService,
235244
Environment env) {
236245
this.repository = ccRepository;
@@ -251,6 +260,7 @@ public CcServiceImpl(
251260
this.eventPublisher = eventPublisher;
252261
this.jobInvalidator = jobInvalidator;
253262
this.vocabularyService = vocabularyService;
263+
this.permissionService = permissionService;
254264
this.genericConversionService = genericConversionService;
255265
this.versionService = versionService;
256266
this.env = env;
@@ -531,12 +541,24 @@ public CohortCharacterization findDesignByGenerationId(@CcGenerationId final Lon
531541

532542
@Override
533543
public Page<CohortCharacterizationEntity> getPageWithLinkedEntities(final Pageable pageable) {
534-
return repository.findAll(pageable, defaultEntityGraph);
544+
return repository.findAll(pageable, defaultEntityGraph);
545+
535546
}
536547

537548
@Override
538549
public Page<CohortCharacterizationEntity> getPage(final Pageable pageable) {
539-
return repository.findAll(pageable);
550+
List<CohortCharacterizationEntity> ccList = repository.findAll()
551+
.stream().filter(!defaultGlobalReadPermissions ? entity -> permissionService.hasReadAccess(entity) : entity -> true)
552+
.collect(Collectors.toList());
553+
return getPageFromResults(pageable, ccList);
554+
}
555+
556+
private Page<CohortCharacterizationEntity> getPageFromResults(Pageable pageable, List<CohortCharacterizationEntity> results) {
557+
// Calculate the start and end indices for the current page
558+
int startIndex = pageable.getPageNumber() * pageable.getPageSize();
559+
int endIndex = Math.min(startIndex + pageable.getPageSize(), results.size());
560+
561+
return new PageImpl<>(results.subList(startIndex, endIndex), pageable, results.size());
540562
}
541563

542564
@Override

src/main/java/org/ohdsi/webapi/estimation/EstimationController.java

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import org.slf4j.Logger;
2323
import org.slf4j.LoggerFactory;
2424
import org.springframework.beans.factory.annotation.Autowired;
25+
import org.springframework.beans.factory.annotation.Value;
2526
import org.springframework.core.convert.support.GenericConversionService;
2627
import org.springframework.stereotype.Controller;
2728

@@ -70,7 +71,10 @@ public class EstimationController {
7071
private final ScriptExecutionService executionService;
7172
private EstimationChecker checker;
7273
private PermissionService permissionService;
73-
74+
75+
@Value("#{'${security.defaultGlobalReadPermissions}'.equals(false)}")
76+
private boolean defaultGlobalReadPermissions;
77+
7478
public EstimationController(EstimationService service,
7579
GenericConversionService conversionService,
7680
CommonGenerationSensitiveInfoService sensitiveInfoService,
@@ -97,11 +101,12 @@ public EstimationController(EstimationService service,
97101
@Path("/")
98102
@Produces(MediaType.APPLICATION_JSON)
99103
public List<EstimationShortDTO> getAnalysisList() {
100-
101104
return StreamSupport.stream(service.getAnalysisList().spliterator(), false)
105+
.filter(!defaultGlobalReadPermissions ? entity -> permissionService.hasReadAccess(entity) : entity -> true)
102106
.map(analysis -> {
103107
EstimationShortDTO dto = conversionService.convert(analysis, EstimationShortDTO.class);
104108
permissionService.fillWriteAccess(analysis, dto);
109+
permissionService.fillReadAccess(analysis, dto);
105110
return dto;
106111
})
107112
.collect(Collectors.toList());

src/main/java/org/ohdsi/webapi/pathway/PathwayController.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,15 +25,18 @@
2525
import org.ohdsi.webapi.versioning.dto.VersionDTO;
2626
import org.ohdsi.webapi.versioning.dto.VersionUpdateDTO;
2727
import org.springframework.beans.factory.annotation.Autowired;
28+
import org.springframework.beans.factory.annotation.Value;
2829
import org.springframework.core.convert.ConversionService;
2930
import org.springframework.data.domain.Page;
31+
import org.springframework.data.domain.PageImpl;
3032
import org.springframework.data.domain.Pageable;
3133
import org.springframework.stereotype.Controller;
3234
import org.springframework.web.bind.annotation.RequestBody;
3335

3436
import javax.transaction.Transactional;
3537
import javax.ws.rs.*;
3638
import javax.ws.rs.core.MediaType;
39+
import java.util.ArrayList;
3740
import java.util.Collections;
3841
import java.util.List;
3942
import java.util.Map;
@@ -156,13 +159,14 @@ public PathwayAnalysisDTO importAnalysis(final PathwayAnalysisExportDTO dto) {
156159
@Consumes(MediaType.APPLICATION_JSON)
157160
@Transactional
158161
public Page<PathwayAnalysisDTO> list(@Pagination Pageable pageable) {
159-
160162
return pathwayService.getPage(pageable).map(pa -> {
161163
PathwayAnalysisDTO dto = conversionService.convert(pa, PathwayAnalysisDTO.class);
162164
permissionService.fillWriteAccess(pa, dto);
165+
permissionService.fillReadAccess(pa, dto);
163166
return dto;
164167
});
165168
}
169+
166170

167171
/**
168172
* Check that a pathway analysis name exists.

src/main/java/org/ohdsi/webapi/pathway/PathwayServiceImpl.java

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,10 @@
9696
import static org.ohdsi.webapi.Constants.Params.JOB_NAME;
9797
import static org.ohdsi.webapi.Constants.Params.PATHWAY_ANALYSIS_ID;
9898
import static org.ohdsi.webapi.Constants.Params.SOURCE_ID;
99+
import org.ohdsi.webapi.cohortcharacterization.domain.CohortCharacterizationEntity;
100+
import org.ohdsi.webapi.security.PermissionService;
101+
import org.springframework.beans.factory.annotation.Value;
102+
import org.springframework.data.domain.PageImpl;
99103

100104
@Service
101105
@Transactional
@@ -116,6 +120,11 @@ public class PathwayServiceImpl extends AbstractDaoService implements PathwaySer
116120
private final CohortDefinitionService cohortDefinitionService;
117121
private final VersionService<PathwayVersion> versionService;
118122

123+
private PermissionService permissionService;
124+
125+
@Value("#{'${security.defaultGlobalReadPermissions}'.equals(false)}")
126+
private boolean defaultGlobalReadPermissions;
127+
119128
private final List<String> STEP_COLUMNS = Arrays.asList(new String[]{"step_1", "step_2", "step_3", "step_4", "step_5", "step_6", "step_7", "step_8", "step_9", "step_10"});
120129

121130
private final EntityGraph defaultEntityGraph = EntityUtils.fromAttributePaths(
@@ -142,7 +151,8 @@ public PathwayServiceImpl(
142151
@Qualifier("conversionService") GenericConversionService genericConversionService,
143152
StepBuilderFactory stepBuilderFactory,
144153
CohortDefinitionService cohortDefinitionService,
145-
VersionService<PathwayVersion> versionService) {
154+
VersionService<PathwayVersion> versionService,
155+
PermissionService permissionService) {
146156

147157
this.pathwayAnalysisRepository = pathwayAnalysisRepository;
148158
this.pathwayAnalysisGenerationRepository = pathwayAnalysisGenerationRepository;
@@ -159,6 +169,7 @@ public PathwayServiceImpl(
159169
this.stepBuilderFactory = stepBuilderFactory;
160170
this.cohortDefinitionService = cohortDefinitionService;
161171
this.versionService = versionService;
172+
this.permissionService = permissionService;
162173

163174
SerializedPathwayAnalysisToPathwayAnalysisConverter.setConversionService(conversionService);
164175
}
@@ -218,8 +229,18 @@ public PathwayAnalysisEntity importAnalysis(PathwayAnalysisEntity toImport) {
218229

219230
@Override
220231
public Page<PathwayAnalysisEntity> getPage(final Pageable pageable) {
232+
List<PathwayAnalysisEntity> pathwayList = pathwayAnalysisRepository.findAll(defaultEntityGraph)
233+
.stream().filter(!defaultGlobalReadPermissions ? entity -> permissionService.hasReadAccess(entity) : entity -> true)
234+
.collect(Collectors.toList());
235+
return getPageFromResults(pageable, pathwayList);
236+
}
237+
238+
private Page<PathwayAnalysisEntity> getPageFromResults(Pageable pageable, List<PathwayAnalysisEntity> results) {
239+
// Calculate the start and end indices for the current page
240+
int startIndex = pageable.getPageNumber() * pageable.getPageSize();
241+
int endIndex = Math.min(startIndex + pageable.getPageSize(), results.size());
221242

222-
return pathwayAnalysisRepository.findAll(pageable, defaultEntityGraph);
243+
return new PageImpl<>(results.subList(startIndex, endIndex), pageable, results.size());
223244
}
224245

225246
@Override

src/main/java/org/ohdsi/webapi/prediction/PredictionController.java

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import org.slf4j.Logger;
2222
import org.slf4j.LoggerFactory;
2323
import org.springframework.beans.factory.annotation.Autowired;
24+
import org.springframework.beans.factory.annotation.Value;
2425
import org.springframework.core.convert.support.GenericConversionService;
2526
import org.springframework.stereotype.Controller;
2627

@@ -67,6 +68,9 @@ public class PredictionController {
6768

6869
private PermissionService permissionService;
6970

71+
@Value("#{'${security.defaultGlobalReadPermissions}'.equals(false)}")
72+
private boolean defaultGlobalReadPermissions;
73+
7074
@Autowired
7175
public PredictionController(PredictionService service,
7276
GenericConversionService conversionService,
@@ -94,24 +98,26 @@ public PredictionController(PredictionService service,
9498
@Path("/")
9599
@Produces(MediaType.APPLICATION_JSON)
96100
public List<CommonAnalysisDTO> getAnalysisList() {
97-
98101
return StreamSupport
99102
.stream(service.getAnalysisList().spliterator(), false)
103+
.filter(!defaultGlobalReadPermissions ? entity -> permissionService.hasReadAccess(entity) : entity -> true)
100104
.map(analysis -> {
101105
CommonAnalysisDTO dto = conversionService.convert(analysis, CommonAnalysisDTO.class);
102106
permissionService.fillWriteAccess(analysis, dto);
107+
permissionService.fillReadAccess(analysis, dto);
103108
return dto;
104109
})
105110
.collect(Collectors.toList());
106111
}
107112

108113
/**
109114
* Check to see if a prediction design exists by name
110-
*
115+
*
111116
* @summary Prediction design exists by name
112117
* @param id The prediction design id
113118
* @param name The prediction design name
114-
* @return 1 if a prediction design with the given name and id exist in WebAPI and 0 otherwise
119+
* @return 1 if a prediction design with the given name and id exist in WebAPI
120+
* and 0 otherwise
115121
*/
116122
@GET
117123
@Path("/{id}/exists")

0 commit comments

Comments
 (0)