Tightened up metadata checking, including checking for given resource and select list. Added better debugging info. Updated README.

This commit is contained in:
Joshua Darnell 2020-03-08 03:29:00 -07:00
parent 317436996b
commit 0ae0fee43e
8 changed files with 622 additions and 395 deletions

120
README.md
View File

@ -397,7 +397,7 @@ $ ./gradlew testWebAPIServer_1_0_2_Gold -DpathToRESOScript=/path/to/your.resoscr
##### Windows ##### Windows
``` ```
C:\path\to\web-api-commander> gradlew testWebAPIServer_1_0_2 -DpathToRESOScript=C:\path\to\your.resoscript -DshowResponses=true C:\path\to\web-api-commander> gradlew testWebAPIServer_1_0_2_Gold -DpathToRESOScript=C:\path\to\your.resoscript -DshowResponses=true
``` ```
*Note: the first time you run these tasks, they will take some time as the environment is being configured behind the *Note: the first time you run these tasks, they will take some time as the environment is being configured behind the
@ -416,37 +416,18 @@ $ ./gradlew testWebAPIServer_1_0_2_Platinum -DpathToRESOScript=/path/to/your.res
C:\path\to\web-api-commander> gradlew testWebAPIServer_1_0_2_Platinum -DpathToRESOScript=C:\path\to\your.resoscript -DshowResponses=true C:\path\to\web-api-commander> gradlew testWebAPIServer_1_0_2_Platinum -DpathToRESOScript=C:\path\to\your.resoscript -DshowResponses=true
``` ```
#### General Task Wrapper (Advanced)
You may also run the gradle task wrapper using your own tags.
This runs all tests without any additional parameters given. With no tags, the wrapper is called like this:
##### MacOS or Linux
```
$ ./gradlew testWebAPIServer_1_0_2 -DpathToRESOScript=/path/to/your.resoscript -DshowResponses=true
```
##### Windows
```
C:\path\to\web-api-commander> gradlew.bat testWebAPIServer_1_0_2 -DpathToRESOScript=C:\path\to\your.resoscript -DshowResponses=true
```
This will run the entirety of the tests against the Web API 1.0.2 Server provided as `WebAPIURI` in `your.resoscript` file.
You can pass tags to filter on in order to run one or more tests matching the given tag.
#### Advanced feature: Tag Filtering #### Advanced feature: Tag Filtering
To filter by tags, a command similar to the following would be used: You may also filter by tags. These are the items in the Cucumber .feature files prefixed by an `@` symbol. Expressions
may also be used with tags. See the [Cucumber Documentation](https://cucumber.io/docs/cucumber/api/#tags) for more information.
##### MacOS or Linux ##### MacOS or Linux
``` ```
$ gradle testWebAPIServer_1_0_2 -DpathToRESOScript=/path/to/your.resoscript -Dcucumber.filter.tags="@core" $ gradle testWebAPIServer_1_0_2_Platinum -DpathToRESOScript=/path/to/your.resoscript -Dcucumber.filter.tags="@core"
``` ```
##### Windows ##### Windows
``` ```
C:\path\to\web-api-commander> gradlew.bat testWebAPIServer_1_0_2 -DpathToRESOScript=C:\path\to\your.resoscript -Dcucumber.filter.tags="@core" C:\path\to\web-api-commander> gradlew.bat testWebAPIServer_1_0_2_Platinum -DpathToRESOScript=C:\path\to\your.resoscript -Dcucumber.filter.tags="@core"
``` ```
This would run only the tests marked as `@core` in the This would run only the tests marked as `@core` in the
@ -470,9 +451,11 @@ Please feel free to suggest additional tags that might be useful.
A sample of the runtime terminal output follows: A sample of the runtime terminal output follows:
``` ```gherkin
@REQ-WA103-END3 @core @x.y.z @core-support-endorsement > Task :testWebApiServer_1_0_2_Platinum
Scenario: REQ-WA103-END3 - CORE - Request and Validate Server Metadata
@REQ-WA103-END3 @core @x.y.z @core-endorsement @metadata
Scenario: Request and Validate Server Metadata
Using RESOScript: /path/to/your.resoscript Using RESOScript: /path/to/your.resoscript
Given a RESOScript file was provided Given a RESOScript file was provided
@ -481,21 +464,35 @@ A sample of the runtime terminal output follows:
And Client Settings and Parameters were read from the file And Client Settings and Parameters were read from the file
Bearer token loaded... first 4 characters: abcd Bearer token loaded... first 4 characters: abcd
Service root is: https://api.server.com/serviceRoot Service root is: https://api.server.com/
And an OData client was successfully created from the given RESOScript And an OData client was successfully created from the given RESOScript
Request URI: https://api.server.com/serviceRoot/$metadata?$format=application/xml Fetching XMLMetadata with OData Client from: https://api.server.com/$metadata
Request succeeded...185032 bytes received. When a successful metadata request is made to the service root in "ClientSettings_WebAPIURI"
When a GET request is made to the resolved Url in "REQ-WA103-END3"
Response code is: 200
Then the server responds with a status code of 200
Response is valid XML!
And the response is valid XML
Metadata is valid! Metadata is valid!
And the metadata returned is valid And the metadata returned is valid
Fetching Edm with OData Client from: https://api.server.com/$metadata
Found EntityContainer for the given resource: 'Property'
And the metadata contains the "Parameter_EndpointResource" resource
Searching metadata for fields in given select list: ListingKey,BedroomsTotal,StreetName,PropertyType,ListingContractDate,ModificationTimestamp,Latitude,Longitude
Found: 'ListingKey'
Found: 'BedroomsTotal'
Found: 'StreetName'
Found: 'PropertyType'
Found: 'ListingContractDate'
Found: 'ModificationTimestamp'
Found: 'Latitude'
Found: 'Longitude'
And resource metadata for "Parameter_EndpointResource" contains the fields in "Parameter_SelectList"
1 Scenarios (1 passed)
7 Steps (7 passed)
0m3.244s
``` ```
This shows configuration parameters, requests, and responses in a lightweight-manner. This shows configuration parameters, requests, and responses in a lightweight-manner.
@ -538,31 +535,58 @@ $ docker run -it web-api-commander --help
If you have input files you may need to mount your filesystem into the docker container If you have input files you may need to mount your filesystem into the docker container
``` ```
$ docker run -it -v $PWD:/app darnjo/web-api-commander --validateMetadata --inputFile <pathInContainer> $ docker run -it -v $PWD:/app web-api-commander --validateMetadata --inputFile <pathInContainer>
``` ```
### Automated Web API Testing ### Automated Web API Testing
You may also run the tests in a Docker container locally by issuing the following command. You may also run the tests in a Docker container locally by issuing one of the following commands.
Docker must be running on your local machine. This must be done from the root of the Web API Commander Docker must be running on your local machine.
project directory.
#### MacOS or Linux #### MacOS or Linux All-In-One Commands
##### Gold
``` ```
$ docker run --rm -u gradle -v "$PWD":/home/gradle/project -v /path/to/your/resoscripts:/home/gradle/project/resoscripts -w /home/gradle/project gradle gradle testWebAPIServer_1_0_2 -DpathToRESOScript=/home/gradle/project/resoscripts/your.resoscript -DshowResponses=true cd ~; \
rm -rf commander-tmp/; \
mkdir commander-tmp; \
cd commander-tmp; \
git clone https://github.com/RESOStandards/web-api-commander.git; \
cd web-api-commander; \
docker run --rm -u gradle -v "$PWD":/home/gradle/project -v /path/to/your/resoscripts:/home/gradle/project/resoscripts -w /home/gradle/project gradle gradle testWebAPIServer_1_0_2_Gold -DpathToRESOScript=/home/gradle/project/resoscripts/your.resoscript -DshowResponses=true
``` ```
#### Windows ##### Platinum
``` ```
$ docker run --rm -u gradle -v C:\path\to\web-api-commander\:/home/gradle/project -v C:\path\to\your\resoscripts:/home/gradle/project/resoscripts -w /home/gradle/project gradle gradle testWebAPIServer_1_0_2 -DpathToRESOScript=/home/gradle/project/resoscripts/your.resoscript -DshowResponses=true cd ~; \
rm -rf commander-tmp/; \
mkdir commander-tmp; \
cd commander-tmp; \
git clone https://github.com/RESOStandards/web-api-commander.git; \
cd web-api-commander; \
docker run --rm -u gradle -v "$PWD":/home/gradle/project -v /path/to/your/resoscripts:/home/gradle/project/resoscripts -w /home/gradle/project gradle gradle testWebAPIServer_1_0_2_Platinum -DpathToRESOScript=/home/gradle/project/resoscripts/your.resoscript -DshowResponses=true
``` ```
You may also run the specific task wrappers for Gold or Platinum Web API 1.0.2 Server testing Note that this will create a directory in your home directory for the project, and build artifacts and the log will be placed in that directory,
by replacing `testWebAPIServer_1_0_2` above with `testWebAPIServer_1_0_2_Gold` or `testWebAPIServer_1_0_2_Platinum`, respectively. which is also where you will end up after runtime.
#### Windows All-In-One WIP
##### Gold
```
cd C:\;mkdir commander-tmp;cd commander-tmp;git clone https://github.com/RESOStandards/web-api-commander.git;cd web-api-commander; docker run --rm -u gradle -v C:\current\path\web-api-commander:/home/gradle/project -v C:\path\to\your\resoscripts:/home/gradle/project/resoscripts -w /home/gradle/project gradle gradle testWebAPIServer_1_0_2_Gold -DpathToRESOScript=/home/gradle/project/resoscripts/your.resoscript -DshowResponses=true
```
##### Platinum
```
cd C:\;mkdir commander-tmp;cd commander-tmp;git clone https://github.com/RESOStandards/web-api-commander.git;cd web-api-commander;docker run --rm -u gradle -v C:\current\path\web-api-commander:/home/gradle/project -v C:\path\to\your\resoscripts:/home/gradle/project/resoscripts -w /home/gradle/project gradle gradle testWebAPIServer_1_0_2_Platinum -DpathToRESOScript=/home/gradle/project/resoscripts/your.resoscript -DshowResponses=true
```
--- ---
## Logging ## Logging
In the current version of the Commander, two logs are produced. One is outputted in the terminal at `INFO` level during runtime through `stdout`. A detailed log called `commander.log` will be outputted at runtime and will contain details down to the wire requests. In the current version of the Commander, two logs are produced. One is outputted in the terminal at `INFO` level during runtime through `stdout`. A detailed log called `commander.log` will be outputted at runtime and will contain details down to the wire requests.

View File

@ -67,36 +67,15 @@ tasks.withType(JavaCompile) {
options.deprecation = true options.deprecation = true
} }
// task for general Web API Server Testing - this is what should be run in the command line to run everything
task testWebApiServer_1_0_2() {
dependsOn jar
doLast {
javaexec {
main = "io.cucumber.core.cli.Main"
classpath = configurations.cucumberRuntime + sourceSets.main.output + sourceSets.test.output
args = [
'--strict',
'--plugin',
'pretty',
'--plugin',
'json:build/web-api-server-1.0.2.json',
'--plugin',
'html:build/web-api-server-1.0.2.html',
'--glue',
'org.reso.certification.stepdefs#WebAPIServer_1_0_2',
'src/main/java/org/reso/certification/features'
]
systemProperties = System.getProperties()
}
}
// task for Web API Server 1.0.2 Gold Testing // task for Web API Server 1.0.2 Gold Testing
task testWebApiServer_1_0_2_Gold() { task testWebApiServer_1_0_2_Gold() {
dependsOn jar dependsOn jar
doLast { doLast {
javaexec { javaexec {
main = "io.cucumber.core.cli.Main" main = "io.cucumber.core.cli.Main"
classpath = configurations.cucumberRuntime + sourceSets.main.output + sourceSets.test.output classpath = configurations.cucumberRuntime + sourceSets.main.output + sourceSets.test.output
systemProperties = System.getProperties()
args = [ args = [
'--strict', '--strict',
'--plugin', '--plugin',
@ -110,19 +89,20 @@ task testWebApiServer_1_0_2() {
'src/main/java/org/reso/certification/features', 'src/main/java/org/reso/certification/features',
'--tags', '--tags',
'not @platinum' 'not @platinum'
+ (systemProperties.get('cucumber.filter.tags') != null ? ' and ' + systemProperties.get('cucumber.filter.tags') : '')
] ]
systemProperties = System.getProperties()
}
} }
} }
}
// task for Web API Server 1.0.2 Platinum Testing - currently equivalent to all, but generates a platinum-named report. // task for Web API Server 1.0.2 Platinum Testing - currently equivalent to all, but generates a platinum-named report.
task testWebApiServer_1_0_2_Platinum() { task testWebApiServer_1_0_2_Platinum() {
dependsOn jar dependsOn jar
doLast { doLast {
javaexec { javaexec {
main = "io.cucumber.core.cli.Main" main = "io.cucumber.core.cli.Main"
classpath = configurations.cucumberRuntime + sourceSets.main.output + sourceSets.test.output classpath = configurations.cucumberRuntime + sourceSets.main.output + sourceSets.test.output
systemProperties = System.getProperties()
args = [ args = [
'--strict', '--strict',
'--plugin', '--plugin',
@ -135,8 +115,6 @@ task testWebApiServer_1_0_2() {
'org.reso.certification.stepdefs#WebAPIServer_1_0_2', 'org.reso.certification.stepdefs#WebAPIServer_1_0_2',
'src/main/java/org/reso/certification/features' 'src/main/java/org/reso/certification/features'
] ]
systemProperties = System.getProperties()
}
} }
} }
} }

Binary file not shown.

View File

@ -7,5 +7,4 @@
* in the user manual at https://docs.gradle.org/5.2.1/userguide/multi_project_builds.html * in the user manual at https://docs.gradle.org/5.2.1/userguide/multi_project_builds.html
*/ */
rootProject.name = 'web-api-commander' rootProject.setName('web-api-commander')

View File

@ -6,12 +6,12 @@ Feature: Web API Server 1.0.2 Certification
And Client Settings and Parameters were read from the file And Client Settings and Parameters were read from the file
And an OData client was successfully created from the given RESOScript And an OData client was successfully created from the given RESOScript
@REQ-WA103-END3 @core @x.y.z @core-endorsement @REQ-WA103-END3 @core @x.y.z @core-endorsement @metadata
Scenario: Request and Validate Server Metadata Scenario: Request and Validate Server Metadata
When a GET request is made to the resolved Url in "REQ-WA103-END3" When a successful metadata request is made to the service root in "ClientSettings_WebAPIURI"
Then the server responds with a status code of 200
And the response is valid XML
And the metadata returned is valid And the metadata returned is valid
And the metadata contains the "Parameter_EndpointResource" resource
And resource metadata for "Parameter_EndpointResource" contains the fields in "Parameter_SelectList"
@REQ-WA103-END2 @core @x.y.z @core-endorsement @REQ-WA103-END2 @core @x.y.z @core-endorsement
Scenario: Data System Endpoint test Scenario: Data System Endpoint test

View File

@ -14,8 +14,11 @@ import org.apache.olingo.client.api.communication.ODataClientErrorException;
import org.apache.olingo.client.api.communication.request.retrieve.ODataRawRequest; import org.apache.olingo.client.api.communication.request.retrieve.ODataRawRequest;
import org.apache.olingo.client.api.communication.response.ODataRawResponse; import org.apache.olingo.client.api.communication.response.ODataRawResponse;
import org.apache.olingo.client.api.edm.xml.XMLMetadata; import org.apache.olingo.client.api.edm.xml.XMLMetadata;
import org.apache.olingo.commons.api.edm.Edm;
import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeException; import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeException;
import org.apache.olingo.commons.api.format.ContentType; import org.apache.olingo.commons.api.edm.provider.CsdlEntityContainer;
import org.apache.olingo.commons.api.edm.provider.CsdlProperty;
import org.apache.olingo.commons.api.edm.provider.CsdlSchema;
import org.apache.olingo.commons.core.edm.primitivetype.EdmDate; import org.apache.olingo.commons.core.edm.primitivetype.EdmDate;
import org.apache.olingo.commons.core.edm.primitivetype.EdmDateTimeOffset; import org.apache.olingo.commons.core.edm.primitivetype.EdmDateTimeOffset;
import org.apache.olingo.commons.core.edm.primitivetype.EdmTimeOfDay; import org.apache.olingo.commons.core.edm.primitivetype.EdmTimeOfDay;
@ -28,7 +31,10 @@ import java.io.*;
import java.net.URI; import java.net.URI;
import java.sql.Time; import java.sql.Time;
import java.sql.Timestamp; import java.sql.Timestamp;
import java.time.*; import java.time.Instant;
import java.time.LocalDate;
import java.time.OffsetDateTime;
import java.time.Year;
import java.util.*; import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
@ -116,6 +122,10 @@ public class WebAPIServer_1_0_2 implements En {
serverODataHeaderVersion.set(Utils.getHeaderData(HEADER_ODATA_VERSION, cex.getHeaderInfo())); serverODataHeaderVersion.set(Utils.getHeaderData(HEADER_ODATA_VERSION, cex.getHeaderInfo()));
responseCode.set(cex.getStatusLine().getStatusCode()); responseCode.set(cex.getStatusLine().getStatusCode());
oDataClientErrorExceptionHandled.set(true); oDataClientErrorExceptionHandled.set(true);
throw cex;
} catch (Exception ex) {
fail(ex.toString());
throw ex;
} }
return null; return null;
}; };
@ -127,12 +137,15 @@ public class WebAPIServer_1_0_2 implements En {
if (pathToRESOScript == null) { if (pathToRESOScript == null) {
pathToRESOScript = System.getProperty("pathToRESOScript"); pathToRESOScript = System.getProperty("pathToRESOScript");
} }
assertNotNull("ERROR: pathToRESOScript must be present in command arguments, see README", pathToRESOScript);
LOG.info("Using RESOScript: " + pathToRESOScript); LOG.info("Using RESOScript: " + pathToRESOScript);
}); });
And("^Client Settings and Parameters were read from the file$", () -> { And("^Client Settings and Parameters were read from the file$", () -> {
if (settings == null) { if (settings == null) {
settings = Settings.loadFromRESOScript(new File(System.getProperty("pathToRESOScript"))); settings = Settings.loadFromRESOScript(new File(System.getProperty("pathToRESOScript")));
} }
assertNotNull("ERROR: Settings could not be loaded.", settings);
LOG.info("RESOScript loaded successfully!"); LOG.info("RESOScript loaded successfully!");
}); });
Given("^an OData client was successfully created from the given RESOScript$", () -> { Given("^an OData client was successfully created from the given RESOScript$", () -> {
@ -165,6 +178,10 @@ public class WebAPIServer_1_0_2 implements En {
.bearerToken(bearerToken) .bearerToken(bearerToken)
.useEdmEnabledClient(true) .useEdmEnabledClient(true)
.build()); .build());
assertNotNull(commander.get());
assertTrue("ERROR: Commander must either have a valid bearer token or Client Credentials configuration.",
commander.get().isTokenClient() || (commander.get().isOAuthClient() && commander.get().getTokenUri() != null));
}); });
@ -180,13 +197,17 @@ public class WebAPIServer_1_0_2 implements En {
* REQ-WA103-END3 * REQ-WA103-END3
*/ */
And("^the metadata returned is valid$", () -> { And("^the metadata returned is valid$", () -> {
//store the metadata for later comparisons if (xmlMetadata.get() == null) {
xmlMetadata.set(commander.get().getClient().getDeserializer(ContentType.APPLICATION_XML) fail("ERROR: No XML Metadata Exists!");
.toMetadata(new ByteArrayInputStream(responseData.get().getBytes()))); }
try {
boolean isValid = commander.get().validateMetadata(xmlMetadata.get()); boolean isValid = commander.get().validateMetadata(xmlMetadata.get());
LOG.info("Metadata is " + (isValid ? "valid" : "invalid") + "!"); LOG.info("Metadata is " + (isValid ? "valid" : "invalid") + "!");
assertTrue(isValid); assertTrue(isValid);
} catch (Exception ex) {
fail(ex.getMessage());
}
}); });
@ -194,6 +215,7 @@ public class WebAPIServer_1_0_2 implements En {
* REQ-WA103-QR1 * REQ-WA103-QR1
*/ */
And("^the provided \"([^\"]*)\" is returned in \"([^\"]*)\"$", (String parameterUniqueIdValue, String parameterUniqueId) -> { And("^the provided \"([^\"]*)\" is returned in \"([^\"]*)\"$", (String parameterUniqueIdValue, String parameterUniqueId) -> {
try {
String expectedValueAsString = Settings.resolveParametersString(parameterUniqueIdValue, settings), resolvedValueAsString = null; String expectedValueAsString = Settings.resolveParametersString(parameterUniqueIdValue, settings), resolvedValueAsString = null;
Object resolvedValue = from(responseData.get()).get(Settings.resolveParametersString(parameterUniqueId, settings)); Object resolvedValue = from(responseData.get()).get(Settings.resolveParametersString(parameterUniqueId, settings));
@ -213,6 +235,9 @@ public class WebAPIServer_1_0_2 implements En {
} else { } else {
assertEquals(expectedValueAsString, resolvedValue.toString()); assertEquals(expectedValueAsString, resolvedValue.toString());
} }
} catch (Exception ex) {
fail(ex.getMessage());
}
}); });
@ -220,11 +245,13 @@ public class WebAPIServer_1_0_2 implements En {
* REQ-WA103-QR3 - $select * REQ-WA103-QR3 - $select
*/ */
And("^data are present in fields contained within \"([^\"]*)\"$", (String parameterSelectList) -> { And("^data are present in fields contained within \"([^\"]*)\"$", (String parameterSelectList) -> {
try {
AtomicInteger numFieldsWithData = new AtomicInteger(); AtomicInteger numFieldsWithData = new AtomicInteger();
List<String> fieldList = new ArrayList<>(Arrays.asList(Settings.resolveParametersString(parameterSelectList, settings).split(","))); List<String> fieldList = new ArrayList<>(Arrays.asList(Settings.resolveParametersString(parameterSelectList, settings).split(",")));
AtomicInteger numResults = new AtomicInteger(); AtomicInteger numResults = new AtomicInteger();
//iterate over the items and count the number of fields with data to determine whether there are data present //iterate over the items and count the number of fields with data to determine whether there are data present
from(responseData.get()).getList(JSON_VALUE_PATH, HashMap.class).forEach(item -> { from(responseData.get()).getList(JSON_VALUE_PATH, HashMap.class).forEach(item -> {
if (item != null) { if (item != null) {
@ -246,6 +273,9 @@ public class WebAPIServer_1_0_2 implements En {
LOG.info("Percent Fill: 0% - no fields with data found!"); LOG.info("Percent Fill: 0% - no fields with data found!");
} }
assertTrue(numFieldsWithData.get() > 0); assertTrue(numFieldsWithData.get() > 0);
} catch (Exception ex) {
fail(ex.getMessage());
}
}); });
@ -254,6 +284,7 @@ public class WebAPIServer_1_0_2 implements En {
* $top=*Parameter_TopCount* * $top=*Parameter_TopCount*
*/ */
And("^the results contain at most \"([^\"]*)\" records$", (String parameterTopCount) -> { And("^the results contain at most \"([^\"]*)\" records$", (String parameterTopCount) -> {
try {
List<String> items = from(responseData.get()).getList(JSON_VALUE_PATH); List<String> items = from(responseData.get()).getList(JSON_VALUE_PATH);
AtomicInteger numResults = new AtomicInteger(items.size()); AtomicInteger numResults = new AtomicInteger(items.size());
@ -261,6 +292,9 @@ public class WebAPIServer_1_0_2 implements En {
LOG.info("Number of values returned: " + numResults.get() + ", top count is: " + topCount); LOG.info("Number of values returned: " + numResults.get() + ", top count is: " + topCount);
assertTrue(numResults.get() > 0 && numResults.get() <= topCount); assertTrue(numResults.get() > 0 && numResults.get() <= topCount);
} catch (Exception ex) {
fail(ex.getMessage());
}
}); });
@ -269,6 +303,7 @@ public class WebAPIServer_1_0_2 implements En {
* $skip=*Parameter_TopCount* * $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 requirementId, String parameterTopCount) -> {
try {
int skipCount = Integer.parseInt(Settings.resolveParametersString(parameterTopCount, settings)); int skipCount = Integer.parseInt(Settings.resolveParametersString(parameterTopCount, settings));
LOG.info("Skip count is: " + skipCount); LOG.info("Skip count is: " + skipCount);
@ -279,8 +314,12 @@ public class WebAPIServer_1_0_2 implements En {
URI requestUri = Commander.prepareURI(Settings.resolveParameters(settings.getRequests().get(requirementId), settings).getUrl() + "&$skip=" + skipCount); URI requestUri = Commander.prepareURI(Settings.resolveParameters(settings.getRequests().get(requirementId), settings).getUrl() + "&$skip=" + skipCount);
executeGetRequest.apply(requestUri); executeGetRequest.apply(requestUri);
} catch (Exception ex) {
fail(ex.getMessage());
}
}); });
And("^data in the \"([^\"]*)\" fields are different in the second request than in the first$", (String parameterUniqueId) -> { And("^data in the \"([^\"]*)\" fields are different in the second request than in the first$", (String parameterUniqueId) -> {
try {
List<POJONode> l1 = from(initialResponseData.get()).getJsonObject(JSON_VALUE_PATH); List<POJONode> l1 = from(initialResponseData.get()).getJsonObject(JSON_VALUE_PATH);
List<POJONode> l2 = from(responseData.get()).getJsonObject(JSON_VALUE_PATH); List<POJONode> l2 = from(responseData.get()).getJsonObject(JSON_VALUE_PATH);
@ -292,6 +331,9 @@ public class WebAPIServer_1_0_2 implements En {
LOG.info("Response Page 2: " + new POJONode(l2)); LOG.info("Response Page 2: " + new POJONode(l2));
assertEquals(combinedCount, combined.size()); assertEquals(combinedCount, combined.size());
} catch (Exception ex) {
fail(ex.getMessage());
}
}); });
//================================================================================================================== //==================================================================================================================
@ -302,34 +344,47 @@ public class WebAPIServer_1_0_2 implements En {
* GET request by requirementId (see generic.resoscript) * GET request by requirementId (see generic.resoscript)
*/ */
When("^a GET request is made to the resolved Url in \"([^\"]*)\"$", (String requirementId) -> { When("^a GET request is made to the resolved Url in \"([^\"]*)\"$", (String requirementId) -> {
try {
//reset local state each time a get request is run //reset local state each time a get request is run
resetRequestState.run(); resetRequestState.run();
URI requestUri = Commander.prepareURI(Settings.resolveParameters(settings.getRequests().get(requirementId), settings).getUrl()); URI requestUri = Commander.prepareURI(Settings.resolveParameters(settings.getRequests().get(requirementId), settings).getUrl());
executeGetRequest.apply(requestUri); executeGetRequest.apply(requestUri);
} catch (Exception ex) {
LOG.debug("Exception was thrown in " + this.getClass() + ": " + ex.toString());
}
}); });
/* /*
* Assert response code * Assert response code
*/ */
Then("^the server responds with a status code of (\\d+)$", (Integer assertedResponseCode) -> { Then("^the server responds with a status code of (\\d+)$", (Integer assertedResponseCode) -> {
try {
LOG.info("Asserted Response Code: " + assertedResponseCode + ", Server Response Code: " + responseCode); LOG.info("Asserted Response Code: " + assertedResponseCode + ", Server Response Code: " + responseCode);
assertTrue(responseCode.get() > 0 && assertedResponseCode > 0); assertTrue(responseCode.get() > 0 && assertedResponseCode > 0);
assertEquals(assertedResponseCode.intValue(), responseCode.get().intValue()); assertEquals(assertedResponseCode.intValue(), responseCode.get().intValue());
} catch (Exception ex) {
fail(ex.getMessage());
}
}); });
/* /*
* validate XML wrapper * validate XML wrapper
*/ */
And("^the response is valid XML$", () -> { And("^the response is valid XML$", () -> {
try {
assertTrue(Commander.validateXML(responseData.get())); assertTrue(Commander.validateXML(responseData.get()));
LOG.info("Response is valid XML!"); LOG.info("Response is valid XML!");
} catch (Exception ex) {
fail(ex.getMessage());
}
}); });
/* /*
* validate JSON wrapper * validate JSON wrapper
*/ */
And("^the response is valid JSON$", () -> { And("^the response is valid JSON$", () -> {
try {
assertTrue(Utils.isValidJson(responseData.get())); assertTrue(Utils.isValidJson(responseData.get()));
LOG.info("Response is valid JSON!"); LOG.info("Response is valid JSON!");
@ -337,14 +392,9 @@ public class WebAPIServer_1_0_2 implements En {
if (Boolean.parseBoolean(showResponses)) { if (Boolean.parseBoolean(showResponses)) {
LOG.info("Response: " + new ObjectMapper().readTree(responseData.get()).toPrettyString()); LOG.info("Response: " + new ObjectMapper().readTree(responseData.get()).toPrettyString());
} }
}); } catch (Exception ex) {
fail(ex.getMessage());
/* }
* Assert OData version
*/
And("^the server reports OData version \"([^\"]*)\"$", (String assertODataVersion) -> {
LOG.info("Asserted version: " + assertODataVersion + ", Reported OData Version: " + serverODataHeaderVersion.get()); ;
assertEquals(serverODataHeaderVersion.get(), assertODataVersion);
}); });
/* /*
@ -353,6 +403,7 @@ public class WebAPIServer_1_0_2 implements En {
* TODO: make a general Header assertion function * TODO: make a general Header assertion function
*/ */
Then("^the server responds with a status code of (\\d+) if the server headers report OData version \"([^\"]*)\"$", (Integer assertedHttpResponseCode, String assertedODataVersion) -> { Then("^the server responds with a status code of (\\d+) if the server headers report OData version \"([^\"]*)\"$", (Integer assertedHttpResponseCode, String assertedODataVersion) -> {
try {
boolean versionsMatch = serverODataHeaderVersion.get().equals(assertedODataVersion), boolean versionsMatch = serverODataHeaderVersion.get().equals(assertedODataVersion),
responseCodesMatch = responseCode.get().intValue() == assertedHttpResponseCode.intValue(); responseCodesMatch = responseCode.get().intValue() == assertedHttpResponseCode.intValue();
@ -362,6 +413,10 @@ public class WebAPIServer_1_0_2 implements En {
LOG.info("Asserted Response Code: " + assertedHttpResponseCode + ", Response code: " + responseCode.get()); LOG.info("Asserted Response Code: " + assertedHttpResponseCode + ", Response code: " + responseCode.get());
assertTrue(responseCodesMatch); assertTrue(responseCodesMatch);
} }
} catch (Exception ex) {
//DEBUG only in this case as we are expecting an error and don't want to throw or print it
LOG.debug(ex.toString());
}
}); });
/* /*
@ -369,6 +424,7 @@ public class WebAPIServer_1_0_2 implements En {
* and is used to select among the supported comparisons. * and is used to select among the supported comparisons.
*/ */
And("^Integer data in \"([^\"]*)\" \"([^\"]*)\" \"([^\"]*)\"$", (String parameterFieldName, String op, String parameterAssertedValue) -> { And("^Integer data in \"([^\"]*)\" \"([^\"]*)\" \"([^\"]*)\"$", (String parameterFieldName, String op, String parameterAssertedValue) -> {
try {
String fieldName = Settings.resolveParametersString(parameterFieldName, settings); String fieldName = Settings.resolveParametersString(parameterFieldName, settings);
int assertedValue = Integer.parseInt(Settings.resolveParametersString(parameterAssertedValue, settings)); int assertedValue = Integer.parseInt(Settings.resolveParametersString(parameterAssertedValue, settings));
@ -386,42 +442,58 @@ public class WebAPIServer_1_0_2 implements En {
}); });
assertTrue(result.get()); assertTrue(result.get());
} catch (Exception ex) {
fail(ex.getMessage());
}
}); });
/* /*
* True if response has results, meaning value.length > 0 * True if response has results, meaning value.length > 0
*/ */
And("^the response has results$", () -> { And("^the response has results$", () -> {
try {
int count = from(responseData.get()).getList(JSON_VALUE_PATH, HashMap.class).size(); int count = from(responseData.get()).getList(JSON_VALUE_PATH, HashMap.class).size();
LOG.info("Results count is: " + count); LOG.info("Results count is: " + count);
assertTrue(count > 0); assertTrue(count > 0);
} catch (Exception ex) {
fail(ex.getMessage());
}
}); });
/* /*
* True if data are present in the response * True if data are present in the response
*/ */
And("^the response has singleton results in \"([^\"]*)\"", (String parameterFieldName) -> { And("^the response has singleton results in \"([^\"]*)\"", (String parameterFieldName) -> {
try {
String value = Settings.resolveParametersString(parameterFieldName, settings); String value = Settings.resolveParametersString(parameterFieldName, settings);
boolean isPresent = from(responseData.get()).get() != null; boolean isPresent = from(responseData.get()).get() != null;
LOG.info("Response value is: " + value); LOG.info("Response value is: " + value);
LOG.info("IsPresent: " + isPresent); LOG.info("IsPresent: " + isPresent);
assertTrue(isPresent); assertTrue(isPresent);
} catch (Exception ex) {
fail(ex.getMessage());
}
}); });
/* /*
* True if results count less than or equal to limit * True if results count less than or equal to limit
*/ */
And("^the number of results is less than or equal to \"([^\"]*)\"$", (String limitField) -> { And("^the number of results is less than or equal to \"([^\"]*)\"$", (String limitField) -> {
try {
int count = from(responseData.get()).getList(JSON_VALUE_PATH, HashMap.class).size(), int count = from(responseData.get()).getList(JSON_VALUE_PATH, HashMap.class).size(),
limit = Integer.parseInt(Settings.resolveParametersString(limitField, settings)); limit = Integer.parseInt(Settings.resolveParametersString(limitField, settings));
LOG.info("Results count is: " + count + ", Limit is: " + limit); LOG.info("Results count is: " + count + ", Limit is: " + limit);
assertTrue(count <= limit); assertTrue(count <= limit);
} catch (Exception ex) {
fail(ex.getMessage());
}
}); });
/* /*
* True if data in the lhs expression and rhs expressions pass the AND or OR condition given in andOrOp * True if data in the lhs expression and rhs expressions pass the AND or OR condition given in andOrOp
*/ */
And("^Integer data in \"([^\"]*)\" \"([^\"]*)\" \"([^\"]*)\" \"([^\"]*)\" \"([^\"]*)\" \"([^\"]*)\"$", (String parameterFieldName, String opLhs, String parameterAssertedLhsValue, String andOrOp, String opRhs, String parameterAssertedRhsValue) -> { And("^Integer data in \"([^\"]*)\" \"([^\"]*)\" \"([^\"]*)\" \"([^\"]*)\" \"([^\"]*)\" \"([^\"]*)\"$", (String parameterFieldName, String opLhs, String parameterAssertedLhsValue, String andOrOp, String opRhs, String parameterAssertedRhsValue) -> {
try {
String fieldName = Settings.resolveParametersString(parameterFieldName, settings); String fieldName = Settings.resolveParametersString(parameterFieldName, settings);
Integer assertedLhsValue = Integer.parseInt(Settings.resolveParametersString(parameterAssertedLhsValue, settings)), Integer assertedLhsValue = Integer.parseInt(Settings.resolveParametersString(parameterAssertedLhsValue, settings)),
assertedRhsValue = Integer.parseInt(Settings.resolveParametersString(parameterAssertedRhsValue, settings)); assertedRhsValue = Integer.parseInt(Settings.resolveParametersString(parameterAssertedRhsValue, settings));
@ -456,12 +528,16 @@ public class WebAPIServer_1_0_2 implements En {
assertTrue(itemResult.get()); assertTrue(itemResult.get());
} }
}); });
} catch (Exception ex) {
fail(ex.getMessage());
}
}); });
/* /*
* Date Comparison glue * Date Comparison glue
*/ */
And("^Date data in \"([^\"]*)\" \"([^\"]*)\" \"([^\"]*)\"$", (String parameterFieldName, String op, String parameterAssertedValue) -> { And("^Date data in \"([^\"]*)\" \"([^\"]*)\" \"([^\"]*)\"$", (String parameterFieldName, String op, String parameterAssertedValue) -> {
try {
String fieldName = Settings.resolveParametersString(parameterFieldName, settings); String fieldName = Settings.resolveParametersString(parameterFieldName, settings);
AtomicReference<Date> fieldValue = new AtomicReference<>(); AtomicReference<Date> fieldValue = new AtomicReference<>();
AtomicReference<Date> assertedValue = new AtomicReference<>(); AtomicReference<Date> assertedValue = new AtomicReference<>();
@ -473,16 +549,21 @@ public class WebAPIServer_1_0_2 implements En {
try { try {
fieldValue.set(Utils.parseDateFromEdmDateTimeOffsetString(item.get(fieldName).toString())); fieldValue.set(Utils.parseDateFromEdmDateTimeOffsetString(item.get(fieldName).toString()));
assertTrue(Utils.compare(fieldValue.get(), op, assertedValue.get())); assertTrue(Utils.compare(fieldValue.get(), op, assertedValue.get()));
} catch (Exception ex){ } catch (Exception ex) {
LOG.error(ex.toString()); fail(ex.toString());
} }
}); });
} catch (Exception ex) {
fail(ex.getMessage());
}
}); });
/* /*
* Time comparison glue * Time comparison glue
*/ */
And("^TimeOfDay data in \"([^\"]*)\" \"([^\"]*)\" \"([^\"]*)\"$", (String parameterFieldName, String op, String parameterAssertedValue) -> { And("^TimeOfDay data in \"([^\"]*)\" \"([^\"]*)\" \"([^\"]*)\"$", (String parameterFieldName, String op, String parameterAssertedValue) -> {
try {
String fieldName = Settings.resolveParametersString(parameterFieldName, settings); String fieldName = Settings.resolveParametersString(parameterFieldName, settings);
AtomicReference<Time> fieldValue = new AtomicReference<>(); AtomicReference<Time> fieldValue = new AtomicReference<>();
AtomicReference<Time> assertedValue = new AtomicReference<>(); AtomicReference<Time> assertedValue = new AtomicReference<>();
@ -494,35 +575,42 @@ public class WebAPIServer_1_0_2 implements En {
try { try {
fieldValue.set(Utils.parseTimeOfDayFromEdmDateTimeOffsetString(item.get(fieldName).toString())); fieldValue.set(Utils.parseTimeOfDayFromEdmDateTimeOffsetString(item.get(fieldName).toString()));
assertTrue(Utils.compare(fieldValue.get(), op, assertedValue.get())); assertTrue(Utils.compare(fieldValue.get(), op, assertedValue.get()));
} catch (Exception ex){ } catch (Exception ex) {
LOG.error(ex.toString()); LOG.error(ex.getMessage());
} }
}); });
} catch (Exception ex) {
fail(ex.getMessage());
}
}); });
/*
* Year comparison glue
*/
/* /*
* Timestamp comparison glue * Timestamp comparison glue
*/ */
And("^DateTimeOffset data in \"([^\"]*)\" \"([^\"]*)\" \"([^\"]*)\"$", (String parameterFieldName, String op, String parameterAssertedValue) -> { And("^DateTimeOffset data in \"([^\"]*)\" \"([^\"]*)\" \"([^\"]*)\"$", (String parameterFieldName, String op, String parameterAssertedValue) -> {
try {
Utils.assertDateTimeOffset(parameterFieldName, op, parameterAssertedValue, responseData.get()); Utils.assertDateTimeOffset(parameterFieldName, op, parameterAssertedValue, responseData.get());
} catch (Exception ex) {
fail(ex.getMessage());
}
}); });
/* /*
* Timestamp comparison to now() * Timestamp comparison to now()
*/ */
And("^DateTimeOffset data in \"([^\"]*)\" \"([^\"]*)\" now\\(\\)$", (String parameterFieldName, String op) -> { And("^DateTimeOffset data in \"([^\"]*)\" \"([^\"]*)\" now\\(\\)$", (String parameterFieldName, String op) -> {
try {
Utils.assertDateTimeOffset(parameterFieldName, op, Timestamp.from(Instant.now()), responseData.get()); Utils.assertDateTimeOffset(parameterFieldName, op, Timestamp.from(Instant.now()), responseData.get());
} catch (Exception ex) {
fail(ex.getMessage());
}
}); });
/* /*
* Single-Valued enumerations * Single-Valued enumerations
*/ */
And("^Single Valued Enumeration Data in \"([^\"]*)\" has \"([^\"]*)\"$", (String parameterFieldName, String parameterAssertedValue) -> { And("^Single Valued Enumeration Data in \"([^\"]*)\" has \"([^\"]*)\"$", (String parameterFieldName, String parameterAssertedValue) -> {
try {
String fieldName = Settings.resolveParametersString(parameterFieldName, settings); String fieldName = Settings.resolveParametersString(parameterFieldName, settings);
AtomicReference<String> fieldValue = new AtomicReference<>(); AtomicReference<String> fieldValue = new AtomicReference<>();
AtomicReference<String> assertedValue = new AtomicReference<>(); AtomicReference<String> assertedValue = new AtomicReference<>();
@ -538,6 +626,9 @@ public class WebAPIServer_1_0_2 implements En {
LOG.info("Assert True: " + fieldValue.get() + " equals " + assertedValue.get() + " ==> " + result.get()); LOG.info("Assert True: " + fieldValue.get() + " equals " + assertedValue.get() + " ==> " + result.get());
assertTrue(result.get()); assertTrue(result.get());
}); });
} catch (Exception ex) {
fail(ex.getMessage());
}
}); });
/* /*
@ -545,6 +636,7 @@ public class WebAPIServer_1_0_2 implements En {
* TODO: turn array into JSON array and parse values from there * TODO: turn array into JSON array and parse values from there
*/ */
And("^Multiple Valued Enumeration Data in \"([^\"]*)\" has \"([^\"]*)\"$", (String parameterFieldName, String parameterAssertedValue) -> { And("^Multiple Valued Enumeration Data in \"([^\"]*)\" has \"([^\"]*)\"$", (String parameterFieldName, String parameterAssertedValue) -> {
try {
String fieldName = Settings.resolveParametersString(parameterFieldName, settings); String fieldName = Settings.resolveParametersString(parameterFieldName, settings);
AtomicReference<String> fieldValue = new AtomicReference<>(); AtomicReference<String> fieldValue = new AtomicReference<>();
AtomicReference<String> assertedValue = new AtomicReference<>(); AtomicReference<String> assertedValue = new AtomicReference<>();
@ -560,12 +652,16 @@ public class WebAPIServer_1_0_2 implements En {
LOG.info("Assert True: " + fieldValue.get() + " has " + assertedValue.get() + " ==> " + result.get()); LOG.info("Assert True: " + fieldValue.get() + " has " + assertedValue.get() + " ==> " + result.get());
assertTrue(result.get()); assertTrue(result.get());
}); });
} catch (Exception ex) {
fail(ex.getMessage());
}
}); });
/* /*
* Date comparison ordering (asc, desc). * Date comparison ordering (asc, desc).
*/ */
And("^DateTimeOffset data in \"([^\"]*)\" is sorted in \"([^\"]*)\" order$", (String parameterFieldName, String parameterOrderByDirection) -> { And("^DateTimeOffset data in \"([^\"]*)\" is sorted in \"([^\"]*)\" order$", (String parameterFieldName, String parameterOrderByDirection) -> {
try {
String fieldName = Settings.resolveParametersString(parameterFieldName, settings); String fieldName = Settings.resolveParametersString(parameterFieldName, settings);
final String ASC = "asc", DESC = "desc"; final String ASC = "asc", DESC = "desc";
AtomicReference<String> orderBy = new AtomicReference<>(parameterOrderByDirection.toLowerCase()); AtomicReference<String> orderBy = new AtomicReference<>(parameterOrderByDirection.toLowerCase());
@ -592,10 +688,12 @@ public class WebAPIServer_1_0_2 implements En {
} }
count.getAndIncrement(); count.getAndIncrement();
} catch (EdmPrimitiveTypeException ptex) { } catch (EdmPrimitiveTypeException ptex) {
LOG.error("ERROR: exception thrown parsing Timestamp from given string." + ptex); fail(ptex.getMessage());
fail();
} }
}); });
} catch (Exception ex) {
fail(ex.getMessage());
}
}); });
And("^\"([^\"]*)\" data in Date Field \"([^\"]*)\" \"([^\"]*)\" \"([^\"]*)\"$", (String stringDatePart, String parameterFieldName, String op, String parameterAssertedValue) -> { And("^\"([^\"]*)\" data in Date Field \"([^\"]*)\" \"([^\"]*)\" \"([^\"]*)\"$", (String stringDatePart, String parameterFieldName, String op, String parameterAssertedValue) -> {
@ -614,13 +712,11 @@ public class WebAPIServer_1_0_2 implements En {
fieldValue.set(Utils.getDatePart(datePart.get(), item.get(fieldName))); fieldValue.set(Utils.getDatePart(datePart.get(), item.get(fieldName)));
assertTrue(Utils.compare(fieldValue.get(), operator.get(), assertedValue.get())); assertTrue(Utils.compare(fieldValue.get(), operator.get(), assertedValue.get()));
} catch (Exception ex){ } catch (Exception ex){
//fail(); fail(ex.getMessage());
LOG.error("ERROR: exception thrown." + ex);
} }
}); });
} catch (Exception ex) { } catch (Exception ex) {
fail(); fail(ex.getMessage());
LOG.error("ERROR: exception thrown." + ex);
} }
}); });
@ -629,6 +725,7 @@ public class WebAPIServer_1_0_2 implements En {
* TODO: consolidate with Year comparison with Date Field * TODO: consolidate with Year comparison with Date Field
*/ */
And("^\"([^\"]*)\" data in Timestamp Field \"([^\"]*)\" \"([^\"]*)\" \"([^\"]*)\"$", (String stringDatePart, String parameterFieldName, String op, String parameterAssertedValue) -> { And("^\"([^\"]*)\" data in Timestamp Field \"([^\"]*)\" \"([^\"]*)\" \"([^\"]*)\"$", (String stringDatePart, String parameterFieldName, String op, String parameterAssertedValue) -> {
try {
String fieldName = Settings.resolveParametersString(parameterFieldName, settings); String fieldName = Settings.resolveParametersString(parameterFieldName, settings);
AtomicReference<Integer> fieldValue = new AtomicReference<>(); AtomicReference<Integer> fieldValue = new AtomicReference<>();
AtomicReference<Integer> assertedValue = new AtomicReference<>(); AtomicReference<Integer> assertedValue = new AtomicReference<>();
@ -644,13 +741,14 @@ public class WebAPIServer_1_0_2 implements En {
fieldValue.set(Utils.getTimestampPart(datePart.get(), item.get(fieldName).toString())); fieldValue.set(Utils.getTimestampPart(datePart.get(), item.get(fieldName).toString()));
assertTrue(Utils.compare(fieldValue.get(), operator.get(), assertedValue.get())); assertTrue(Utils.compare(fieldValue.get(), operator.get(), assertedValue.get()));
} catch (Exception ex){ } catch (Exception ex){
fail(); fail(ex.getMessage());
LOG.error("ERROR: exception thrown." + ex);
} }
}); });
} catch (Exception ex) { } catch (Exception ex) {
fail(); fail(ex.getMessage());
LOG.error("ERROR: exception thrown." + ex); }
} catch (Exception ex) {
fail(ex.getMessage());
} }
}); });
@ -658,6 +756,7 @@ public class WebAPIServer_1_0_2 implements En {
* String functions * String functions
*/ */
And("^String data in \"([^\"]*)\" \"([^\"]*)\" \"([^\"]*)\"$", (String parameterFieldName, String op, String parameterAssertedValue) -> { And("^String data in \"([^\"]*)\" \"([^\"]*)\" \"([^\"]*)\"$", (String parameterFieldName, String op, String parameterAssertedValue) -> {
try {
String fieldName = Settings.resolveParametersString(parameterFieldName, settings); String fieldName = Settings.resolveParametersString(parameterFieldName, settings);
AtomicReference<String> assertedValue = new AtomicReference<>(Settings.resolveParametersString(parameterAssertedValue, settings)); AtomicReference<String> assertedValue = new AtomicReference<>(Settings.resolveParametersString(parameterAssertedValue, settings));
AtomicReference<String> operator = new AtomicReference<>(op.toLowerCase()); AtomicReference<String> operator = new AtomicReference<>(op.toLowerCase());
@ -665,8 +764,120 @@ public class WebAPIServer_1_0_2 implements En {
from(responseData.get()).getList(JSON_VALUE_PATH, HashMap.class).forEach(item -> { from(responseData.get()).getList(JSON_VALUE_PATH, HashMap.class).forEach(item -> {
assertTrue(Utils.compare(item.get(fieldName).toString(), operator.get(), assertedValue.get())); assertTrue(Utils.compare(item.get(fieldName).toString(), operator.get(), assertedValue.get()));
}); });
} catch (Exception ex) {
fail(ex.getMessage());
}
}); });
/*
* Metadata validation methods
*/
/*
Checks that metadata are accessible and contain the resource name specified in generic.resoscript
*/
And("^the metadata contains the \"([^\"]*)\" resource$", (String parameterResourceName) -> {
final String resourceName = Settings.resolveParametersString(parameterResourceName, settings);
AtomicReference<CsdlEntityContainer> entityContainer = new AtomicReference<>();
try {
entityContainer.set(ODataHelper.findDefaultEntityContainer(commander.get(), xmlMetadata.get()));
assertNotNull("ERROR: server metadata does not contain the given resource name: " + resourceName,
entityContainer.get().getEntitySet(resourceName));
LOG.info("Found EntityContainer for the given resource: '" + resourceName + "'");
} catch (Exception ex) {
fail(ex.getMessage());
}
});
And("^resource metadata for \"([^\"]*)\" contains the fields in \"([^\"]*)\"$", (String parameterResourceName, String parameterSelectList) -> {
final String FIELD_SEPARATOR = ",";
final String selectList = Settings.resolveParametersString(parameterSelectList, settings);
try {
final String resourceName = Settings.resolveParametersString(parameterResourceName, settings);
List<String> fieldNames = Arrays.asList(selectList.split(FIELD_SEPARATOR));
//create field lookup
Map<String, CsdlProperty> fieldMap = new HashMap<>();
ODataHelper.findEntityTypesForEntityTypeName(commander.get(), xmlMetadata.get(), 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() + "'");
});
} catch (Exception ex) {
fail(ex.getMessage());
}
});
When("^a successful metadata request is made to the service root in \"([^\"]*)\"$", (String clientSettingsServiceRoot) -> {
final String serviceRoot = Settings.resolveParametersString(clientSettingsServiceRoot, settings);
assertEquals("ERROR: given service root doesn't match the one configured in the Commander", serviceRoot, commander.get().getServiceRoot());
try {
xmlMetadata.set(commander.get().getXMLMetadata());
} catch (ODataClientErrorException cex) {
responseCode.set(cex.getStatusLine().getStatusCode());
fail(cex.getMessage());
} catch (Exception ex) {
fail(ex.getMessage());
}
});
}
private static final class ODataHelper {
/**
* Finds the default entity container for the given configuration.
* @param commander a commander instance with a valid service root.
* @param xmlMetadata XML Metadata to search through
* @return the default CSDL Container for the given XMLMetadata
* @throws Exception if required metadata cannot be parsed, an exception will be thrown with an appropriate message.
*/
private static CsdlEntityContainer findDefaultEntityContainer(Commander commander, XMLMetadata xmlMetadata) throws Exception {
Edm edm = commander.getEdm();
if (edm == null)
throw new Exception("ERROR: Could not retrieve Edm from server using the given service root!");
if (xmlMetadata == null)
throw new Exception("ERROR: the provided XML metadata was null!");
if (edm.getEntityContainer() == null)
throw new Exception("ERROR: Could not find default EntityContainer for service root: " + commander.getServiceRoot());
String entityContainerNamespace = edm.getEntityContainer().getNamespace();
if (entityContainerNamespace == null)
throw new Exception("ERROR: no default EntityContainer namespace could be found");
return xmlMetadata.getSchema(entityContainerNamespace).getEntityContainer();
}
/**
* Gets a list of CsdlProperty items for the given entityTypeName.
* @param commander a Commander instance with a valid service root.
* @param xmlMetadata the metadata to search.
* @param entityTypeName the name of the entityType to search for. MUST be in the default EntityContainer.
* @return a list of CsdlProperty items for the given entityTypeName
* @throws Exception is thrown if the given metadata doesn't contain the given type name.
*/
private static List<CsdlProperty> findEntityTypesForEntityTypeName(Commander commander, XMLMetadata xmlMetadata, String entityTypeName) throws Exception {
CsdlEntityContainer entityContainer = findDefaultEntityContainer(commander, xmlMetadata);
CsdlSchema schemaForType = xmlMetadata.getSchema(entityContainer.getEntitySet(entityTypeName).getTypeFQN().getNamespace());
if (schemaForType == null)
throw new Exception("ERROR: could not find type corresponding to given type name: " + entityTypeName);
return schemaForType.getEntityType(entityTypeName).getProperties();
}
} }
/** /**
@ -766,7 +977,6 @@ public class WebAPIServer_1_0_2 implements En {
} else if (operator.contentEquals(Operators.LESS_THAN_OR_EQUAL)) { } else if (operator.contentEquals(Operators.LESS_THAN_OR_EQUAL)) {
result = lhs <= rhs; result = lhs <= rhs;
} }
LOG.info("Compare: " + lhs + " " + operator + " " + rhs + " ==> " + result); LOG.info("Compare: " + lhs + " " + operator + " " + rhs + " ==> " + result);
return result; return result;
} }
@ -793,8 +1003,7 @@ public class WebAPIServer_1_0_2 implements En {
} else if (operator.contentEquals(Operators.TO_UPPER)) { } else if (operator.contentEquals(Operators.TO_UPPER)) {
result = lhs.toUpperCase().equals(rhs); result = lhs.toUpperCase().equals(rhs);
} }
LOG.info("Compare: \"" + lhs + "\" " + operator + " \"" + rhs + "\" ==> " + result);
LOG.info("Compare: " + operator + "(" + lhs + ") == " + rhs + " ==> " + result);
return result; return result;
} }

View File

@ -207,7 +207,7 @@ public class App {
if (request.getOutputFile().toLowerCase().contains(EDMX_EXTENSION.toLowerCase())) { if (request.getOutputFile().toLowerCase().contains(EDMX_EXTENSION.toLowerCase())) {
//Main.main(new String[]{"-g", "org.reso.certification.stepdefs#WebAPIServer_1_0_2", "/home/jdarnell/work/reso/github/web-api-commander/src/main/java/org/reso/certification/features/web-api-server-1.0.2.feature"}); //Main.main(new String[]{"-g", "org.reso.certification.stepdefs#WebAPIServer_1_0_2", "/home/jdarnell/work/reso/github/web-api-commander/src/main/java/org/reso/certification/features/web-api-server-1.0.2.feature"});
if (validateMetadata(commander, outputFilePath)) { if (validateMetadata(commander, outputFilePath)) {
metadata = commander.getMetadata(outputFilePath); metadata = commander.getEdm(outputFilePath);
} else { } else {
LOG.error("Error: Invalid metadata retrieved. Cannot continue!!"); LOG.error("Error: Invalid metadata retrieved. Cannot continue!!");
System.exit(NOT_OK); System.exit(NOT_OK);
@ -248,7 +248,7 @@ public class App {
if (cmd.hasOption(APP_OPTIONS.ACTIONS.GET_METADATA)) { if (cmd.hasOption(APP_OPTIONS.ACTIONS.GET_METADATA)) {
APP_OPTIONS.validateAction(cmd, APP_OPTIONS.ACTIONS.GET_METADATA); APP_OPTIONS.validateAction(cmd, APP_OPTIONS.ACTIONS.GET_METADATA);
metadata = commander.getMetadata(); metadata = commander.getEdm();
getMetadataReport(metadata); getMetadataReport(metadata);
} else if (cmd.hasOption(APP_OPTIONS.ACTIONS.VALIDATE_METADATA)) { } else if (cmd.hasOption(APP_OPTIONS.ACTIONS.VALIDATE_METADATA)) {

View File

@ -1,25 +1,20 @@
package org.reso.commander; package org.reso.commander;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import org.apache.commons.io.FileUtils; import org.apache.commons.io.FileUtils;
import org.apache.commons.io.input.ReaderInputStream;
import org.apache.commons.io.output.WriterOutputStream;
import org.apache.http.HttpStatus; import org.apache.http.HttpStatus;
import org.apache.http.client.utils.URIBuilder; import org.apache.http.client.utils.URIBuilder;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import org.apache.olingo.client.api.ODataClient; import org.apache.olingo.client.api.ODataClient;
import org.apache.olingo.client.api.communication.ODataClientErrorException; import org.apache.olingo.client.api.communication.ODataClientErrorException;
import org.apache.olingo.client.api.communication.request.retrieve.EdmMetadataRequest;
import org.apache.olingo.client.api.communication.response.ODataRawResponse; import org.apache.olingo.client.api.communication.response.ODataRawResponse;
import org.apache.olingo.client.api.communication.response.ODataRetrieveResponse; import org.apache.olingo.client.api.communication.response.ODataRetrieveResponse;
import org.apache.olingo.client.api.domain.ClientEntity; import org.apache.olingo.client.api.domain.ClientEntity;
import org.apache.olingo.client.api.domain.ClientEntitySet; import org.apache.olingo.client.api.domain.ClientEntitySet;
import org.apache.olingo.client.api.edm.xml.Edmx;
import org.apache.olingo.client.api.edm.xml.XMLMetadata; import org.apache.olingo.client.api.edm.xml.XMLMetadata;
import org.apache.olingo.client.core.ODataClientFactory; import org.apache.olingo.client.core.ODataClientFactory;
import org.apache.olingo.client.core.domain.ClientEntitySetImpl; import org.apache.olingo.client.core.domain.ClientEntitySetImpl;
import org.apache.olingo.client.core.edm.ClientCsdlXMLMetadata;
import org.apache.olingo.commons.api.data.Entity;
import org.apache.olingo.commons.api.edm.Edm; import org.apache.olingo.commons.api.edm.Edm;
import org.apache.olingo.commons.api.format.ContentType; import org.apache.olingo.commons.api.format.ContentType;
import org.reso.auth.OAuth2HttpClientFactory; import org.reso.auth.OAuth2HttpClientFactory;
@ -52,7 +47,14 @@ public class Commander {
private ODataClient client; private ODataClient client;
private boolean useEdmEnabledClient; private boolean useEdmEnabledClient;
String serviceRoot, bearerToken, clientId, clientSecret, authorizationUri, tokenUri, redirectUri, scope; private String serviceRoot;
String bearerToken;
String clientId;
String clientSecret;
String authorizationUri;
String tokenUri;
String redirectUri;
String scope;
boolean isTokenClient, isOAuthClient; boolean isTokenClient, isOAuthClient;
private static final Logger LOG = LogManager.getLogger(Commander.class); private static final Logger LOG = LogManager.getLogger(Commander.class);
@ -69,6 +71,14 @@ public class Commander {
this.client = client; this.client = client;
} }
public String getTokenUri() {
return tokenUri;
}
public String getServiceRoot() {
return serviceRoot;
}
/** /**
* Builder pattern for creating Commander instances. * Builder pattern for creating Commander instances.
*/ */
@ -167,24 +177,50 @@ public class Commander {
//private constructor for internal use, use Builder to construct instances //private constructor for internal use, use Builder to construct instances
private Commander() { } private Commander() { }
private Edm edm;
private XMLMetadata xmlMetadata;
/** /**
* Gets server metadata in Edm format. * Gets server metadata in Edm format.
* <p> * @implNote the data in this item are cached in the commander once fetched
* @return XMLMetadata representation of the server metadata. * @return Edm representation of the server metadata.
*/ */
public Edm getMetadata() { public Edm getEdm() {
Edm metadata = null; if (edm == null) {
try { try {
LOG.info("Fetching Metadata from " + serviceRoot + "..."); EdmMetadataRequest metadataRequest = client.getRetrieveRequestFactory().getMetadataRequest(serviceRoot);
metadata = client.getRetrieveRequestFactory().getMetadataRequest(serviceRoot).execute().getBody(); LOG.info("Fetching Edm with OData Client from: " + metadataRequest.getURI().toString());
LOG.info("Transfer complete! KBytes received: " + (metadata.toString().getBytes().length / 1024)); edm = metadataRequest.execute().getBody();
} catch (ODataClientErrorException cex) { } catch (ODataClientErrorException cex) {
LOG.error(cex.getStackTrace()); LOG.error("ERROR: could not retrieve Metadata for the given service root!");
LOG.error(cex.getStatusLine().toString());
throw cex;
} }
return metadata; }
return edm;
} }
public Edm getMetadata(String pathToXmlMetadata) { /**
* Gets server metadata in XMLMetadata format.
* @implNote the data in this item are cached in the commander once fetched
* @return XMLMetadata representation of the server metadata.
*/
public XMLMetadata getXMLMetadata() {
if (xmlMetadata == null) {
try {
EdmMetadataRequest metadataRequest = client.getRetrieveRequestFactory().getMetadataRequest(serviceRoot);
LOG.info("Fetching XMLMetadata with OData Client from: " + metadataRequest.getURI().toString());
xmlMetadata = metadataRequest.getXMLMetadata();
} catch (ODataClientErrorException cex) {
LOG.error("ERROR: could not retrieve Metadata for the given service root!");
LOG.error(cex.getStatusLine().toString());
throw cex;
}
}
return xmlMetadata;
}
public Edm getEdm(String pathToXmlMetadata) {
try { try {
return client.getReader().readMetadata(new FileInputStream(pathToXmlMetadata)); return client.getReader().readMetadata(new FileInputStream(pathToXmlMetadata));
} catch (FileNotFoundException fex) { } catch (FileNotFoundException fex) {
@ -202,31 +238,6 @@ public class Commander {
} }
} }
/**
* Converts Edm to XMLMetadata
* @param edm
* @return the XMLMetadata representation of the given Edm
*/
private XMLMetadata convertEdmToXMLMetadata(Edm edm) {
XMLMetadata xmlMetadata = null;
try {
ByteArrayInputStream inputStream = new ByteArrayInputStream(edm.toString().getBytes());
xmlMetadata = client.getDeserializer(ContentType.APPLICATION_XML).toMetadata(inputStream);
} catch (Exception ex) {
LOG.error(ex.getStackTrace());
}
return xmlMetadata;
}
public boolean validateMetadata(Edm metadata) {
try {
return validateMetadata(convertEdmToXMLMetadata(metadata));
} catch (Exception ex) {
LOG.error(ex.getStackTrace());
}
return false;
}
/** /**
* Validates given XMLMetadata * Validates given XMLMetadata
* *
@ -256,10 +267,8 @@ public class Commander {
public boolean validateMetadata(InputStream inputStream) { public boolean validateMetadata(InputStream inputStream) {
try { try {
// deserialize metadata from given file // deserialize metadata from given file
XMLMetadata metadata = XMLMetadata metadata = client.getDeserializer(ContentType.APPLICATION_XML).toMetadata(inputStream);
client.getDeserializer(ContentType.APPLICATION_XML).toMetadata(inputStream);
return validateMetadata(metadata); return validateMetadata(metadata);
} catch (Exception ex) { } catch (Exception ex) {
LOG.error("ERROR in validateMetadata! " + ex.toString() ); LOG.error("ERROR in validateMetadata! " + ex.toString() );
} }
@ -283,6 +292,14 @@ public class Commander {
return false; return false;
} }
public boolean isTokenClient() {
return isTokenClient;
}
public boolean isOAuthClient() {
return isOAuthClient;
}
public static boolean validateXML(String xmlString) { public static boolean validateXML(String xmlString) {
return validateXML(new ByteArrayInputStream(xmlString.getBytes(UTF_8))); return validateXML(new ByteArrayInputStream(xmlString.getBytes(UTF_8)));
} }