From a4793668e1f15d18a91504ac0c0843538dd8d6ba Mon Sep 17 00:00:00 2001 From: Joshua Darnell Date: Fri, 27 Mar 2020 21:00:00 -0700 Subject: [PATCH] Refactored Test Containers, RESOScript queries and templates and bumped versions, improved response errors, added dynamic select list processing, and added metadata checking for each dynamic select list --- generic.gold.resoscript | 606 ------------------ generic.resoscript | 138 ++-- .../web-api/web-api-server-1.0.2.feature | 172 +++-- .../stepdefs/WebAPIServer_1_0_2.java | 96 +-- .../java/org/reso/commander/Commander.java | 26 +- .../containers/WebApiTestContainer.java | 96 ++- src/main/java/org/reso/models/Parameters.java | 5 +- src/main/java/org/reso/models/Settings.java | 21 +- 8 files changed, 321 insertions(+), 839 deletions(-) delete mode 100644 generic.gold.resoscript diff --git a/generic.gold.resoscript b/generic.gold.resoscript deleted file mode 100644 index 57fed97..0000000 --- a/generic.gold.resoscript +++ /dev/null @@ -1,606 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - ]> - - - - - - - 1.1.0 - - - - - - - - - - - - - authorization_code - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/generic.resoscript b/generic.resoscript index b874bf5..bd53652 100644 --- a/generic.resoscript +++ b/generic.resoscript @@ -44,7 +44,7 @@ ############################################################--> - 1.1.0 + 2.0.0 - - - - + + + - + - + @@ -160,30 +160,26 @@ + - - - - + + + + - + - - - - - @@ -242,18 +238,6 @@ - - - - - - - - - @@ -309,121 +293,121 @@ { + And("^data are present for fields contained within the given select list$", () -> { try { AtomicInteger numFieldsWithData = new AtomicInteger(); - List fieldList = new ArrayList<>(Arrays.asList(Settings.resolveParametersString(parameterSelectList, getTestContainer().getSettings()).split(FIELD_SEPARATOR))); DecimalFormat df = new DecimalFormat(); df.setMaximumFractionDigits(1); double fill = 0; + assertNotNull("ERROR: no fields found within the given $select list. Check request Id: " + getTestContainer().getRequest().getRequestId() + " in your .resoscript file!", + getTestContainer().getSelectList()); + + LOG.info(QueryOption.SELECT + " list is: " + getTestContainer().getSelectList() ); + AtomicInteger numResults = new AtomicInteger(); //iterate over the items and count the number of fields with data to determine whether there are data present from(getTestContainer().getResponseData()).getList(JSON_VALUE_PATH, HashMap.class).forEach(item -> { - if (item != null) { - numResults.getAndIncrement(); - fieldList.forEach(field -> { - if (item.get(field) != null) { - numFieldsWithData.getAndIncrement(); - } - }); - } + numResults.getAndIncrement(); + getTestContainer().getSelectList().forEach(field -> { + if (item.get(field) != null) { + numFieldsWithData.getAndIncrement(); + } + }); }); LOG.info("Number of Results: " + numResults.get()); - LOG.info("Number of Fields: " + fieldList.size()); + LOG.info("Number of Fields: " + getTestContainer().getSelectList().size()); LOG.info("Fields with Data: " + numFieldsWithData.get()); - if (numResults.get() > 0 && fieldList.size() > 0) { - fill = ((100.0 * numFieldsWithData.get()) / (numResults.get() * fieldList.size())); + if (numResults.get() > 0 && getTestContainer().getSelectList().size() > 0) { + fill = ((100.0 * numFieldsWithData.get()) / (numResults.get() * getTestContainer().getSelectList().size())); LOG.info("Percent Fill: " + df.format(fill) + "%"); } else { LOG.info("Percent Fill: 0% - no fields with data found!"); @@ -275,17 +276,20 @@ public class WebAPIServer_1_0_2 implements En { * REQ-WA103-QR5 - $skip * $skip=*Parameter_TopCount* */ - And("^a GET request is made to the resolved Url in \"([^\"]*)\" with \\$skip=\"([^\"]*)\"$", (String requirementId, String parameterTopCount) -> { + And("^a GET request is made to the resolved Url in \"([^\"]*)\" with \\$skip=\"([^\"]*)\"$", (String requestId, String parameterTopCount) -> { try { int skipCount = Integer.parseInt(Settings.resolveParametersString(parameterTopCount, getTestContainer().getSettings())); LOG.info("Skip count is: " + skipCount); + getTestContainer().setRequest(requestId); + //preserve initial response data for later comparisons getTestContainer().setInitialResponseData(getTestContainer().getResponseData()); //TODO: convert to OData filter factory - getTestContainer().setRequestUri(Commander.prepareURI(Settings.resolveParameters(getTestContainer().getSettings().getRequestById(requirementId), getTestContainer().getSettings()).getUrl() - + "&" + Commander.ODATA_QUERY_OPTIONS.SKIP + "=" + skipCount)); + getTestContainer().setRequestUri(Commander.prepareURI( + Settings.resolveParameters(getTestContainer().getSettings().getRequest(requestId), getTestContainer().getSettings()).getUrl() + + AMPERSAND + DOLLAR_SIGN + QueryOption.SKIP.toString() + EQUALS + skipCount)); getTestContainer().executePreparedGetRequest(); } catch (Exception ex) { fail(ex.toString()); @@ -320,10 +324,7 @@ public class WebAPIServer_1_0_2 implements En { /* * GET request by requirementId (see generic.resoscript) */ - When("^a GET request is made to the resolved Url in \"([^\"]*)\"$", (String requirementId) -> executeGetRequest(requirementId)); - - When("^a GET request is made to the resolved Url in \"([^\"]*)\" without service root validation$", (String requirementId) -> { - }); + When("^a GET request is made to the resolved Url in \"([^\"]*)\"$", this::executeGetRequest); /* * Assert response code @@ -774,23 +775,17 @@ public class WebAPIServer_1_0_2 implements En { * Ensures that the server metadata for the given resource in parameterResourceName contains * all of the fields in the given parameterSelectList. */ - And("^resource metadata for \"([^\"]*)\" contains the fields in \"([^\"]*)\"$", (String parameterResourceName, String parameterSelectList) -> { - final String selectList = Settings.resolveParametersString(parameterSelectList, getTestContainer().getSettings()); - + And("^resource metadata for \"([^\"]*)\" contains the fields in the given select list$", (String parameterResourceName) -> { try { final String resourceName = Settings.resolveParametersString(parameterResourceName, getTestContainer().getSettings()); - List fieldNames = Arrays.asList(selectList.split(FIELD_SEPARATOR)); - //create field lookup - Map fieldMap = new HashMap<>(); - TestUtils.findEntityTypesForEntityTypeName(getTestContainer().getEdm(), getTestContainer().getXMLMetadata(), resourceName) - .forEach(csdlProperty -> fieldMap.put(csdlProperty.getName(), csdlProperty)); - - LOG.info("Searching metadata for fields in given select list: " + selectList); - fieldNames.forEach(fieldName -> { - //trim string just in case spaces were used after the commas - assertNotNull("ERROR: Field name '" + fieldName + "' is not present in server metadata!", fieldMap.get(fieldName.trim())); - LOG.info("Found: '" + fieldName.trim() + "'"); + LOG.info("Searching metadata for fields in given select list: " + getTestContainer().getSelectList().toString()); + getTestContainer().getSelectList().forEach(fieldName -> { + //need to skip the expand field when looking through the metadata + if (!fieldName.contentEquals(getTestContainer().getExpandField())) { + assertNotNull("ERROR: Field name '" + fieldName + "' is not present in server metadata!", getTestContainer().getCsdlForFieldName(fieldName)); + LOG.info("Found: '" + fieldName.trim() + "'"); + } }); } catch (Exception ex) { fail(ex.toString()); @@ -924,10 +919,12 @@ public class WebAPIServer_1_0_2 implements En { * Checks the Standard Resources requirement from Section 2.6 of the Web API specification */ And("^the metadata contains at least one resource from \"([^\"]*)\"$", (String parameterRequiredResourceList) -> { - String requiredResourceString = Settings.resolveParametersString(parameterRequiredResourceList, getTestContainer().getSettings()).replace(" ", ""); - List requiredResources = Arrays.asList(requiredResourceString.split(",")); + String requiredResourceString = + Settings.resolveParametersString(parameterRequiredResourceList, getTestContainer().getSettings()).replace(SINGLE_SPACE, EMPTY_STRING); + List requiredResources = Arrays.asList(requiredResourceString.split(FIELD_SEPARATOR)); - LOG.info("Searching the default entity container for one of the following Standard Resources: " + requiredResourceString.replace(FIELD_SEPARATOR, PRETTY_FIELD_SEPARATOR)); + LOG.info("Searching the default entity container for one of the following Standard Resources: " + + requiredResourceString.replace(FIELD_SEPARATOR, PRETTY_FIELD_SEPARATOR)); AtomicBoolean found = new AtomicBoolean(false); requiredResources.forEach(requiredResource -> { @@ -958,12 +955,14 @@ public class WebAPIServer_1_0_2 implements En { }); - When("^a GET request is made to the resolved Url in \"([^\"]*)\" using the OData Client$", (String parameterRequestId) -> { - String uriString = Settings.resolveParameters(getTestContainer().getSettings().getRequestById(parameterRequestId), getTestContainer().getSettings()).getUrl(); - assertTrue("ERROR: the resolved Url in '" + parameterRequestId + "' was invalid!", uriString != null && uriString.length() > 0); + When("^a GET request is made to the resolved Url in \"([^\"]*)\" using the OData Client$", (String requestId) -> { + Request request = getTestContainer().getSettings().getRequest(requestId); + String uriString = Settings.resolveParameters(request, getTestContainer().getSettings()).getUrl(); + assertTrue("ERROR: the resolved Url in '" + requestId + "' was invalid!", uriString != null && uriString.length() > 0); - LOG.info("Request Id: " + parameterRequestId); + LOG.info("Request Id: " + requestId); try { + getTestContainer().setRequest(request); getTestContainer().setRequestUri(prepareUri(uriString)); getTestContainer().setClientEntitySetRequest(getTestContainer().getCommander().getClient().getRetrieveRequestFactory().getEntitySetRequest(getTestContainer().getRequestUri())); LOG.info("OData Client Request being made to: " + uriString); @@ -1018,9 +1017,12 @@ public class WebAPIServer_1_0_2 implements En { //reset local state each time a get request is run getTestContainer().resetState(); + assertNotNull("ERROR: request Id cannot be null!", requestId); + getTestContainer().setRequest(getTestContainer().getSettings().getRequest(requestId)); LOG.info("Request ID: " + requestId); + getTestContainer().setRequestUri(Commander.prepareURI(Settings.resolveParameters( - getTestContainer().getSettings().getRequestById(requestId), getTestContainer().getSettings()).getUrl())); + getTestContainer().getSettings().getRequest(requestId), getTestContainer().getSettings()).getUrl())); LOG.info("Request URI: " + getTestContainer().getRequestUri().toString()); getTestContainer().executePreparedGetRequest(); } catch (Exception ex) { diff --git a/src/main/java/org/reso/commander/Commander.java b/src/main/java/org/reso/commander/Commander.java index b5749d1..456d3ed 100644 --- a/src/main/java/org/reso/commander/Commander.java +++ b/src/main/java/org/reso/commander/Commander.java @@ -15,6 +15,7 @@ import org.apache.olingo.client.api.communication.response.ODataRetrieveResponse import org.apache.olingo.client.api.domain.ClientEntitySet; import org.apache.olingo.client.api.edm.xml.XMLMetadata; import org.apache.olingo.client.api.serialization.ODataSerializerException; +import org.apache.olingo.client.api.uri.QueryOption; import org.apache.olingo.client.core.ODataClientFactory; import org.apache.olingo.commons.api.edm.Edm; import org.apache.olingo.commons.api.format.ContentType; @@ -38,7 +39,6 @@ import java.util.concurrent.atomic.AtomicReference; import java.util.function.Function; /** - * URI * Most of the work done by the WebAPI commander is done by this class. Its public methods are, therefore, * the ones the Client programmer is expected to use. */ @@ -46,6 +46,9 @@ public class Commander { //TODO move to utils class public static final int OK = 0; public static final int NOT_OK = 1; + public static final String AMPERSAND = "&"; //TODO: find the corresponding query params constant for this + public static final String EQUALS = "="; //TODO: find the corresponding query params constant for this + public static final Integer DEFAULT_PAGE_SIZE = 10; public static final Integer DEFAULT_PAGE_LIMIT = 1; public static final String REPORT_DIVIDER = "=============================================================="; @@ -53,8 +56,7 @@ public class Commander { public static final String RESOSCRIPT_EXTENSION = ".resoscript"; public static final String EDMX_EXTENSION = ".xml"; private static final Logger LOG = LogManager.getLogger(Commander.class); - private static final String EDM_4_0_3_XSD = "edm.4.0.3.xsd", - EDMX_4_0_3_XSD = "edmx.4.0.3.xsd"; + private static final String EDM_4_0_3_XSD = "edm.4.0.3.xsd", EDMX_4_0_3_XSD = "edmx.4.0.3.xsd"; private static String bearerToken; private static String clientId; @@ -104,7 +106,7 @@ public class Commander { public static boolean validateXML(InputStream inputStream) { try { SAXParserFactory factory = SAXParserFactory.newInstance(); - factory.setValidating(false); //turn off expectation for having DTD in DOCTYPE tag + factory.setValidating(false); //turn off expectation of having DTD in DOCTYPE tag factory.setNamespaceAware(true); factory.setSchema(SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI).newSchema(new StreamSource[]{ @@ -121,6 +123,7 @@ public class Commander { reader.parse(inputSource); return true; } catch (SAXException saxEx) { + LOG.error(saxEx); return false; } } catch (Exception ex) { @@ -203,8 +206,8 @@ public class Commander { if (requestUri != null && requestUri.length() > 0 && preparedUri != null) { uriBuilder = new URIBuilder(preparedUri); - if (skip != null && skip > 0) uriBuilder.setParameter(ODATA_QUERY_OPTIONS.SKIP, skip.toString()); - uriBuilder.setParameter(ODATA_QUERY_OPTIONS.TOP, pageSize == null || pageSize == 0 ? DEFAULT_PAGE_SIZE.toString() : pageSize.toString()); + if (skip != null && skip > 0) uriBuilder.setParameter(QueryOption.SKIP.toString(), skip.toString()); + uriBuilder.setParameter(QueryOption.TOP.toString(), pageSize == null || pageSize == 0 ? DEFAULT_PAGE_SIZE.toString() : pageSize.toString()); URI uri = uriBuilder.build(); LOG.debug("URI created: " + uri.toString()); @@ -636,14 +639,6 @@ public class Commander { serializeEntitySet(entitySet, outputFilePath, ContentType.JSON); } - /** - * Constants for OData query parameters - */ - public static final class ODATA_QUERY_OPTIONS { - public static final String TOP = "$top"; - public static final String SKIP = "$skip"; - } - /** * Error handler class for SAX parser */ @@ -766,8 +761,7 @@ public class Commander { Commander.useEdmEnabledClient = this.useEdmEnabledClient; //items required for OAuth client - isOAuthClient = - clientId != null && clientId.length() > 0 + isOAuthClient = clientId != null && clientId.length() > 0 && clientSecret != null && clientSecret.length() > 0 && tokenUri != null && tokenUri.length() > 0; diff --git a/src/main/java/org/reso/commander/certfication/containers/WebApiTestContainer.java b/src/main/java/org/reso/commander/certfication/containers/WebApiTestContainer.java index 5f2e615..89a0204 100644 --- a/src/main/java/org/reso/commander/certfication/containers/WebApiTestContainer.java +++ b/src/main/java/org/reso/commander/certfication/containers/WebApiTestContainer.java @@ -11,7 +11,9 @@ import org.apache.olingo.client.api.communication.response.ODataRawResponse; import org.apache.olingo.client.api.communication.response.ODataRetrieveResponse; import org.apache.olingo.client.api.domain.ClientEntitySet; import org.apache.olingo.client.api.edm.xml.XMLMetadata; +import org.apache.olingo.client.api.uri.QueryOption; import org.apache.olingo.commons.api.edm.Edm; +import org.apache.olingo.commons.api.edm.provider.CsdlProperty; import org.apache.olingo.commons.api.format.ContentType; import org.reso.commander.Commander; import org.reso.commander.TestUtils; @@ -21,10 +23,13 @@ import org.reso.models.Request; import org.reso.models.Settings; import java.net.URI; +import java.util.*; import java.util.concurrent.atomic.AtomicReference; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.fail; +import static org.reso.commander.Commander.AMPERSAND; +import static org.reso.commander.Commander.EQUALS; import static org.reso.commander.TestUtils.HEADER_ODATA_VERSION; /** @@ -33,7 +38,30 @@ import static org.reso.commander.TestUtils.HEADER_ODATA_VERSION; public final class WebApiTestContainer implements TestContainer { private static final Logger LOG = LogManager.getLogger(WebApiTestContainer.class); + public static final String FIELD_SEPARATOR = ","; + public static final String EMPTY_STRING = ""; + public static final String SINGLE_SPACE = " "; + public static final String DOLLAR_SIGN = "$"; + public static final String PRETTY_FIELD_SEPARATOR = FIELD_SEPARATOR + SINGLE_SPACE; + private AtomicReference commander = new AtomicReference<>(); + private AtomicReference xmlMetadata = new AtomicReference<>(); + private AtomicReference edm = new AtomicReference<>(); + private AtomicReference settings = new AtomicReference<>(); + private AtomicReference serviceRoot = new AtomicReference<>(); + private AtomicReference bearerToken = new AtomicReference<>(); + private AtomicReference clientId = new AtomicReference<>(); + private AtomicReference clientSecret = new AtomicReference<>(); + private AtomicReference authorizationUri = new AtomicReference<>(); + private AtomicReference tokenUri = new AtomicReference<>(); + private AtomicReference redirectUri = new AtomicReference<>(); + private AtomicReference scope = new AtomicReference<>(); + private AtomicReference pathToRESOScript = new AtomicReference<>(); + private AtomicReference> fieldMap = new AtomicReference<>(new HashMap<>()); + + + // request instance variables - these get reset with every request + private AtomicReference selectList = new AtomicReference<>(); private AtomicReference oDataRawResponse = new AtomicReference<>(); private AtomicReference request = new AtomicReference<>(); private AtomicReference requestUri = new AtomicReference<>(); @@ -48,19 +76,6 @@ public final class WebApiTestContainer implements TestContainer { private AtomicReference> clientEntitySetRequest = new AtomicReference<>(); private AtomicReference> clientEntitySetResponse = new AtomicReference<>(); private AtomicReference clientEntitySet = new AtomicReference<>(); - private AtomicReference xmlMetadata = new AtomicReference<>(); - private AtomicReference edm = new AtomicReference<>(); - private AtomicReference settings = new AtomicReference<>(); - private AtomicReference serviceRoot = new AtomicReference<>(); - private AtomicReference bearerToken = new AtomicReference<>(); - private AtomicReference clientId = new AtomicReference<>(); - private AtomicReference clientSecret = new AtomicReference<>(); - private AtomicReference authorizationUri = new AtomicReference<>(); - private AtomicReference tokenUri = new AtomicReference<>(); - private AtomicReference redirectUri = new AtomicReference<>(); - private AtomicReference scope = new AtomicReference<>(); - private AtomicReference pathToRESOScript = new AtomicReference<>(); - public void initialize() { setServiceRoot(getSettings().getClientSettings().get(ClientSettings.SERVICE_ROOT)); @@ -92,6 +107,11 @@ public final class WebApiTestContainer implements TestContainer { .useEdmEnabledClient(shouldUseEdmClient()) .build()); } + + //build a map of all of the discovered fields on the server for the given resource by field name + //this can also be used to look up type information + TestUtils.findEntityTypesForEntityTypeName(getEdm(), getXMLMetadata(), getSettings().getParameters().getValue(Parameters.WELL_KNOWN.RESOURCE_NAME)) + .forEach(csdlProperty -> fieldMap.get().put(csdlProperty.getName(), csdlProperty)); } /** @@ -158,14 +178,60 @@ public final class WebApiTestContainer implements TestContainer { } } + /** + * Gets OData Csdl for given field + * + * @param fieldName the name of the field to retrieve metadata about + * @return the metadata for the given field + */ + public CsdlProperty getCsdlForFieldName(String fieldName) { + return fieldMap.get().get(fieldName); + } + + /** + * Csdl property getter + * + * @return gets the local collection of Csdl Properties + */ + public Collection getCsdlProperties() { + return fieldMap.get().values(); + } + + public Collection getSelectList() { + final String SELECT_OPERATOR = DOLLAR_SIGN + QueryOption.SELECT.toString(); + Arrays.stream(getRequestUri().getQuery().split(AMPERSAND)).forEach(fragment -> { + if (fragment.contains(QueryOption.SELECT.toString())) { + selectList.set(fragment.replace(SELECT_OPERATOR, EMPTY_STRING).replace(EQUALS, EMPTY_STRING)); + } + }); + + return new ArrayList<>(Arrays.asList(selectList.get().split(FIELD_SEPARATOR))); + } + + /** + * Settings getter + * @return local settings instance + */ public Settings getSettings() { return settings.get(); } + /** + * Settings setter + * @param settings sets local settings instance to the given settings + */ public void setSettings(Settings settings) { this.settings.set(settings); } + /** + * Gets the Expand field from the RESOScript + * @return the configured Expand field + */ + public String getExpandField() { + return getSettings().getParameters().getValue(Parameters.WELL_KNOWN.EXPAND_FIELD); + } + /** * Gets server metadata in Edm format. * @@ -220,6 +286,10 @@ public final class WebApiTestContainer implements TestContainer { this.request.set(request); } + public void setRequest(String requestId) { + setRequest(getSettings().getRequest(requestId)); + } + public URI getRequestUri() { return requestUri.get(); } diff --git a/src/main/java/org/reso/models/Parameters.java b/src/main/java/org/reso/models/Parameters.java index cf31966..9e3012b 100644 --- a/src/main/java/org/reso/models/Parameters.java +++ b/src/main/java/org/reso/models/Parameters.java @@ -73,7 +73,8 @@ public class Parameters { public static final class WELL_KNOWN { public static final String - RESOURCE_ENDPOINT = "EndpointResource", - DATASYSTEM_ENDPOINT = "EndpointDataSystem"; + RESOURCE_NAME = "EndpointResource", + DATASYSTEM_ENDPOINT = "EndpointDataSystem", + EXPAND_FIELD = "ExpandField"; } } \ No newline at end of file diff --git a/src/main/java/org/reso/models/Settings.java b/src/main/java/org/reso/models/Settings.java index 5d5bde5..f742cc8 100644 --- a/src/main/java/org/reso/models/Settings.java +++ b/src/main/java/org/reso/models/Settings.java @@ -17,7 +17,7 @@ public class Settings { public static final String PARAMETER_PREFIX = "Parameter_"; private ClientSettings clientSettings; private Parameters parameters; - private Map requests; + private Map requests; public Settings() { clientSettings = new ClientSettings(); @@ -135,7 +135,7 @@ public class Settings { * * @return The request map that was loaded, indexed by request name. */ - public Map getRequests() { + public Map getRequests() { return requests; } @@ -145,8 +145,8 @@ public class Settings { * @param requests a list of requests to create the request map from */ private void setRequests(List requests) { - this.requests = new LinkedHashMap(); - requests.forEach(request -> this.requests.put(request, request)); + this.requests = new LinkedHashMap(); + requests.forEach(request -> this.requests.put(request.getRequestId(), request)); } /** @@ -154,17 +154,8 @@ public class Settings { * * @return The request map that was loaded, indexed by request name. */ - public Request getRequestById(String requestId) { - Request found = null; - for (Map.Entry entry : getRequests().entrySet()) { - Request key = entry.getKey(); - Request value = entry.getValue(); - if (key.getRequestId().contentEquals(requestId)) { - found = value; - break; - } - } - return found; + public Request getRequest(String requestId) { + return getRequests().get(requestId); } /**