diff --git a/CHANGELOG.md b/CHANGELOG.md index ffe275949..1873f1be6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,9 +9,13 @@ ### Deprecated ### Removed + - Dependency to deprecated oslc4j-json4j-provider + ### Fixed +- Client now picks the correct ResponseInfo object when an OSLC Query response contains multiple ResponseInfo objects. + ## [6.0.0] ### Security diff --git a/client/oslc-client/src/main/java/org/eclipse/lyo/client/query/OslcQueryResult.java b/client/oslc-client/src/main/java/org/eclipse/lyo/client/query/OslcQueryResult.java index 5654c10c7..04094c85f 100644 --- a/client/oslc-client/src/main/java/org/eclipse/lyo/client/query/OslcQueryResult.java +++ b/client/oslc-client/src/main/java/org/eclipse/lyo/client/query/OslcQueryResult.java @@ -13,14 +13,7 @@ */ package org.eclipse.lyo.client.query; -import java.io.InputStream; -import java.lang.reflect.InvocationTargetException; -import java.net.URISyntaxException; -import java.util.ArrayList; -import java.util.Iterator; - -import javax.xml.datatype.DatatypeConfigurationException; - +import jakarta.ws.rs.core.Response; import org.apache.jena.rdf.model.Model; import org.apache.jena.rdf.model.ModelFactory; import org.apache.jena.rdf.model.Property; @@ -37,213 +30,337 @@ import org.eclipse.lyo.oslc4j.core.exception.OslcCoreApplicationException; import org.eclipse.lyo.oslc4j.core.model.OslcConstants; import org.eclipse.lyo.oslc4j.provider.jena.JenaModelHelper; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; -import jakarta.ws.rs.core.Response; +import javax.xml.datatype.DatatypeConfigurationException; +import java.io.InputStream; +import java.lang.reflect.InvocationTargetException; +import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; /** - * The results of an OSLC query. If the query was paged, subsequent pages can be retrieved using the Iterator interface. - * + * The results of an OSLC query. If the query was paged, subsequent pages can be retrieved using + * the Iterator interface. + *

