diff --git a/build/libs/web-api-commander.jar b/build/libs/web-api-commander.jar index 6385f49..29b509f 100644 Binary files a/build/libs/web-api-commander.jar and b/build/libs/web-api-commander.jar differ diff --git a/src/main/java/org/reso/certification/features/web-api/web-api-server-1.0.2.feature b/src/main/java/org/reso/certification/features/web-api/web-api-server-1.0.2.feature index 402c197..0c244aa 100644 --- a/src/main/java/org/reso/certification/features/web-api/web-api-server-1.0.2.feature +++ b/src/main/java/org/reso/certification/features/web-api/web-api-server-1.0.2.feature @@ -20,7 +20,7 @@ Feature: Web API Server 1.0.2 Certification Then the server responds with a status code of 200 And the server has an OData-Version header value of "4.0" or "4.01" And the XML Metadata response is valid XML - And the XML metadata returned by the server are valid + And the XML Metadata returned by the server are valid And Edm metadata are requested from the service root in "ClientSettings_WebAPIURI" Then the server responds with a status code of 200 And the server has an OData-Version header value of "4.0" or "4.01" @@ -32,6 +32,7 @@ Feature: Web API Server 1.0.2 Certification @REQ-WA103-END2 @core @2.4.1 @core-endorsement @datasystem Scenario: REQ-WA103-END2 - Data System Endpoint test + Given valid metadata have been retrieved When a GET request is made to the resolved Url in "REQ-WA103-END2" Then the server responds with a status code of 200 And the server has an OData-Version header value of "4.0" or "4.01" @@ -41,6 +42,7 @@ Feature: Web API Server 1.0.2 Certification @REQ-WA103-QR1 @core @2.4.1 @query-functions-endorsement Scenario: REQ-WA103-QR1 - Search Parameters: Select KeyOrKeyNumeric Field + Given valid metadata have been retrieved When a GET request is made to the resolved Url in "REQ-WA103-QR1" Then the server responds with a status code of 200 And the server has an OData-Version header value of "4.0" or "4.01" @@ -50,6 +52,7 @@ Feature: Web API Server 1.0.2 Certification @REQ-WA103-QR3 @core @2.4.2 @query-functions-endorsement Scenario: REQ-WA103-QR3 - Query Support: $select + Given valid metadata have been retrieved When a GET request is made to the resolved Url in "REQ-WA103-QR3" Then the server responds with a status code of 200 And the server has an OData-Version header value of "4.0" or "4.01" @@ -60,6 +63,7 @@ Feature: Web API Server 1.0.2 Certification @REQ-WA103-QR4 @core @2.4.2 @client-paging-endorsement Scenario: REQ-WA103-QR4 - Query Support: $top + Given valid metadata have been retrieved When a GET request is made to the resolved Url in "REQ-WA103-QR4" Then the server responds with a status code of 200 And the server has an OData-Version header value of "4.0" or "4.01" @@ -71,6 +75,7 @@ Feature: Web API Server 1.0.2 Certification @REQ-WA103-QR5 @core @2.4.2 @query-support-endorsement Scenario: REQ-WA103-QR5 - Query Support: $skip + Given valid metadata have been retrieved When a GET request is made to the resolved Url in "REQ-WA103-QR5" Then the server responds with a status code of 200 And the server has an OData-Version header value of "4.0" or "4.01" @@ -89,6 +94,7 @@ Feature: Web API Server 1.0.2 Certification @REQ-WA103-QO1.1 @core @2.4.4 @core-endorsement @OData-4.0 Scenario: REQ-WA103-QO1.1 - Query Support: $select case-sensitivity for OData 4.0 + Given valid metadata have been retrieved When a GET request is made to the resolved Url in "REQ-WA103-QO1.1" And the server has an OData-Version header value of "4.0" or "4.01" Then the server responds with a status code of 400 if the server reports OData-Version "4.0" @@ -96,6 +102,7 @@ Feature: Web API Server 1.0.2 Certification @REQ-WA103-QO1.2 @core @2.4.4 @core-endorsement @OData-4.0 Scenario: REQ-WA103-QO1.2 - Query Support: $filter case-sensitivity for OData 4.0 + Given valid metadata have been retrieved When a GET request is made to the resolved Url in "REQ-WA103-QO1.2" And the server has an OData-Version header value of "4.0" or "4.01" Then the server responds with a status code of 400 if the server reports OData-Version "4.0" @@ -103,6 +110,7 @@ Feature: Web API Server 1.0.2 Certification @REQ-WA103-QO1.3 @core @2.4.4 @core-endorsement @OData-4.0 Scenario: REQ-WA103-QO1.3 - Query Support: $orderby asc case-sensitivity for OData 4.0 + Given valid metadata have been retrieved When a GET request is made to the resolved Url in "REQ-WA103-QO1.3" And the server has an OData-Version header value of "4.0" or "4.01" Then the server responds with a status code of 400 if the server reports OData-Version "4.0" @@ -110,6 +118,7 @@ Feature: Web API Server 1.0.2 Certification @REQ-WA103-QO1.4 @core @2.4.4 @core-endorsement @OData-4.0 Scenario: REQ-WA103-QO1.4 - Query Support: $orderby desc case-sensitivity for OData 4.0 + Given valid metadata have been retrieved When a GET request is made to the resolved Url in "REQ-WA103-QO1.4" And the server has an OData-Version header value of "4.0" or "4.01" Then the server responds with a status code of 400 if the server reports OData-Version "4.0" @@ -117,6 +126,7 @@ Feature: Web API Server 1.0.2 Certification @REQ-WA103-QO2 @core @2.4.4 @filterability-endorsement Scenario: REQ-WA103-QO2 - Query Support: $filter - Integer Comparison: eq + Given valid metadata have been retrieved When a GET request is made to the resolved Url in "REQ-WA103-QO2" Then the server responds with a status code of 200 And the server has an OData-Version header value of "4.0" or "4.01" @@ -128,6 +138,7 @@ Feature: Web API Server 1.0.2 Certification @REQ-WA103-QO3 @core @2.4.4 @filterability-endorsement Scenario: REQ-WA103-QO3 - Query Support: $filter - Integer Comparison: ne + Given valid metadata have been retrieved When a GET request is made to the resolved Url in "REQ-WA103-QO3" Then the server responds with a status code of 200 And the server has an OData-Version header value of "4.0" or "4.01" @@ -139,6 +150,7 @@ Feature: Web API Server 1.0.2 Certification @REQ-WA103-QO4 @core @2.4.4 @filterability-endorsement Scenario: REQ-WA103-QO4 - Query Support: $filter - Integer Comparison: gt + Given valid metadata have been retrieved When a GET request is made to the resolved Url in "REQ-WA103-QO4" Then the server responds with a status code of 200 And the server has an OData-Version header value of "4.0" or "4.01" @@ -150,6 +162,7 @@ Feature: Web API Server 1.0.2 Certification @REQ-WA103-QO5 @core @2.4.4 @filterability-endorsement Scenario: REQ-WA103-QO5 - Query Support: $filter - Integer Comparison: ge + Given valid metadata have been retrieved When a GET request is made to the resolved Url in "REQ-WA103-QO5" Then the server responds with a status code of 200 And the server has an OData-Version header value of "4.0" or "4.01" @@ -161,6 +174,7 @@ Feature: Web API Server 1.0.2 Certification @REQ-WA103-QO6 @core @2.4.4 @filterability-endorsement Scenario: REQ-WA103-QO6 - Query Support: $filter - Integer Comparison: lt + Given valid metadata have been retrieved When a GET request is made to the resolved Url in "REQ-WA103-QO6" Then the server responds with a status code of 200 And the server has an OData-Version header value of "4.0" or "4.01" @@ -172,6 +186,7 @@ Feature: Web API Server 1.0.2 Certification @REQ-WA103-QO7 @core @2.4.4 @filterability-endorsement Scenario: REQ-WA103-QO7 - Query Support: $filter - Integer Comparison: le + Given valid metadata have been retrieved When a GET request is made to the resolved Url in "REQ-WA103-QO7" Then the server responds with a status code of 200 And the server has an OData-Version header value of "4.0" or "4.01" @@ -183,6 +198,7 @@ Feature: Web API Server 1.0.2 Certification @REQ-WA103-QO9 @core @2.4.4 @filterability-endorsement Scenario: REQ-WA103-QO9 - Query Support: $filter - Integer Comparison: and + Given valid metadata have been retrieved When a GET request is made to the resolved Url in "REQ-WA103-QO9" Then the server responds with a status code of 200 And the server has an OData-Version header value of "4.0" or "4.01" @@ -194,6 +210,7 @@ Feature: Web API Server 1.0.2 Certification @REQ-WA103-QO10 @core @2.4.4 @filterability-endorsement Scenario: REQ-WA103-QO10 - Query Support: $filter - Integer Comparison: or + Given valid metadata have been retrieved When a GET request is made to the resolved Url in "REQ-WA103-QO10" Then the server responds with a status code of 200 And the server has an OData-Version header value of "4.0" or "4.01" @@ -205,6 +222,7 @@ Feature: Web API Server 1.0.2 Certification @REQ-WA103-QO11 @core @2.4.4 @filterability-endorsement Scenario: REQ-WA103-QO11 - Query Support: $filter - Integer Comparison: not() (operator) + Given valid metadata have been retrieved When a GET request is made to the resolved Url in "REQ-WA103-QO11" Then the server responds with a status code of 200 And the server has an OData-Version header value of "4.0" or "4.01" @@ -216,6 +234,7 @@ Feature: Web API Server 1.0.2 Certification @REQ-WA103-QO25 @core @2.4.4 @filterability-endorsement Scenario: REQ-WA103-QO25 - Query Support: $filter: Date portion of EdmDateTimeOffset field is greater than EdmDate value + Given valid metadata have been retrieved When a GET request is made to the resolved Url in "REQ-WA103-QO25" Then the server responds with a status code of 200 And the server has an OData-Version header value of "4.0" or "4.01" @@ -227,6 +246,7 @@ Feature: Web API Server 1.0.2 Certification @REQ-WA103-QO26 @core @2.4.4 @filterability-endorsement Scenario: REQ-WA103-QO26 - Query Support: $filter: Time portion of EdmDateTimeOffset field is less than EdmTime value + Given valid metadata have been retrieved When a GET request is made to the resolved Url in "REQ-WA103-QO26" Then the server responds with a status code of 200 And the server has an OData-Version header value of "4.0" or "4.01" @@ -238,6 +258,7 @@ Feature: Web API Server 1.0.2 Certification @REQ-WA103-QO26.2 @core @2.4.4 @filterability-endorsement Scenario: REQ-WA103-QO26.2 - Query Support: $filter: Date: EdmDateTimeOffset field is less than EdmDateTimeOffset value + Given valid metadata have been retrieved When a GET request is made to the resolved Url in "REQ-WA103-QO26.2" Then the server responds with a status code of 200 And the server has an OData-Version header value of "4.0" or "4.01" @@ -249,6 +270,7 @@ Feature: Web API Server 1.0.2 Certification @REQ-WA103-QO27 @core @2.4.4 @filterability-endorsement Scenario: REQ-WA103-QO27 - Query Support: $filter: DateTimeOffset le now() + Given valid metadata have been retrieved When a GET request is made to the resolved Url in "REQ-WA103-QO27" Then the server responds with a status code of 200 And the server has an OData-Version header value of "4.0" or "4.01" @@ -265,24 +287,28 @@ Feature: Web API Server 1.0.2 Certification @REQ-WA103-END1 @core @2.4.1 @core-endorsement Scenario: REQ-WA103-END1 - Service Endpoint + Given valid metadata have been retrieved When a GET request is made to the resolved Url in "REQ-WA103-END1" Then the server responds with a status code of 200 And the server has an OData-Version header value of "4.0" or "4.01" @REQ-WA103-RC3 @core @2.5.2 @core-endorsement Scenario: REQ-WA103-RC3 - 200 OK Request + Given valid metadata have been retrieved When a GET request is made to the resolved Url in "REQ-WA103-RC3" Then the server responds with a status code of 200 And the server has an OData-Version header value of "4.0" or "4.01" @REQ-WA103-RC5 @core @2.4.2 @core-endorsement Scenario: REQ-WA103-RC5 - 400 Bad Request + Given valid metadata have been retrieved When a GET request is made to the resolved Url in "REQ-WA103-RC5" Then the server responds with a status code of 400 And the server has an OData-Version header value of "4.0" or "4.01" @REQ-WA103-RC07 @core @2.5.2 @core-endorsement Scenario: REQ-WA103-RC07 - 404 Not Found Request + Given valid metadata have been retrieved When a GET request is made to the resolved Url in "REQ-WA103-RC07" Then the server responds with a status code of 404 And the server has an OData-Version header value of "4.0" or "4.01" @@ -293,6 +319,7 @@ Feature: Web API Server 1.0.2 Certification @REQ-WA103-QM7 @bronze @2.4.9 @filterability-endorsement Scenario: REQ-WA103-QM7 - Support Single Value Lookups + Given valid metadata have been retrieved When a GET request is made to the resolved Url in "REQ-WA103-QM7" Then the server responds with a status code of 200 And the server has an OData-Version header value of "4.0" or "4.01" @@ -304,6 +331,7 @@ Feature: Web API Server 1.0.2 Certification @REQ-WA103-QM8 @bronze @2.4.10 @filterability-endorsement Scenario: REQ-WA103-QM8 - Support Multi Value Lookups + Given valid metadata have been retrieved When a GET request is made to the resolved Url in "REQ-WA103-QM8" Then the server responds with a status code of 200 And the server has an OData-Version header value of "4.0" or "4.01" @@ -315,6 +343,7 @@ Feature: Web API Server 1.0.2 Certification @REQ-WA103-QM8.2 @bronze @2.4.10 @filterability-endorsement Scenario: REQ-WA103-QM8.2 - Support Multi Value Lookups multiple values + Given valid metadata have been retrieved When a GET request is made to the resolved Url in "REQ-WA103-QM8.2" Then the server responds with a status code of 200 And the server has an OData-Version header value of "4.0" or "4.01" @@ -327,6 +356,7 @@ Feature: Web API Server 1.0.2 Certification @REQ-WA103-QO8 @bronze @2.4.4 @filterability-endorsement Scenario: REQ-WA103-QO8 - Query Support: $filter - Comparison: has + Given valid metadata have been retrieved When a GET request is made to the resolved Url in "REQ-WA103-QO8" Then the server responds with a status code of 200 And the server has an OData-Version header value of "4.0" or "4.01" @@ -338,6 +368,7 @@ Feature: Web API Server 1.0.2 Certification @REQ-WA103-QO28.1 @bronze @2.4.4 @sortability-endorsement Scenario: REQ-WA103-QO28.1 - Query Support: $orderby asc filtered + Given valid metadata have been retrieved When a GET request is made to the resolved Url in "REQ-WA103-QO28.1" Then the server responds with a status code of 200 And the server has an OData-Version header value of "4.0" or "4.01" @@ -349,6 +380,7 @@ Feature: Web API Server 1.0.2 Certification @REQ-WA103-QO28.2 @bronze @2.4.4 @sortability-endorsement Scenario: REQ-WA103-QO28.2 - Query Support: $orderby asc no filter + Given valid metadata have been retrieved When a GET request is made to the resolved Url in "REQ-WA103-QO28.2" Then the server responds with a status code of 200 And the server has an OData-Version header value of "4.0" or "4.01" @@ -360,6 +392,7 @@ Feature: Web API Server 1.0.2 Certification @REQ-WA103-QO28.3 @bronze @2.4.4 @sortability-endorsement Scenario: REQ-WA103-QO28.3 - Query Support: $orderby desc filtered + Given valid metadata have been retrieved When a GET request is made to the resolved Url in "REQ-WA103-QO28.3" Then the server responds with a status code of 200 And the server has an OData-Version header value of "4.0" or "4.01" @@ -371,6 +404,7 @@ Feature: Web API Server 1.0.2 Certification @REQ-WA103-QO28.4 @bronze @2.4.4 @sortability-endorsement Scenario: REQ-WA103-QO28.4 - Query Support: $orderby desc no filter + Given valid metadata have been retrieved When a GET request is made to the resolved Url in "REQ-WA103-QO28.4" Then the server responds with a status code of 200 And the server has an OData-Version header value of "4.0" or "4.01" @@ -387,6 +421,7 @@ Feature: Web API Server 1.0.2 Certification @REQ-WA103-QO18.1 @gold @2.4.4 @filterability-endorsement Scenario: REQ-WA103-QO18.1 - Query Support: $filter: Date: year + Given valid metadata have been retrieved When a GET request is made to the resolved Url in "REQ-WA103-QO18.1" Then the server responds with a status code of 200 And the server has an OData-Version header value of "4.0" or "4.01" @@ -398,6 +433,7 @@ Feature: Web API Server 1.0.2 Certification @REQ-WA103-QO18.2 @gold @2.4.4 @filterability-endorsement Scenario: REQ-WA103-QO18.2 - Query Support: $filter: Date: year comparison with timestamp + Given valid metadata have been retrieved When a GET request is made to the resolved Url in "REQ-WA103-QO18.2" Then the server responds with a status code of 200 And the server has an OData-Version header value of "4.0" or "4.01" @@ -409,6 +445,7 @@ Feature: Web API Server 1.0.2 Certification @REQ-WA103-QO19.1 @gold @2.4.4 @filterability-endorsement Scenario: REQ-WA103-QO19.1 - Query Support: $filter: Date: month + Given valid metadata have been retrieved When a GET request is made to the resolved Url in "REQ-WA103-QO19.1" Then the server responds with a status code of 200 And the server has an OData-Version header value of "4.0" or "4.01" @@ -420,6 +457,7 @@ Feature: Web API Server 1.0.2 Certification @REQ-WA103-QO19.2 @gold @2.4.4 @filterability-endorsement Scenario: REQ-WA103-QO19.2 - Query Support: $filter: Date: month comparison with timestamp + Given valid metadata have been retrieved When a GET request is made to the resolved Url in "REQ-WA103-QO19.2" Then the server responds with a status code of 200 And the server has an OData-Version header value of "4.0" or "4.01" @@ -431,6 +469,7 @@ Feature: Web API Server 1.0.2 Certification @REQ-WA103-QO20.1 @gold @2.4.4 @filterability-endorsement Scenario: REQ-WA103-QO20.1 - Query Support: $filter: Date: day + Given valid metadata have been retrieved When a GET request is made to the resolved Url in "REQ-WA103-QO20.1" Then the server responds with a status code of 200 And the server has an OData-Version header value of "4.0" or "4.01" @@ -442,6 +481,7 @@ Feature: Web API Server 1.0.2 Certification @REQ-WA103-QO20.2 @gold @2.4.4 @filterability-endorsement Scenario: REQ-WA103-QO20.2 - Query Support: $filter: Date: day comparison with timestamp + Given valid metadata have been retrieved When a GET request is made to the resolved Url in "REQ-WA103-QO20.2" Then the server responds with a status code of 200 And the server has an OData-Version header value of "4.0" or "4.01" @@ -453,6 +493,7 @@ Feature: Web API Server 1.0.2 Certification @REQ-WA103-QO21 @gold @2.4.4 @filterability-endorsement Scenario: REQ-WA103-QO21 - Query Support: $filter: Date: hour comparison with timestamp + Given valid metadata have been retrieved When a GET request is made to the resolved Url in "REQ-WA103-QO21" Then the server responds with a status code of 200 And the server has an OData-Version header value of "4.0" or "4.01" @@ -464,6 +505,7 @@ Feature: Web API Server 1.0.2 Certification @REQ-WA103-QO22 @gold @2.4.4 @filterability-endorsement Scenario: REQ-WA103-QO22 - Query Support: $filter: Date: minute comparison with timestamp + Given valid metadata have been retrieved When a GET request is made to the resolved Url in "REQ-WA103-QO22" Then the server responds with a status code of 200 And the server has an OData-Version header value of "4.0" or "4.01" @@ -475,6 +517,7 @@ Feature: Web API Server 1.0.2 Certification @REQ-WA103-QO23 @gold @2.4.4 @filterability-endorsement Scenario: REQ-WA103-QO23 - Query Support: $filter: Date: second comparison with timestamp + Given valid metadata have been retrieved When a GET request is made to the resolved Url in "REQ-WA103-QO23" Then the server responds with a status code of 200 And the server has an OData-Version header value of "4.0" or "4.01" @@ -486,6 +529,7 @@ Feature: Web API Server 1.0.2 Certification @REQ-WA103-QO24 @gold @2.4.4 @filterability-endorsement Scenario: REQ-WA103-QO24 - Query Support: $filter: Date: fractional seconds comparison with timestamp + Given valid metadata have been retrieved When a GET request is made to the resolved Url in "REQ-WA103-QO24" Then the server responds with a status code of 200 And the server has an OData-Version header value of "4.0" or "4.01" @@ -501,6 +545,7 @@ Feature: Web API Server 1.0.2 Certification @REQ-WA103-QO13 @platinum @2.4.4 @filterability-endorsement Scenario: REQ-WA103-QO13 - Query Support: $filter - String: contains + Given valid metadata have been retrieved When a GET request is made to the resolved Url in "REQ-WA103-QO13" Then the server responds with a status code of 200 And the server has an OData-Version header value of "4.0" or "4.01" @@ -512,6 +557,7 @@ Feature: Web API Server 1.0.2 Certification @REQ-WA103-QO14 @platinum @2.4.4 @filterability-endorsement Scenario: REQ-WA103-QO14 - Query Support: $filter - String: ends with + Given valid metadata have been retrieved When a GET request is made to the resolved Url in "REQ-WA103-QO14" Then the server responds with a status code of 200 And the server has an OData-Version header value of "4.0" or "4.01" @@ -523,6 +569,7 @@ Feature: Web API Server 1.0.2 Certification @REQ-WA103-QO15 @platinum @2.4.4 @filterability-endorsement Scenario: REQ-WA103-QO15 - Query Support: $filter - String: starts with + Given valid metadata have been retrieved When a GET request is made to the resolved Url in "REQ-WA103-QO15" Then the server responds with a status code of 200 And the server has an OData-Version header value of "4.0" or "4.01" @@ -534,6 +581,7 @@ Feature: Web API Server 1.0.2 Certification @REQ-WA103-QO16 @platinum @2.4.4 @filterability-endorsement Scenario: REQ-WA103-QO16 - Query Support: $filter - String: tolower() support + Given valid metadata have been retrieved When a GET request is made to the resolved Url in "REQ-WA103-QO16" Then the server responds with a status code of 200 And the server has an OData-Version header value of "4.0" or "4.01" @@ -545,6 +593,7 @@ Feature: Web API Server 1.0.2 Certification @REQ-WA103-QO17 @platinum @2.4.4 @filterability-endorsement Scenario: REQ-WA103-QO17 - Query Support: $filter - String: toupper() support + Given valid metadata have been retrieved When a GET request is made to the resolved Url in "REQ-WA103-QO17" Then the server responds with a status code of 200 And the server has an OData-Version header value of "4.0" or "4.01" @@ -556,6 +605,7 @@ Feature: Web API Server 1.0.2 Certification @REQ-WA103-QO29.1 @platinum @2.4.4 @expandability-endorsement Scenario: REQ-WA103-QO29.1 - Query Support: $expand + Given valid metadata have been retrieved When a GET request is made to the resolved Url in "REQ-WA103-QO29.1" Then the server responds with a status code of 200 And the server has an OData-Version header value of "4.0" or "4.01" @@ -569,6 +619,7 @@ Feature: Web API Server 1.0.2 Certification @REQ-WA103-QO29.2 @platinum @2.4.4 @expandability-endorsement @todo Scenario: REQ-WA103-QO29.2 - Query Support: $expand media photo count (TODO) + Given valid metadata have been retrieved When a GET request is made to the resolved Url in "REQ-WA103-QO29.2" Then the server responds with a status code of 200 And the server has an OData-Version header value of "4.0" or "4.01" @@ -579,6 +630,7 @@ Feature: Web API Server 1.0.2 Certification @REQ-WA103-QO29.3 @platinum @2.4.4 @expandability-endorsement @todo Scenario: REQ-WA103-QO29.3 - Query Support: $expand required field (TODO) + Given valid metadata have been retrieved When a GET request is made to the resolved Url in "REQ-WA103-QO29.3" Then the server responds with a status code of 200 And the server has an OData-Version header value of "4.0" or "4.01" @@ -589,6 +641,7 @@ Feature: Web API Server 1.0.2 Certification @REQ-WA103-QM3 @platinum @2.4.6 @queryability-endorsement @todo Scenario: REQ-WA103-QM3 - Support Literals: any() Lambda Expression (TODO) + Given valid metadata have been retrieved When a GET request is made to the resolved Url in "REQ-WA103-QM3" Then the server responds with a status code of 200 And the server has an OData-Version header value of "4.0" or "4.01" @@ -599,6 +652,7 @@ Feature: Web API Server 1.0.2 Certification @REQ-WA103-QM4 @platinum @2.4.6 @queryability-endorsement @todo Scenario: REQ-WA103-QM4 - Query Support Literals: all() Lambda Operator (TODO) + Given valid metadata have been retrieved When a GET request is made to the resolved Url in "REQ-WA103-QM4" Then the server responds with a status code of 200 And the server has an OData-Version header value of "4.0" or "4.01" @@ -609,6 +663,7 @@ Feature: Web API Server 1.0.2 Certification @REQ-WA103-QM5.1 @platinum @2.4.7 @queryability-endorsement @geospatial @todo Scenario: REQ-WA103-QM5.1 - Query Support: GeoSpatial Search Implementation (TODO) + Given valid metadata have been retrieved When a GET request is made to the resolved Url in "REQ-WA103-QM5.1" Then the server responds with a status code of 200 And the server has an OData-Version header value of "4.0" or "4.01" @@ -619,6 +674,7 @@ Feature: Web API Server 1.0.2 Certification @REQ-WA103-QM5.2 @platinum @2.4.7 @queryability-endorsement @geospatial @todo Scenario: REQ-WA103-QM5.2 - Query Support: GeoSpatial Search Implementation (TODO) + Given valid metadata have been retrieved When a GET request is made to the resolved Url in "REQ-WA103-QM5.2" Then the server responds with a status code of 200 And the server has an OData-Version header value of "4.0" or "4.01" @@ -629,6 +685,7 @@ Feature: Web API Server 1.0.2 Certification @REQ-WA103-QO12 @platinum @2.4.4 @filterability-endorsement @filterability-endorsement Scenario: REQ-WA103-QO12 - Query Support: $filter - Grouping: filter (ge, le) and (gt, lt) and expect (gt, lt) + Given valid metadata have been retrieved When a GET request is made to the resolved Url in "REQ-WA103-QO12" using the OData Client Then the server responds with a status code of 200 And the server has an OData-Version header value of "4.0" or "4.01" diff --git a/src/main/java/org/reso/certification/stepdefs/WebAPIServer_1_0_2.java b/src/main/java/org/reso/certification/stepdefs/WebAPIServer_1_0_2.java index 9016d55..81938cd 100644 --- a/src/main/java/org/reso/certification/stepdefs/WebAPIServer_1_0_2.java +++ b/src/main/java/org/reso/certification/stepdefs/WebAPIServer_1_0_2.java @@ -21,7 +21,7 @@ import org.apache.olingo.commons.api.edm.provider.CsdlEntityContainer; import org.apache.olingo.commons.api.edm.provider.CsdlNavigationProperty; import org.apache.olingo.commons.api.format.ContentType; import org.reso.commander.Commander; -import org.reso.commander.TestUtils; +import org.reso.commander.common.TestUtils; import org.reso.commander.certfication.containers.WebApiTestContainer; import org.reso.models.Request; import org.reso.models.Settings; @@ -40,11 +40,11 @@ import java.util.concurrent.atomic.AtomicReference; import static io.restassured.path.json.JsonPath.from; import static org.junit.Assert.*; import static org.reso.commander.Commander.*; -import static org.reso.commander.TestUtils.*; -import static org.reso.commander.TestUtils.Operators.*; +import static org.reso.commander.common.TestUtils.*; +import static org.reso.commander.common.TestUtils.Operators.*; import static org.reso.commander.certfication.containers.WebApiTestContainer.*; -import static org.reso.common.ErrorMsg.getAssertResponseCodeErrorMessage; -import static org.reso.common.ErrorMsg.getDefaultErrorMessage; +import static org.reso.commander.common.ErrorMsg.getAssertResponseCodeErrorMessage; +import static org.reso.commander.common.ErrorMsg.getDefaultErrorMessage; /** * Contains the glue code for the Web API Server 1.0.2 Platinum Certification @@ -66,43 +66,8 @@ public class WebAPIServer_1_0_2 implements En { * Entry point to the Web API Server tests */ public WebAPIServer_1_0_2() { - - /* - * Background - */ - Given("^a RESOScript file was provided$", () -> { - if (getTestContainer().getPathToRESOScript() == null) { - getTestContainer().setPathToRESOScript(System.getProperty("pathToRESOScript")); - } - assertNotNull("ERROR: pathToRESOScript must be present in command arguments, see README", getTestContainer().getPathToRESOScript()); - LOG.info("Using RESOScript: " + getTestContainer().getPathToRESOScript()); - }); - And("^Client Settings and Parameters were read from the file$", () -> { - if (getTestContainer().getSettings() == null) { - getTestContainer().setSettings(Settings.loadFromRESOScript(new File(System.getProperty("pathToRESOScript")))); - } - assertNotNull("ERROR: Settings could not be loaded.", getTestContainer().getSettings()); - LOG.info("RESOScript loaded successfully!"); - }); - - Given("^a test container was successfully created from the given RESOScript$", () -> { - getTestContainer().initialize(); - }); - - /* - * Ensures that the client either uses Authorization Codes or Client Credentials - */ - And("^the test container uses an authorization_code or client_credentials for authentication$", () -> { - assertNotNull(getTestContainer().getCommander()); - assertTrue("ERROR: Commander must either have a valid Authorization Code or Client Credentials configuration.", - getTestContainer().getCommander().isTokenClient() || (getTestContainer().getCommander().isOAuthClient() && getTestContainer().getCommander().hasValidAuthConfig())); - - if (getTestContainer().getCommander().isTokenClient()) { - LOG.info("Authentication Type: authorization_code"); - } else if (getTestContainer().getCommander().isOAuthClient()) { - LOG.info("Authentication Type: client_credentials"); - } - }); + + runBackground(); /* * REQ-WA103-END2 - validate DataSystem endpoint, if present. @@ -141,6 +106,7 @@ public class WebAPIServer_1_0_2 implements En { try { boolean isValid = getTestContainer().getCommander().validateMetadata(getTestContainer().getEdm()); + getTestContainer().setIsValidEdm(isValid); LOG.info("Edm Metadata is " + (isValid ? "valid" : "invalid") + "!"); assertTrue("Edm Metadata at the given service root is not valid! " + getTestContainer().getServiceRoot(), isValid); } catch (Exception ex) { @@ -151,11 +117,12 @@ public class WebAPIServer_1_0_2 implements En { /* * XML Metadata Validator */ - And("^the XML metadata returned by the server are valid$", () -> { + And("^the XML Metadata returned by the server are valid$", () -> { assertNotNull("ERROR: XML Metadata (EDMX) Exists!", getTestContainer().getXMLMetadata()); try { boolean isValid = getTestContainer().getCommander().validateMetadata(getTestContainer().getXMLMetadata()); + getTestContainer().setIsValidXMLMetadata(isValid); LOG.info("XML Metadata is " + (isValid ? "valid" : "invalid") + "!"); assertTrue("XML Metadata at the given service root is not valid! " + getTestContainer().getServiceRoot(), isValid); } catch (Exception ex) { @@ -274,7 +241,7 @@ public class WebAPIServer_1_0_2 implements En { getTestContainer().setRequestUri(Commander.prepareURI( Settings.resolveParameters(getTestContainer().getSettings().getRequest(requestId), getTestContainer().getSettings()).getUrl() + AMPERSAND + ODATA_QUERY_PARAMS.SKIP + EQUALS + skipCount)); - getTestContainer().executePreparedGetRequest(); + getTestContainer().executePreparedRawGetRequest(); } catch (Exception ex) { fail(getDefaultErrorMessage(ex)); } @@ -308,7 +275,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 \"([^\"]*)\"$", this::prepareAndExecuteGetRequest); + When("^a GET request is made to the resolved Url in \"([^\"]*)\"$", this::prepareAndExecuteRawGetRequest); /* * Assert response code @@ -339,8 +306,15 @@ public class WebAPIServer_1_0_2 implements En { * validate XML wrapper */ And("^the XML Metadata response is valid XML$", () -> { + LOG.info("Validating XML Metadata response to ensure it's valid XML and matches OASIS OData XSDs..."); + LOG.info( "See: https://docs.oasis-open.org/odata/odata/v4.0/errata03/os/complete/schemas/"); + assertNotNull("XML response data were not found in the test container! Please ensure the XML Metadata request succeeded.", + getTestContainer().getXMLResponseData()); + try { - assertTrue("ERROR: invalid XML response!", Commander.validateXML(getTestContainer().getXMLResponseData())); + boolean isValid = Commander.validateXML(getTestContainer().getXMLResponseData()); + getTestContainer().setIsXMLMetadataValidXML(isValid); + assertTrue("ERROR: invalid XML response!", isValid); LOG.info("Response is valid XML!"); } catch (Exception ex) { fail(getDefaultErrorMessage(ex)); @@ -351,6 +325,9 @@ public class WebAPIServer_1_0_2 implements En { * validate JSON wrapper */ And("^the response is valid JSON$", () -> { + assertNotNull(getDefaultErrorMessage("JSON response data were not found in the test container! Please ensure your request succeeded.", + getTestContainer().getResponseData())); + try { assertTrue("ERROR: invalid JSON response!", TestUtils.isValidJson(getTestContainer().getResponseData())); LOG.info("Response is valid JSON!"); @@ -776,8 +753,12 @@ public class WebAPIServer_1_0_2 implements En { getTestContainer().getSelectList().forEach(fieldName -> { //need to skip the expand field when looking through the metadata if (getTestContainer().getExpandField() == null || !fieldName.contentEquals(getTestContainer().getExpandField())) { - assertNotNull("ERROR: Field name '" + fieldName + "' is not present in server metadata!", getTestContainer().getCsdlForFieldName(fieldName)); - LOG.info("Found: '" + fieldName.trim() + "'"); + try { + assertNotNull("ERROR: Field name '" + fieldName + "' is not present in server metadata!", getTestContainer().getCsdlForFieldName(fieldName)); + LOG.info("Found: '" + fieldName.trim() + "'"); + } catch (Exception ex) { + LOG.error(getDefaultErrorMessage(ex)); + } } }); } catch (Exception ex) { @@ -807,18 +788,19 @@ public class WebAPIServer_1_0_2 implements En { */ And("^XML Metadata are requested from the service root in \"([^\"]*)\"$", (String clientSettingsServiceRoot) -> { final String serviceRoot = Settings.resolveParametersString(clientSettingsServiceRoot, getTestContainer().getSettings()); - assertEquals("ERROR: given service root doesn't match the one configured in the Commander", serviceRoot, getTestContainer().getCommander().getServiceRoot()); + assertEquals(getDefaultErrorMessage("given service root doesn't match the one configured in the Commander"), + serviceRoot, + getTestContainer().getCommander().getServiceRoot()); - LOG.info("Requesting XML Metadata from: " + serviceRoot); try { - assertNotNull("ERROR: could not find valid XML Metadata for given service root: " + serviceRoot, getTestContainer().getXMLMetadata()); + assertNotNull(getDefaultErrorMessage("could not find valid XML Metadata for given service root:", serviceRoot), + getTestContainer().getXMLMetadata()); + } catch (ODataClientErrorException cex) { getTestContainer().setResponseCode(cex.getStatusLine().getStatusCode()); - fail(cex.toString()); - } catch (IllegalArgumentException aex) { - fail(getDefaultErrorMessage(aex.toString())); + fail(getDefaultErrorMessage(cex)); } catch (Exception ex) { - fail("ERROR: "+ ex.toString()); + fail(getDefaultErrorMessage(ex)); } }); @@ -932,8 +914,12 @@ public class WebAPIServer_1_0_2 implements En { AtomicBoolean found = new AtomicBoolean(false); requiredResources.forEach(requiredResource -> { - if (!found.get()) - found.set(found.get() || getTestContainer().getEdm().getEntityContainer().getEntitySet(requiredResource) != null); + try { + if (!found.get()) + found.set(found.get() || getTestContainer().getEdm().getEntityContainer().getEntitySet(requiredResource) != null); + } catch (Exception ex) { + fail(getDefaultErrorMessage(ex)); + } }); assertTrue("ERROR: could not find one of the following Standard Resource Names in the default entity container: " + requiredResourceString.replace(FIELD_SEPARATOR, PRETTY_FIELD_SEPARATOR), @@ -1031,12 +1017,29 @@ public class WebAPIServer_1_0_2 implements En { || getTestContainer().getServerODataHeaderVersion().contentEquals(val2) ); }); + /* + * Ensures valid metadata have been retrieved from the server + */ + Given("^valid metadata have been retrieved$", () -> { + if (getTestContainer().hasNotFetchedMetadata()) { + getTestContainer().setIsValidXMLMetadata(getTestContainer().getCommander().validateMetadata(getTestContainer().getXMLMetadata())); + getTestContainer().setIsXMLMetadataValidXML(Commander.validateXML(getTestContainer().getXMLResponseData())); + + if (getTestContainer().getIsValidXMLMetadata() && getTestContainer().getIsXMLMetadataValidXML()) { + getTestContainer().setIsValidEdm(getTestContainer().getCommander().validateMetadata(getTestContainer().getEdm())); + } + } + + assertTrue(getDefaultErrorMessage("Valid metadata could not be retrieved from the server! Please check the log for more information."), + getTestContainer().getIsMetadataValid()); + + }); } /* * Execute Get Request Wrapper */ - void prepareAndExecuteGetRequest(String requestId) { + void prepareAndExecuteRawGetRequest(String requestId) { try { //reset local state each time a get request is run getTestContainer().resetState(); @@ -1051,7 +1054,7 @@ public class WebAPIServer_1_0_2 implements En { LOG.info("Request URI: " + getTestContainer().getRequestUri().toString()); //execute request - getTestContainer().executePreparedGetRequest(); + getTestContainer().executePreparedRawGetRequest(); } catch (Exception ex) { LOG.debug("Exception was thrown in " + this.getClass() + ": " + ex.toString()); } @@ -1060,4 +1063,47 @@ public class WebAPIServer_1_0_2 implements En { static WebApiTestContainer getTestContainer() { return container.get(); } + + /** + * Contains background logic - make sure to update if the background changes + */ + final void runBackground() { + + /* + * Background + */ + Given("^a RESOScript file was provided$", () -> { + if (getTestContainer().getPathToRESOScript() == null) { + getTestContainer().setPathToRESOScript(System.getProperty("pathToRESOScript")); + } + assertNotNull("ERROR: pathToRESOScript must be present in command arguments, see README", getTestContainer().getPathToRESOScript()); + LOG.info("Using RESOScript: " + getTestContainer().getPathToRESOScript()); + }); + And("^Client Settings and Parameters were read from the file$", () -> { + if (getTestContainer().getSettings() == null) { + getTestContainer().setSettings(Settings.loadFromRESOScript(new File(System.getProperty("pathToRESOScript")))); + } + assertNotNull("ERROR: Settings could not be loaded.", getTestContainer().getSettings()); + LOG.info("RESOScript loaded successfully!"); + }); + + Given("^a test container was successfully created from the given RESOScript$", () -> { + getTestContainer().initialize(); + }); + + /* + * Ensures that the client either uses Authorization Codes or Client Credentials + */ + And("^the test container uses an authorization_code or client_credentials for authentication$", () -> { + assertNotNull(getTestContainer().getCommander()); + assertTrue("ERROR: Commander must either have a valid Authorization Code or Client Credentials configuration.", + getTestContainer().getCommander().isTokenClient() || (getTestContainer().getCommander().isOAuthClient() && getTestContainer().getCommander().hasValidAuthConfig())); + + if (getTestContainer().getCommander().isTokenClient()) { + LOG.info("Authentication Type: authorization_code"); + } else if (getTestContainer().getCommander().isOAuthClient()) { + LOG.info("Authentication Type: client_credentials"); + } + }); + } } diff --git a/src/main/java/org/reso/commander/Commander.java b/src/main/java/org/reso/commander/Commander.java index fd8bb55..481a6be 100644 --- a/src/main/java/org/reso/commander/Commander.java +++ b/src/main/java/org/reso/commander/Commander.java @@ -22,6 +22,7 @@ import org.apache.olingo.commons.api.edm.Edm; import org.apache.olingo.commons.api.format.ContentType; import org.reso.auth.OAuth2HttpClientFactory; import org.reso.auth.TokenHttpClientFactory; +import org.reso.commander.common.TestUtils; import org.xml.sax.*; import javax.xml.XMLConstants; @@ -79,17 +80,6 @@ public class Commander { //private constructor, should not be used. Use Builder instead. } - /** - * Validates XML for the given xmlMetadata item - * - * @param xmlMetadata the XML metadata to validate - * @return true if valid, false otherwise - * @throws ODataSerializerException if the given XML metadata could not be serialized - */ - public static boolean validateXML(XMLMetadata xmlMetadata) throws ODataSerializerException { - return validateXML(serializeXMLMetadataToXMLString(xmlMetadata)); - } - /** * Uses an XML validator to validate that the given string contains valid XML. * @@ -212,19 +202,6 @@ public class Commander { return null; } - /** - * Serializes XML Metadata to an XML string - * - * @param xmlMetadata the metadata to serialize - * @return a String containing the metadata - * @throws ODataSerializerException - */ - private static String serializeXMLMetadataToXMLString(XMLMetadata xmlMetadata) throws ODataSerializerException { - StringWriter writer = new StringWriter(); - client.getSerializer(ContentType.APPLICATION_XML).write(writer, xmlMetadata); - return writer.getBuffer().toString(); - } - /** * Metadata Pretty Printer * diff --git a/src/main/java/org/reso/commander/TestUtils.java b/src/main/java/org/reso/commander/TestUtils.java deleted file mode 100644 index 708f01b..0000000 --- a/src/main/java/org/reso/commander/TestUtils.java +++ /dev/null @@ -1,568 +0,0 @@ -package org.reso.commander; - -import com.fasterxml.jackson.databind.ObjectMapper; -import org.apache.http.Header; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.apache.olingo.client.api.communication.response.ODataRawResponse; -import org.apache.olingo.client.api.communication.response.ODataResponse; -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.provider.CsdlEntityContainer; -import org.apache.olingo.commons.api.edm.provider.CsdlNavigationProperty; -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.EdmDateTimeOffset; -import org.apache.olingo.commons.core.edm.primitivetype.EdmTimeOfDay; -import org.reso.models.Settings; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.net.URI; -import java.sql.Time; -import java.sql.Timestamp; -import java.time.LocalDate; -import java.time.OffsetDateTime; -import java.time.Year; -import java.util.*; -import java.util.concurrent.atomic.AtomicReference; - -import static io.restassured.path.json.JsonPath.from; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; - -public final class TestUtils { - public static final String JSON_VALUE_PATH = "value"; - public static final String HEADER_ODATA_VERSION = "OData-Version"; - private static final Logger LOG = LogManager.getLogger(TestUtils.class); - - /** - * Used to prepare URIs given that the input queryStrings can sometimes contain special characters - * that need to be ensured that they be processed. - * - * @param queryString the queryString to prepare - * @return the prepared URI with special characters in the queryString processed. - */ - public static URI prepareUri(final String queryString) { - /* replace spaces with %20 */ - return URI.create( - queryString.replace(" ", "%20") - /* add other handlers here */ - ); - } - - /** - * Finds the default entity container for the given configuration. - * - * @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. - */ - public static CsdlEntityContainer findDefaultEntityContainer(Edm edm, XMLMetadata xmlMetadata) { - assertNotNull("Edm Cannot be Null!", edm); - assertNotNull("XMLMetadata Cannot be Null!", xmlMetadata); - assertNotNull("ERROR: Could not find default EntityContainer for given service root!", edm.getEntityContainer()); - - String entityContainerNamespace = edm.getEntityContainer().getNamespace(); - assertNotNull("ERROR: no default EntityContainer namespace could be found", entityContainerNamespace); - - return xmlMetadata.getSchema(entityContainerNamespace).getEntityContainer(); - } - - /** - * Gets a list of CsdlProperty items for the given entityTypeName. - * - * @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. - */ - public static List findEntityTypesForEntityTypeName(Edm edm, XMLMetadata xmlMetadata, String entityTypeName) { - assertNotNull("ERROR: Edm Cannot be Null!", edm); - assertNotNull("ERROR: XMLMetadata Cannot be Null!", xmlMetadata); - assertNotNull("ERROR: entityTypeName cannot be null!", entityTypeName); - - CsdlEntityContainer entityContainer = findDefaultEntityContainer(edm, xmlMetadata); - assertNotNull("ERROR: could not find a default entity container for the given server!", entityContainer); - - CsdlSchema schemaForType = xmlMetadata.getSchema(entityContainer.getEntitySet(entityTypeName).getTypeFQN().getNamespace()); - - assertNotNull("ERROR: could not find type corresponding to given type name: " + entityTypeName, schemaForType); - - return schemaForType.getEntityType(entityTypeName).getProperties(); - } - - /** - * Gets a list of CsdlProperty items for the given entityTypeName. - * - * @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. - */ - public static List findNavigationPropertiesForEntityTypeName(Edm edm, XMLMetadata xmlMetadata, String entityTypeName) { - assertNotNull("ERROR: Edm Cannot be Null!", edm); - assertNotNull("ERROR: XMLMetadata Cannot be Null!", xmlMetadata); - assertNotNull("ERROR: entityTypeName cannot be null!", entityTypeName); - - CsdlEntityContainer entityContainer = findDefaultEntityContainer(edm, xmlMetadata); - assertNotNull("ERROR: could not find a default entity container for the given server!", entityContainer); - - CsdlSchema schemaForType = xmlMetadata.getSchema(entityContainer.getEntitySet(entityTypeName).getTypeFQN().getNamespace()); - - assertNotNull("ERROR: could not find type corresponding to given type name: " + entityTypeName, schemaForType); - - return schemaForType.getEntityType(entityTypeName).getNavigationProperties(); - } - - /** - * Returns true if each item in the list is true - * - * @param lhs Integer value - * @param op an OData binary operator for use for comparisons - * @param rhs Integer value - * @return true if lhs op rhs produces true, false otherwise - */ - public static boolean compare(Integer lhs, String op, Integer rhs) { - String operator = op.toLowerCase(); - boolean result = false; - - if (operator.contentEquals(Operators.GREATER_THAN)) { - result = lhs > rhs; - } else if (operator.contentEquals(Operators.GREATER_THAN_OR_EQUAL)) { - result = lhs >= rhs; - } else if (operator.contentEquals(Operators.EQ)) { - result = lhs.equals(rhs); - } else if (operator.contentEquals(Operators.NE)) { - result = !lhs.equals(rhs); - } else if (operator.contentEquals(Operators.LESS_THAN)) { - result = lhs < rhs; - } else if (operator.contentEquals(Operators.LESS_THAN_OR_EQUAL)) { - result = lhs <= rhs; - } - LOG.info("Compare: " + lhs + " " + operator + " " + rhs + " ==> " + result); - return result; - } - - /** - * Returns true if each item in the list is true - * - * @param lhs Integer value - * @param op an OData binary operator for use for comparisons - * @param rhs Integer value - * @return true if lhs op rhs produces true, false otherwise - */ - public static boolean compare(String lhs, String op, String rhs) { - String operator = op.toLowerCase(); - boolean result = false; - - if (operator.contentEquals(Operators.CONTAINS)) { - result = lhs.contains(rhs); - } else if (operator.contentEquals(Operators.STARTS_WITH)) { - result = lhs.startsWith(rhs); - } else if (operator.contentEquals(Operators.ENDS_WITH)) { - result = lhs.endsWith(rhs); - } else if (operator.contentEquals(Operators.TO_LOWER)) { - result = lhs.toLowerCase().equals(rhs); - } else if (operator.contentEquals(Operators.TO_UPPER)) { - result = lhs.toUpperCase().equals(rhs); - } - LOG.info("Compare: \"" + lhs + "\" " + operator + " \"" + rhs + "\" ==> " + result); - return result; - } - - /** - * Timestamp Comparator - * - * @param lhs Timestamp to compare - * @param op an OData binary operator to use for comparisons - * @param rhs Timestamp to compare - * @return true if lhs op rhs, false otherwise - */ - public static boolean compare(Timestamp lhs, String op, Timestamp rhs) { - String operator = op.toLowerCase(); - boolean result = false; - - if (operator.contentEquals(Operators.GREATER_THAN)) { - result = lhs.after(rhs); - } else if (operator.contentEquals(Operators.GREATER_THAN_OR_EQUAL)) { - result = lhs.after(rhs) || lhs.equals(rhs); - } else if (operator.contentEquals(Operators.EQ)) { - result = lhs.equals(rhs); - } else if (operator.contentEquals(Operators.NE)) { - result = !lhs.equals(rhs); - } else if (operator.contentEquals(Operators.LESS_THAN)) { - result = lhs.before(rhs); - } else if (operator.contentEquals(Operators.LESS_THAN_OR_EQUAL)) { - result = lhs.before(rhs) || lhs.equals(rhs); - } - LOG.info("Compare: " + lhs + " " + operator + " " + rhs + " ==> " + result); - return result; - } - - /** - * Year Comparator - * - * @param lhs Year to compare - * @param op an OData binary operator to use for comparisons - * @param rhs Timestamp to compare - * @return true if lhs op rhs, false otherwise - */ - public static boolean compare(Year lhs, String op, Year rhs) { - String operator = op.toLowerCase(); - boolean result = false; - - if (operator.contentEquals(Operators.GREATER_THAN)) { - result = lhs.isAfter(rhs); - } else if (operator.contentEquals(Operators.GREATER_THAN_OR_EQUAL)) { - result = lhs.isAfter(rhs) || lhs.equals(rhs); - } else if (operator.contentEquals(Operators.EQ)) { - result = lhs.equals(rhs); - } else if (operator.contentEquals(Operators.NE)) { - result = !lhs.equals(rhs); - } else if (operator.contentEquals(Operators.LESS_THAN)) { - result = lhs.isBefore(rhs); - } else if (operator.contentEquals(Operators.LESS_THAN_OR_EQUAL)) { - result = lhs.isBefore(rhs) || lhs.equals(rhs); - } - LOG.info("Compare: " + lhs + " " + operator + " " + rhs + " ==> " + result); - return result; - } - - /** - * Time Comparator - * - * @param lhs Time to compare - * @param op an OData binary operator to use for comparisons - * @param rhs Time to compare - * @return true if lhs op rhs, false otherwise - */ - public static boolean compare(Time lhs, String op, Time rhs) { - String operator = op.toLowerCase(); - boolean result = false; - - if (operator.contentEquals(Operators.GREATER_THAN)) { - result = lhs.toLocalTime().isAfter(rhs.toLocalTime()); - } else if (operator.contentEquals(Operators.GREATER_THAN_OR_EQUAL)) { - result = lhs.toLocalTime().isAfter(rhs.toLocalTime()) || lhs.toLocalTime().equals(rhs.toLocalTime()); - } else if (operator.contentEquals(Operators.EQ)) { - result = lhs.toLocalTime().equals(rhs.toLocalTime()); - } else if (operator.contentEquals(Operators.NE)) { - result = !lhs.toLocalTime().equals(rhs.toLocalTime()); - } else if (operator.contentEquals(Operators.LESS_THAN)) { - result = lhs.toLocalTime().isBefore(rhs.toLocalTime()); - } else if (operator.contentEquals(Operators.LESS_THAN_OR_EQUAL)) { - result = lhs.toLocalTime().isBefore(rhs.toLocalTime()) || lhs.toLocalTime().equals(rhs.toLocalTime()); - } - LOG.info("Compare: " + lhs + " " + operator + " " + rhs + " ==> " + result); - return result; - } - - /** - * Date Comparator - * - * @param lhs Date to compare - * @param op an OData binary operator to use for comparisons - * @param rhs Date to compare - * @return true if lhs op rhs, false otherwise - */ - public static boolean compare(Date lhs, String op, Date rhs) { - String operator = op.toLowerCase(); - boolean result = false; - - if (operator.contentEquals(Operators.GREATER_THAN)) { - result = lhs.after(rhs); - } else if (operator.contentEquals(Operators.GREATER_THAN_OR_EQUAL)) { - result = lhs.after(rhs) || lhs.equals(rhs); - } else if (operator.contentEquals(Operators.EQ)) { - result = lhs.equals(rhs); - } else if (operator.contentEquals(Operators.NE)) { - result = !lhs.equals(rhs); - } else if (operator.contentEquals(Operators.LESS_THAN)) { - result = lhs.before(rhs); - } else if (operator.contentEquals(Operators.LESS_THAN_OR_EQUAL)) { - result = lhs.before(rhs) || lhs.equals(rhs); - } - LOG.info("Compare: " + lhs + " " + operator + " " + rhs + " ==> " + result); - return result; - } - - - /** - * Tests the given string to see if it's valid JSON - * - * @param jsonString the JSON string to test the validity of - * @return true if valid, false otherwise. Throws {@link IOException} - */ - public static boolean isValidJson(String jsonString) { - try { - final ObjectMapper mapper = new ObjectMapper(); - mapper.readTree(jsonString); - return true; - } catch (IOException e) { - return false; - } - } - - /** - * Returns the String data contained within a given ODataRawResponse. - * - * @param oDataRawResponse the response to convert. - * @return the response stream as a string. - */ - public static String getResponseData(ODataRawResponse oDataRawResponse) { - return convertInputStreamToString(oDataRawResponse.getRawResponse()); - } - - /** - * Helper method to find headers with a given key in an an array of headers - * @param key the header to get - * @param headers an array containing Header objects - * @return the value of the header with key, or null - */ - public static String getHeaderData(String key, Collection
headers) { - String data = null; - - for (Header header : headers) { - if (header.getName().toLowerCase().contains(key.toLowerCase())) { - data = header.getValue(); - } - } - return data; - } - - /** - * Helper method to unpack headers from a raw OData response - * @param key the header to get - * @param oDataResponse the OData raw response from the request - * @return the value of the header with key, or null - */ - public static String getHeaderData(String key, ODataResponse oDataResponse) { - if (key == null || oDataResponse.getHeader(key) == null) return null; - ArrayList result = new ArrayList<>(oDataResponse.getHeader(key)); - - if (result.size() > 0) { - return result.get(0); - } else { - return null; - } - - } - - /** - * Parses the given edmDateTimeOffsetString into a Java Instant (the type expected by the Olingo type converter). - * - * @param edmDateTimeOffsetString string representation of an Edm DateTimeOffset to parse. - * @return the corresponding Instant value. - * @throws EdmPrimitiveTypeException thrown if given value cannot be parsed. - */ - public static Timestamp parseTimestampFromEdmDateTimeOffsetString(String edmDateTimeOffsetString) throws EdmPrimitiveTypeException { - return EdmDateTimeOffset.getInstance().valueOfString(edmDateTimeOffsetString, true, null, null, null, null, Timestamp.class); - } - - /** - * Parses the given edmDateString into a Java Timestamp. - * - * @param edmDateString the date string to convert. - * @return the corresponding Timestamp value. - * @throws EdmPrimitiveTypeException thrown if given value cannot be parsed. - */ - public static Timestamp parseTimestampFromEdmDateString(String edmDateString) throws EdmPrimitiveTypeException { - return EdmDate.getInstance().valueOfString(edmDateString, true, null, null, null, null, Timestamp.class); - } - - /** - * Parses the given edmDateString into a Java Time. - * - * @param edmTimeOfDayOffsetString the date string to convert. - * @return the corresponding Time value. - * @throws EdmPrimitiveTypeException thrown if given value cannot be parsed. - */ - public static Time parseTimeOfDayFromEdmTimeOfDayString(String edmTimeOfDayOffsetString) throws EdmPrimitiveTypeException { - return EdmTimeOfDay.getInstance().valueOfString(edmTimeOfDayOffsetString, true, null, null, null, null, Time.class); - } - - /** - * Parses the given DateTimeOffsetString into a Java Time. - * - * @param edmDateTimeOffsetString the DateTimeOffsetString to parse. - * @return the corresponding Time value. - * @throws EdmPrimitiveTypeException thrown if given value cannot be parsed. - */ - public static Time parseTimeOfDayFromEdmDateTimeOffsetString(String edmDateTimeOffsetString) throws EdmPrimitiveTypeException { - return EdmDateTimeOffset.getInstance().valueOfString(edmDateTimeOffsetString, true, null, null, null, null, Time.class); - } - - /** - * Parses the given edmDateString into a Java Date - * - * @param edmDateString the date string to convert. - * @return the corresponding Date value. - * @throws EdmPrimitiveTypeException thrown if given value cannot be parsed. - */ - public static Date parseDateFromEdmDateString(String edmDateString) throws EdmPrimitiveTypeException { - return EdmDate.getInstance().valueOfString(edmDateString, true, null, null, null, null, Date.class); - } - - /** - * Parses the given edmDateTimeOffsetString into a Java Date - * - * @param edmDateTimeOffsetString string representation of an Edm DateTimeOffset to parse. - * @return the corresponding Date value. - * @throws EdmPrimitiveTypeException thrown if given value cannot be parsed. - */ - public static Date parseDateFromEdmDateTimeOffsetString(String edmDateTimeOffsetString) throws EdmPrimitiveTypeException { - return EdmDateTimeOffset.getInstance().valueOfString(edmDateTimeOffsetString, true, null, null, null, null, Date.class); - } - - /*** - * Tries to parse datePart from the given Object value - * @param datePart the timestamp part, "Year", "Month", "Day", etc. to try and parse - * @param value the value to try and parse - * @return the Integer portion of the date if successful, otherwise throws an Exception - * @exception throws an exception if value cannot be parsed into a date. - */ - public static Integer getDatePart(String datePart, Object value) throws EdmPrimitiveTypeException { - LocalDate date = LocalDate.parse(parseDateFromEdmDateString(value.toString()).toString()); - switch (datePart) { - case DateParts.YEAR: - return date.getYear(); - case DateParts.MONTH: - return date.getMonthValue(); - case DateParts.DAY: - return date.getDayOfMonth(); - default: - return null; - } - } - - /*** - * Tries to parse datePart from the given Object value - * @param timestampPart the timestamp part, "Year", "Month", "Day", etc. to try and parse - * @param value the value to try and parse - * @return the Integer portion of the date if successful, otherwise throws an Exception - */ - public static Integer getTimestampPart(String timestampPart, Object value) throws EdmPrimitiveTypeException { - //Turns nanoseconds into two most significant 2 digits for fractional comparisons - Integer ADJUSTMENT_FACTOR = 10000000; - OffsetDateTime offsetDateTime = OffsetDateTime.parse(value.toString()); - - if (timestampPart.equals(DateParts.YEAR)) { - return offsetDateTime.getYear(); - } else if (timestampPart.equals(DateParts.MONTH)) { - return offsetDateTime.getMonthValue(); - } else if (timestampPart.equals(DateParts.DAY)) { - return offsetDateTime.getDayOfMonth(); - } else if (timestampPart.equals(DateParts.HOUR)) { - return offsetDateTime.getHour(); - } else if (timestampPart.equals(DateParts.MINUTE)) { - return offsetDateTime.getMinute(); - } else if (timestampPart.equals(DateParts.SECOND)) { - return offsetDateTime.getSecond(); - } else if (timestampPart.equals(DateParts.FRACTIONAL)) { - return offsetDateTime.getNano() / ADJUSTMENT_FACTOR; - } else { - return null; - } - } - - /** - * Converts the given inputStream to a string. - * - * @param inputStream the input stream to convert. - * @return the string value contained in the stream. - */ - public static String convertInputStreamToString(InputStream inputStream) { - InputStreamReader isReader = new InputStreamReader(inputStream); - BufferedReader reader = new BufferedReader(isReader); - StringBuilder sb = new StringBuilder(); - String str; - try { - while ((str = reader.readLine()) != null) { - sb.append(str); - } - return sb.toString(); - } catch (Exception ex) { - LOG.error("Error in convertInputStreamToString: " + ex); - } - return null; - } - - /** - * For each parameterFieldName value in responseData, assertTrue(value op timestamp) - * - * @param parameterFieldName the field name containing data - * @param op an OData binary operator to be used for comparsions - * @param parameterAssertedValue value to be used for comparisons - * @param responseData string containing JSON response data - */ - public static void assertDateTimeOffset(String parameterFieldName, String op, String parameterAssertedValue, String responseData, Settings settings) { - AtomicReference assertedValue = new AtomicReference<>(); - try { - assertedValue.set(parseTimestampFromEdmDateTimeOffsetString(Settings.resolveParametersString(parameterAssertedValue, settings))); - assertDateTimeOffset(parameterFieldName, op, assertedValue.get(), responseData, settings); - } catch (EdmPrimitiveTypeException tex) { - LOG.error("ERROR: Cannot Convert the value in " - + Settings.resolveParametersString(parameterAssertedValue, settings) + " to a Timestamp value!!" + tex); - } - } - - /** - * For each parameterFieldName value in responseData, assertTrue(value op timestamp) - * - * @param parameterFieldName the field name containing data - * @param op an OData binary operator to be used for comparsions - * @param timestamp value to be used for comparisons - * @param responseData string containing JSON response data - */ - public static void assertDateTimeOffset(String parameterFieldName, String op, Timestamp timestamp, String responseData, Settings settings) { - LOG.info("Asserted time is: " + timestamp); - String fieldName = Settings.resolveParametersString(parameterFieldName, settings); - AtomicReference fieldValue = new AtomicReference<>(); - - from(responseData).getList(JSON_VALUE_PATH, HashMap.class).forEach(item -> { - try { - fieldValue.set(parseTimestampFromEdmDateTimeOffsetString(item.get(fieldName).toString())); - assertTrue(compare(fieldValue.get(), op, timestamp)); - } catch (EdmPrimitiveTypeException tex) { - LOG.error("ERROR: Cannot Convert the value in " + fieldValue.get() + " to a Timestamp value!!" + tex); - } - }); - } - - /** - * Contains the list of supported operators for use in query expressions. - */ - public static class Operators { - public static final String - AND = "and", - OR = "or", - NE = "ne", - EQ = "eq", - GREATER_THAN = "gt", - GREATER_THAN_OR_EQUAL = "ge", - LESS_THAN = "lt", - LESS_THAN_OR_EQUAL = "le", - CONTAINS = "contains", - ENDS_WITH = "endswith", - STARTS_WITH = "startswith", - TO_LOWER = "tolower", - TO_UPPER = "toupper"; - } - - public static class DateParts { - public static final String - YEAR = "year", - MONTH = "month", - DAY = "day", - HOUR = "hour", - MINUTE = "minute", - SECOND = "second", - FRACTIONAL = "fractional"; - } -} - 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 656f183..1622894 100644 --- a/src/main/java/org/reso/commander/certfication/containers/WebApiTestContainer.java +++ b/src/main/java/org/reso/commander/certfication/containers/WebApiTestContainer.java @@ -16,7 +16,7 @@ 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; +import org.reso.commander.common.TestUtils; import org.reso.models.ClientSettings; import org.reso.models.Parameters; import org.reso.models.Request; @@ -26,60 +26,25 @@ import java.io.ByteArrayInputStream; import java.net.URI; import java.nio.charset.StandardCharsets; import java.util.*; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; import static org.junit.Assert.*; import static org.reso.commander.Commander.AMPERSAND; import static org.reso.commander.Commander.EQUALS; -import static org.reso.commander.TestUtils.HEADER_ODATA_VERSION; -import static org.reso.common.ErrorMsg.getDefaultErrorMessage; +import static org.reso.commander.common.ErrorMsg.getDefaultErrorMessage; +import static org.reso.commander.common.TestUtils.HEADER_ODATA_VERSION; /** * Encapsulates Commander Requests and Responses during runtime */ 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; - - public Map getFieldMap() { - if (fieldMap.get() == null) { - fieldMap.set(new HashMap<>()); - - LOG.info("Building Field Map...this may take a moment depending on size of metadata and connection speed."); - //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)); - assertTrue("ERROR: No field were found in the server's metadata!", fieldMap.get().size() > 0); - LOG.info("Metadata Field Map created!"); - } - return fieldMap.get(); - } - - public String getXMLResponseData() { - return xmlResponseData.get(); - } - - public static final class ODATA_QUERY_PARAMS { - private static String format = DOLLAR_SIGN + "%s"; - - //TODO: add additional items as needed, and see if there's a lib for this in Olingo - public static final String - COUNT = String.format(format, QueryOption.COUNT), - EXPAND = String.format(format, QueryOption.EXPAND), - FILTER = String.format(format, QueryOption.FILTER), - ORDERBY = String.format(format, QueryOption.ORDERBY), - SELECT = String.format(format, QueryOption.SELECT), - SEARCH = String.format(format, QueryOption.SEARCH), - SKIP = String.format(format, QueryOption.SKIP), - TOP = String.format(format, QueryOption.TOP); - } - + private static final Logger LOG = LogManager.getLogger(WebApiTestContainer.class); private AtomicReference commander = new AtomicReference<>(); private AtomicReference xmlMetadata = new AtomicReference<>(); private AtomicReference edm = new AtomicReference<>(); @@ -96,6 +61,12 @@ public final class WebApiTestContainer implements TestContainer { private AtomicReference> fieldMap = new AtomicReference<>(); private AtomicReference xmlResponseData = new AtomicReference<>(); + // Metadata state variables + private AtomicBoolean isValidXMLMetadata = new AtomicBoolean(false); + private AtomicBoolean isValidEdm = new AtomicBoolean(false); + private AtomicBoolean isXMLMetadataValidXML = new AtomicBoolean(false); + private AtomicBoolean hasXMLMetadataBeenRequested = new AtomicBoolean(false); + private AtomicBoolean hasEdmBeenRequested = new AtomicBoolean(false); // request instance variables - these get reset with every request private AtomicReference selectList = new AtomicReference<>(); @@ -114,6 +85,25 @@ public final class WebApiTestContainer implements TestContainer { private AtomicReference> clientEntitySetResponse = new AtomicReference<>(); private AtomicReference clientEntitySet = new AtomicReference<>(); + public Map getFieldMap() throws Exception { + if (fieldMap.get() == null) { + fieldMap.set(new HashMap<>()); + + LOG.info("Building Field Map...this may take a moment depending on size of metadata and connection speed."); + //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)); + assertTrue("ERROR: No field were found in the server's metadata!", fieldMap.get().size() > 0); + LOG.info("Metadata Field Map created!"); + } + return fieldMap.get(); + } + + public String getXMLResponseData() { + return xmlResponseData.get(); + } + /** * Resets the state of the test container */ @@ -188,7 +178,7 @@ public final class WebApiTestContainer implements TestContainer { * Executes HTTP GET request and sets the expected local variables in the WebApiTestContainer * Handles exceptions and sets response codes as well. */ - public void executePreparedGetRequest() { + public void executePreparedRawGetRequest() throws Exception { try { setRawRequest(getCommander().getClient().getRetrieveRequestFactory().getRawRequest(getRequestUri())); getRawRequest().setFormat(ContentType.JSON.toContentTypeString()); @@ -197,21 +187,8 @@ public final class WebApiTestContainer implements TestContainer { setServerODataHeaderVersion(TestUtils.getHeaderData(HEADER_ODATA_VERSION, getODataRawResponse())); setResponseCode(getODataRawResponse().getStatusCode()); LOG.info("Request succeeded..." + getResponseData().getBytes().length + " bytes received."); - } catch (ODataClientErrorException cex) { - LOG.debug("ODataClientErrorException caught. Check tests for asserted conditions..."); - LOG.debug(cex); - setODataClientErrorException(cex); - setServerODataHeaderVersion(TestUtils.getHeaderData(HEADER_ODATA_VERSION, Arrays.asList(cex.getHeaderInfo()))); - setResponseCode(cex.getStatusLine().getStatusCode()); - } catch (ODataServerErrorException ode) { - LOG.debug("ODataServerErrorException thrown in executeGetRequest. Check tests for asserted conditions..."); - //TODO: look for better ways to do this in Olingo or open PR - if (ode.getMessage().contains(Integer.toString(HttpStatus.SC_NOT_IMPLEMENTED))) { - setResponseCode(HttpStatus.SC_NOT_IMPLEMENTED); - } - setODataServerErrorException(ode); } catch (Exception ex) { - fail("ERROR: unhandled Exception in executeGetRequest()!\n" + ex.toString()); + processODataRequestException(ex); } } @@ -221,7 +198,7 @@ public final class WebApiTestContainer implements TestContainer { * @param fieldName the name of the field to retrieve metadata about * @return the metadata for the given field */ - public CsdlProperty getCsdlForFieldName(String fieldName) { + public CsdlProperty getCsdlForFieldName(String fieldName) throws Exception { return getFieldMap().get(fieldName); } @@ -230,22 +207,26 @@ public final class WebApiTestContainer implements TestContainer { * * @return gets the local collection of Csdl Properties */ - public Collection getCsdlProperties() { + public Collection getCsdlProperties() throws Exception { return getFieldMap().values(); } + /** + * Parses an OData $select list + * @return the de-duplicated set of select list items + */ public Collection getSelectList() { Arrays.stream(getRequestUri().getQuery().split(AMPERSAND)).forEach(fragment -> { if (fragment.contains(QueryOption.SELECT.toString())) { selectList.set(fragment.replace(ODATA_QUERY_PARAMS.SELECT, EMPTY_STRING).replace(EQUALS, EMPTY_STRING)); } }); - - return new ArrayList<>(Arrays.asList(selectList.get().split(FIELD_SEPARATOR))); + return new LinkedHashSet<>((Arrays.asList(selectList.get().split(FIELD_SEPARATOR)))); } /** * Settings getter + * * @return local settings instance */ public Settings getSettings() { @@ -254,6 +235,7 @@ public final class WebApiTestContainer implements TestContainer { /** * Settings setter + * * @param settings sets local settings instance to the given settings */ public void setSettings(Settings settings) { @@ -262,6 +244,7 @@ public final class WebApiTestContainer implements TestContainer { /** * Gets the Expand field from the RESOScript + * * @return the configured Expand field */ public String getExpandField() { @@ -274,19 +257,26 @@ public final class WebApiTestContainer implements TestContainer { * @return * @implNote the data in this item are cached in the test container once fetched */ - public Edm getEdm() { + public Edm getEdm() throws Exception { if (edm.get() == null) { - ODataRetrieveResponse response = getCommander().prepareEdmMetadataRequest().execute(); - responseCode.set(response.getStatusCode()); - setServerODataHeaderVersion(TestUtils.getHeaderData(HEADER_ODATA_VERSION, response)); - edm.set(response.getBody()); + try { + LOG.info("Requesting the entity data model (Edm) from service root at: " + getServiceRoot()); + ODataRetrieveResponse response = getCommander().prepareEdmMetadataRequest().execute(); + responseCode.set(response.getStatusCode()); + setServerODataHeaderVersion(TestUtils.getHeaderData(HEADER_ODATA_VERSION, response)); + edm.set(response.getBody()); + } catch(Exception ex){ + processODataRequestException(ex); + } finally { + hasEdmBeenRequested.set(true); + } } return edm.get(); } /** * Gets server metadata in XMLMetadata format. - * + *

* Note: this method takes a slightly different approach than getting XML Metadata did previously in that * rather than fetching the metadata directly from the server using the Olingo getXmlMetadata method, * we make a raw request instead so that we can capture the response string for XML validation, and @@ -295,7 +285,7 @@ public final class WebApiTestContainer implements TestContainer { * @return XMLMetadata representation of the server metadata. * @implNote the data in this item are cached in the test container once fetched */ - public XMLMetadata getXMLMetadata() { + public XMLMetadata getXMLMetadata() throws Exception { if (xmlMetadata.get() == null) { try { String requestUri = Settings.resolveParameters(getSettings().getRequest(Request.WELL_KNOWN.METADATA_ENDPOINT), getSettings()).getUrl(); @@ -304,20 +294,22 @@ public final class WebApiTestContainer implements TestContainer { ODataRawRequest request = getCommander().getClient().getRetrieveRequestFactory().getRawRequest(URI.create(requestUri)); request.setFormat(ContentType.JSON.toContentTypeString()); + LOG.info("Requesting XML Metadata from service root at: " + getServiceRoot()); ODataRawResponse response = request.execute(); + responseCode.set(response.getStatusCode()); + setServerODataHeaderVersion(TestUtils.getHeaderData(HEADER_ODATA_VERSION, response)); + xmlResponseData.set(TestUtils.convertInputStreamToString(response.getRawResponse())); //deserialize response into XML Metadata - will throw an exception if metadata are in valid XMLMetadata metadata = getCommander().getClient().getDeserializer(ContentType.APPLICATION_XML) .toMetadata(new ByteArrayInputStream(xmlResponseData.get().getBytes(StandardCharsets.UTF_8))); - responseCode.set(response.getStatusCode()); - setServerODataHeaderVersion(TestUtils.getHeaderData(HEADER_ODATA_VERSION, response)); - xmlMetadata.set(metadata); - } catch (Exception ex) { - getDefaultErrorMessage(ex); + processODataRequestException(ex); + } finally { + hasXMLMetadataBeenRequested.set(true); } } return xmlMetadata.get(); @@ -518,4 +510,82 @@ public final class WebApiTestContainer implements TestContainer { public void setPathToRESOScript(String pathToRESOScript) { this.pathToRESOScript.set(pathToRESOScript); } + + private void processODataRequestException(Exception exception, boolean bubble) throws Exception { + if (exception instanceof ODataClientErrorException) processODataRequestException((ODataClientErrorException)exception); + else if (exception instanceof ODataServerErrorException) processODataRequestException(((ODataServerErrorException)exception)); + else LOG.error(getDefaultErrorMessage(exception)); + + if (bubble) throw exception; + } + + private void processODataRequestException(Exception exception) throws Exception { + processODataRequestException(exception, true); + } + + private void processODataRequestException(ODataClientErrorException exception) { + LOG.debug("ODataClientErrorException caught. Check tests for asserted conditions..."); + LOG.debug(exception); + setODataClientErrorException(exception); + setServerODataHeaderVersion(TestUtils.getHeaderData(HEADER_ODATA_VERSION, Arrays.asList(exception.getHeaderInfo()))); + setResponseCode(exception.getStatusLine().getStatusCode()); + } + + private void processODataRequestException(ODataServerErrorException exception) { + LOG.debug("ODataServerErrorException thrown in executeGetRequest. Check tests for asserted conditions..."); + //TODO: look for better ways to do this in Olingo or open PR + if (exception.getMessage().contains(Integer.toString(HttpStatus.SC_NOT_IMPLEMENTED))) { + setResponseCode(HttpStatus.SC_NOT_IMPLEMENTED); + } + setODataServerErrorException(exception); + } + + public boolean getIsMetadataValid() { + return xmlMetadata.get() != null && getIsValidXMLMetadata() + && xmlResponseData.get() != null && getIsXMLMetadataValidXML() + && edm.get() != null && getIsValidEdm(); + } + + public boolean getIsValidXMLMetadata() { + return isValidXMLMetadata.get(); + } + + public void setIsValidXMLMetadata(boolean isValid) { + isValidXMLMetadata.set(isValid); + } + + public boolean getIsValidEdm() { + return isValidEdm.get(); + } + + public void setIsValidEdm(boolean isValid) { + isValidEdm.set(isValid); + } + + public boolean getIsXMLMetadataValidXML() { + return isXMLMetadataValidXML.get(); + } + + public void setIsXMLMetadataValidXML(boolean isValid) { + isXMLMetadataValidXML.set(isValid); + } + + public boolean hasNotFetchedMetadata() { + return !hasXMLMetadataBeenRequested.get() && !hasEdmBeenRequested.get(); + } + + public static final class ODATA_QUERY_PARAMS { + private static String format = DOLLAR_SIGN + "%s"; + + //TODO: add additional items as needed, and see if there's a lib for this in Olingo + public static final String + COUNT = String.format(format, QueryOption.COUNT), + EXPAND = String.format(format, QueryOption.EXPAND), + FILTER = String.format(format, QueryOption.FILTER), + ORDERBY = String.format(format, QueryOption.ORDERBY), + SELECT = String.format(format, QueryOption.SELECT), + SEARCH = String.format(format, QueryOption.SEARCH), + SKIP = String.format(format, QueryOption.SKIP), + TOP = String.format(format, QueryOption.TOP); + } } \ No newline at end of file diff --git a/src/main/java/org/reso/common/ErrorMsg.java b/src/main/java/org/reso/commander/common/ErrorMsg.java similarity index 87% rename from src/main/java/org/reso/common/ErrorMsg.java rename to src/main/java/org/reso/commander/common/ErrorMsg.java index 0f6171d..7ddce7b 100644 --- a/src/main/java/org/reso/common/ErrorMsg.java +++ b/src/main/java/org/reso/commander/common/ErrorMsg.java @@ -1,6 +1,6 @@ -package org.reso.common; +package org.reso.commander.common; -import static org.reso.commander.certfication.containers.WebApiTestContainer.EMPTY_STRING; +import static org.reso.commander.certfication.containers.WebApiTestContainer.SINGLE_SPACE; public final class ErrorMsg { private static final String ERROR_MESSAGE_TEMPLATE = "ERROR: %s"; @@ -12,7 +12,7 @@ public final class ErrorMsg { * @return a string containing the default error message for display */ public static String getDefaultErrorMessage(final String... msgs) { - return String.format(ERROR_MESSAGE_TEMPLATE, String.join(EMPTY_STRING, msgs)); + return String.format(ERROR_MESSAGE_TEMPLATE, String.join(SINGLE_SPACE, msgs)); } /** @@ -21,7 +21,7 @@ public final class ErrorMsg { * @return formatted error message using the default format */ public static String getDefaultErrorMessage(final Exception ex) { - return String.format(ex.toString()); + return getDefaultErrorMessage(ex.toString()); } /**