How to validate with yml file a karate structure response [duplicate] - yaml

This question already has an answer here:
Karate - Nested JSON object schema validation causes KarateException
(1 answer)
Closed 1 year ago.
recently I started working with Karate and Yaml for the first time. I was able to validate simple response structures where all the answer data were on the same level. But now I have to validate a more complicated structure and I spent a lot of time without success.
When I perform a GET request I receive the next answer:
[
{
"factories": [
{
"id": 1,
"scopes": [
{
"id": null,
"name": "name 1",
"expireD": 10,
"isExpired": true
}
],
"companyName": "TEST",
},
{
"id": 2,
"scopes": [
{
"id": null,
"name": "name 2",
"expireD": 7,
"isExpired": true
}
],
"companyName": "TEST2",
}
],
"scopeId": null
}
]
The structure validation is not directly in the karate code. It is on a yml file like this:
operationId: getTest
statusCode: 200
params:
body: null
matchResponse: true
responseMatches:
scopeId: '##number'
factories:
companyName: '#string'
id: '#number'
scopes:
expireD: '#number'
name: '#string'
id: '#null'
isExpired: '#boolean'
I review the structure around 100 times and I have the same error all the time when I arrive here:
* match response contains responseMatches
The error is the next one:
$[1].factories | data types don't match (LIST:MAP)
I tried to use match each, ignore one by one the structures to see which one is failing and also reduce the validations as #array and it is not working.
Any help will be more than welcome. Thank you.

I really recommend NOT using YAML especially in a testing / validation scenario. But finally it is your call.
Here is a tip to save you some time, you can print out the value of the YAML and see where you were going wrong. I don't know YAML (and avoid it as far as possible), but I made a guess after a few failed attempts and managed to insert a - at the right place (apparently there are many other ways) to make some of the YAML a JSON array - which is what you want.
* text foo =
"""
operationId: getTest
statusCode: 200
params:
body: null
matchResponse: true
responseMatches:
scopeId: '##number'
factories:
-
companyName: '#string'
id: '#number'
scopes:
expireD: '#number'
name: '#string'
id: '#null'
isExpired: '#boolean'
"""
* yaml foo = foo
* print foo
Try the above and see how it differs from your example.

Finally, a solution was found. The Karate documentation offers an idea about how to use it defining a structure of data that could be used as a type. I tried before but I added before the responseMatches section. The yml file looks like this:
operationId: getTest
statusCode: 200
params:
body: null
matchResponse: true
responseMatches:
scopeId: '##number'
factories: '#[_ <= 5] factoryStructure'
factoryStructure:
companyName: '#string'
id: '#number'
scopes: '#[] scopeStructure'
scopesStructure:
expireD: '#number'
name: '#string'
id: '#null'
isExpired: '#boolean'

Related

Spring boot code generated from swaggerhub is not able to throw 400 on additional properties

