diff --git a/dsf-bpe/dsf-bpe-server/src/main/java/dev/dsf/bpe/subscription/TaskHandler.java b/dsf-bpe/dsf-bpe-server/src/main/java/dev/dsf/bpe/subscription/TaskHandler.java index 78b6948e0..cc6ef2d96 100644 --- a/dsf-bpe/dsf-bpe-server/src/main/java/dev/dsf/bpe/subscription/TaskHandler.java +++ b/dsf-bpe/dsf-bpe-server/src/main/java/dev/dsf/bpe/subscription/TaskHandler.java @@ -312,8 +312,7 @@ private ProcessDefinition getProcessDefinition(String processDomain, String proc if (processVersion != null && !processVersion.isBlank()) return repositoryService.createProcessDefinitionQuery().active() .processDefinitionKey(processDomain + "_" + processDefinitionKey).versionTag(processVersion).list() - .stream().sorted(Comparator.comparing(ProcessDefinition::getVersion).reversed()).findFirst() - .orElse(null); + .stream().max(Comparator.comparing(ProcessDefinition::getVersion)).orElse(null); else return repositoryService.createProcessDefinitionQuery().active() .processDefinitionKey(processDomain + "_" + processDefinitionKey).latestVersion().singleResult(); diff --git a/dsf-bpe/dsf-bpe-server/src/main/java/dev/dsf/bpe/webservice/ProcessService.java b/dsf-bpe/dsf-bpe-server/src/main/java/dev/dsf/bpe/webservice/ProcessService.java index 72291f8fc..f4a58a3b5 100755 --- a/dsf-bpe/dsf-bpe-server/src/main/java/dev/dsf/bpe/webservice/ProcessService.java +++ b/dsf-bpe/dsf-bpe-server/src/main/java/dev/dsf/bpe/webservice/ProcessService.java @@ -4,6 +4,7 @@ import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.Base64; +import java.util.Comparator; import java.util.Objects; import java.util.function.Consumer; @@ -101,7 +102,8 @@ private ProcessDefinition getProcessDefinition(String processDefinitionKey, Stri { if (versionTag != null && !versionTag.isBlank()) return repositoryService.createProcessDefinitionQuery().processDefinitionKey(processDefinitionKey) - .versionTag(versionTag).singleResult(); + .versionTag(versionTag).list().stream().max(Comparator.comparing(ProcessDefinition::getVersion)) + .orElse(null); else return repositoryService.createProcessDefinitionQuery().processDefinitionKey(processDefinitionKey) .latestVersion().singleResult(); diff --git a/dsf-bpe/dsf-bpe-server/src/main/java/dev/dsf/bpe/webservice/RootService.java b/dsf-bpe/dsf-bpe-server/src/main/java/dev/dsf/bpe/webservice/RootService.java index f77c6db58..bd85aabd1 100644 --- a/dsf-bpe/dsf-bpe-server/src/main/java/dev/dsf/bpe/webservice/RootService.java +++ b/dsf-bpe/dsf-bpe-server/src/main/java/dev/dsf/bpe/webservice/RootService.java @@ -74,7 +74,7 @@ private List processes() return repositoryService.createProcessDefinitionQuery().active().unlimitedList().stream() .map(def -> new ProcessEntry("Process/" + def.getKey() + "/" + def.getVersionTag(), def.getKey() + " | " + def.getVersionTag())) - .sorted(Comparator.comparing(ProcessEntry::value)).toList(); + .sorted(Comparator.comparing(ProcessEntry::value)).distinct().toList(); } private List processInstances() diff --git a/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/adapter/AbstractSearchSet.java b/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/adapter/AbstractSearchSet.java index a00ee5e29..9fdf9167d 100644 --- a/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/adapter/AbstractSearchSet.java +++ b/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/adapter/AbstractSearchSet.java @@ -96,9 +96,9 @@ protected void doSetVariables(BiConsumer variables, Bundle resou int currentPage = getPage(params); int count = getCount(params, defaultPageCount); - int maxPages = (int) Math.ceil((double) resource.getTotal() / count); + int maxPages = count <= 0 ? 0 : (int) Math.ceil((double) resource.getTotal() / count); int offset = Math.multiplyExact(currentPage - 1, count); - int firstResource = Integer.MAX_VALUE - 1 < offset ? 0 : offset + 1; + int firstResource = count == 0 ? 0 : Integer.MAX_VALUE - 1 < offset ? 0 : offset + 1; int lastResource = Integer.MAX_VALUE - matchResources.size() < offset ? 0 : offset + matchResources.size(); long includeResources = resource.getEntry().stream().filter( @@ -142,11 +142,11 @@ private int getPage(MultivaluedMap params) } catch (NumberFormatException e) { - return 1; + return 0; } } else - return 1; + return 0; } private int getCount(MultivaluedMap params, int defaultPageCount) diff --git a/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/authorization/AbstractAuthorizationRule.java b/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/authorization/AbstractAuthorizationRule.java index 558083ada..b2343cc27 100644 --- a/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/authorization/AbstractAuthorizationRule.java +++ b/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/authorization/AbstractAuthorizationRule.java @@ -33,6 +33,7 @@ import dev.dsf.fhir.dao.ResourceDao; import dev.dsf.fhir.dao.provider.DaoProvider; import dev.dsf.fhir.help.ParameterConverter; +import dev.dsf.fhir.search.PageAndCount; import dev.dsf.fhir.search.PartialResult; import dev.dsf.fhir.search.SearchQuery; import dev.dsf.fhir.search.SearchQueryParameterError; @@ -189,7 +190,7 @@ protected final boolean organizationWithIdentifierExists(Connection connection, Map> queryParameters = Map.of("identifier", Collections.singletonList(iSystem + "|" + iValue)); OrganizationDao dao = daoProvider.getOrganizationDao(); - SearchQuery query = dao.createSearchQueryWithoutUserFilter(0, 0) + SearchQuery query = dao.createSearchQueryWithoutUserFilter(PageAndCount.exists()) .configureParameters(queryParameters); List uQp = query.getUnsupportedQueryParameters(); @@ -223,7 +224,7 @@ protected final boolean roleExists(Connection connection, Coding coding) Map> queryParameters = Map.of("url", Collections.singletonList(cSystem + (coding.hasVersion() ? "|" + cVersion : ""))); CodeSystemDao dao = daoProvider.getCodeSystemDao(); - SearchQuery query = dao.createSearchQueryWithoutUserFilter(1, 1) + SearchQuery query = dao.createSearchQueryWithoutUserFilter(PageAndCount.single()) .configureParameters(queryParameters); List uQp = query.getUnsupportedQueryParameters(); diff --git a/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/authorization/EndpointAuthorizationRule.java b/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/authorization/EndpointAuthorizationRule.java index 70b271a18..9c663bea3 100644 --- a/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/authorization/EndpointAuthorizationRule.java +++ b/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/authorization/EndpointAuthorizationRule.java @@ -21,6 +21,7 @@ import dev.dsf.fhir.dao.EndpointDao; import dev.dsf.fhir.dao.provider.DaoProvider; import dev.dsf.fhir.help.ParameterConverter; +import dev.dsf.fhir.search.PageAndCount; import dev.dsf.fhir.search.PartialResult; import dev.dsf.fhir.search.SearchQuery; import dev.dsf.fhir.search.SearchQueryParameterError; @@ -31,7 +32,7 @@ public class EndpointAuthorizationRule extends AbstractMetaTagAuthorizationRule< private static final Logger logger = LoggerFactory.getLogger(EndpointAuthorizationRule.class); private static final String ENDPOINT_IDENTIFIER_SYSTEM = "http://dsf.dev/sid/endpoint-identifier"; - private static final String ENDPOINT_ADDRESS_PATTERN_STRING = "https://([0-9a-zA-Z\\.-]+)+(:\\d{1,4})?([-\\w/]*)"; + private static final String ENDPOINT_ADDRESS_PATTERN_STRING = "https://[0-9a-zA-Z.-]+(?::\\d{1,4})?[-\\w/]*"; private static final Pattern ENDPOINT_ADDRESS_PATTERN = Pattern.compile(ENDPOINT_ADDRESS_PATTERN_STRING); public EndpointAuthorizationRule(DaoProvider daoProvider, String serverBase, ReferenceResolver referenceResolver, @@ -111,7 +112,8 @@ private boolean endpointWithAddressExists(Connection connection, String address) { Map> queryParameters = Map.of("address", Collections.singletonList(address)); EndpointDao dao = getDao(); - SearchQuery query = dao.createSearchQueryWithoutUserFilter(0, 0).configureParameters(queryParameters); + SearchQuery query = dao.createSearchQueryWithoutUserFilter(PageAndCount.exists()) + .configureParameters(queryParameters); List uQp = query.getUnsupportedQueryParameters(); if (!uQp.isEmpty()) @@ -140,7 +142,8 @@ private boolean endpointWithIdentifierExists(Connection connection, String ident Map> queryParameters = Map.of("identifier", Collections.singletonList(ENDPOINT_IDENTIFIER_SYSTEM + "|" + identifierValue)); EndpointDao dao = getDao(); - SearchQuery query = dao.createSearchQueryWithoutUserFilter(0, 0).configureParameters(queryParameters); + SearchQuery query = dao.createSearchQueryWithoutUserFilter(PageAndCount.exists()) + .configureParameters(queryParameters); List uQp = query.getUnsupportedQueryParameters(); if (!uQp.isEmpty()) diff --git a/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/authorization/OrganizationAffiliationAuthorizationRule.java b/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/authorization/OrganizationAffiliationAuthorizationRule.java index fdfe258db..afa3fab62 100644 --- a/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/authorization/OrganizationAffiliationAuthorizationRule.java +++ b/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/authorization/OrganizationAffiliationAuthorizationRule.java @@ -23,6 +23,7 @@ import dev.dsf.fhir.dao.OrganizationAffiliationDao; import dev.dsf.fhir.dao.provider.DaoProvider; import dev.dsf.fhir.help.ParameterConverter; +import dev.dsf.fhir.search.PageAndCount; import dev.dsf.fhir.search.PartialResult; import dev.dsf.fhir.search.SearchQuery; import dev.dsf.fhir.search.SearchQueryParameterError; @@ -154,7 +155,7 @@ private Map> queryParameters(OrganizationAffiliation newRes private boolean organizationAffiliationExists(Connection connection, Map> queryParameters) { OrganizationAffiliationDao dao = getDao(); - SearchQuery query = dao.createSearchQueryWithoutUserFilter(0, 0) + SearchQuery query = dao.createSearchQueryWithoutUserFilter(PageAndCount.exists()) .configureParameters(queryParameters); List uQp = query.getUnsupportedQueryParameters(); diff --git a/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/authorization/SubscriptionAuthorizationRule.java b/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/authorization/SubscriptionAuthorizationRule.java index 319bc0ade..c1e1c978f 100644 --- a/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/authorization/SubscriptionAuthorizationRule.java +++ b/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/authorization/SubscriptionAuthorizationRule.java @@ -24,6 +24,7 @@ import dev.dsf.fhir.dao.SubscriptionDao; import dev.dsf.fhir.dao.provider.DaoProvider; import dev.dsf.fhir.help.ParameterConverter; +import dev.dsf.fhir.search.PageAndCount; import dev.dsf.fhir.search.PartialResult; import dev.dsf.fhir.search.SearchQuery; import dev.dsf.fhir.search.SearchQueryParameterError; @@ -87,7 +88,8 @@ private Optional newResourceOk(Connection connection, Subscription newRe Optional> optDao = daoProvider.getDao(cComponentes.getPathSegments().get(0)); if (optDao.isPresent()) { - SearchQuery searchQuery = optDao.get().createSearchQueryWithoutUserFilter(1, 1); + SearchQuery searchQuery = optDao.get().createSearchQueryWithoutUserFilter(PageAndCount.exists()) + .configureParameters(cComponentes.getQueryParams()); List uQp = searchQuery.getUnsupportedQueryParameters(); if (!uQp.isEmpty()) { @@ -131,7 +133,7 @@ protected boolean resourceExists(Connection connection, Subscription newResource Collections.singletonList(newResource.getChannel().getType().toCode()), "payload", Collections.singletonList(newResource.getChannel().getPayload())); SubscriptionDao dao = getDao(); - SearchQuery query = dao.createSearchQueryWithoutUserFilter(1, 1) + SearchQuery query = dao.createSearchQueryWithoutUserFilter(PageAndCount.exists()) .configureParameters(queryParameters); List uQp = query.getUnsupportedQueryParameters(); diff --git a/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/dao/ResourceDao.java b/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/dao/ResourceDao.java index d937ff90b..4151d8399 100755 --- a/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/dao/ResourceDao.java +++ b/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/dao/ResourceDao.java @@ -14,6 +14,7 @@ import dev.dsf.fhir.dao.exception.ResourceNotMarkedDeletedException; import dev.dsf.fhir.dao.exception.ResourceVersionNoMatchException; import dev.dsf.fhir.search.DbSearchQuery; +import dev.dsf.fhir.search.PageAndCount; import dev.dsf.fhir.search.PartialResult; import dev.dsf.fhir.search.SearchQuery; @@ -355,9 +356,21 @@ R updateWithTransaction(Connection connection, R resource, Long expectedVersion) */ PartialResult searchWithTransaction(Connection connection, DbSearchQuery query) throws SQLException; - SearchQuery createSearchQuery(Identity identity, int page, int count); + /** + * @param identity + * not null + * @param pageAndCount + * not null + * @return query + */ + SearchQuery createSearchQuery(Identity identity, PageAndCount pageAndCount); - SearchQuery createSearchQueryWithoutUserFilter(int page, int count); + /** + * @param pageAndCount + * not null + * @return query + */ + SearchQuery createSearchQueryWithoutUserFilter(PageAndCount pageAndCount); /** * Permanently delete a resource that was previously marked as deleted. diff --git a/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/dao/command/CreateCommand.java b/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/dao/command/CreateCommand.java index c8dfde974..f89e0d129 100755 --- a/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/dao/command/CreateCommand.java +++ b/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/dao/command/CreateCommand.java @@ -33,6 +33,7 @@ import dev.dsf.fhir.help.ParameterConverter; import dev.dsf.fhir.help.ResponseGenerator; import dev.dsf.fhir.prefer.PreferReturnType; +import dev.dsf.fhir.search.PageAndCount; import dev.dsf.fhir.search.PartialResult; import dev.dsf.fhir.search.SearchQuery; import dev.dsf.fhir.search.SearchQueryParameterError; @@ -199,7 +200,7 @@ private Optional checkAlreadyExists(Connection connection, String ifNo .collect(Collectors.toMap(Entry::getKey, Entry::getValue)); } - SearchQuery query = dao.createSearchQueryWithoutUserFilter(1, 1); + SearchQuery query = dao.createSearchQueryWithoutUserFilter(PageAndCount.single()); query.configureParameters(queryParameters); List unsupportedQueryParameters = query.getUnsupportedQueryParameters(); diff --git a/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/dao/command/DeleteCommand.java b/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/dao/command/DeleteCommand.java index 18962bc96..d1ce790c3 100755 --- a/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/dao/command/DeleteCommand.java +++ b/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/dao/command/DeleteCommand.java @@ -31,6 +31,7 @@ import dev.dsf.fhir.help.ParameterConverter; import dev.dsf.fhir.help.ResponseGenerator; import dev.dsf.fhir.prefer.PreferReturnType; +import dev.dsf.fhir.search.PageAndCount; import dev.dsf.fhir.search.PartialResult; import dev.dsf.fhir.search.SearchQuery; import dev.dsf.fhir.search.SearchQueryParameterError; @@ -172,7 +173,7 @@ private Optional search(Connection connection, ResourceDao dao, .collect(Collectors.toMap(Entry::getKey, Entry::getValue)); } - SearchQuery query = dao.createSearchQueryWithoutUserFilter(1, 1); + SearchQuery query = dao.createSearchQueryWithoutUserFilter(PageAndCount.single()); query.configureParameters(queryParameters); List unsupportedQueryParameters = query.getUnsupportedQueryParameters(); diff --git a/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/dao/command/ReadCommand.java b/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/dao/command/ReadCommand.java index acd56321e..e23704f73 100755 --- a/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/dao/command/ReadCommand.java +++ b/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/dao/command/ReadCommand.java @@ -30,6 +30,7 @@ import dev.dsf.fhir.help.ResponseGenerator; import dev.dsf.fhir.prefer.PreferHandlingType; import dev.dsf.fhir.prefer.PreferReturnType; +import dev.dsf.fhir.search.PageAndCount; import dev.dsf.fhir.search.PartialResult; import dev.dsf.fhir.search.SearchQuery; import dev.dsf.fhir.search.SearchQueryParameterError; @@ -193,14 +194,8 @@ private void readByCondition(Connection connection, String resourceTypeName, responseResult = Response.status(Status.NOT_FOUND).build(); else { - Integer page = parameterConverter.getFirstInt(cleanQueryParameters, SearchQuery.PARAMETER_PAGE); - int effectivePage = page == null ? 1 : page; - - Integer count = parameterConverter.getFirstInt(cleanQueryParameters, SearchQuery.PARAMETER_COUNT); - int effectiveCount = (count == null || count < 0) ? defaultPageCount : count; - - SearchQuery query = optDao.get().createSearchQuery(identity, effectivePage, - effectiveCount); + SearchQuery query = optDao.get().createSearchQuery(identity, + PageAndCount.from(cleanQueryParameters, defaultPageCount)); query.configureParameters(cleanQueryParameters); List errors = query.getUnsupportedQueryParameters(); diff --git a/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/dao/command/UpdateCommand.java b/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/dao/command/UpdateCommand.java index 826a0e435..b5cd55580 100755 --- a/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/dao/command/UpdateCommand.java +++ b/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/dao/command/UpdateCommand.java @@ -35,6 +35,7 @@ import dev.dsf.fhir.help.ParameterConverter; import dev.dsf.fhir.help.ResponseGenerator; import dev.dsf.fhir.prefer.PreferReturnType; +import dev.dsf.fhir.search.PageAndCount; import dev.dsf.fhir.search.PartialResult; import dev.dsf.fhir.search.SearchQuery; import dev.dsf.fhir.search.SearchQueryParameterError; @@ -151,7 +152,7 @@ private boolean addMissingIdToTranslationTableAndCheckConditionFindsResource(Map .collect(Collectors.toMap(Entry::getKey, Entry::getValue)); } - SearchQuery query = dao.createSearchQueryWithoutUserFilter(1, 1); + SearchQuery query = dao.createSearchQueryWithoutUserFilter(PageAndCount.single()); query.configureParameters(queryParameters); List unsupportedParams = query.getUnsupportedQueryParameters(); diff --git a/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/dao/jdbc/AbstractResourceDaoJdbc.java b/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/dao/jdbc/AbstractResourceDaoJdbc.java index d65ecdd2e..679daa506 100755 --- a/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/dao/jdbc/AbstractResourceDaoJdbc.java +++ b/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/dao/jdbc/AbstractResourceDaoJdbc.java @@ -40,6 +40,7 @@ import dev.dsf.fhir.dao.exception.ResourceNotMarkedDeletedException; import dev.dsf.fhir.dao.exception.ResourceVersionNoMatchException; import dev.dsf.fhir.search.DbSearchQuery; +import dev.dsf.fhir.search.PageAndCount; import dev.dsf.fhir.search.PartialResult; import dev.dsf.fhir.search.SearchQuery; import dev.dsf.fhir.search.SearchQuery.SearchQueryBuilder; @@ -898,20 +899,24 @@ private void getResources(ResultSet result, int columnIndex, List createSearchQuery(Identity identity, int page, int count) + public final SearchQuery createSearchQuery(Identity identity, PageAndCount pageAndCount) { - return doCreateSearchQuery(identity, page, count); + Objects.requireNonNull(identity, "identity"); + + return doCreateSearchQuery(identity, pageAndCount); } @Override - public SearchQuery createSearchQueryWithoutUserFilter(int page, int count) + public SearchQuery createSearchQueryWithoutUserFilter(PageAndCount pageAndCount) { - return doCreateSearchQuery(null, page, count); + return doCreateSearchQuery(null, pageAndCount); } - private SearchQuery doCreateSearchQuery(Identity identity, int page, int count) + private SearchQuery doCreateSearchQuery(Identity identity, PageAndCount pageAndCount) { - var builder = SearchQueryBuilder.create(resourceType, getResourceTable(), getResourceColumn(), page, count); + Objects.requireNonNull(pageAndCount, "pageAndCount"); + + var builder = SearchQueryBuilder.create(resourceType, getResourceTable(), getResourceColumn(), pageAndCount); if (identity != null) builder = builder.with(identityFilter.apply(identity)); diff --git a/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/help/ResponseGenerator.java b/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/help/ResponseGenerator.java index f074e8023..8686c9998 100755 --- a/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/help/ResponseGenerator.java +++ b/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/help/ResponseGenerator.java @@ -186,8 +186,7 @@ public Bundle createSearchSet(PartialResult result, List 0) + boolean countOnly = pageAndCount.isCountOnly(total); + if (!countOnly) { bundleUri = bundleUri.replaceQueryParam("_count", pageAndCount.getCount()); - bundleUri = bundleUri.replaceQueryParam("_page", isEmpty ? 1 : pageAndCount.getPage()); + bundleUri = bundleUri.replaceQueryParam("_page", pageAndCount.getPage()); } else bundleUri = bundleUri.replaceQueryParam("_count", "0"); bundle.addLink().setRelation("self").setUrlElement(new UriType(bundleUri.build())); - if (pageAndCount.getCount() > 0 && !isEmpty) + if (!countOnly && pageAndCount.getCount() > 0) { bundleUri = bundleUri.replaceQueryParam("_page", 1); bundleUri = bundleUri.replaceQueryParam("_count", pageAndCount.getCount()); diff --git a/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/history/HistoryServiceImpl.java b/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/history/HistoryServiceImpl.java index 65b97ba5c..b2681fcda 100644 --- a/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/history/HistoryServiceImpl.java +++ b/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/history/HistoryServiceImpl.java @@ -87,13 +87,7 @@ public Bundle getHistory(Identity identity, UriInfo uri, HttpHeaders headers, { MultivaluedMap queryParameters = uri.getQueryParameters(); - Integer page = parameterConverter.getFirstInt(queryParameters, SearchQuery.PARAMETER_PAGE); - int effectivePage = page == null ? 1 : page; - - Integer count = parameterConverter.getFirstInt(queryParameters, SearchQuery.PARAMETER_COUNT); - int effectiveCount = count == null || count < 0 ? defaultPageCount : count; - - PageAndCount pageAndCount = new PageAndCount(effectivePage, effectiveCount); + PageAndCount pageAndCount = PageAndCount.from(uri.getQueryParameters(), defaultPageCount); List atParameters = new ArrayList<>(); SinceParameter sinceParameter = new SinceParameter(); diff --git a/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/search/PageAndCount.java b/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/search/PageAndCount.java index 10d67b13b..c1847b5ce 100755 --- a/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/search/PageAndCount.java +++ b/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/search/PageAndCount.java @@ -1,50 +1,110 @@ package dev.dsf.fhir.search; +import java.util.Collections; +import java.util.List; +import java.util.Map; + public class PageAndCount { - private final int page; - private final int count; + private static final PageAndCount EXISTS = new PageAndCount(0, 0, 0); + private static final PageAndCount SINGLE = new PageAndCount(1, 1, 1); - public PageAndCount(int page, int count) + public static PageAndCount single() { - this.page = page; - this.count = count; + return SINGLE; } - public String getSql() + public static PageAndCount exists() { - return " LIMIT " + count + (page > 1 ? " OFFSET " + ((page - 1) * count) : ""); + return EXISTS; } - public boolean isCountOnly(int total) + public static PageAndCount from(Map> queryParameters, int defaultPageCount) { - return page < 1 || count < 1 || getPageStart() > total; + Integer page = getFirstInt(queryParameters, SearchQuery.PARAMETER_PAGE); + Integer count = getFirstInt(queryParameters, SearchQuery.PARAMETER_COUNT); + + return new PageAndCount(page, count, defaultPageCount); } - public int getPage() + private static Integer getFirstInt(Map> queryParameters, String key) { - return page; + List values = queryParameters.getOrDefault(key, Collections.emptyList()); + if (values.isEmpty()) + return null; + else + { + // TODO control flow by exception + try + { + return Integer.valueOf(values.get(0)); + } + catch (NumberFormatException e) + { + return null; + } + } } - public int getCount() + /** + * @param page + * @param count + * @return + * @throws ArithmeticException + * if page * count > Integer.MAX_VALUE + * @see Math#multiplyExact(int, int) + */ + public static PageAndCount from(int page, int count) { - return count; + Math.multiplyExact(page, count); // throws ArithmeticException + + return new PageAndCount(page, count, count); } - public int getPageStart() + + private final int page; + private final int count; + + private PageAndCount(Integer page, Integer count, int defaultPageCount) { - if (page < 1 || count < 1) - return 0; + int effectivePage = page == null ? 1 : page < 0 ? 0 : page; + int effectiveCount = count == null ? defaultPageCount : count < 0 ? 0 : count; - return Math.min(1, page - 1) * count + 1; + // Bundle.total is an unsigned integer, so we can't access more resources than Integer.MAX_VALUE + long testOverflow = (long) effectivePage * (long) effectiveCount; + if (testOverflow > Integer.MAX_VALUE) + { + effectivePage = 1; + effectiveCount = defaultPageCount; + } + + this.page = effectivePage; + this.count = effectiveCount; } - public int getPageEnd() + public String getSql() { - if (page < 1 || count < 1) - return 0; + return " LIMIT " + count + (page > 1 ? " OFFSET " + getOffset() : ""); + } + + private int getOffset() + { + return (page - 1) * count; + } + + public boolean isCountOnly(int total) + { + return page < 1 || count < 1 || page > getLastPage(total); + } - return getPageStart() - 1 + count; + public int getPage() + { + return page; + } + + public int getCount() + { + return count; } public boolean isLastPage(int total) @@ -54,6 +114,6 @@ public boolean isLastPage(int total) public int getLastPage(int total) { - return (int) Math.ceil((double) total / count); + return count < 1 ? 0 : (int) Math.ceil((double) total / count); } } \ No newline at end of file diff --git a/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/search/SearchQuery.java b/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/search/SearchQuery.java index d70f2cbb4..40669b5e6 100755 --- a/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/search/SearchQuery.java +++ b/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/search/SearchQuery.java @@ -48,32 +48,30 @@ public class SearchQuery implements DbSearchQuery, Matcher public static class SearchQueryBuilder { public static SearchQueryBuilder create(Class resourceType, String resourceTable, - String resourceColumn, int page, int count) + String resourceColumn, PageAndCount pageAndCount) { - return new SearchQueryBuilder<>(resourceType, resourceTable, resourceColumn, page, count); + return new SearchQueryBuilder<>(resourceType, resourceTable, resourceColumn, pageAndCount); } private final Class resourceType; private final String resourceTable; private final String resourceColumn; - private final int page; - private final int count; + private final PageAndCount pageAndCount; private final List> searchParameters = new ArrayList<>(); private final List revIncludeParameters = new ArrayList<>(); private SearchQueryIdentityFilter identityFilter; // may be null - private SearchQueryBuilder(Class resourceType, String resourceTable, String resourceColumn, int page, - int count) + private SearchQueryBuilder(Class resourceType, String resourceTable, String resourceColumn, + PageAndCount pageAndCount) { this.resourceType = resourceType; this.resourceTable = resourceTable; this.resourceColumn = resourceColumn; - this.page = page; - this.count = count; + this.pageAndCount = pageAndCount; } public SearchQueryBuilder with(SearchQueryIdentityFilter identityFilter) @@ -119,7 +117,7 @@ public SearchQueryBuilder withRevInclude(List build() { - return new SearchQuery<>(resourceType, resourceTable, resourceColumn, identityFilter, page, count, + return new SearchQuery<>(resourceType, resourceTable, resourceColumn, identityFilter, pageAndCount, searchParameters, revIncludeParameters); } } @@ -151,7 +149,7 @@ public SearchQuery build() private String revIncludeSql; SearchQuery(Class resourceType, String resourceTable, String resourceColumn, - SearchQueryIdentityFilter identityFilter, int page, int count, + SearchQueryIdentityFilter identityFilter, PageAndCount pageAndCount, List> searchParameterFactories, List searchRevIncludeParameterFactories) { @@ -161,7 +159,7 @@ public SearchQuery build() this.identityFilter = identityFilter; - this.pageAndCount = new PageAndCount(page, count); + this.pageAndCount = pageAndCount; if (searchParameterFactories != null) { diff --git a/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/search/SearchQueryParameterFactory.java b/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/search/SearchQueryParameterFactory.java index 563e1d25a..9388622bd 100644 --- a/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/search/SearchQueryParameterFactory.java +++ b/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/search/SearchQueryParameterFactory.java @@ -107,7 +107,7 @@ public SearchQuerySortParameter createQuerySortParameter() public boolean isIncludeParameter() { - return includeSupplier != null && includeParameterValues != null; + return includeSupplier != null; } public SearchQueryIncludeParameter createQueryIncludeParameter() diff --git a/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/search/SearchQueryRevIncludeParameterFactory.java b/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/search/SearchQueryRevIncludeParameterFactory.java index 5dae3158b..3177260fc 100644 --- a/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/search/SearchQueryRevIncludeParameterFactory.java +++ b/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/search/SearchQueryRevIncludeParameterFactory.java @@ -37,7 +37,7 @@ public Stream getRevIncludeParameterValues() public boolean isIncludeParameter() { - return revIncludeSupplier != null && revIncludeParameterValues != null; + return revIncludeSupplier != null; } public SearchQueryRevIncludeParameter createQueryRevIncludeParameter() diff --git a/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/service/ReferenceResolverImpl.java b/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/service/ReferenceResolverImpl.java index f06810925..b8f59320c 100755 --- a/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/service/ReferenceResolverImpl.java +++ b/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/service/ReferenceResolverImpl.java @@ -31,6 +31,7 @@ import dev.dsf.fhir.help.ExceptionHandler; import dev.dsf.fhir.help.ParameterConverter; import dev.dsf.fhir.help.ResponseGenerator; +import dev.dsf.fhir.search.PageAndCount; import dev.dsf.fhir.search.PartialResult; import dev.dsf.fhir.search.SearchQuery; import dev.dsf.fhir.search.SearchQueryParameterError; @@ -323,7 +324,7 @@ private Optional search(Identity identity, Connection connection, Reso .collect(Collectors.toMap(Entry::getKey, Entry::getValue)); } - SearchQuery query = referenceTargetDao.createSearchQuery(identity, 1, 1); + SearchQuery query = referenceTargetDao.createSearchQuery(identity, PageAndCount.single()); query.configureParameters(queryParameters); List unsupportedQueryParameters = query.getUnsupportedQueryParameters(); @@ -588,7 +589,7 @@ private Optional search(Identity identity, Resource resource, .collect(Collectors.toMap(Entry::getKey, Entry::getValue)); } - SearchQuery query = referenceTargetDao.createSearchQuery(identity, 1, 1); + SearchQuery query = referenceTargetDao.createSearchQuery(identity, PageAndCount.exists()); query.configureParameters(queryParameters); List unsupportedQueryParameters = query.getUnsupportedQueryParameters(); diff --git a/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/subscription/MatcherFactory.java b/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/subscription/MatcherFactory.java index fa84adf60..a533cafdc 100755 --- a/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/subscription/MatcherFactory.java +++ b/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/subscription/MatcherFactory.java @@ -11,6 +11,7 @@ import dev.dsf.fhir.dao.ResourceDao; import dev.dsf.fhir.search.Matcher; +import dev.dsf.fhir.search.PageAndCount; import dev.dsf.fhir.search.SearchQuery; public class MatcherFactory @@ -33,7 +34,7 @@ public Optional createMatcher(String uri) if (daosByResourceName.containsKey(path)) { ResourceDao dao = daosByResourceName.get(path); - SearchQuery query = dao.createSearchQueryWithoutUserFilter(1, 1); + SearchQuery query = dao.createSearchQueryWithoutUserFilter(PageAndCount.exists()); query.configureParameters(queryParameters); return Optional.of(query); } diff --git a/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/webservice/impl/AbstractResourceServiceImpl.java b/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/webservice/impl/AbstractResourceServiceImpl.java index c9a019a8e..ee28a4a57 100755 --- a/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/webservice/impl/AbstractResourceServiceImpl.java +++ b/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/webservice/impl/AbstractResourceServiceImpl.java @@ -47,6 +47,7 @@ import dev.dsf.fhir.help.SummaryMode; import dev.dsf.fhir.history.HistoryService; import dev.dsf.fhir.prefer.PreferHandlingType; +import dev.dsf.fhir.search.PageAndCount; import dev.dsf.fhir.search.PartialResult; import dev.dsf.fhir.search.SearchQuery; import dev.dsf.fhir.search.SearchQueryParameterError; @@ -124,7 +125,6 @@ public void afterPropertiesSet() throws Exception Objects.requireNonNull(resourceType, "resourceType"); Objects.requireNonNull(resourceTypeName, "resourceTypeName"); Objects.requireNonNull(serverBase, "serverBase"); - Objects.requireNonNull(defaultPageCount, "defaultPageCount"); Objects.requireNonNull(dao, "dao"); Objects.requireNonNull(validator, "validator"); Objects.requireNonNull(eventHandler, "eventHandler"); @@ -311,7 +311,7 @@ private void checkAlreadyExists(HttpHeaders headers) throws WebApplicationExcept .collect(Collectors.toMap(Entry::getKey, Entry::getValue)); } - SearchQuery query = dao.createSearchQueryWithoutUserFilter(1, 1); + SearchQuery query = dao.createSearchQueryWithoutUserFilter(PageAndCount.single()); query.configureParameters(queryParameters); List unsupportedQueryParameters = query.getUnsupportedQueryParameters(); @@ -624,14 +624,8 @@ public Response delete(UriInfo uri, HttpHeaders headers) public Response search(UriInfo uri, HttpHeaders headers) { MultivaluedMap queryParameters = uri.getQueryParameters(); - - Integer page = parameterConverter.getFirstInt(queryParameters, SearchQuery.PARAMETER_PAGE); - int effectivePage = page == null ? 1 : page; - - Integer count = parameterConverter.getFirstInt(queryParameters, SearchQuery.PARAMETER_COUNT); - int effectiveCount = (count == null || count < 0) ? defaultPageCount : count; - - SearchQuery query = dao.createSearchQuery(getCurrentIdentity(), effectivePage, effectiveCount); + PageAndCount pageAndCount = PageAndCount.from(queryParameters, defaultPageCount); + SearchQuery query = dao.createSearchQuery(getCurrentIdentity(), pageAndCount); query.configureParameters(queryParameters); List errors = query.getUnsupportedQueryParameters(); diff --git a/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/webservice/impl/StructureDefinitionServiceImpl.java b/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/webservice/impl/StructureDefinitionServiceImpl.java index 537b755cd..6d775e3d3 100755 --- a/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/webservice/impl/StructureDefinitionServiceImpl.java +++ b/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/webservice/impl/StructureDefinitionServiceImpl.java @@ -31,6 +31,7 @@ import dev.dsf.fhir.help.ParameterConverter; import dev.dsf.fhir.help.ResponseGenerator; import dev.dsf.fhir.history.HistoryService; +import dev.dsf.fhir.search.PageAndCount; import dev.dsf.fhir.search.PartialResult; import dev.dsf.fhir.search.SearchQuery; import dev.dsf.fhir.search.parameters.ResourceLastUpdated; @@ -225,7 +226,8 @@ else if (urlType == null && resource.isPresent() && resource.get().getResource() private Response getSnapshot(String url, UriInfo uri, HttpHeaders headers) { - SearchQuery query = snapshotDao.createSearchQuery(getCurrentIdentity(), 1, 1); + SearchQuery query = snapshotDao.createSearchQuery(getCurrentIdentity(), + PageAndCount.single()); Map> searchParameters = new HashMap<>(); searchParameters.put(StructureDefinitionUrl.PARAMETER_NAME, Collections.singletonList(url)); searchParameters.put(SearchQuery.PARAMETER_SORT, diff --git a/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/webservice/secure/AbstractResourceServiceSecure.java b/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/webservice/secure/AbstractResourceServiceSecure.java index 1f793b02d..e14555412 100755 --- a/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/webservice/secure/AbstractResourceServiceSecure.java +++ b/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/webservice/secure/AbstractResourceServiceSecure.java @@ -28,6 +28,7 @@ import dev.dsf.fhir.help.ParameterConverter; import dev.dsf.fhir.help.ResponseGenerator; import dev.dsf.fhir.prefer.PreferReturnType; +import dev.dsf.fhir.search.PageAndCount; import dev.dsf.fhir.search.PartialResult; import dev.dsf.fhir.search.SearchQuery; import dev.dsf.fhir.search.SearchQueryParameterError; @@ -542,7 +543,7 @@ private PartialResult getExisting(Map> queryParameters) .collect(Collectors.toMap(Entry::getKey, Entry::getValue)); } - SearchQuery query = dao.createSearchQueryWithoutUserFilter(1, 1); + SearchQuery query = dao.createSearchQueryWithoutUserFilter(PageAndCount.single()); query.configureParameters(queryParameters); List unsupportedQueryParameters = query.getUnsupportedQueryParameters(); @@ -622,7 +623,7 @@ public Response delete(UriInfo uri, HttpHeaders headers) .collect(Collectors.toMap(Entry::getKey, Entry::getValue)); } - SearchQuery query = dao.createSearchQuery(getCurrentIdentity(), 1, 1); + SearchQuery query = dao.createSearchQuery(getCurrentIdentity(), PageAndCount.single()); query.configureParameters(queryParameters); List unsupportedQueryParameters = query.getUnsupportedQueryParameters(); diff --git a/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/websocket/ServerEndpoint.java b/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/websocket/ServerEndpoint.java index 7b78d7aba..183077bef 100755 --- a/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/websocket/ServerEndpoint.java +++ b/dsf-fhir/dsf-fhir-server/src/main/java/dev/dsf/fhir/websocket/ServerEndpoint.java @@ -75,8 +75,7 @@ public void onOpen(Session session, EndpointConfig config) return; } - logger.info("Websocket open, session {}, identity '{}'", session.getId(), - principal == null ? null : principal.getName()); + logger.info("Websocket open, session {}, identity '{}'", session.getId(), principal.getName()); session.addMessageHandler(new Whole() // don't use lambda { diff --git a/dsf-fhir/dsf-fhir-server/src/main/resources/static/form.js b/dsf-fhir/dsf-fhir-server/src/main/resources/static/form.js index 4c886d468..9814b85d6 100644 --- a/dsf-fhir/dsf-fhir-server/src/main/resources/static/form.js +++ b/dsf-fhir/dsf-fhir-server/src/main/resources/static/form.js @@ -22,9 +22,9 @@ function readTaskInputsFromForm() { document.querySelectorAll(`div.row[for^="${CSS.escape(id)}"]`).forEach(row => { const result = readAndValidateTaskInput(input, row) - if (result.input !== null) + if (result.input) newInputs.push(result.input) - else if (!result.optional) + else if (!result.valid) valid = false }) } else { @@ -78,95 +78,95 @@ function readAndValidateTaskInput(input, row) { return newTaskInputBoolean(input.type, id, htmlInputs[0].checked, htmlInputs[1].checked, optional) } - return null + return { input: null, valid: false } } function newTaskInputReferenceReference(type, id, inputValue, optional) { - const value = validateAndConvert(id, "valueReference", inputValue, optional, "Input") + const result = validateAndConvert(id, "valueReference", inputValue, optional, "Input") - if (value !== null) { + if (result.valid && result.value !== null) { return { - optional: optional, input: { type: type, valueReference: { - reference: value + reference: result.value } - } + }, + valid: true } } else - return { optional: optional, input: null } + return { input: null, valid: result.valid } } function newTaskInputTyped(type, id, fhirType, inputValue, optional) { - const value = validateAndConvert(id, fhirType, inputValue, optional, "Input") + const result = validateAndConvert(id, fhirType, inputValue, optional, "Input") - if (value !== null) { + if (result.valid && result.value !== null) { const input = { type: type } - input[fhirType] = value + input[fhirType] = result.value - return { optional: optional, input: input } + return { input: input, valid: true } } else - return { optional: optional, input: null } + return { input: null, valid: result.valid } } function newTaskInputCoding(type, id, system, code, optional) { - const validated = validateCoding(id, system, code, optional, "Input") + const result = validateCoding(id, system, code, optional, "Input") - if (validated !== null) { + if (result.valid && result.value !== null) { return { - optional: optional, input: { type: type, valueCoding: { - system: validated.system, - code: validated.code + system: result.value.system, + code: result.value.code } - } + }, + valid: true } } else - return { optional: optional, input: null } + return { input: null, valid: result.valid } } function newTaskInputIdentifier(type, id, system, value, optional) { - const validated = validateIdentifier(id, system, value, optional, "Input") + const result = validateIdentifier(id, system, value, optional, "Input") - if (validated !== null) { + if (result.valid && result.value !== null) { return { - optional: optional, input: { type: type, valueIdentifier: { - system: validated.system, - value: validated.value + system: result.value.system, + value: result.value.value } - } + }, + valid: true } } else - return { optional: optional, input: null } + return { input: null, valid: result.valid } } function newTaskInputReferenceIdentifier(type, id, system, value, optional, referenceType) { - const validated = validateIdentifier(id, system, value, optional, "Input") + const result = validateIdentifier(id, system, value, optional, "Input") - if (validated !== null) { + if (result.valid && result.value !== null) { return { - optional: optional, input: { type: type, valueReference: { type: referenceType, identifier: { - system: validated.system, - value: validated.value + system: result.value.system, + value: result.value.value } } - } + }, + valid: true } } else - return { optional: optional, input: null } + return { input: null, valid: result.valid } } function newTaskInputBoolean(type, id, checkedTrue, checkedFalse, optional) { @@ -179,15 +179,15 @@ function newTaskInputBoolean(type, id, checkedTrue, checkedFalse, optional) { if (value) { return { - optional: optional, input: { type: type, valueBoolean: value - } + }, + valid: true } } else - return { optional: optional, input: null } + return { input: null, valid: optional } } function completeQuestionnaireResponse() { @@ -207,18 +207,18 @@ function readQuestionnaireResponseAnswersFromForm() { var valid = true questionnaireResponse.item.forEach(item => { - if (item?.linkId !== undefined && item?.answer !== undefined) { + if (item?.linkId !== undefined) { const id = item.linkId - if (id !== "business-key" && id !== "user-task-id") { + if (id === "business-key" || id === "user-task-id" || item?.answer === undefined) { + newItems.push(item) + } else { const result = readAndValidateQuestionnaireResponseItem(item, id) if (result.item) newItems.push(result.item) else if (!result.valid) valid = false - } else { - newItems.push(item) } } }) @@ -254,88 +254,87 @@ function readAndValidateQuestionnaireResponseItem(item, id) { return newQuestionnaireResponseItemBoolean(item.text, id, htmlInputs[0].checked, htmlInputs[1].checked, optional) } - return null + return { item: null, valid: false } } function newQuestionnaireResponseItemReferenceReference(text, id, inputValue, optional) { - const value = validateAndConvert(id, "valueReference", inputValue, optional, "Item") + const result = validateAndConvert(id, "valueReference", inputValue, optional, "Item") - if (optional && value === null) - return { item: null, valid: true } - else if (value !== null) { - const item = { - linkId: id, - text: text, - answer: [{ - valueReference: { - reference: value - } - }] + if (result.valid && result.value !== null) { + return { + item: { + linkId: id, + text: text, + answer: [{ + valueReference: { + reference: result.value + } + }] + }, + valid: true } - - return { item: item, valid: true } } else - return { item: null, valid: true } + return { item: null, valid: result.valid } } function newQuestionnaireResponseItemTyped(text, id, fhirType, inputValue, optional) { - const value = validateAndConvert(id, fhirType, inputValue, optional, "Item") + const result = validateAndConvert(id, fhirType, inputValue, optional, "Item") - if (optional && value === null) - return { item: null, valid: true } - else if (value !== null) { + if (result.valid && result.value !== null) { const item = { linkId: id, text: text, answer: [{}] } - item.answer[0][fhirType] = value + item.answer[0][fhirType] = result.value return { item: item, valid: true } } else - return { item: null, valid: false } + return { item: null, valid: result.valid } } function newQuestionnaireResponseItemCoding(text, id, system, code, optional) { - const validated = validateCoding(id, system, code, optional, "Item") + const result = validateCoding(id, system, code, optional, "Item") - if (validated !== null) { - const item = { - linkId: id, - text: text, - answer: [{ - valueCoding: { - system: validated.system, - code: validated.code - } - }] + if (result.valid && result.value !== null) { + return { + item: { + linkId: id, + text: text, + answer: [{ + valueCoding: { + system: result.value.system, + code: result.value.code + } + }] + }, + valid: true } - - return { item: item, valid: true } } else - return { item: null, valid: optional } + return { item: null, valid: result.valid } } function newQuestionnaireResponseItemReferenceIdentifier(text, id, system, value, optional) { - const validated = validateIdentifier(id, system, value, optional, "Item") + const result = validateIdentifier(id, system, value, optional, "Item") - if (validated !== null) { - const item = { - linkId: id, - text: text, - answer: [{ - valueReference: { - identifier: { - system: validated.system, - value: validated.value + if (result.valid && result.value !== null) { + return { + item: { + linkId: id, + text: text, + answer: [{ + valueReference: { + identifier: { + system: result.value.system, + value: result.value.value + } } - } - }] + }] + }, + valid: true } - - return { item: item, valid: true } } else - return { item: null, valid: optional } + return { item: null, valid: result.valid } } function newQuestionnaireResponseItemBoolean(text, id, checkedTrue, checkedFalse, optional) { @@ -386,7 +385,7 @@ function validateAndConvert(id, fhirType, inputValue, optional, valueName) { else if (fhirType === "valueReference") return validateReference(errorListElement, inputValue, optional, valueName) else - return undefined + return { value: null, valid: false } } function validateCoding(id, system, code, optional, valueName) { @@ -394,21 +393,24 @@ function validateCoding(id, system, code, optional, valueName) { const codeEmpty = code === null || code.trim() === "" if (optional && systemEmpty && codeEmpty) - return null - + return { value: null, valid: true } else { const errorListElement = document.querySelector(`ul[for="${CSS.escape(id)}"]`) - const validatedSystem = validateUrl(errorListElement, system, false, valueName + " system") - const validatedCode = validateString(errorListElement, code, false, valueName + " code") + const resultSystem = validateUrl(errorListElement, system, false, valueName + " system") + const resultCode = validateString(errorListElement, code, false, valueName + " code") - if (validatedSystem !== null && validatedCode !== null) + if (resultSystem.valid && resultSystem.value !== null && resultCode.valid && resultCode.value !== null) { return { - system: validatedSystem, - code: validatedCode + value: { + system: resultSystem.value, + code: resultCode.value + }, + valid: true } + } else - return null + return { value: null, valid: false } } } @@ -417,21 +419,24 @@ function validateIdentifier(id, system, value, optional, valueName) { const valueEmpty = value === null || value.trim() === "" if (optional && systemEmpty && valueEmpty) - return null - + return { value: null, valid: true } else { const errorListElement = document.querySelector(`ul[for="${CSS.escape(id)}"]`) - const validatedSystem = validateUrl(errorListElement, system, false, valueName + " system") - const validatedValue = validateString(errorListElement, value, false, valueName + " value") + const resultSystem = validateUrl(errorListElement, system, false, valueName + " system") + const resultValue = validateString(errorListElement, value, false, valueName + " value") - if (validatedSystem !== null && validatedValue !== null) + if (resultSystem.valid && resultSystem.value !== null && resultValue.valid && resultValue.value !== null) { return { - system: validatedSystem, - value: validatedValue + value: { + system: resultSystem.value, + value: resultValue.value + }, + valid: true } + } else - return null + return { value: null, valid: false } } } @@ -440,16 +445,18 @@ function validateType(errorListElement, value, optional, valueName, typeValid, t if (!optional && !stringValid) { addError(errorListElement, valueName + " mandatory") - return null + return { value: null, valid: false } } else if (stringValid) { - if (typeValid(value)) - return toType(value) + if (typeValid(value)) { + const typedValue = toType(value) + return { value: typedValue, valid: true } + } else { addError(errorListElement, valueName + " " + typeSpecificError) - return null + return { value: null, valid: false } } } else - return null + return { value: null, valid: true } } function validateString(errorListElement, value, optional, valueName) { diff --git a/dsf-fhir/dsf-fhir-server/src/main/resources/template/resourceTask.html b/dsf-fhir/dsf-fhir-server/src/main/resources/template/resourceTask.html index 53036ecd9..0b870c8f2 100644 --- a/dsf-fhir/dsf-fhir-server/src/main/resources/template/resourceTask.html +++ b/dsf-fhir/dsf-fhir-server/src/main/resources/template/resourceTask.html @@ -37,8 +37,8 @@

Input

- - + +
Insert Placeholder Value Copy to Clipboard diff --git a/dsf-fhir/dsf-fhir-server/src/test/java/dev/dsf/fhir/dao/AbstractReadAccessDaoTest.java b/dsf-fhir/dsf-fhir-server/src/test/java/dev/dsf/fhir/dao/AbstractReadAccessDaoTest.java index dd71e1b5e..c9a88b407 100644 --- a/dsf-fhir/dsf-fhir-server/src/test/java/dev/dsf/fhir/dao/AbstractReadAccessDaoTest.java +++ b/dsf-fhir/dsf-fhir-server/src/test/java/dev/dsf/fhir/dao/AbstractReadAccessDaoTest.java @@ -32,6 +32,7 @@ import dev.dsf.fhir.authorization.read.ReadAccessHelperImpl; import dev.dsf.fhir.dao.jdbc.OrganizationAffiliationDaoJdbc; import dev.dsf.fhir.dao.jdbc.OrganizationDaoJdbc; +import dev.dsf.fhir.search.PageAndCount; import dev.dsf.fhir.search.PartialResult; import dev.dsf.fhir.search.SearchQuery; @@ -1054,7 +1055,7 @@ private void testSearchWithUserFilterAfterReadAccessTrigger(String accessType, C D createdD = getDao().create(d); assertReadAccessEntryCount(1, 1, createdD, accessType); - SearchQuery query = getDao().createSearchQuery(userCreator.apply(createdOrg), 1, 20) + SearchQuery query = getDao().createSearchQuery(userCreator.apply(createdOrg), PageAndCount.from(1, 20)) .configureParameters(Map.of("id", Collections.singletonList(createdD.getIdElement().getIdPart()))); PartialResult searchResult = getDao().search(query); assertNotNull(searchResult); diff --git a/dsf-fhir/dsf-fhir-server/src/test/java/dev/dsf/fhir/dao/BinaryDaoTest.java b/dsf-fhir/dsf-fhir-server/src/test/java/dev/dsf/fhir/dao/BinaryDaoTest.java index 49dad32cb..e06b4a6f8 100755 --- a/dsf-fhir/dsf-fhir-server/src/test/java/dev/dsf/fhir/dao/BinaryDaoTest.java +++ b/dsf-fhir/dsf-fhir-server/src/test/java/dev/dsf/fhir/dao/BinaryDaoTest.java @@ -39,6 +39,7 @@ import dev.dsf.fhir.dao.jdbc.OrganizationAffiliationDaoJdbc; import dev.dsf.fhir.dao.jdbc.OrganizationDaoJdbc; import dev.dsf.fhir.dao.jdbc.ResearchStudyDaoJdbc; +import dev.dsf.fhir.search.PageAndCount; import dev.dsf.fhir.search.PartialResult; import dev.dsf.fhir.search.SearchQuery; @@ -145,7 +146,7 @@ public void testSearch() throws Exception Binary createdB = dao.create(b); assertNotNull(createdB); - SearchQuery query = dao.createSearchQuery(TestOrganizationIdentity.local(org), 1, 1); + SearchQuery query = dao.createSearchQuery(TestOrganizationIdentity.local(org), PageAndCount.single()); query.configureParameters(Collections.emptyMap()); assertNotNull(query); @@ -172,7 +173,7 @@ public void testSearchBinaryWithSecurityContext() throws Exception Binary createdB = dao.create(b); assertNotNull(createdB); - SearchQuery query = dao.createSearchQuery(TestOrganizationIdentity.local(org), 1, 1); + SearchQuery query = dao.createSearchQuery(TestOrganizationIdentity.local(org), PageAndCount.single()); query.configureParameters(Collections.emptyMap()); assertNotNull(query); @@ -210,7 +211,8 @@ public void testSearchBinaryWithSecurityContextOrganization() throws Exception Binary createdB = dao.create(b); assertNotNull(createdB); - SearchQuery query = dao.createSearchQuery(TestOrganizationIdentity.local(createdOrg), 1, 1); + SearchQuery query = dao.createSearchQuery(TestOrganizationIdentity.local(createdOrg), + PageAndCount.single()); query.configureParameters(Collections.emptyMap()); assertNotNull(query); @@ -268,7 +270,8 @@ public void testSearchBinaryWithSecurityContextRole() throws Exception Binary createdB = dao.create(b); assertNotNull(createdB); - SearchQuery query = dao.createSearchQuery(TestOrganizationIdentity.local(createdMemberOrg), 1, 1); + SearchQuery query = dao.createSearchQuery(TestOrganizationIdentity.local(createdMemberOrg), + PageAndCount.single()); query.configureParameters(Collections.emptyMap()); assertNotNull(query); diff --git a/dsf-fhir/dsf-fhir-server/src/test/java/dev/dsf/fhir/dao/HistoryDaoTest.java b/dsf-fhir/dsf-fhir-server/src/test/java/dev/dsf/fhir/dao/HistoryDaoTest.java index c180c03f9..b8e335ce0 100644 --- a/dsf-fhir/dsf-fhir-server/src/test/java/dev/dsf/fhir/dao/HistoryDaoTest.java +++ b/dsf-fhir/dsf-fhir-server/src/test/java/dev/dsf/fhir/dao/HistoryDaoTest.java @@ -78,7 +78,7 @@ public void testReadHistory() throws Exception History history = dao.readHistory( filterFactory.getIdentityFilters(TestOrganizationIdentity.local(createdOrganization)), - new PageAndCount(1, 1000), Collections.singletonList(new AtParameter()), new SinceParameter()); + PageAndCount.from(1, 1000), Collections.singletonList(new AtParameter()), new SinceParameter()); assertNotNull(history); assertEquals(1, history.getTotal()); assertNotNull(history.getEntries()); @@ -97,7 +97,7 @@ public void testReadHistoryOrganization() throws Exception History history = dao.readHistory( filterFactory.getIdentityFilter(TestOrganizationIdentity.local(createdOrganization), Organization.class), - new PageAndCount(1, 1000), Collections.singletonList(new AtParameter()), new SinceParameter(), + PageAndCount.from(1, 1000), Collections.singletonList(new AtParameter()), new SinceParameter(), Organization.class); assertNotNull(history); assertEquals(1, history.getTotal()); @@ -117,7 +117,7 @@ public void testReadHistoryOrganizationWithId() throws Exception History history = dao.readHistory( filterFactory.getIdentityFilter(TestOrganizationIdentity.local(createdOrganization), Organization.class), - new PageAndCount(1, 1000), Collections.singletonList(new AtParameter()), new SinceParameter(), + PageAndCount.from(1, 1000), Collections.singletonList(new AtParameter()), new SinceParameter(), Organization.class, UUID.fromString(createdOrganization.getIdElement().getIdPart())); assertNotNull(history); diff --git a/dsf-fhir/dsf-fhir-server/src/test/java/dev/dsf/fhir/integration/BinaryIntegrationTest.java b/dsf-fhir/dsf-fhir-server/src/test/java/dev/dsf/fhir/integration/BinaryIntegrationTest.java index 19a5ca801..8f22e6504 100755 --- a/dsf-fhir/dsf-fhir-server/src/test/java/dev/dsf/fhir/integration/BinaryIntegrationTest.java +++ b/dsf-fhir/dsf-fhir-server/src/test/java/dev/dsf/fhir/integration/BinaryIntegrationTest.java @@ -43,6 +43,7 @@ import dev.dsf.fhir.dao.PatientDao; import dev.dsf.fhir.dao.ResearchStudyDao; import dev.dsf.fhir.dao.exception.ResourceDeletedException; +import dev.dsf.fhir.search.PageAndCount; import dev.dsf.fhir.search.PartialResult; import jakarta.ws.rs.core.MediaType; @@ -222,8 +223,9 @@ public void testReadAllowedLocalUserViaStreamMediaTypeNotSupported() throws Exce public void testReadAllowedExternalUser() throws Exception { OrganizationDao orgDao = getSpringWebApplicationContext().getBean(OrganizationDao.class); - PartialResult result = orgDao.search(orgDao.createSearchQueryWithoutUserFilter(1, 1) - .configureParameters(Map.of("name", Arrays.asList("External Test Organization")))); + PartialResult result = orgDao + .search(orgDao.createSearchQueryWithoutUserFilter(PageAndCount.single()) + .configureParameters(Map.of("name", Arrays.asList("External Test Organization")))); assertNotNull(result); assertEquals(1, result.getTotal()); assertNotNull(result.getPartialResult()); @@ -264,8 +266,9 @@ public void testReadAllowedExternalUserNotFoundWithVersion() throws Exception public void testReadAllowedExternalUserViaSecurityContext() throws Exception { OrganizationDao orgDao = getSpringWebApplicationContext().getBean(OrganizationDao.class); - PartialResult result = orgDao.search(orgDao.createSearchQueryWithoutUserFilter(1, 1) - .configureParameters(Map.of("name", Arrays.asList("External Test Organization")))); + PartialResult result = orgDao + .search(orgDao.createSearchQueryWithoutUserFilter(PageAndCount.single()) + .configureParameters(Map.of("name", Arrays.asList("External Test Organization")))); assertNotNull(result); assertEquals(1, result.getTotal()); assertNotNull(result.getPartialResult()); @@ -300,8 +303,9 @@ public void testReadAllowedExternalUserViaSecurityContext() throws Exception public void testReadAllowedExternalUserViaSecurityContextDocumentReference() throws Exception { OrganizationDao orgDao = getSpringWebApplicationContext().getBean(OrganizationDao.class); - PartialResult result = orgDao.search(orgDao.createSearchQueryWithoutUserFilter(1, 1) - .configureParameters(Map.of("name", Arrays.asList("External Test Organization")))); + PartialResult result = orgDao + .search(orgDao.createSearchQueryWithoutUserFilter(PageAndCount.single()) + .configureParameters(Map.of("name", Arrays.asList("External Test Organization")))); assertNotNull(result); assertEquals(1, result.getTotal()); assertNotNull(result.getPartialResult()); @@ -337,8 +341,9 @@ public void testReadAllowedExternalUserViaSecurityContextDocumentReference() thr public void testReadNotAllowedExternalUser() throws Exception { OrganizationDao orgDao = getSpringWebApplicationContext().getBean(OrganizationDao.class); - PartialResult result = orgDao.search(orgDao.createSearchQueryWithoutUserFilter(1, 1) - .configureParameters(Map.of("name", Arrays.asList("Test Organization")))); + PartialResult result = orgDao + .search(orgDao.createSearchQueryWithoutUserFilter(PageAndCount.single()) + .configureParameters(Map.of("name", Arrays.asList("Test Organization")))); assertNotNull(result); assertEquals(1, result.getTotal()); assertNotNull(result.getPartialResult()); @@ -395,8 +400,9 @@ public void testReadAllowedLocalUserViaTransactionBundle() throws Exception public void testReadAllowedExternalUserViaTransactionBundle() throws Exception { OrganizationDao orgDao = getSpringWebApplicationContext().getBean(OrganizationDao.class); - PartialResult result = orgDao.search(orgDao.createSearchQueryWithoutUserFilter(1, 1) - .configureParameters(Map.of("name", Arrays.asList("External Test Organization")))); + PartialResult result = orgDao + .search(orgDao.createSearchQueryWithoutUserFilter(PageAndCount.single()) + .configureParameters(Map.of("name", Arrays.asList("External Test Organization")))); assertNotNull(result); assertEquals(1, result.getTotal()); assertNotNull(result.getPartialResult()); @@ -486,8 +492,9 @@ public void testReadAllowedLocalUserViaBatchBundle() throws Exception public void testReadAllowedExternalUserViaBatchBundle() throws Exception { OrganizationDao orgDao = getSpringWebApplicationContext().getBean(OrganizationDao.class); - PartialResult result = orgDao.search(orgDao.createSearchQueryWithoutUserFilter(1, 1) - .configureParameters(Map.of("name", Arrays.asList("External Test Organization")))); + PartialResult result = orgDao + .search(orgDao.createSearchQueryWithoutUserFilter(PageAndCount.single()) + .configureParameters(Map.of("name", Arrays.asList("External Test Organization")))); assertNotNull(result); assertEquals(1, result.getTotal()); assertNotNull(result.getPartialResult()); @@ -1008,8 +1015,9 @@ public void testHeadAllowedLocalUserViaTransactionBundle() throws Exception public void testHeadAllowedExternalUserViaTransactionBundle() throws Exception { OrganizationDao orgDao = getSpringWebApplicationContext().getBean(OrganizationDao.class); - PartialResult result = orgDao.search(orgDao.createSearchQueryWithoutUserFilter(1, 1) - .configureParameters(Map.of("name", Arrays.asList("External Test Organization")))); + PartialResult result = orgDao + .search(orgDao.createSearchQueryWithoutUserFilter(PageAndCount.single()) + .configureParameters(Map.of("name", Arrays.asList("External Test Organization")))); assertNotNull(result); assertEquals(1, result.getTotal()); assertNotNull(result.getPartialResult()); @@ -1093,8 +1101,9 @@ public void testHeadAllowedLocalUserViaBatchBundle() throws Exception public void testHeadAllowedExternalUserViaBatchBundle() throws Exception { OrganizationDao orgDao = getSpringWebApplicationContext().getBean(OrganizationDao.class); - PartialResult result = orgDao.search(orgDao.createSearchQueryWithoutUserFilter(1, 1) - .configureParameters(Map.of("name", Arrays.asList("External Test Organization")))); + PartialResult result = orgDao + .search(orgDao.createSearchQueryWithoutUserFilter(PageAndCount.single()) + .configureParameters(Map.of("name", Arrays.asList("External Test Organization")))); assertNotNull(result); assertEquals(1, result.getTotal()); assertNotNull(result.getPartialResult()); diff --git a/dsf-fhir/dsf-fhir-server/src/test/java/dev/dsf/fhir/integration/EndpointIntegrationTest.java b/dsf-fhir/dsf-fhir-server/src/test/java/dev/dsf/fhir/integration/EndpointIntegrationTest.java index 36243b699..64c58dc01 100644 --- a/dsf-fhir/dsf-fhir-server/src/test/java/dev/dsf/fhir/integration/EndpointIntegrationTest.java +++ b/dsf-fhir/dsf-fhir-server/src/test/java/dev/dsf/fhir/integration/EndpointIntegrationTest.java @@ -28,6 +28,7 @@ import ca.uhn.fhir.context.FhirContext; import dev.dsf.fhir.dao.EndpointDao; import dev.dsf.fhir.dao.OrganizationDao; +import dev.dsf.fhir.search.PageAndCount; import dev.dsf.fhir.search.PartialResult; import dev.dsf.fhir.search.SearchQuery; @@ -328,7 +329,7 @@ public void testUpdateWithRelativeLiteralReferenceNotExisting() throws Exception OrganizationDao organizationDao = getSpringWebApplicationContext().getBean(OrganizationDao.class); EndpointDao endpointDao = getSpringWebApplicationContext().getBean(EndpointDao.class); - SearchQuery query = organizationDao.createSearchQueryWithoutUserFilter(1, 1) + SearchQuery query = organizationDao.createSearchQueryWithoutUserFilter(PageAndCount.single()) .configureParameters(Map.of("identifier", Collections.singletonList("http://dsf.dev/sid/organization-identifier|Test_Organization"))); PartialResult organizationResult = organizationDao.search(query); @@ -356,7 +357,7 @@ public void testUpdateViaBundleWithRelativeLiteralReferenceNotExisting() throws OrganizationDao organizationDao = getSpringWebApplicationContext().getBean(OrganizationDao.class); EndpointDao endpointDao = getSpringWebApplicationContext().getBean(EndpointDao.class); - SearchQuery query = organizationDao.createSearchQueryWithoutUserFilter(1, 1) + SearchQuery query = organizationDao.createSearchQueryWithoutUserFilter(PageAndCount.single()) .configureParameters(Map.of("identifier", Collections.singletonList("http://dsf.dev/sid/organization-identifier|Test_Organization"))); PartialResult organizationResult = organizationDao.search(query); diff --git a/dsf-fhir/dsf-fhir-server/src/test/java/dev/dsf/fhir/integration/MeasureIntegrationTest.java b/dsf-fhir/dsf-fhir-server/src/test/java/dev/dsf/fhir/integration/MeasureIntegrationTest.java index 6b72db99b..67f40100d 100644 --- a/dsf-fhir/dsf-fhir-server/src/test/java/dev/dsf/fhir/integration/MeasureIntegrationTest.java +++ b/dsf-fhir/dsf-fhir-server/src/test/java/dev/dsf/fhir/integration/MeasureIntegrationTest.java @@ -6,6 +6,7 @@ import java.nio.charset.StandardCharsets; import java.util.Collections; +import java.util.List; import java.util.Map; import org.hl7.fhir.r4.model.Bundle; @@ -140,4 +141,13 @@ public void testSearchMeasureDependingOnRelatedArtifactLibrary() throws Exceptio assertNotNull(resultBundle.getEntryFirstRep().getResource()); assertEquals(measureId, resultBundle.getEntryFirstRep().getResource().getIdElement().getIdPart()); } + + @Test + public void testSearchMeasureWithMaxIntegerPageAndCount() throws Exception + { + List integerMax = List.of(String.valueOf(Integer.MAX_VALUE)); + Bundle result = getWebserviceClient().search(Measure.class, Map.of("_page", integerMax, "_count", integerMax)); + assertNotNull(result); + assertEquals(0, result.getTotal()); + } } diff --git a/dsf-fhir/dsf-fhir-server/src/test/java/dev/dsf/fhir/integration/SubscriptionIntegrationTest.java b/dsf-fhir/dsf-fhir-server/src/test/java/dev/dsf/fhir/integration/SubscriptionIntegrationTest.java new file mode 100644 index 000000000..0bf6dc68f --- /dev/null +++ b/dsf-fhir/dsf-fhir-server/src/test/java/dev/dsf/fhir/integration/SubscriptionIntegrationTest.java @@ -0,0 +1,161 @@ +package dev.dsf.fhir.integration; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import org.hl7.fhir.r4.model.Subscription; +import org.hl7.fhir.r4.model.Subscription.SubscriptionChannelType; +import org.hl7.fhir.r4.model.Subscription.SubscriptionStatus; +import org.junit.Test; + +import dev.dsf.fhir.dao.SubscriptionDao; + +public class SubscriptionIntegrationTest extends AbstractIntegrationTest +{ + private Subscription newSubscription(String criteria) + { + Subscription s = new Subscription(); + s.setStatus(SubscriptionStatus.ACTIVE); + s.getChannel().setType(SubscriptionChannelType.WEBSOCKET).setPayload("application/fhir+json"); + s.setCriteria(criteria); + s.setReason("Integration-Test"); + + readAccessHelper.addLocal(s); + + return s; + } + + @Test + public void testCreateOkJson() throws Exception + { + Subscription t = newSubscription("Task?status=completed"); + + Subscription created = getWebserviceClient().create(t); + assertNotNull(created); + assertTrue(created.getIdElement().hasValue()); + assertEquals("1", created.getMeta().getVersionId()); + } + + @Test + public void testCreateOkXml() throws Exception + { + Subscription t = newSubscription("Task?status=completed"); + t.getChannel().setPayload("application/fhir+xml"); + + Subscription created = getWebserviceClient().create(t); + assertNotNull(created); + assertTrue(created.getIdElement().hasValue()); + assertEquals("1", created.getMeta().getVersionId()); + } + + @Test + public void testCreateOkNoPayload() throws Exception + { + Subscription t = newSubscription("Task?status=completed"); + t.getChannel().setPayload(null); + + Subscription created = getWebserviceClient().create(t); + assertNotNull(created); + assertTrue(created.getIdElement().hasValue()); + assertEquals("1", created.getMeta().getVersionId()); + } + + @Test + public void testCreateInvalid() throws Exception + { + Subscription noStatus = newSubscription("Task?status=completed"); + noStatus.setStatus(null); + expectForbidden(() -> getWebserviceClient().create(noStatus)); + + Subscription noChannel = newSubscription("Task?status=completed"); + noChannel.setChannel(null); + expectForbidden(() -> getWebserviceClient().create(noChannel)); + + Subscription noChannelType = newSubscription("Task?status=completed"); + noChannelType.getChannel().setType(null); + expectForbidden(() -> getWebserviceClient().create(noChannelType)); + + Subscription channelTypeEmail = newSubscription("Task?status=completed"); + channelTypeEmail.getChannel().setType(SubscriptionChannelType.EMAIL); + expectForbidden(() -> getWebserviceClient().create(channelTypeEmail)); + + // no payload allowed -> websockets sends "ping" (standard FHIR behavior for websockets) + + Subscription channelPayloadPdf = newSubscription("Task?status=completed"); + channelPayloadPdf.getChannel().setPayload("application/pdf"); + expectForbidden(() -> getWebserviceClient().create(channelPayloadPdf)); + + Subscription noCriteria = newSubscription(null); + expectForbidden(() -> getWebserviceClient().create(noCriteria)); + + Subscription noReadAccessTag = newSubscription("Task?status=completed"); + noReadAccessTag.getMeta().setTag(null); + expectForbidden(() -> getWebserviceClient().create(noReadAccessTag)); + } + + @Test + public void testCreateExisting() throws Exception + { + Subscription t = newSubscription("Task?status=requested"); + expectForbidden(() -> getWebserviceClient().create(t)); + + Subscription q = newSubscription("QuestionnaireResponse?status=completed"); + expectForbidden(() -> getWebserviceClient().create(q)); + } + + @Test + public void testCreateUnsupportedSearchParameterInCriteria() throws Exception + { + Subscription t = newSubscription("Task?status=requested&unsupported=true"); + expectForbidden(() -> getWebserviceClient().create(t)); + } + + @Test + public void testCreateUnsupportedResourcerInCriteria() throws Exception + { + Subscription t = newSubscription("CareTeam?status=active"); + expectForbidden(() -> getWebserviceClient().create(t)); + } + + @Test + public void testCreateUnsupportedResourcerInCriteriaPath() throws Exception + { + Subscription t = newSubscription("fhir/Task?status=completed"); + expectForbidden(() -> getWebserviceClient().create(t)); + } + + @Test + public void testUpdateOk() throws Exception + { + Subscription t = newSubscription("Task?status=completed"); + SubscriptionDao dao = getSpringWebApplicationContext().getBean(SubscriptionDao.class); + Subscription created = dao.create(t); + assertNotNull(created); + + created.setReason("Update Test"); + Subscription updated = getWebserviceClient().update(created); + assertNotNull(updated); + assertTrue(updated.getIdElement().hasValue()); + assertEquals("2", updated.getMeta().getVersionId()); + } + + @Test + public void testUpdateNotAllowed() throws Exception + { + Subscription t = newSubscription("Task?status=completed"); + SubscriptionDao dao = getSpringWebApplicationContext().getBean(SubscriptionDao.class); + Subscription created = dao.create(t); + assertNotNull(created); + + created.setCriteria("Task?status=failed"); + expectForbidden(() -> getWebserviceClient().update(created)); + + created.setCriteria("Task?status=completed"); + created.getChannel().setPayload("application/fhir+xml"); + expectForbidden(() -> getWebserviceClient().update(created)); + + created.getChannel().setPayload(null); + expectForbidden(() -> getWebserviceClient().update(created)); + } +} diff --git a/dsf-fhir/dsf-fhir-server/src/test/java/dev/dsf/fhir/search/PageAndCountTest.java b/dsf-fhir/dsf-fhir-server/src/test/java/dev/dsf/fhir/search/PageAndCountTest.java new file mode 100644 index 000000000..7e5ddc857 --- /dev/null +++ b/dsf-fhir/dsf-fhir-server/src/test/java/dev/dsf/fhir/search/PageAndCountTest.java @@ -0,0 +1,236 @@ +package dev.dsf.fhir.search; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import java.util.List; +import java.util.Map; + +import org.junit.Test; + +public class PageAndCountTest +{ + private static final int DEFAULT_PAGE_COUNT = 20; + + @Test + public void testSingle() throws Exception + { + PageAndCount pC = PageAndCount.single(); + assertNotNull(pC); + assertEquals(1, pC.getCount()); + assertEquals(1, pC.getPage()); + assertEquals(" LIMIT 1", pC.getSql()); + + assertTrue(pC.isLastPage(0)); + assertTrue(pC.isLastPage(1)); + assertFalse(pC.isLastPage(2)); + assertFalse(pC.isLastPage(Integer.MAX_VALUE)); + + assertEquals(0, pC.getLastPage(0)); + assertEquals(1, pC.getLastPage(1)); + assertEquals(2, pC.getLastPage(2)); + assertEquals(Integer.MAX_VALUE, pC.getLastPage(Integer.MAX_VALUE)); + + assertTrue(pC.isCountOnly(0)); + assertFalse(pC.isCountOnly(1)); + assertFalse(pC.isCountOnly(2)); + assertFalse(pC.isCountOnly(Integer.MAX_VALUE)); + } + + @Test + public void testExists() throws Exception + { + PageAndCount pC = PageAndCount.exists(); + assertNotNull(pC); + assertEquals(0, pC.getCount()); + assertEquals(0, pC.getPage()); + assertEquals(" LIMIT 0", pC.getSql()); + + assertTrue(pC.isLastPage(0)); + assertTrue(pC.isLastPage(1)); + assertTrue(pC.isLastPage(2)); + assertTrue(pC.isLastPage(Integer.MAX_VALUE)); + + assertEquals(0, pC.getLastPage(0)); + assertEquals(0, pC.getLastPage(1)); + assertEquals(0, pC.getLastPage(2)); + assertEquals(0, pC.getLastPage(Integer.MAX_VALUE)); + + assertTrue(pC.isCountOnly(0)); + assertTrue(pC.isCountOnly(1)); + assertTrue(pC.isCountOnly(2)); + assertTrue(pC.isCountOnly(Integer.MAX_VALUE)); + } + + @Test + public void testPage1Count0() throws Exception + { + PageAndCount pC = PageAndCount.from(1, 0); + assertNotNull(pC); + assertEquals(0, pC.getCount()); + assertEquals(1, pC.getPage()); + assertEquals(" LIMIT 0", pC.getSql()); + + assertTrue(pC.isLastPage(0)); + assertTrue(pC.isLastPage(1)); + assertTrue(pC.isLastPage(2)); + assertTrue(pC.isLastPage(Integer.MAX_VALUE)); + + assertEquals(0, pC.getLastPage(0)); + assertEquals(0, pC.getLastPage(1)); + assertEquals(0, pC.getLastPage(2)); + assertEquals(0, pC.getLastPage(Integer.MAX_VALUE)); + + assertTrue(pC.isCountOnly(0)); + assertTrue(pC.isCountOnly(1)); + assertTrue(pC.isCountOnly(2)); + assertTrue(pC.isCountOnly(Integer.MAX_VALUE)); + } + + @Test + public void testPage1Count20() throws Exception + { + PageAndCount pC = PageAndCount.from(1, 20); + assertNotNull(pC); + assertEquals(20, pC.getCount()); + assertEquals(1, pC.getPage()); + assertEquals(" LIMIT 20", pC.getSql()); + + assertTrue(pC.isLastPage(0)); + assertTrue(pC.isLastPage(1)); + assertTrue(pC.isLastPage(2)); + assertTrue(pC.isLastPage(19)); + assertTrue(pC.isLastPage(20)); + assertFalse(pC.isLastPage(21)); + assertFalse(pC.isLastPage(Integer.MAX_VALUE)); + + assertEquals(0, pC.getLastPage(0)); + assertEquals(1, pC.getLastPage(1)); + assertEquals(1, pC.getLastPage(2)); + assertEquals(1, pC.getLastPage(19)); + assertEquals(1, pC.getLastPage(20)); + assertEquals(2, pC.getLastPage(21)); + assertEquals(107374183, pC.getLastPage(Integer.MAX_VALUE)); + + assertTrue(pC.isCountOnly(0)); + assertFalse(pC.isCountOnly(1)); + assertFalse(pC.isCountOnly(2)); + assertFalse(pC.isCountOnly(19)); + assertFalse(pC.isCountOnly(20)); + assertFalse(pC.isCountOnly(21)); + assertFalse(pC.isCountOnly(Integer.MAX_VALUE)); + } + + @Test + public void testPage2Count20() throws Exception + { + PageAndCount pC = PageAndCount.from(2, 20); + assertNotNull(pC); + assertEquals(20, pC.getCount()); + assertEquals(2, pC.getPage()); + assertEquals(" LIMIT 20 OFFSET 20", pC.getSql()); + + assertTrue(pC.isLastPage(0)); + assertTrue(pC.isLastPage(1)); + assertTrue(pC.isLastPage(2)); + assertTrue(pC.isLastPage(19)); + assertTrue(pC.isLastPage(20)); + assertTrue(pC.isLastPage(21)); + assertTrue(pC.isLastPage(39)); + assertTrue(pC.isLastPage(40)); + assertFalse(pC.isLastPage(41)); + assertFalse(pC.isLastPage(Integer.MAX_VALUE)); + + assertEquals(0, pC.getLastPage(0)); + assertEquals(1, pC.getLastPage(1)); + assertEquals(1, pC.getLastPage(2)); + assertEquals(1, pC.getLastPage(19)); + assertEquals(1, pC.getLastPage(20)); + assertEquals(2, pC.getLastPage(21)); + assertEquals(2, pC.getLastPage(39)); + assertEquals(2, pC.getLastPage(40)); + assertEquals(3, pC.getLastPage(41)); + assertEquals(107374183, pC.getLastPage(Integer.MAX_VALUE)); + + assertTrue(pC.isCountOnly(0)); + assertTrue(pC.isCountOnly(1)); + assertTrue(pC.isCountOnly(2)); + assertTrue(pC.isCountOnly(19)); + assertTrue(pC.isCountOnly(20)); + assertFalse(pC.isCountOnly(21)); + assertFalse(pC.isCountOnly(39)); + assertFalse(pC.isCountOnly(40)); + assertFalse(pC.isCountOnly(41)); + assertFalse(pC.isCountOnly(Integer.MAX_VALUE)); + } + + @Test + public void testFromQueryParameters() throws Exception + { + PageAndCount pC_m1_m1 = PageAndCount.from(Map.of("_page", List.of("-1"), "_count", List.of("-1")), + DEFAULT_PAGE_COUNT); + assertPageAndCount(pC_m1_m1, 0, 0); + PageAndCount pC_0_0 = PageAndCount.from(Map.of("_page", List.of("0"), "_count", List.of("0")), + DEFAULT_PAGE_COUNT); + assertPageAndCount(pC_0_0, 0, 0); + PageAndCount pC_1_1 = PageAndCount.from(Map.of("_page", List.of("1"), "_count", List.of("1")), + DEFAULT_PAGE_COUNT); + assertPageAndCount(pC_1_1, 1, 1); + PageAndCount pC_2_2 = PageAndCount.from(Map.of("_page", List.of("2"), "_count", List.of("2")), + DEFAULT_PAGE_COUNT); + assertPageAndCount(pC_2_2, 2, 2); + PageAndCount pC_1_19 = PageAndCount.from(Map.of("_page", List.of("1"), "_count", List.of("19")), + DEFAULT_PAGE_COUNT); + assertPageAndCount(pC_1_19, 1, 19); + PageAndCount pC_1_20 = PageAndCount.from(Map.of("_page", List.of("1"), "_count", List.of("20")), + DEFAULT_PAGE_COUNT); + assertPageAndCount(pC_1_20, 1, 20); + PageAndCount pC_1_21 = PageAndCount.from(Map.of("_page", List.of("1"), "_count", List.of("21")), + DEFAULT_PAGE_COUNT); + assertPageAndCount(pC_1_21, 1, 21); + + PageAndCount pC_m1_ = PageAndCount.from(Map.of("_page", List.of("-1")), DEFAULT_PAGE_COUNT); + assertPageAndCount(pC_m1_, 0, DEFAULT_PAGE_COUNT); + PageAndCount pC_0_ = PageAndCount.from(Map.of("_page", List.of("0")), DEFAULT_PAGE_COUNT); + assertPageAndCount(pC_0_, 0, DEFAULT_PAGE_COUNT); + PageAndCount pC_1_ = PageAndCount.from(Map.of("_page", List.of("1")), DEFAULT_PAGE_COUNT); + assertPageAndCount(pC_1_, 1, DEFAULT_PAGE_COUNT); + PageAndCount pC_2_ = PageAndCount.from(Map.of("_page", List.of("2")), DEFAULT_PAGE_COUNT); + assertPageAndCount(pC_2_, 2, DEFAULT_PAGE_COUNT); + + PageAndCount pC__m1 = PageAndCount.from(Map.of("_count", List.of("-1")), DEFAULT_PAGE_COUNT); + assertPageAndCount(pC__m1, 1, 0); + PageAndCount pC__0 = PageAndCount.from(Map.of("_count", List.of("0")), DEFAULT_PAGE_COUNT); + assertPageAndCount(pC__0, 1, 0); + PageAndCount pC__1 = PageAndCount.from(Map.of("_count", List.of("1")), DEFAULT_PAGE_COUNT); + assertPageAndCount(pC__1, 1, 1); + PageAndCount pC__2 = PageAndCount.from(Map.of("_count", List.of("2")), DEFAULT_PAGE_COUNT); + assertPageAndCount(pC__2, 1, 2); + PageAndCount pC__19 = PageAndCount.from(Map.of("_count", List.of("19")), DEFAULT_PAGE_COUNT); + assertPageAndCount(pC__19, 1, 19); + PageAndCount pC__20 = PageAndCount.from(Map.of("_count", List.of("20")), DEFAULT_PAGE_COUNT); + assertPageAndCount(pC__20, 1, 20); + PageAndCount pC__21 = PageAndCount.from(Map.of("_count", List.of("21")), DEFAULT_PAGE_COUNT); + assertPageAndCount(pC__21, 1, 21); + + PageAndCount pC_max_max = PageAndCount.from(Map.of("_page", List.of(String.valueOf(Integer.MAX_VALUE)), + "_count", List.of(String.valueOf(Integer.MAX_VALUE))), DEFAULT_PAGE_COUNT); + assertPageAndCount(pC_max_max, 1, DEFAULT_PAGE_COUNT); + + PageAndCount pC_s_30 = PageAndCount.from(Map.of("_page", List.of("foo"), "_count", List.of("30")), + DEFAULT_PAGE_COUNT); + assertPageAndCount(pC_s_30, 1, 30); + PageAndCount pC_1_s = PageAndCount.from(Map.of("_page", List.of("2"), "_count", List.of("bar")), + DEFAULT_PAGE_COUNT); + assertPageAndCount(pC_1_s, 2, DEFAULT_PAGE_COUNT); + } + + private void assertPageAndCount(PageAndCount pC, int expectedPage, int expectedCount) + { + assertNotNull(pC); + assertEquals("page", expectedPage, pC.getPage()); + assertEquals("count", expectedCount, pC.getCount()); + } +} diff --git a/pom.xml b/pom.xml index 6c85afdda..85825c4fa 100755 --- a/pom.xml +++ b/pom.xml @@ -161,7 +161,7 @@ org.postgresql postgresql - 42.7.1 + 42.7.2