We are running a Dotnet Core 2.2 service using ServiceStack 5.7, and need to throttle it. So we want to put it behind a Azure Api Management Gateway (apim) - it runs in a Azure App Service.
We have enabled OpenApi feature using
self.Plugins.Add(new OpenApiFeature());
When we export our OpenApi definition we get the following:
"paths": {
...
"/api/search": {
"post": {
"tags": [
"api"
],
"operationId": "SearchRequestsearch_Post",
"consumes": [
"application/x-www-form-urlencoded"
],
"produces": [
"application/json"
],
"parameters": [
{
"name": "Filters",
"in": "formData",
"type": "array",
"items": {
"$ref": "#/definitions/FilterDto"
},
"collectionFormat": "multi",
"required": false
}
],
"responses": {
"200": {
"description": "Success",
"schema": {
"$ref": "#/definitions/SearchResponse"
}
}
},
"deprecated": false,
"security": [
{
"Bearer": []
}
]
},
"parameters": [
{
"$ref": "#/parameters/Accept"
}
]
}
}
...
"definitions": {
"FilterDto": {
"title": "FilterDto",
"properties": {
"Field": {
"description": "The field to filter on",
"type": "string",
"enum": [
"None",
"DestinationName",
"DocumentId"
]
},
"Values": {
type": "array",
"items": {
"type": "string"
}
},
"Type": {
"type": "string",
"enum": [
"Equals",
"NotEquals",
"RangeNumeric",
"RangeDate"
]
}
},
"description": "FilterDto",
"type": "object"
}
...
}
The problem is that it is not supported to have a parameter with an array of a type (defined in #/definitions/FilterDto). And it fails with:
Parsing error(s): JSON is valid against no schemas from 'oneOf'. Path 'paths['/api/search'].post.parameters[1]', line 1, position 666.
Parsing error(s): The input OpenAPI file is not valid for the OpenAPI specificate https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md (schema https://github.com/OAI/OpenAPI-Specification/blob/master/schemas/v2.0/schema.json).
In the Azure portal.
In c# (ServiceStack) we have defined the following:
public class SearchRequest : SearchRequestBase, IReturn<SearchResponse>
{
public SearchRequest()
{
Filters = new List<FilterDto>();
}
[ApiMember(Name = "Filters"]
public List<FilterDto> Filters { get; set; }
}
public class FilterDto
{
[ApiMember(Name = "Field"]
[ApiAllowableValues("Field", typeof(FilterAndFacetField))]
public FilterAndFacetField Field { get; set; }
[ApiMember(Name = "Values")]
public List<string> Values { get; set; }
[ApiMember(Name = "Type")]
[ApiAllowableValues("Type", typeof(FilterType))]
public FilterType Type { get; set; }
public FilterDto()
{
Values = new List<string>();
}
}
Have anyone successfully managed to import a OpenApi using array of $ref in the parameters from ServiceStack into a Api Management?
Related
I am trying to validate on the provider side but getting error -
Verifying a pact between DataConsumer and DataProvider
[Using File pact/DataConsumer-DataProvider.json]
Given some state
a request for json data
Request Failed - For input string: "\null"
Not sure what did I miss here.
My Pojo -
#EqualsAndHashCode
#RequiredArgsConstructor
#Builder(toBuilder = true)
#JsonDeserialize(builder = DataModel.DataModelBuilder.class)
public class DataModel {
#JsonProperty("name")
private final String name;
#JsonProperty("price")
private final double price;
}
Pact -
{
"provider": {
"name": "DataProvider"
},
"consumer": {
"name": "DataConsumer"
},
"interactions": [
{
"description": "a request for json data",
"request": {
"method": "GET",
"path": "/get/ice/2.0"
},
"response": {
"status": 200,
"headers": {
"Content-Type": "application/json; charset\u003dUTF-8"
},
"body": {
"price": 10,
"name": "some name"
},
"matchingRules": {
"header": {
"Content-Type": {
"matchers": [
{
"match": "regex",
"regex": "application/json(;\\s?charset\u003d[\\w\\-]+)?"
}
],
"combine": "AND"
}
}
},
"generators": {
"body": {
"$.name": {
"type": "ProviderState",
"expression": "\\${name}",
"dataType": "STRING"
},
"$.price": {
"type": "ProviderState",
"expression": "\\${price}",
"dataType": "FLOAT"
}
}
}
},
"providerStates": [
{
"name": "some state"
}
]
}
],
"metadata": {
"pactSpecification": {
"version": "3.0.0"
},
"pact-jvm": {
"version": "3.6.15"
}
}
}
Test -
#ExtendWith(SpringExtension.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
#Provider("DataProvider")
#PactFolder(value = "pact")
public class ContractVerificationTest {
#TestTemplate
#ExtendWith(PactVerificationSpringProvider.class)
void pactVerificationTestTemplate(PactVerificationContext context) {
context.verifyInteraction();
}
#State("some state")
void testPact() {
}
}
Code -
https://github.com/nrworld4/pact-consumer-demo
https://github.com/nrworld4/pact-demo-provider
You aren't returning the values (name, price) from your provider state annotation in your provider test (it's currently doing nothing) so when Pact tries to replace the values dynamically in the request they are null.
Do you actually need them to be generated by the provider in the first place?
See
https://pactflow.io/blog/injecting-values-from-provider-states/ for a detailed example of how to use and fix.
Update
Could it be that you're double escaping the parameters?
In the example:
.queryParameterFromProviderState("accountNumber", "\${accountNumber}", "100")
In your code:
.valueFromProviderState("price", "\\${price}", 10.0)
I'm using PACT JVM https://github.com/DiUS/pact-jvm/tree/master/provider/pact-jvm-provider-junit
I don't know why the matching rules in my contact are being ignored.
My HTTP test
#RunWith(SpringRestPactRunner.class) // Say JUnit to run tests with custom Runner
#Provider("provDataTest")// Set up name of tested provider
#PactBroker(host="pact-broker.services", port = "80")
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT,
properties = {"spring.profiles.active=dev"},
classes = Application.class)
public class ContractTesting {
private static String token;
#BeforeClass //Method will be run once: before whole contract test suite
public static void setUpService() {
//Run DB, create schema
//Run service
//...
System.out.println("Now service in default state");
}
#TargetRequestFilter
public void exampleRequestFilter(HttpRequest request) {
}
#State("Check correct data json structure in case code: 401")
public void checkDataInCaseUnauthorized() {
}
#TestTarget // Annotation denotes Target that will be used for tests
public final Target target = new HttpTarget(8082);
And my Contract file
{
"provider": {
"name": "provDataTest"
},
"consumer": {
"name": "test"
},
"interactions": [
{
"description": "Read all links_401",
"request": {
"method": "GET",
"path": "/v1/consumer/me/link_social_account",
"headers": {
"invalidAuth": "401"
}
},
"response": {
"status": 401,
"headers": {
"Content-Type": "text/json;charset=utf-8"
},
"body": {
"error": {
"code": 401,
"message": "session incorrect",
"errors": []
}
},
"matchingRules": {
"body": {
"$.error": {
"matchers": [
{
"match": "type"
}
],
"combine": "AND"
},
"$.error.code": {
"matchers": [
{
"match": "integer"
}
],
"combine": "AND"
},
"$.error.message": {
"matchers": [
{
"match": "type"
}
],
"combine": "AND"
},
"$.error.errors[*].message": {
"matchers": [
{
"match": "type"
}
],
"combine": "AND"
},
"$.error.errors[*].field": {
"matchers": [
{
"match": "type"
}
],
"combine": "AND"
}
},
"header": {
"Content-Type": {
"matchers": [
{
"match": "text/json;charset=utf-8",
"regex": "regex"
}
],
"combine": "AND"
}
}
}
},
"providerStates": [
{
"name": "Check correct data json structure in case code: 401"
}
]
}
],
"metadata": {
"pactSpecification": {
"version": "3.0.0"
},
"pact-jvm": {
"version": "3.2.11"
}
}
}
And show error after run
Read all links_401 returns a response which has a matching body
Read all links_401 returns a response which has a matching
body=BodyComparisonResult(mismatches={/=[BodyMismatch(expected=[B#480e8a7a, actual=[B#10378c35, mismatch=Actual body '[B#10378c35' is not equal to the expected body '[B#480e8a7a', path=/, diff=null)]},
diff=[-{"error":{"code":401,"message":"session incorrect","errors":[]}},
+{"error":{"code":401,"message":"session incorrect, please login again (CR_A1004)","errors":[]}}])
I have tried to change regex but the problem is still happening.
It is only correct when message data correct.
Please help to give my point incorrect.
Thanks,
The problem is with your content-type header: "Content-Type": "text/json;charset=utf-8". The proper content type for JSON payloads should be application/json. That is why the matchers are not being applied, because the body is being treated as a text payload. Just change the content type and then the body will be parsed as a JSON document and the matchers will be applied to the fields.
I just start to use Api platform and immediately stuck with problem how to filter data.
I have entity User and i want to filter data that are present in response ( JSON API format)
{
"links": {
"self": "/api/users"
},
"meta": {
"totalItems": 2,
"itemsPerPage": 30,
"currentPage": 1
},
"data": [
{
"id": "/api/users/1",
"type": "User",
"attributes": {
"_id": 1,
"username": "jonhdoe",
"isActive": true,
"address": null
}
},
{
"id": "/api/users/3",
"type": "User",
"attributes": {
"_id": 3,
"username": "test",
"isActive": true,
"address": null
}
}
]
}
so I want to remove e.g. User with id 3, but not use filters sent via request. I just want to set filter that will be always run when someone go to /api/users.
I look to api-platform extensions but this will be applied on each request e.g. /api/trucks. So at end I just want to get something like
{
"links": {
"self": "/api/users"
},
"meta": {
"totalItems": 1,
"itemsPerPage": 30,
"currentPage": 1
},
"data": [
{
"id": "/api/users/1",
"type": "User",
"attributes": {
"_id": 1,
"username": "jonhdoe",
"isActive": true,
"address": null
}
}
]
}
As you pointed out, extensions are the way to go.
The applyToCollection method gets a $resourceClass parameter containing the current resource class.
So you can apply the WHERE clause only for a specific class in this method like:
public function applyToCollection(QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $resourceClass, string $operationName = null)
{
if (User::class === $resourceClass) {
$queryBuilder->doSomething();
}
// Do nothing for other classes
}
As per (https://learn.microsoft.com/en-us/azure/virtual-machines/windows/extensions-dsc-template#default-configuration-script) you can not pass arguments if default configuration script is used. So how can I use both Azure Automation onboarding and pass argument to DSC script at the same time? It seems to be impossible to do both.
"resources": [
{
"name": "Microsoft.Powershell.DSC",
"type": "extensions",
"location": "[resourceGroup().location]",
"apiVersion": "2015-06-15",
"dependsOn": [
"[resourceId('Microsoft.Compute/virtualMachines', parameters('swarmmanager1Name'))]"
],
"tags": {
"displayName": "DSC"
},
"properties": {
"publisher": "Microsoft.Powershell",
"typeHandlerVersion": "2.26",
"type": "DSC",
"autoUpgradeMinorVersion": true,
//"forceUpdateTag": "[parameters('DSCExtensionManagerTagVersion')]",
"settings": {
"wmfVersion": "latest",
"configurationArguments": {
"RegistrationKey": {
"UserName": "PLACEHOLDER_DONOTUSE",
"Password": "[parameters('RegistrationKey')]"
},
"CustomData" : "hello from ARM",
"RegistrationUrl": "[parameters('registrationUrl')]",
"NodeConfigurationName": "SwarmManager.localhost",
"RebootNodeIfNeeded": true
}
}
}
}
]
},
DSC configuration
Configuration SwarmManager
{
param
(
[string] $CustomData
)
Import-DscResource -ModuleName PSDesiredStateConfiguration
Import-DscResource -ModuleName PackageManagement -ModuleVersion "1.1.7.0"
Node localhost
{
PackageManagement xPSDesiredStateConfiguration {
Ensure = 'present'
Name = "xPSDesiredStateConfiguration"
Source = "PSGallery"
}
File DumpParameters {
Destinationpath = "c:\out.txt"
Contents = "Hello $CustomData"
}
}
}
Error received
The DSC Extension received an incorrect input: A parameter cannot be found that matches parameter name 'CustomData'
I have a JSON something like this:
{
"key": "Target",
"value": {
"__type": "Entity:http://schemas.microsoft.com/xrm/2011/Contracts",
"Attributes": [
{
"key": "prioritycode",
"value": {
"__type": "OptionSetValue:http://schemas.microsoft.com/xrm/2011/Contracts",
"Value": 1
}
},
{
"key": "completeinternalreview",
"value": false
},
{
"key": "stepname",
"value": "10-Lead"
},
{
"key": "createdby",
"value": {
"__type": "EntityReference:http://schemas.microsoft.com/xrm/2011/Contracts",
"Id": "ca2ead0c-8786-e511-80f9-3863bb347b18",
"KeyAttributes": [],
"LogicalName": "systemuser",
"Name": null,
"RowVersion": null
}
}
]
}
}
How do the toke for the key/values by searching for the value of the key?
Eg I want to get the key value pair 'completeinternalreview'
Assuming you have a C# class like this to represent that attributes object from your JSON:
public class MyValue
{
[JsonProperty("Attributes")]
public List<KeyValuePair<string, object>> Attributes { get; set; }
}
you can simply deserialize the string:
var result = JsonConvert.DeserializeObject<KeyValuePair<string, MyValue>>(jsonString);
and then find the correct key-value pair with:
var kvp = result.Value.Attributes.Find(a => a.Value == "completeinternalreview");