I've logged in to swaggerhub and generated a sample inventory API which is part of their template.
In codegen options selected spring as server. ticked on useBeanValidation checkbox. Generated the code, tested it. Validations are working properly as expected except only one thing. I don't want to allow any additional property on the POST payload.
NB: The openAPI specification doesn't have 'additionalProperties' flag specified. Which defaults it to 'false' in my understanding. I tried to explicitly specify the same too. but no luck. There is no exception thrown while I add an additionalProperty.
The OpenAPI specification
openapi: 3.0.0
servers:
# Added by API Auto Mocking Plugin
- description: SwaggerHub API Auto Mocking
url: https://virtserver.swaggerhub.com/xxxx/apionopenApi3/1.0.0
info:
description: This is a simple API
version: "1.0.0"
title: Simple Inventory API
contact:
email: you#your-company.com
license:
name: Apache 2.0
url: 'http://www.apache.org/licenses/LICENSE-2.0.html'
tags:
- name: admins
description: Secured Admin-only calls
- name: developers
description: Operations available to regular developers
paths:
/inventory:
get:
tags:
- developers
summary: searches inventory
operationId: searchInventory
description: |
By passing in the appropriate options, you can search for
available inventory in the system
parameters:
- in: query
name: searchString
description: pass an optional search string for looking up inventory
required: false
schema:
type: string
- in: query
name: skip
description: number of records to skip for pagination
schema:
type: integer
format: int32
minimum: 0
- in: query
name: limit
description: maximum number of records to return
schema:
type: integer
format: int32
minimum: 0
maximum: 50
responses:
'200':
description: search results matching criteria
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/InventoryItem'
'400':
description: bad input parameter
post:
tags:
- admins
summary: adds an inventory item
operationId: addInventory
description: Adds an item to the system
responses:
'201':
description: item created
'400':
description: 'invalid input, object invalid'
'409':
description: an existing item already exists
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/InventoryItem'
description: Inventory item to add
components:
schemas:
InventoryItem:
type: object
additionalProperties: False
required:
- id
- name
- manufacturer
- releaseDate
properties:
id:
type: string
format: uuid
example: d290f1ee-6c54-4b01-90e6-d701748f0851
name:
type: string
example: Widget Adapter
releaseDate:
type: string
format: date-time
example: '2016-08-29T09:12:33.001Z'
manufacturer:
$ref: '#/components/schemas/Manufacturer'
Manufacturer:
required:
- name
properties:
name:
type: string
example: ACME Corporation
homePage:
type: string
format: url
example: 'https://www.acme-corp.com'
phone:
type: string
example: 408-867-5309
type: object
additionalProperties: False
Got 400 for this payload on post API.
{
"id": "7f125802-7840-4ff0-8ddb-7c78c0948987",
"name11111": "Widget Adapter",
"releaseDate": "2016-08-29T09:12:33.001Z",
"manufacturer": {
"name": "ACME Corporation",
"homePage": "",
"phone": "408-867-5309"
}
}
Expected 400 but didn't get on this payload on post
{
"id": "7f125802-7840-4ff0-8ddb-7c78c0948983",
"name": "Widget Adapter",
"releaseDate": "2016-08-29T09:12:33.001Z",
"manufacturer": {
"name": "ACME Corporation",
"homePage": "",
"phone": "408-867-5309"
},
"newkey" : "newValue"
}
Can anybody pls help on this.. I want to completely disallow additional properties and throw a 400 on those requests..
The default for additionalProperties is actually True, not False.
Adding false does not cause the swagger-codegen for Spring to actually disallow additional fields though, annoyingly enough. After a few hours of running in the wrong direction I found the solution:
In application.properties
spring.jackson.deserialization.FAIL_ON_UNKNOWN_PROPERTIES=true
I had been having the same trouble and have tried to leverage filters but have had problems tying the models that are used in the automatic deserialization of #RequestBody to the servlet request and keeping the behavior consistent across model changes. Coming to that conclusion required writing quite a bit of unnecessary boilerplate and wasting a lot of time.
I also tried to find a way to inject some behavior in the automatic use of the model or any model annotation that would change how the model automatically disregards additional fields without reporting the bad request. After exhausting the road of annotations I found the solution.

Prisma: Field ... is not defined in ... input type OR type ... expected but non object was submitted