* This class is not currently thread safe. */ public class OslcQueryResult implements Iterator { - /** - * The default member property to look for in OSLC query results - * (rdfs:member). Can be changed using {@link #setMemberProperty(String)}. - */ - public final static Property DEFAULT_MEMBER_PROPERTY = RDFS.member; - - /** - * If system property {@value} is set to true, find any member in the - */ - public final static String SELECT_ANY_MEMBER = "org.eclipse.lyo.client.oslc.query.selectAnyMember"; - - /** - * Treat any resource in the members resource as a query result (except rdf:type). - * - * @see OslcQueryResult#SELECT_ANY_MEMBER - */ - private final class AnyMemberSelector extends SimpleSelector { - private AnyMemberSelector(Resource subject) { - super(subject, null, (RDFNode) null); - } - - public boolean selects(Statement s) { - String fqPredicateName = s.getPredicate().getNameSpace() + s.getPredicate().getLocalName(); - if (OSLCConstants.RDF_TYPE_PROP.equals(fqPredicateName)) { - return false; - } - - return s.getObject().isResource(); - } + private static final Logger log = LoggerFactory.getLogger(OslcQueryResult.class); + /** + * The default member property to look for in OSLC query results + * (rdfs:member). Can be changed using {@link #setMemberProperty(String)}. + */ + public final static Property DEFAULT_MEMBER_PROPERTY = RDFS.member; + + /** + * If system property {@value} is set to true, find any member in the + */ + public final static String SELECT_ANY_MEMBER = "org.eclipse.lyo.client.oslc.query" + + ".selectAnyMember"; + + /** + * Treat any resource in the members resource as a query result (except rdf:type). + * + * @see OslcQueryResult#SELECT_ANY_MEMBER + */ + private final class AnyMemberSelector extends SimpleSelector { + private AnyMemberSelector(Resource subject) { + super(subject, null, (RDFNode) null); + } + + public boolean selects(Statement s) { + String fqPredicateName = + s.getPredicate().getNameSpace() + s.getPredicate().getLocalName(); + if (OSLCConstants.RDF_TYPE_PROP.equals(fqPredicateName)) { + return false; + } + + return s.getObject().isResource(); + } + } + + private final OslcQuery query; + + private final Response response; + + private final int pageNumber; + + private Property memberProperty = DEFAULT_MEMBER_PROPERTY; + + private Model rdfModel; + + private Resource infoResource, membersResource; + + private String nextPageUrl = ""; + + private boolean rdfInitialized = false; + + public OslcQueryResult(OslcQuery query, Response response) { + this.query = query; + this.response = response; + + this.pageNumber = 1; + + + } + + private OslcQueryResult(OslcQueryResult prev) { + this.query = new OslcQuery(prev); + this.response = this.query.getResponse(); + this.membersResource = prev.membersResource; + this.memberProperty = prev.memberProperty; + + this.pageNumber = prev.pageNumber + 1; + + } + + private synchronized void initializeRdf() { + if (!rdfInitialized) { + rdfInitialized = true; + rdfModel = ModelFactory.createDefaultModel(); + rdfModel.read(response.readEntity(InputStream.class), query.getCapabilityUrl()); + + //Find a resource with rdf:type of oslc:ResourceInfo + Property rdfType = rdfModel.createProperty(OslcConstants.RDF_NAMESPACE, "type"); + Property responseInfo = rdfModel.createProperty(OslcConstants.OSLC_CORE_NAMESPACE, + "ResponseInfo"); + ResIterator iter = rdfModel.listResourcesWithProperty(rdfType, responseInfo); + + //The main ResponseInfo shall have the query URI or a page URI + List responseInfos = iter.toList(); + + infoResource = null; + membersResource = rdfModel.getResource(query.getCapabilityUrl()); + + if (responseInfos.isEmpty()) { + return; + } + + infoResource = tryFindOnlyResponseInfo(responseInfos); + if (infoResource == null) { + log.trace("Cannot find exactly one ResponseInfo"); + } else { + log.debug("Found exactly one ResponseInfo"); + return; + } + + infoResource = tryFindOnlyWithNextPage(responseInfos); + if (infoResource == null) { + log.trace("Cannot find exactly one ResponseInfo with nextPage"); + } else { + log.debug("Found exactly one ResponseInfo with nextPage"); + return; + } + + infoResource = tryFindExactResponseInfoUri(responseInfos); + if (infoResource == null) { + log.trace("Cannot a ResponseInfo whose URI matches the query URI exactly"); + } else { + log.debug("Found a ResponseInfo whose URI matches the query URI exactly"); + return; + } + + infoResource = tryFindPrefixedResponseInfoUri(responseInfos); + if (infoResource == null) { + log.trace("Cannot find exactly one ResponseInfo whose URI starts with the query URI"); + } else { + log.debug("Found exactly one ResponseInfo whose URI starts with the query URI"); + return; + } + + if (infoResource == null) { + throw new IllegalStateException("Failed to find an appropriate ResponseInfo object"); + } + + } + } + + /** + * Extracts a ResourceInfo resource if one and only one has a property with the nextPage + * predicate. + * + * @param responseInfos from OSLC Query results + * @return a ResourceInfo resource if one satisfies the conditions; null if none satisfy + */ + private Resource tryFindOnlyWithNextPage(List responseInfos) { + Property nextPagePredicate = rdfModel.getProperty(OslcConstants.OSLC_CORE_NAMESPACE, + "nextPage"); + var responsesWithNextPage = + responseInfos.stream().filter(ri -> ri.getProperty(nextPagePredicate) != null).toList(); + if (responsesWithNextPage.size() == 1) { + return responsesWithNextPage.get(0); + } + else if (responsesWithNextPage.size() > 1) { + log.warn("Multiple ResponseInfo objects found with nextPage predicate"); + } + return null; } - private final OslcQuery query; - - private final Response response; - - private final int pageNumber; - - private Property memberProperty = DEFAULT_MEMBER_PROPERTY; - - private Model rdfModel; - - private Resource infoResource, membersResource; - - private String nextPageUrl = ""; - - private boolean rdfInitialized = false; - - public OslcQueryResult(OslcQuery query, Response response) { - this.query = query; - this.response = response; - - this.pageNumber = 1; - - - } - - private OslcQueryResult(OslcQueryResult prev) { - this.query = new OslcQuery(prev); - this.response = this.query.getResponse(); - this.membersResource = prev.membersResource; - this.memberProperty = prev.memberProperty; - - this.pageNumber = prev.pageNumber + 1; - - } - - private synchronized void initializeRdf() { - if (!rdfInitialized) { - rdfInitialized = true; - rdfModel = ModelFactory.createDefaultModel(); - rdfModel.read(response.readEntity(InputStream.class), query.getCapabilityUrl()); - - //Find a resource with rdf:type of oslc:ResourceInfo - Property rdfType = rdfModel.createProperty(OslcConstants.RDF_NAMESPACE, "type"); - Property responseInfo = rdfModel.createProperty(OslcConstants.OSLC_CORE_NAMESPACE, "ResponseInfo"); - ResIterator iter = rdfModel.listResourcesWithProperty(rdfType, responseInfo); - - //There should only be one - take the first - infoResource = null; - while (iter.hasNext()) { - infoResource = iter.next(); - break; - } - membersResource = rdfModel.getResource(query.getCapabilityUrl()); - } - } - - String getNextPageUrl() { - initializeRdf(); - if ((nextPageUrl == null || nextPageUrl.isEmpty()) && infoResource != null) { - Property predicate = rdfModel.getProperty(OslcConstants.OSLC_CORE_NAMESPACE, "nextPage"); - Selector select = new SimpleSelector(infoResource, predicate, (RDFNode) null); - StmtIterator iter = rdfModel.listStatements(select); - if (iter.hasNext()) { - Statement nextPage = iter.next(); - nextPageUrl = nextPage.getResource().getURI(); - } else { - nextPageUrl = ""; - } - } - return nextPageUrl; - } - - /** - * @return whether there is another page of results after this - */ - public boolean hasNext() { - return (!"".equals(getNextPageUrl())); - } - - /** - * @return the next page of results - */ - public OslcQueryResult next() { - return new OslcQueryResult(this); - } - - /** - * @throws UnsupportedOperationException always - */ - public void remove() { - throw new UnsupportedOperationException(); - } - - public OslcQuery getQuery() { - return query; - } - - /** - * Returns the member property to find query result resources. - * - * @return the member property URI - * @see #setMemberProperty(String) - */ - public String getMemberProperty() { - return this.memberProperty.getURI(); - } - - /** - * Sets the predicate to use to find query result resources. If unset, - * defaults to {@code http://www.w3.org/2000/01/rdf-schema#member}. - * - * @param memberPredicate - * the RDF predicate for member resources from the provider's - * query shape - * @see Specifying the sahpe of a query - */ - public void setMemberProperty(String memberPredicate) { - this.memberProperty = ModelFactory.createDefaultModel().createProperty(memberPredicate); - } - - /** - * Get the raw Wink client response to a query. - * - * NOTE: Using this method and consuming the response will make other methods - * which examine the response unavailable (Examples: getMemberUrls(), next() and hasNext()). - * When this method is invoked, the consumer is responsible for OSLC page processing - * - * @return - */ - public Response getRawResponse() { - return response; - } - - private Selector getMemberSelector() { - if ("true".equalsIgnoreCase(System.getProperty(SELECT_ANY_MEMBER))) { - return new AnyMemberSelector(membersResource); - } - - return new SimpleSelector(membersResource, memberProperty, (RDFNode) null); - } - - /** - * Return the subject URLs of the query response. The URLs are the location of all artifacts - * which satisfy the query conditions. - * - * NOTE: Using this method consumes the query response and makes other methods - * which examine the response unavailable (Example: getRawResponse(). - * @return - */ - public String[] getMembersUrls() { - initializeRdf(); - ArrayList membersUrls = new ArrayList<>(); + /** + * Extracts a ResourceInfo resource if one and only one has the same prefix as the query URI. + * + * @param responseInfos from OSLC Query results + * @return a ResourceInfo resource if one satisfies the conditions; null if none satisfy + */ + private Resource tryFindPrefixedResponseInfoUri(List responseInfos) { + List filteredObjects = + responseInfos.stream().filter(ri -> ri.getURI().startsWith(query.getQueryUrl())).toList(); + if (filteredObjects.size() == 1) { + return filteredObjects.get(0); + } else if (filteredObjects.size() > 1) { + log.warn("Multiple ResponseInfo objects found starting with the same Query URI"); + } + return null; + } + + /** + * Extracts a ResourceInfo resource if one and only one has exactly the same URI as the query + * URI. + * + * @param responseInfos from OSLC Query results + * @return a ResourceInfo resource if one satisfies the conditions; null if none satisfy + * @throws IllegalStateException if multiple resources satisfy the same condition + */ + private Resource tryFindExactResponseInfoUri(List responseInfos) { + List filteredObjects = + responseInfos.stream().filter(ri -> ri.getURI().equals(query.getQueryUrl())).toList(); + if (filteredObjects.size() == 1) { + return filteredObjects.get(0); + } else if (filteredObjects.size() > 1) { + throw new IllegalStateException("Multiple ResponseInfo objects found with the same URI"); + } + return null; + } + + /** + * Extracts a ResourceInfo resource if one and only one exists in the results. + * + * @param responseInfos from OSLC Query results + * @return a ResourceInfo resource if one satisfies the conditions; null if none satisfy + */ + private Resource tryFindOnlyResponseInfo(List responseInfos) { + if (responseInfos.size() == 1) { + return responseInfos.get(0); + } + return null; + } + + String getNextPageUrl() { + initializeRdf(); + if ((nextPageUrl == null || nextPageUrl.isEmpty()) && infoResource != null) { + Property predicate = rdfModel.getProperty(OslcConstants.OSLC_CORE_NAMESPACE, + "nextPage"); + Selector select = new SimpleSelector(infoResource, predicate, (RDFNode) null); + StmtIterator iter = rdfModel.listStatements(select); + if (iter.hasNext()) { + Statement nextPage = iter.next(); + nextPageUrl = nextPage.getResource().getURI(); + } else { + nextPageUrl = ""; + } + } + return nextPageUrl; + } + + /** + * @return whether there is another page of results after this + */ + public boolean hasNext() { + return (!"".equals(getNextPageUrl())); + } + + /** + * @return the next page of results + */ + public OslcQueryResult next() { + return new OslcQueryResult(this); + } + + /** + * @throws UnsupportedOperationException always + */ + public void remove() { + throw new UnsupportedOperationException(); + } + + public OslcQuery getQuery() { + return query; + } + + /** + * Returns the member property to find query result resources. + * + * @return the member property URI + * @see #setMemberProperty(String) + */ + public String getMemberProperty() { + return this.memberProperty.getURI(); + } + + /** + * Sets the predicate to use to find query result resources. If unset, + * defaults to {@code http://www.w3.org/2000/01/rdf-schema#member}. + * + * @param memberPredicate the RDF predicate for member resources from the provider's + * query shape + * @see + * Specifying the sahpe of a query + */ + public void setMemberProperty(String memberPredicate) { + this.memberProperty = ModelFactory.createDefaultModel().createProperty(memberPredicate); + } + + /** + * Get the raw Wink client response to a query. + *

+ * NOTE: Using this method and consuming the response will make other methods + * which examine the response unavailable (Examples: getMemberUrls(), next() and hasNext()). + * When this method is invoked, the consumer is responsible for OSLC page processing + * + * @return + */ + public Response getRawResponse() { + return response; + } + + private Selector getMemberSelector() { + if ("true".equalsIgnoreCase(System.getProperty(SELECT_ANY_MEMBER))) { + return new AnyMemberSelector(membersResource); + } + + return new SimpleSelector(membersResource, memberProperty, (RDFNode) null); + } + + /** + * Return the subject URLs of the query response. The URLs are the location of all artifacts + * which satisfy the query conditions. + *

+ * NOTE: Using this method consumes the query response and makes other methods + * which examine the response unavailable (Example: getRawResponse(). + * + * @return + */ + public String[] getMembersUrls() { + initializeRdf(); + ArrayList membersUrls = new ArrayList<>(); Selector select = getMemberSelector(); - StmtIterator iter = rdfModel.listStatements(select); - while (iter.hasNext()) { - Statement member = iter.next(); - membersUrls.add(member.getResource().getURI()); - } - return membersUrls.toArray(new String[membersUrls.size()]); - } - - /** - * Return the enumeration of queried results from this page - * - * @return member statements from current page. - */ + StmtIterator iter = rdfModel.listStatements(select); + while (iter.hasNext()) { + Statement member = iter.next(); + membersUrls.add(member.getResource().getURI()); + } + return membersUrls.toArray(new String[membersUrls.size()]); + } + + /** + * Return the enumeration of queried results from this page + * + * @return member statements from current page. + */ public Iterable getMembers(final Class clazz) { initializeRdf(); @@ -259,11 +376,12 @@ public T next() { Statement member = iter.next(); try { - return (T) JenaModelHelper.fromJenaResource((Resource) member.getObject(), clazz); + return (T) JenaModelHelper.fromJenaResource((Resource) member.getObject(), + clazz); } catch (DatatypeConfigurationException | IllegalAccessException | InvocationTargetException | InstantiationException | - OslcCoreApplicationException - | NoSuchMethodException | URISyntaxException e) { + OslcCoreApplicationException | NoSuchMethodException | + URISyntaxException e) { throw new LyoModelException(e); } } diff --git a/client/oslc-client/src/test/java/org/eclipse/lyo/client/OslcClientTest.java b/client/oslc-client/src/test/java/org/eclipse/lyo/client/OslcClientTest.java index f84966671..5a84d94d1 100644 --- a/client/oslc-client/src/test/java/org/eclipse/lyo/client/OslcClientTest.java +++ b/client/oslc-client/src/test/java/org/eclipse/lyo/client/OslcClientTest.java @@ -1,53 +1,57 @@ /* - * Copyright (c) 2021 Contributors to the Eclipse Foundation See the NOTICE file(s) distributed with this work for - * additional information regarding copyright ownership. This program and the accompanying materials are made available - * under the terms of the Eclipse Public License 2.0 which is available at http://www.eclipse.org/legal/epl-2.0, or the - * Eclipse Distribution License 1.0 which is available at http://www.eclipse.org/org/documents/edl-v10.php. + * Copyright (c) 2021 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License 1.0 + * which is available at http://www.eclipse.org/org/documents/edl-v10.php. + * * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause */ package org.eclipse.lyo.client; -import static java.time.Duration.ofSeconds; -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.Assert.assertNull; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTimeout; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.clearInvocations; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; - -import java.io.IOException; -import java.net.URISyntaxException; -import java.util.Map; - -import javax.xml.namespace.QName; - +import jakarta.ws.rs.client.Invocation.Builder; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.core.Response.Status.Family; import org.apache.http.HttpHeaders; import org.eclipse.lyo.oslc4j.core.model.ServiceProvider; import org.junit.jupiter.api.Test; import org.mockito.Mockito; -import jakarta.ws.rs.client.Invocation.Builder; -import jakarta.ws.rs.core.Response; -import jakarta.ws.rs.core.Response.Status.Family; +import javax.xml.namespace.QName; +import java.io.IOException; +import java.net.URISyntaxException; +import java.util.Map; + +import static java.time.Duration.*; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.Assert.assertNull; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; public class OslcClientTest { - /* - * Tests that the RDF/XML MessageBodyWriter doesn't go into an infinite loop when given bad data on the client (Bug - * 417749). ClientRuntimeException no longer expected in Lyo 4.0. + /** + * Tests that the RDF/XML MessageBodyWriter doesn't go into an infinite loop when + * given bad data on the client (Bug 417749). ClientRuntimeException no longer expected in + * Lyo 4.0. */ - // @Disabled("Unit test actually POSTs data to example.com, which we shouldn't do as we don't own that domain.") +// @Disabled("Unit test actually POSTs data to example.com, which we shouldn't do as we don't +// own that domain.") @Test public void postInvalidOlscResource() throws IOException, URISyntaxException { assertTimeout(ofSeconds(10), () -> { final OslcClient client = new OslcClient(); final ServiceProvider request = new ServiceProvider(); - request.getExtendedProperties().put(new QName("http://example.com/ns#", "test"), "test"); - Response response = client.createResource( - "http://open-services.net/.well-known/resource-that-should-not-exist-whose-status-code-should-not-be-200", - request, OSLCConstants.CT_RDF); + request.getExtendedProperties().put(new QName("http://example.com/ns#", "test"), + "test"); + Response response = client.createResource("http://open-services.net/" + + ".well-known/resource-that-should-not-exist-whose-status-code-should-not-be-200", + request, OSLCConstants.CT_RDF); assertThat(response.getStatusInfo().getFamily() != Family.SUCCESSFUL); // assertThrows(ClientErrorException.class, () -> { // @@ -64,7 +68,7 @@ public void initTest() { @Test public void connectionTest() { final OslcClient client = new OslcClient(); - final Response resource = client.getResource("http://open-services.net"); + final Response resource = client.getResource("https://open-services.net"); assertThat(resource).isNotNull(); assertThat(resource.getStatus()).isLessThan(400); } @@ -72,72 +76,84 @@ public void connectionTest() { @Test public void testGetResource() { OslcClient client = mock(OslcClient.class, Mockito.CALLS_REAL_METHODS); - doReturn(null).when(client).doRequest(any(), any(), any(), any(), any(), any(), any(), any()); + doReturn(null).when(client).doRequest(any(), any(), any(), any(), any(), any(), any(), + any()); client.getResource("test.url"); - verify(client).doRequest("GET", "test.url", null, null, null, null, "application/rdf+xml", null); + verify(client).doRequest("GET", "test.url", null, null, null, null, "application/rdf+xml" + , null); clearInvocations(client); client.getResource("test.url", "application/rdf+xml"); - verify(client).doRequest("GET", "test.url", null, null, null, "application/rdf+xml", "application/rdf+xml", - null); + verify(client).doRequest("GET", "test.url", null, null, null, "application/rdf+xml", + "application/rdf+xml", null); clearInvocations(client); client.getResource("test.url", Map.of("a", "b"), "application/rdf+xml", "oslc.context"); - verify(client).doRequest("GET", "test.url", null, "oslc.context", null, "application/rdf+xml", - "application/rdf+xml", Map.of("a", "b")); + verify(client).doRequest("GET", "test.url", null, "oslc.context", null, "application/rdf" + + "+xml", "application/rdf+xml", Map.of("a", "b")); } @Test public void testPutResource() { OslcClient client = mock(OslcClient.class, Mockito.CALLS_REAL_METHODS); - doReturn(null).when(client).doRequest(any(), any(), any(), any(), any(), any(), any(), any()); + doReturn(null).when(client).doRequest(any(), any(), any(), any(), any(), any(), any(), + any()); client.updateResource("test.url", "artifact", "application/json"); - verify(client).doRequest("PUT", "test.url", "artifact", null, null, "application/json", "*/*", null); + verify(client).doRequest("PUT", "test.url", "artifact", null, null, "application/json", + "*/*", null); clearInvocations(client); - client.updateResource("test.url", "artifact", "application/json", "*/*", "ifmatch", "configContext"); - verify(client).doRequest("PUT", "test.url", "artifact", "configContext", "ifmatch", "application/json", "*/*", - null); + client.updateResource("test.url", "artifact", "application/json", "*/*", "ifmatch", + "configContext"); + verify(client).doRequest("PUT", "test.url", "artifact", "configContext", "ifmatch", + "application/json", "*/*", null); clearInvocations(client); client.updateResource("test.url", "artifact", "application/json", "*/*", "ifmatch"); - verify(client).doRequest("PUT", "test.url", "artifact", null, "ifmatch", "application/json", "*/*", null); + verify(client).doRequest("PUT", "test.url", "artifact", null, "ifmatch", "application" + + "/json", "*/*", null); } @Test public void testDeleteResource() { OslcClient client = mock(OslcClient.class, Mockito.CALLS_REAL_METHODS); - doReturn(null).when(client).doRequest(any(), any(), any(), any(), any(), any(), any(), any()); + doReturn(null).when(client).doRequest(any(), any(), any(), any(), any(), any(), any(), + any()); client.deleteResource("test.url"); verify(client).doRequest("DELETE", "test.url", null, null, null, null, null, null); clearInvocations(client); client.deleteResource("test.url", "configContext"); - verify(client).doRequest("DELETE", "test.url", null, "configContext", null, null, null, null); + verify(client).doRequest("DELETE", "test.url", null, "configContext", null, null, null, + null); } @Test public void testCreateResource() { OslcClient client = mock(OslcClient.class, Mockito.CALLS_REAL_METHODS); - doReturn(null).when(client).doRequest(any(), any(), any(), any(), any(), any(), any(), any()); + doReturn(null).when(client).doRequest(any(), any(), any(), any(), any(), any(), any(), + any()); client.createResource("test.url", "artifact", "application/rdf+xml"); - verify(client).doRequest("POST", "test.url", "artifact", null, null, "application/rdf+xml", "*/*", null); + verify(client).doRequest("POST", "test.url", "artifact", null, null, "application/rdf+xml" + , "*/*", null); clearInvocations(client); client.createResource("test.url", "artifact", "application/rdf+xml", "*/*", "oslc.ctx"); - verify(client).doRequest("POST", "test.url", "artifact", "oslc.ctx", null, "application/rdf+xml", "*/*", null); + verify(client).doRequest("POST", "test.url", "artifact", "oslc.ctx", null, "application" + + "/rdf+xml", "*/*", null); } @Test public void testAddHeaders() { OslcClient client = new OslcClient(); Builder builder = mock(Builder.class); - Map headers = client.addHeaders(builder, Map.of("a", "b"), "ifmatch", "ctx"); + Map headers = client.addHeaders(builder, Map.of("a", "b"), "ifmatch", + "ctx"); assertEquals(4, headers.size()); assertEquals("b", headers.get("a")); diff --git a/client/oslc-client/src/test/java/org/eclipse/lyo/client/OslcQueryResultTest.java b/client/oslc-client/src/test/java/org/eclipse/lyo/client/OslcQueryResultTest.java deleted file mode 100644 index 1c289a5ed..000000000 --- a/client/oslc-client/src/test/java/org/eclipse/lyo/client/OslcQueryResultTest.java +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright (c) 2020 Contributors to the Eclipse Foundation - * - * See the NOTICE file(s) distributed with this work for additional - * information regarding copyright ownership. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License 1.0 - * which is available at http://www.eclipse.org/org/documents/edl-v10.php. - * - * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause - */ -package org.eclipse.lyo.client; - -import static org.junit.Assert.assertEquals; -import static org.mockito.Mockito.when; - -import java.io.InputStream; - -import org.eclipse.lyo.client.query.OslcQuery; -import org.eclipse.lyo.client.query.OslcQueryParameters; -import org.eclipse.lyo.client.query.OslcQueryResult; -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mockito; - -import jakarta.ws.rs.core.Response; - -/** - * @author Samuel Padgett - */ -public class OslcQueryResultTest { - @Before - public void clearPublicURISystemProperty() { - System.clearProperty(OslcQueryResult.SELECT_ANY_MEMBER); - } - - @Test - public void testEmpty() { - Response mockedResponse = mockClientResponse("/emptyQuery.rdf"); - - OslcQueryParameters params = new OslcQueryParameters(); - params.setWhere("dceterms:identifier=3"); - OslcQuery query = new OslcQuery(new OslcClient(), "http://example.com/provider/query", params); - OslcQueryResult result = new OslcQueryResult(query, mockedResponse); - assertEquals(0, result.getMembersUrls().length); - } - - @Test - public void testNoParameters() { - Response mockedResponse = mockClientResponse("/noParamQuery.rdf"); - - OslcQuery query = new OslcQuery(new OslcClient(), "http://example.com/provider/query"); - OslcQueryResult result = new OslcQueryResult(query, mockedResponse); - assertEquals(2, result.getMembersUrls().length); - } - - @Test - public void testFolderQuery() { - Response mockedResponse = mockClientResponse("/queryFolderResponse.rdf"); - - OslcQueryParameters params = new OslcQueryParameters(); - params.setPrefix("dcterms=,nav="); - params.setSelect("*"); - - OslcQuery query = new OslcQuery(new OslcClient(), "https://192.168.99.3:9443/rm/folders", params); - OslcQueryResult result = new OslcQueryResult(query, mockedResponse); - assertEquals(1, result.getMembersUrls().length); - } - - @Test - public void testQuery() { - Response mockedResponse = mockClientResponse("/queryResponse.rdf"); - - OslcQueryParameters params = new OslcQueryParameters(); - params.setWhere("ex:product=\"Product A\""); - OslcQuery query = new OslcQuery(new OslcClient(), "http://example.com/provider/query", params); - OslcQueryResult result = new OslcQueryResult(query, mockedResponse); - assertEquals(2, result.getMembersUrls().length); - } - - @Test - public void testBlogQuery() { - Response mockedResponse = mockClientResponse("/blogQuery.rdf"); - - OslcQueryParameters params = new OslcQueryParameters(); - params.setSelect("dcterms:title"); - OslcQuery query = new OslcQuery(new OslcClient(), "http://example.com/query"); - OslcQueryResult result = new OslcQueryResult(query, mockedResponse); - result.setMemberProperty("http://open-services.net/ns/bogus/blogs#comment"); - assertEquals(5, result.getMembersUrls().length); - } - - @Test - public void testAnyMember() { - System.setProperty(OslcQueryResult.SELECT_ANY_MEMBER, "true"); - Response mockedResponse = mockClientResponse("/blogQuery.rdf"); - - OslcQueryParameters params = new OslcQueryParameters(); - params.setSelect("dcterms:title"); - OslcQuery query = new OslcQuery(new OslcClient(), "http://example.com/query"); - OslcQueryResult result = new OslcQueryResult(query, mockedResponse); - assertEquals(5, result.getMembersUrls().length); - } - - private Response mockClientResponse(String file) { - final InputStream is = OslcQueryResultTest.class.getResourceAsStream(file); - Response mockedResponse = Mockito.mock(Response.class); - when(mockedResponse.readEntity(InputStream.class)).thenReturn(is); - - return mockedResponse; - } - -} diff --git a/client/oslc-client/src/test/java/org/eclipse/lyo/client/query/OslcQueryResultTest.java b/client/oslc-client/src/test/java/org/eclipse/lyo/client/query/OslcQueryResultTest.java new file mode 100644 index 000000000..126069d24 --- /dev/null +++ b/client/oslc-client/src/test/java/org/eclipse/lyo/client/query/OslcQueryResultTest.java @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2020 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License 1.0 + * which is available at http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause + */ +package org.eclipse.lyo.client.query; + +import jakarta.ws.rs.core.Response; +import org.eclipse.lyo.client.OslcClient; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mockito; + +import java.io.InputStream; + +import static org.junit.Assert.*; +import static org.mockito.Mockito.*; + +/** + * @author Samuel Padgett + */ +public class OslcQueryResultTest { + @Before + public void clearPublicURISystemProperty() { + System.clearProperty(OslcQueryResult.SELECT_ANY_MEMBER); + } + + @Test + public void testEmpty() { + Response mockedResponse = mockClientResponse("/emptyQuery.rdf"); + + OslcQueryParameters params = new OslcQueryParameters(); + params.setWhere("dceterms:identifier=3"); + OslcQuery query = new OslcQuery(new OslcClient(), "http://example.com/provider/query", + params); + OslcQueryResult result = new OslcQueryResult(query, mockedResponse); + assertEquals(0, result.getMembersUrls().length); + } + + @Test + public void testNoParameters() { + Response mockedResponse = mockClientResponse("/noParamQuery.rdf"); + + OslcQuery query = new OslcQuery(new OslcClient(), "http://example.com/provider/query"); + OslcQueryResult result = new OslcQueryResult(query, mockedResponse); + assertEquals(2, result.getMembersUrls().length); + } + + @Test + public void testFolderQuery() { + Response mockedResponse = mockClientResponse("/queryFolderResponse.rdf"); + + OslcQueryParameters params = new OslcQueryParameters(); + params.setPrefix("dcterms=,nav="); + params.setSelect("*"); + + OslcQuery query = new OslcQuery(new OslcClient(), "https://192.168.99.3:9443/rm/folders", + params); + OslcQueryResult result = new OslcQueryResult(query, mockedResponse); + assertEquals(1, result.getMembersUrls().length); + } + + @Test + public void testQuery() { + Response mockedResponse = mockClientResponse("/queryResponse.rdf"); + + OslcQueryParameters params = new OslcQueryParameters(); + params.setWhere("ex:product=\"Product A\""); + OslcQuery query = new OslcQuery(new OslcClient(), "http://example.com/provider/query", + params); + OslcQueryResult result = new OslcQueryResult(query, mockedResponse); + assertEquals(2, result.getMembersUrls().length); + } + + @Test + public void testBlogQuery() { + Response mockedResponse = mockClientResponse("/blogQuery.rdf"); + + OslcQueryParameters params = new OslcQueryParameters(); + params.setSelect("dcterms:title"); + OslcQuery query = new OslcQuery(new OslcClient(), "http://example.com/query"); + OslcQueryResult result = new OslcQueryResult(query, mockedResponse); + result.setMemberProperty("http://open-services.net/ns/bogus/blogs#comment"); + assertEquals(5, result.getMembersUrls().length); + } + + @Test + public void testAnyMember() { + System.setProperty(OslcQueryResult.SELECT_ANY_MEMBER, "true"); + Response mockedResponse = mockClientResponse("/blogQuery.rdf"); + + OslcQueryParameters params = new OslcQueryParameters(); + params.setSelect("dcterms:title"); + OslcQuery query = new OslcQuery(new OslcClient(), "http://example.com/query"); + OslcQueryResult result = new OslcQueryResult(query, mockedResponse); + assertEquals(5, result.getMembersUrls().length); + } + + @Test + public void testMultiResponseInfos() { + // seems to work with both +// System.setProperty(OslcQueryResult.SELECT_ANY_MEMBER, "false"); + Response mockedResponse = mockClientResponse("/multiResponseQuery.rdf"); + + OslcQueryParameters params = new OslcQueryParameters(); + params.setSelect("dcterms:title"); + OslcQuery query = new OslcQuery(new OslcClient(), "https://nordic.clm.ibmcloud" + + ".com/ccm/oslc/contexts/_2nC4UBNvEeutmoeSPr3-Ag/workitems"); + OslcQueryResult result = new OslcQueryResult(query, mockedResponse); + assertEquals(20, result.getMembersUrls().length); + } + + private Response mockClientResponse(String file) { + final InputStream is = OslcQueryResultTest.class.getResourceAsStream(file); + Response mockedResponse = Mockito.mock(Response.class); + when(mockedResponse.readEntity(InputStream.class)).thenReturn(is); + + return mockedResponse; + } + +} diff --git a/client/oslc-client/src/test/resources/multiResponseQuery.rdf b/client/oslc-client/src/test/resources/multiResponseQuery.rdf new file mode 100644 index 000000000..28de9614d --- /dev/null +++ b/client/oslc-client/src/test/resources/multiResponseQuery.rdf @@ -0,0 +1,1397 @@ + + + + + + + 126285: Program + + + + + + + DP-479 + + + 2021-09-16T18:32:37.865Z + + + 2021-09-16T18:32:38.238Z + + + Image elements must provide a description in the 'alt' attribute for consumption by screen readers. + + + false + 1535 + + + false + + 1535 + + + + false + + + + + Implement accessibility in Pet Store application (updated) + _2nC4UBNvEeutmoeSPr3-Ag + + + + + false + false + false + + false + false + + + Task + New + + Task 1535 + + + + + Defect + false + + + 1115 + + + false + + + + 2021-04-07T10:38:21.136Z + + _2nC4UBNvEeutmoeSPr3-Ag + 2021-04-07T10:38:21.155Z + This is a System Change Request (A Defect) + + + + + false + New + Defect 1115 + + + + + + + + + + false + 1115 + + + false + false + false + false + + + + + + + + + 1044: PI Objective (Team) + + + Subscribed By + 1 + + + + + + + + DP-471 + + + + + + + + + Story + false + + false + 2020-10-21T09:38:25.551Z + + + false + 1046 + New + 2020-10-21T10:00:12.918Z + + Story + + Story 1046 + + false + + + false + + + + + + false + + 1046 + + false + false + + false + + 0 + + + + + + + _2nC4UBNvEeutmoeSPr3-Ag + + + + + + + + + + + + + + + + Accessibility verification using a screen reader + + + 2021-09-16T18:26:47.443Z + + + + + + + + false + false + + Image elements must provide a description in the 'alt' attribute for consumption by screen readers. + false + _2nC4UBNvEeutmoeSPr3-Ag + + + false + + + + 1531 + + 1531 + false + + New + + + + + Task + Task 1531 + false + 2021-09-16T18:26:48.129Z + + Implement accessibility in Pet Store application (updated) + false + + false + + + + + + + + + PI Objective (Program) + New + + + false + + + PI Objective 1042 + false + + + + + + + false + + + false + + + + + + PI Objective + + 2020-10-21T09:39:42.663Z + false + + 0 + + false + + 1042 + 1042 + + + _2nC4UBNvEeutmoeSPr3-Ag + + + + false + false + + 2020-10-21T09:32:48.097Z + + + + + + + + + Story 1050 + 1050 + false + 2020-12-14T11:45:52.188Z + + + + + + false + + + false + + + false + + + + 0 + 2020-12-14T11:53:03.066Z + _2nC4UBNvEeutmoeSPr3-Ag + + New + + + + + + + + Story + + + false + + false + + false + + + false + 1050 + + false + + + This is my story + + + + + + + configuring Essential SAFe for additional post-project initialization instructions + + + + + + + 1044: PI Objective (Team) + + + + + + + Accessibility verification using a screen reader + + + + + + + + + + + + + + + + + + + + + + + + + Subscribed By + 1 + + + + + + + + Accessibility verification using a screen reader + + + Subscribed By + 1 + + + + Subscribed By + 1 + + + + Defect + 2021-09-16T18:32:39.076Z + + Defect 1536 + + + + + + false + false + + + + + false + + false + + false + + + _2nC4UBNvEeutmoeSPr3-Ag + + + New + Error logging in + + 1536 + + 1536 + + + false + An error occurred when I tried to log in with a user ID that contained the '@' symbol. + + false + + + + + + 2021-09-16T18:32:39.067Z + false + + + + + + + DP-528 + + + + + + + DP-457 + + + 2021-04-08T13:11:56.081Z + false + + + + + + + + _2nC4UBNvEeutmoeSPr3-Ag + + + Task + + + + false + 1116 + + 1116 + New + + + + + + false + false + false + false + + + + + + 2021-04-07T10:40:39.882Z + + Task 1116 + false + false + + + need to create a ChangeRequest from Polarion + + + + + false + + + false + false + Defect 1532 + + + + + + + + + _2nC4UBNvEeutmoeSPr3-Ag + New + false + + + Defect + + + + 1532 + + 2021-09-16T18:26:49.032Z + false + + + Error logging in + false + + + + false + + 2021-09-16T18:26:49.021Z + false + An error occurred when I tried to log in with a user ID that contained the '@' symbol. + + + 1532 + + + + + + + + 126288: Non-Functional Requirement + + + + + + + DP-471 + + + false + false + + false + + + + + 2020-10-21T09:41:53.360Z + For <customers> who <do something> the <solution> is a <something - the "how"> that <provides this value> Unlike <competitor, current solution, or non-existing solution> our solution <does something better - the "why">



Outcomes hypothesis:

*

*



Leading indicators:

* (early innovation accounting measures)

*



NFRs:

*

*

+ _2nC4UBNvEeutmoeSPr3-Ag + + + + + + 1047 + + + + + 2020-10-21T09:41:27.851Z + + Draft + false + 1047 + 0 + + + 0 + Program Epic 1047 + + 0 + + + + + + Program Epic + false + + + + + false + 0.0 + false + Program Epic + + + + false + + + +
+ + + + + + 1045: Feature + + + + + + + Global Verifcation Test + + + Subscribed By + 1 + + + + false + false + + + false + + 1051 + Task 1051 + + + New + Test task demo for Erik + false + + 2021-04-07T10:55:47.342Z + + false + + Task + + _2nC4UBNvEeutmoeSPr3-Ag + + + + + false + + + + + false + 1051 + + + 2021-01-22T15:14:53.538Z + + + + + + + false + + + + + Subscribed By + 1 + + + + + + + + Accessibility verification using a screen reader + + + Subscribed By + 1 + + + + Subscribed By + 1 + + + + + + + + Global Verifcation Test + + + + + + + 126286: Team Non-Functional Requirement + + + + + + + DP-529 + + + + Implement accessibility in Pet Store application (updated) + 2310 + + false + false + + + false + + false + + + Task + + + false + + + Task 2310 + + false + + 2310 + 2023-07-16T19:19:27.418Z + _2nC4UBNvEeutmoeSPr3-Ag + + false + + + + + false + New + + 2023-07-16T19:19:26.374Z + + + + Image elements must provide a description in the 'alt' attribute for consumption by screen readers. + + + + + + + + + + 1046: Story + + + + + + false + + + 1537 + + + + false + + + 1537 + + false + false + + 2021-09-16T18:32:56.025Z + Image elements must provide a description in the 'alt' attribute for consumption by screen readers. + + Task + + + + 2021-09-16T18:32:55.653Z + + + false + false + + Task 1537 + false + + + Implement accessibility in Pet Store application (updated) + _2nC4UBNvEeutmoeSPr3-Ag + + + New + + + false + + + + + Task + 1533 + + _2nC4UBNvEeutmoeSPr3-Ag + New + Implement accessibility in Pet Store application (updated) + + false + Task 1533 + false + + + + 2021-09-16T18:31:15.255Z + + 1533 + + + + + false + + + + + false + false + + false + false + + Image elements must provide a description in the 'alt' attribute for consumption by screen readers. + + + false + 2021-09-16T18:31:15.641Z + + + + + + + + Subscribed By + 1 + + + + + + + + 1042: PI Objective (Program) + + + + + + + DP-477 + + + Subscribed By + 1 + + + + Subscribed By + 1 + + + + Subscribed By + 1 + + + + + + + + DP-527 + + + + + false + + + + false + 1044 + + + + + + + PI Objective 1044 + false + 1044 + + + false + + + + New + false + + + + + PI Objective (Team) + + false + false + + PI Objective + false + + 0 + + + + 2020-10-21T09:40:03.820Z + 2020-10-21T09:33:26.547Z + _2nC4UBNvEeutmoeSPr3-Ag + + + + + + Subscribed By + 1 + + + + + Work Items + 537 + + + + Subscribed By + 1 + + + + + + _2nC4UBNvEeutmoeSPr3-Ag + + + false + + false + + + + 1040 + + Post-Project Initialization + + false + + + 2020-10-21T07:35:20.513Z + + New + false + + + 2020-10-21T07:35:20.370Z + Task 1040 + + See, configuring Essential SAFe for additional post-project initialization instructions.



+ false + + false + Task + + + + false + false + + + + 1040 + + +
+ + Subscribed By + 1 + + + + + + + + 1045: Feature + + + + + + + 126287: Team User Requirement + + + + + + + Accessibility verification using a screen reader + + + + + false + + + + + false + 2021-04-08T13:14:06.622Z + 1118 + + + 1118 + + + Feature 1118 + + false + + + 0 + 0.0 + Draft + false + + + + + false + + + 0 + false + + + + false + + + + + + + + + + + 2021-11-08T08:35:53.632Z + + + A Change Request to trace to many development tasks in Polarion + 0 + + + + + + + false + + Feature + + _2nC4UBNvEeutmoeSPr3-Ag + false + + + + + + + + DP-525 + + + + Defect 1538 + + false + false + + + false + 1538 + + false + _2nC4UBNvEeutmoeSPr3-Ag + + + New + + + + + false + + + false + + + + false + An error occurred when I tried to log in with a user ID that contained the '@' symbol. + + 1538 + + + + + + false + Defect + + 2021-09-16T18:32:56.877Z + Error logging in + 2021-09-16T18:32:56.868Z + + + + + + Subscribed By + 1 + + + + + + + + 126289: User Requirement + + + + + + + DP-479 + + + false + false + + + + + 1534 + + + Error logging in + _2nC4UBNvEeutmoeSPr3-Ag + + New + + false + Defect + false + + 2021-09-16T18:31:16.492Z + + + + + + + false + 2021-09-16T18:31:16.501Z + + + + Defect 1534 + + false + 1534 + + + + + + + An error occurred when I tried to log in with a user ID that contained the '@' symbol. + false + + false + + + + + + + 1046: Story + + + Subscribed By + 1 + + + + + + + + DP-440 + + + + + + + false + + + + + 1045 + + + + + + + + + _2nC4UBNvEeutmoeSPr3-Ag + false + + + + + + false + + false + false + + 2020-10-21T09:37:00.992Z + + false + + + 1045 + 2020-12-11T15:19:32.749Z + Draft + + + + Feature + 0.0 + 0 + + Feature + + + + 0 + + + + Feature 1045 + 0 + + false + + + + + + + + false + false + + + + + + + Global Verifcation Test + + + Subscribed By + 1 + + + + + + + + 1045: Feature + + + + + + + Global Verifcation Test + + + + + + + 1047: Program Epic + + + Subscribed By + 1 + + + + + + + + 1042: PI Objective (Program) + + + Subscribed By + 1 + + + + + + + + DP-528 + +
diff --git a/client/oslc-client/src/test/resources/simplelogger.properties b/client/oslc-client/src/test/resources/simplelogger.properties new file mode 100644 index 000000000..9eb12ca52 --- /dev/null +++ b/client/oslc-client/src/test/resources/simplelogger.properties @@ -0,0 +1,2 @@ +org.slf4j.simpleLogger.defaultLogLevel=info +org.slf4j.simpleLogger.log.org.eclipse.lyo=trace