I'm using Prisma 1.34. fro API development. Testing it through the localhost playground.
Sorry for long text in advance, can't understand where I went wrong.
I have the following scheme representing hierarchy Script template consist of the Cards templates, and Cards include Tasks templates:
type ScriptTemplate {
id: ID!
name: String!
cards: [CardTemplate!]
input: String!
output: String!
}
type CardTemplate {
id: ID!
title: String!
description: String
tasks: [TaskTemplate!]
}
input ExistingCardTemplateInput {
id: ID!
}
input NewCardTemplateInput {
title: String!
description: String!
tasks: [NewTaskTemplateInput!]
}
type TaskTemplate {
id: ID!
label: String!
description: String!
cards: [CardTemplate!]
}
input ExistingTaskTemplateInput {
id: ID!
}
input NewTaskTemplateInput {
label: String!
description: String!
}
Corresponding mutations are:
type Mutation {
createScriptTemplate(name: String!, input: String!, output: String!, cards: [ExistingCardTemplateInput!], new_cards: [NewCardTemplateInput!]): ScriptTemplate
createCardTemplate(title: String!, description: String! tasks:[ExistingTaskTemplateInput!], new_tasks:[NewTaskTemplateInput!]): CardTemplate
createTaskTemplate(label: String!, description: String! cards:[ExistingCardTemplateInput!]): TaskTemplate
}
So basically, if I trying to use createTaskTemplate mutation or createCardTemplate mutation - everything is working fine. I can create these entities, including nested mutation creating new Card with the new Tasks in it or binding already existing Tasks. Or existing Card to newly created Task. That's why explicitly defined input types: ExistingTaskTemplateInput, NewTaskTemplateInput and NewCardTemplateInput.
Everything is working as expected when I'm trying to create a new script with the inclusion of a new Card or connecting it to an existing one, as well.
However, if I'm trying to create Script, Card and include new Tasks in it I've got error messages above.
When trying the following mutation:
mutation{
createScriptTemplate(
name: "Script via API_H2"
input: "Something describing initial state"
output: "Something describing required state at the end"
cards: [
{
id: "cjycl2nup00ta0703sd0kd8oa"
},
{
id: "cjye3ryee01ey070383sxaoxz"
}
]
new_cards:[
{
title:"New card via scriptis2"
description:"desc"
tasks: [
{
description: "test dewscription"
label: "test label"
}
]
},
{
title:"New card through scriptos2"
description: "desc"
}
]
){
id
name
input
output
createdAt
updatedAt
cards{
id
title
tasks{
id
label
}
}
}
}
I'm having error:
{
"data": {
"createScriptTemplate": null
},
"errors": [
{
"message": "Variable '$data' expected value of type 'ScriptTemplateCreateInput!' but got:
{\"name\":\"First Script through API_H2\",\"input\":\"Something describing initial state\",\"output\":\"Something describing requred state at the end\",\"cards\":{\"connect\":[{\"id\":\"cjycl2nup00ta0703sd0kd8oa\"},{\"id\":\"cjye3ryee01ey070383sxaoxz\"}],\"create\":[{\"title\":\"New card via scriptis2\",\"description\":\"desc\",\"tasks\":[{\"label\":\"test label\",\"description\":\"test dewscription\"}]},{\"title\":\"New card through scriptos2\",\"description\":\"desc\"}]}}.
Reason: 'cards.create[0].tasks'
Expected 'TaskTemplateCreateManyWithoutCardsInput', found not an object. (line 1, column 11):\nmutation ($data: ScriptTemplateCreateInput!) {\n ^",
"locations": [
{
"line": 2,
"column": 3
}
],
"path": [
"createScriptTemplate"
]
}
]
}
The actual request looks like this (via console.log) :
{ name: 'First Script through API_H2',
input: 'Something describing initial state',
output: 'Something describing requred state at the end',
cards:
{ connect:
[ [Object: null prototype] { id: 'cjycl2nup00ta0703sd0kd8oa' },
[Object: null prototype] { id: 'cjye3ryee01ey070383sxaoxz' } ],
create:
[ [Object: null prototype] {
title: 'New card via scriptis2',
description: 'desc',
tasks:
[ [Object: null prototype] { label: 'test label', description: 'test dewscription' } ] },
[Object: null prototype] { title: 'New card through scriptos2', description: 'desc' } ] } }
Looks like I missing {connect or create} bit for the tasks field.
However when I'm altering it to look like:
tasks: {create: [
{
description: "test dewscription"
label: "test label"
}
]
}
I'm getting error that Field \"create\" is not defined by type NewTaskTemplateInput and Field NewTaskTemplateInput.label and description of required type String! was not provided
However, this works perfectly fine (same request without tasks) :
mutation{
createScriptTemplate(
name: "Script via API_H2"
input: "Something describing initial state"
output: "Something describing required state at the end"
cards: [
{
id: "cjycl2nup00ta0703sd0kd8oa"
},
{
id: "cjye3ryee01ey070383sxaoxz"
}
]
new_cards:[
{
title:"New card via scriptis2"
description:"desc"
},
{
title:"New card through scriptos2"
description: "desc"
}
]
){
id
name
input
output
createdAt
updatedAt
cards{
id
title
tasks{
id
label
}
}
}
}
Checked generated scheme, can't spot any problems there.
input TaskTemplateCreateManyWithoutCardsInput {
create: [TaskTemplateCreateWithoutCardsInput!]
connect: [TaskTemplateWhereUniqueInput!]
}
input TaskTemplateCreateWithoutCardsInput {
id: ID
label: String!
description: String!
}
Looks like I'm confusing scheme that I defined in gql files and the one I'm doing requests again, but don't know which direction to go.
Description
First outline the entire process of a request.
The above image is taken from Prisma Basics in the official Prisma documentation.
As shown in this picture, localhost playground is the client in the figure. The Server accessed by localhost playground is the API Server in the figure, which is Prisma Client.Here is the first connection, the green arrow in the picture.
Prisma Client gets the data from the database by accessing Prisma Server. This is the second connection, the yellow arrow in the figure.
The process of data flow, sent from localhost playground to API Server to complete the first connection transmission, and then processed by resolve, use Prisma Client to send to Prisma Server to complete the second connection transmission. Prisma Server retrieves data from Database according to the requested data and returns it to API Server, completes the second connection acceptance. API Server returns the data to localhost playground, completing the acceptance of the first connection. A complete process is completed.
In the process of using Prisma, we define two schema, one for defining Prisma Server, the name is usually datamodel.prisma. The other one is used to define Prisma Client, if it is defined in the file , usually named schema.graphql, can also be defined in the JS file through the template string, there are other ways, here is not discussed in depth. What you are showing belongs to Client Schema.
Successful completion of this process requires that the data sent by each part is correct.
Your first error occurred when the second connection was sent. The data sent from API Server did not match the schema definition in Prisma Server.
The second error is that the data sent from localhost playground does not match the schema definition in API Server when the first connection is sent.
Example
Assume the following architecture
# datamodel.prisma
type User {
id: #id!
name: String!
post: [Post!]!
}
type Post {
id: #id!
title: String!
}
# schema.graphql
input CreatePost {
title: String!
}
type Mutation {
createUser(name: String!,posts:[CreatePost])
}
Use the following mutation Create user
mutation {
createUser(name: "prisma", posts: [{ title: "test" }]) {
id
name
}
}
When sent from localhost playground to API Server, the data is as follows
{
"name": "prisma",
"posts": [{ "title": "test" }]
}
If you send this data directly to Prisma Server, it does not conform to the definition of Prisma Server. You need to convert the data to a definition that conforms to the Prisma Server schema.
{
"name": "prisma",
"posts": { "create": [{ "title": "test" }] }
}
Then send it to Prisma Server via Prisma Client.
const resolve = {
Mutation: {
createUser: async (_, args) => {
const userCreateInput = {
name: args.name,
posts: {
create: args.posts,
},
}
return await prisma.createUser(userCreateInput)
},
},
}
This way the final data arrives at the database via Prisma Server.
Solve the problem
Check out the definition of createScriptTemplate in API Server.It should be that it did not convert the received data into an error caused by the format required by API Server.
Understand Prisma

Raml 1.0 Types with examples and mulesoft mocking service

I am using RAML 1.0 in the mulesoft api designer.
I would like to use types/properties to describe my api responses, and also enable the mocking service so I can run the api and get back an example response. I thought if I gave the type an example value the mocking service would be able to generate the example json response. This is my test raml
#%RAML 1.0
title: Test
baseUri: https://mocksvc.mulesoft.com/mocks/<removed>
types:
Email:
description: Email address
example: Steve#test.com
/user:
get:
responses:
200:
body:
application/json:
properties:
email: Email
When I run the api through the mocking service, I expect my response body to be this:
{
"email": "Steve#test.com"
}
but the service reports that it has no information and returns this in the body
{
"message": "RAML had no response information for application/json"
}
No, it would be a cool feature but it doesn't work that way.
You need to add the example in the response:
...
types:
Email:
description: Email address
/user:
get:
responses:
200:
body:
application/json:
properties:
email: Email
example: { "email": "Steve#test.com" }
We need to provide the example type we are expecting, check this link http://raml.org/developers/raml-200-tutorial
below just added example in response body, there we can provide multiple outcomes to.
%RAML 1.0
title: Test
baseUri: https://mocksvc.mulesoft.com/mocks/
types:
Email:
description: Email address
example: Steve#test.com
/user:
get:
responses:
200:
body:
application/json:
properties:
email: Email
example:
"email" : [
{ "email" : "Steve#test.com"}
]
Types tag in Raml 1.0 is more powerful. You have design your custom type as per comfort and also increase reputability of the code
#%RAML 1.0
title: Brochure
version: v1
baseUri: https://mocksvc.mulesoft.com/mocks/63063930-851d-41cc-b021-36d8a435d800 # baseUri: http://localhost:8080
protocols: HTTP
mediaType: application/json
types:
ModelTree:
type: object
properties:
modelTreeReference: string
brand: string
series?: string
constructionSeries?: string
bodyType?: string
AGModelCode?: string
UKModelCode?: string
levelCode?: number
Brochure:
type: object
properties:
recordNumber: number
partNumber: number
name: string
brand: string
brochureType: string
CRMGroup: string
CRMSubGroup: string
isActiveIndicator: string
modelTree: ModelTree
Status:
type: object
properties:
responseStatus:
enum: [COMPLETE, ERROR, FATAL]
responseId: number
Transaction:
type: object
properties:
status: Status
data:
type: object
properties:
brochures?: Brochure[]
/brochures:
get:
responses:
200:
description: Status and a list of Brochures
body:
application/json:
example: {
status: {
responseStatus: 'COMPLETE',
responseId: 123
},
data: {
brochures: [{
recordNumber: 1,
partNumber: 56,
name: "Activity Brochure",
brand: "My Brand Ltd",
brochureType: "HARDCOPY",
CRMGroup: "Sales",
CRMSubGroup: "Lifestyle/Access",
isActiveIndicator: "N",
modelTree: {
modelTreeReference: "My Brand",
brand: "My Brand Ltd",
levelCode: 1
}
}
]
}
}
type: Transaction
I ran into a similar issue using examples with a form encoded body. The examples given in the spec and the RAML 200 tutorial for application/json encoded responses don't work for application/x-www-form-urlencoded.
This type
types:
hub:
type: object
properties:
callback: string
mode:
enum: ["subscribe", "unsubscribe"]
topic: string
works properly with an application/json body and example
/subv1json:
post:
description: "Subscribes to a topic"
body:
application/json:
type: hub
example:
{
"callback": "http://yourcallback.url.com",
"mode": "subscribe",
"topic": "orders"
}
responses:
202:
Returns: 202 Accepted
but changing the encoding to x-www-form-urlencoded
/subv1form: #this doesn't work, returns 400 bad request
post:
description: "Subscribes to a topic"
body:
application/x-www-form-urlencoded:
type: hub
example:
{
"callback": "http://yourcallback.url.com",
"mode": "subscribe",
"topic": "orders"
}
responses:
202:
Returns 400 Bad Request and a syntax error
{
"code": "REQUEST_VALIDATION_ERROR",
"message": "Invalid schema for content type application/x-www-form-urlencoded. Errors: Syntax error in the following text: '}: { "callback": "http://yourcallback.url.com", "mode": "subscribe",'. "
}
I found that moving the example inside of the type definition didn't help - only changed the error to a more precise 'require key [mode] not found'
{
"code": "REQUEST_VALIDATION_ERROR",
"message": "Invalid schema for content type application/x-www-form-urlencoded. Errors: required key [mode] not found. "
}
However, reformatting the sample to yaml syntax does work, but requires that you do NOT specify the example in the type.
/subv3examplereformatted: #this works
post:
description: "Subscribes to a topic"
body:
application/x-www-form-urlencoded:
type: hub
example:
id: "example1"
callback: "http://yourcallback.com"
mode: "subscribe"
topic: "order"
responses:
202:
So, there is quirky-ness to how the mocking services manage serializing examples for form encoded parameters, and more than one way to provide examples. This is one work around if you want to use x-www-form-urlencoded and provide examples that work in the mocking services.

RAML: Nested Schemas

1) When writing RAML, can I use nesting in my schema definition?
For example:
schemas:
- DNSResponse: |
{
"type": "object",
"properties": {
"AnswerSection": {
"type": "array",
"items": (((I want a re-useable schema here. ex: ARecord)))
},
"AA": {"type": "boolean"},
"AD": {"type": "boolean"},
...
}
}
- ARecord: |
{
"type": "object",
"properties": {
"address": "string",
"ttl": "number",
"name": "string"
}
}
2) Can I use a choices/enum around a set of nestable schemas?
"items": [ARecord, MXRecord, PTRRecord, ...]
1) Yes, you can. See this example. That would be:
"items": { "$ref": "ARecord" }
2) I believe this is possible in Draft 4 of JSON Schema, using the oneOf directive. I don't think this is supported by RAML though. Alternatively, you could create a base schema and have ARecord, MXRecord and PTRRecord extend this base schema and then allow items of the base schema. This won't be very semantically rich but could get you started.
Since your question is not 100% requiring JSON, I will add this in the answers...
With the release of the RAML 1.0 specifications, you can use types, which allows you to do just that (in what I consider slightly cleaner).
Here is the reference link : http://docs.raml.org/specs/1.0/#raml-10-spec-types
RAML 1.0 introduces the notion of data types, which provide a concise and powerful way to describe the data in your API. The data can be in a URI parameter (base or resource URI), a query parameter, a request or response header, or of course a request or response body. Some types are built in, while custom types may be defined by extending (inheriting from) the built-in types. In any place where the API expects data, a built-in type may be used to describe the data, or a type may be extended inline to describe that data. CustomSecurityScheme types may also be named and then used like any built-in type.
And for those that want less text and more examples (taken from RAML documentation) :
#%RAML 1.0
title: API with Types
types:
User:
type: object
properties:
firstname: string
lastname: string
age: number
/users/{id}:
get:
responses:
200:
body:
application/json:
type: User
I think the following example applies here. It demonstrates defining two types Url and File (using RAML 1.0) and then using the logical OR to allow either type (aka schema) in Item as a subschema. You could potentially include more types if needed.
I also defined some examples inline which demonstrate how it works if you are using the raml-parser.
#%RAML 1.0
types:
Url:
properties:
url:
type: string
example: http://www.cats.com/kittens.jpg
description: |
The url to ingest.
File:
properties:
filename:
type: string
example: kittens.jpg
description: |
Name of the file that will be uploaded.
Item:
description: |
An example of a allowing multiple types using RAML 1.0
properties:
ext:
type: File | Url
examples:
file_example:
content:
ext:
filename: video.mp4
url_example:
content:
ext:
url: http://heres.a.url.com/asset.jpg
should_fail:
content:
ext:
unexpected: blah

Mapping relationships where the JSON representation can hold either a nested object or an object ID (in RestKit)

Is it possible to get RestKit to properly map relationships where the property might contain either a nested object or an object ID?
The API I'm currently working with often returns deep cyclical object graphs where multiple occurrences of the same object are replaced with the object's ID.
A simple example:
Nested
[
{
id: 1,
title: "Post Title",
category: {
id: 1,
name: "Category Title"
}
},
{
id: 2,
title: "Post Title",
category: 1
}
]
Since the replacement can happen at any level manually fixing the JSON before mapping is not very practical.

Resources