How to use 'Choice Router' in Anypoint studio for this scenario? - mule

I have used RAML file to generate flows through APIKit router. I have example.json for example response.
My RAML code is:
#%RAML 0.8
---
title: TestEmployee API
version: v1
/employee:
get:
queryParameters:
id:
enum: [1,2,3,4]
required: true
type: string
description: Employee id
name:
enum: [Charles,John,Neha,Shruti]
required: true
type: string
description: Employee_name
responses:
200:
body:
application/json:
example: !include example.json
JSON example response is :
[
{
"id": 1,
"name":"Charles",
"code": "C1ENU00",
"dateofjoining":"2019/06/24",
"domain":"ENU",
"address":"Hyderabad",
"phone": 9865458936,
"program": "WASE"
},
{
"id": 2,
"name":"John",
"code": "C2DIG00",
"dateofjoining":"2019/06/24",
"domain":"DIGITAL",
"address":"Chennai",
"phone": 9756359864,
"program": "ELITE"
}
I want to use a choice router to appropriately call flow when 'id' and 'name' matches. Like in query params if the user enters 'id=1&name=Charles' then only choice router would call main flow, else it should call default one. Initially, the payload is set to 'example JSON response' body. Please guide me achieving this.
Here are flows for reference.

Set the when expression attribute in your choice router to a Dataweave expression matching your use case. You can access queryParams by name using attributes.queryParams.YOUR-QUERY-PARAM-NAME syntax:
#[attributes.queryParams.id=='1' and attributes.queryParams.name=='Charles']
Then add a when scope for every route you need and an optional otherwise for a default route:
<choice>
<when
expression="#[attributes.queryParams.id=='1' and attributes.queryParams.name=='Charles']">
<flow-ref name="some-flow" />
</when>
<otherwise>
<flow-ref name="some-other-flow" />
</otherwise>
</choice>
For Mule 3 use:
#[message.inboundProperties.'http.query.params'.id == '1' && message.inboundProperties.'http.query.params'.name =='Charles']

Related

Mule design centre defining 400 response but trying it does not return same response

I am defining my api contract in RAML in Mule Anypoint platform Design centre .
Here is the simple contract :
#%RAML 1.0
title: test_experiment
version: v1
mediaType: application/json
/test:
put:
headers:
trackingId:
type: string
description: "Track each request"
minLength: 3
responses:
200:
body:
application/json:
example:
{
"msg": "successfully done"
}
400:
body:
application/json:
example:
{
"msg": "something bad was submitted",
"id" : "001"
}
Next I am trying to 'test it' in Documentation tab and when I purposefully do not enter a trackingId I do get a 400 response code but the response payload is different.
I have defined the response payload as : ( expected payload )
{
"msg": "something bad was submitted",
"id" : "001"
}
However the payload response in 'try it' in design centre is :
{
"code": "REQUEST_VALIDATION_ERROR",
"message": "Error validating header: expected minLength: 3, actual: 0"
}
So why is the 400 response not being returned as I have it defined in my raml ?
Edit1 : This behaviour is all observed in Design Centre , I am not providing an implementation and then testing it , I am using design centre Documentation and 'Try it' feature as per image below :
Because it is a mock, not a real implementation. When you are testing in Design Center you are using the Mocking Service that simulates reponses. The error you are getting is because the request doesn't match with the specification. The Mocking Service usually uses only the first status code it finds in the API RAML. If you want it to return your error use a behavioral header like MS2-Status-Code to set the error code. For example set the header MS2-Status-Code to 200,400. Although I'm not sure it will work to override the validation error from the Mocking Service. You will need to try.

Mule Soft HTTP Request Path from payload

I have created simple flow in which I send id in url like this:
Path:
/api/courses/find/{id}
Result:
http://localhost:88/api/courses/find/60ee9678070e104b2c57be46
then I set this id part as payload. What I am trying to do next is Call REST API with this payload inserted into URL but no matter what I try it does not return correct JSON.
Here is how I need it to look like:
Path:
http://localhost:1234/api/courses/find/60ee9678070e104b2c57be46
Result from Transform Message:
%dw 2.0
output application/json
---
{
isPublished: payload.isPublished,
tags: payload.tags map ( tag , indexOfTag ) -> tag,
"_id": payload."_id",
dishName: payload.dishName,
category: payload.category,
author: payload.author,
ingredients: payload.ingredients map ( ingredient , indexOfIngredient ) -> {
"_id": ingredient."_id",
quantity: ingredient.quantity,
unit: ingredient.unit,
description: ingredient.description
},
cookingTime: payload.cookingTime,
sourceUrl: payload.sourceUrl,
imageUrl: payload.imageUrl,
price: payload.price,
date: payload.date,
"__v": payload."__v",
id: payload."_id"
}
I tried to do it by putting id into URI Parameters, but it is part of URL not URI parameter so that does not work, same goes with something like this:
Request Path on HTTP Request:
/api/courses/find/#[payload]
Does anyone know if I could insert payload inside URL like this? I can't find anything about it in documentation.
Thank you for help!
EDIT: Could you also please tell me if I wanted to create path for each method GET, POST, PUT, DELETE could it have the same Listener path /api/courses and only after that different HTTP Request elements (for example http://localhost:1234/api/courses/ with method POST) or I would have to change it for each one? I am wondering because I can't find if it will know that it is supposed to choose flow based on what HTTP Request elements are in this flow or it just chooses first one with that path /api/courses.
My understanding is that you need to set an URI parameter for an HTTP Request. Please find below an example of a flow that sets a payload with field id and then sets the URI parameter using this field's value. The URI parameter is set in the path attribute of the HTTP Request enclosed in curly braces ({}).
<flow name="testUriParamsFlow" >
<http:listener doc:name="Listener" config-ref="HTTP_Listener_config" path="/"/>
<ee:transform doc:name="Transform Message">
<ee:message >
<ee:set-payload ><![CDATA[%dw 2.0
output application/java
---
{
id: "123456"
}]]>
</ee:set-payload>
</ee:message>
</ee:transform>
<http:request method="GET" path="/api/courses/find/{id}" config-ref="HTTP_Request_configuration" doc:name="Request">
<http:uri-params ><![CDATA[#[output application/java
---
{
id : payload.id
}]]]>
</http:uri-params>
</http:request>
</flow>
Using HTTP Wire logging we can confirm that the request replaces the URI Parameter as part of the URI:
GET /api/courses/find/123456 HTTP/1.1

How to send different RAML response with different input query parameters

I have a question related to RAML sending different response. In the below RAML, I want to send the response based on the query parameter. If my query parameter (flightId) is "F001", I should get a response only for the data F001. But I am getting the response with all the data specified in the response. May I know how to filter the unwanted data.
#%RAML 1.0
baseUri: https://mocksvc.mulesoft.com/mocks/9b7a0390-ecf4-4ff4-b307-0b7d87ed9495 # baseUri: https://mocksvc.mulesoft.com/mocks/b133e2e4-f0e3-49a0-b224-8f36358e04ca #
title: FlightApi-Rajesh
version: 1.0.1
/{flightId}:
description: Flight Id
get:
queryParameters:
flightId:
displayName: flightId
type: string
required: true
description: Flight name with its ID
example: F0001
description: Get the flight with `flightId = {flightId}`
responses:
200:
body:
application/json:
example: |
{
"F001":{
"flightName": "Ingido",
"Location": "Mumbai",
"flightId": "F001",
"Destination":"Delhi",
"timing":"19:55 HRS"
},
"F002":{
"flightName": "SpiceJet",
"Location": "Pune",
"flightId": "PNQ012",
"Destination":"Chennai",
"timing":"15:00 HRS"
}
}
404:
body:
application/json:
example: |
{"message": "Flight not found"}
RAML is a design specification for REST APIs. When you run the mock service based on RAML, it does not have the capability of generating dynamic responses, which is what you are trying to do. Please refer Mule forum which confirms this - https://forums.mulesoft.com/questions/60487/can-the-example-in-the-raml-be-dynamic.html
If your requirement is to create a mock service with dynamic responses, there are other ways to achieve implementing such mock services - SOAPUI, Dynatrace, etc.
http://blog.maheeka.me/2016/11/parasoft-virtualizer-tips-for.html
https://www.soapui.org/rest-testing-mocking/service-mocking-overview.html
If your requirement is to use RAML and try dynamic responses, you will have to create a Mule ESB project based off the RAML. You can then implement dynamic responses based on input received with Mule configurations.
http://workshop.tools.mulesoft.com/modules/module3_lab1#step-1-create-a-new-mule-project-and-import-raml-definition
You'll likely want to change your example to this. A mock endpoint will return your example response every time, so if you're only expecting one match, the example should reflect that. Keep this in mind, because you will not be able to modify the response of a mocked API based on user input:
/{flightId}:
description: Flight Id
get:
queryParameters:
flightId:
displayName: flightId
type: string
required: true
description: Flight name with its ID
example: F0001
responses:
200:
body:
application/json:
example: |
{
"F001":{
"flightName": "Ingido",
"Location": "Mumbai",
"flightId": "F001",
"Destination":"Delhi",
"timing":"19:55 HRS"
}
}
404:
body:
application/json:
example: |
{"message": "Flight not found"}
If you have received this data from the database connector, even with a single record returned, you'll get a list of results. You'll need to transform it like this:
%dw 1.0
%output application/json
%vars flight = (payload filter $.flightId == flowVars.flightId)[0]
---
{
(flowVars.flightId): flight
}

RAML : How to require parameter A OR parameter B

I'm writing some REST documentation with RAML but I'm stuck.
My problem:
- I have a GET request used for search that can take a parameter "id" or (exclusive or) "reference". Having only one of them is required.
I know how to say "this param is required" but I don't know how to say "having one of these params is required". Is it even possible?
The following example written in RAML 1.0 defines two object types in Url and File then creates another object Item which requires Url OR File in ext. If you change the included examples (which currently validate), you'll see that they fail if the property does not conform to one or the other definition. Hope that helps! LMK if you have any other questions and I'll do my best.
[EDIT: hmm I think I am seeing your problem now, the final example I've just added, named should_fail, (which has one of each type together in the example) still validates and you want a way to make it fail validation.]
[UPDATE: OK I figured a mildly hacky way to do this. Use maxProperties: 1 in the object which should have properties appear alone, see updated code below which fails the final example during validation.]
#%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 yet requiring
one AND ONLY one of two possible types using RAML 1.0
properties:
ext:
maxProperties: 1
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:
url: http://heres.a.url.com/asset.jpg
filename: video.mp4
I had the same problem. User can provide either a textual input OR a file input, but not both.
Both have different fields and I detect the request type from the field names. i.e if the request has [files and parameters], it is a FileInput. If the request has [texts and parameters], it is a TextInput. It is not allowed to provide both text and file within the same request.
I used the union property. See CatAndDog example in
Raml 200 documentation for a small example.
You can define your types as follows.
types:
FileInput:
properties:
parameters:
type: Parameters
description: (...)
files:
type: ArchiveCollection | FileCollection
description: (...)
TextInput:
properties:
parameters:
type: Parameters
description: (...)
texts:
type: TextCollection
description: (...)
Then in my POST request body:
/your_route:
post:
body:
multipart/form-data:
type: TextInput | FileInput
The fields in the body are defined with either TextInput or FileInput type.
In RAML 0.8 you can not describe queryParameters with only one parameter.
In RAML 1.0 you can do this. You should use oneOf in jsonschema for describing Type. Your queryParameters should use this type. Example:
api.raml
#%RAML 1.0
title: AUTH microservice
mediaType: application/json
protocols: [HTTPS]
types:
- example: !include schemas/example.json
/example:
get:
queryParameters:
type: example
schemas/example.json
{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"id": "file://schemas/credentials.json",
"oneOf": [
{
"properties": {"key1": {"type": "string"}},
"additionalProperties": false
},
{
"properties": {"key2": {"type": "string"}},
"additionalProperties": false
}
]
}
Also you can use uriParameters. Maybe it will help in your case.
#%RAML 0.8
title: API Using media type in the URL
version: v1
/users{mediaTypeExtension}:
uriParameters:
mediaTypeExtension:
enum: [ .json, .xml ]
description: Use .json to specify application/json or .xml to specify text/xml

POST parameters support in RAML

I'd like to ask if there is any support for POST parameters in RAML. And if there is - what is the syntax. I've browsed spec 0.8 and spec 1.0 roughly (actually I'm bound to 0.8, since many tools don't support 1.0 yet). I didn't find POST parameters support, but maybe I just missed something.
So what do I mean by POST parameters? These can be either of the two (sorry, I don't know their formal names, if there are any):
HTTP plain parameters, key=value, each parameter in one line, such as
name=John Doe
amount=5
which is not really handy (e.g. no nesting)
parameters as JSON object, just a JSON with all its syntax allowed (server-side needs to parse this json); such as:
{"name":"John Doe","amount":"5"}
Different server-side API implementations use either 1st or 2nd one. Anyway, how does RAML support these?
As it is shown in this reference https://github.com/raml-org/raml-spec/wiki/Breaking-Changes:
For raml 0.8:
body:
application/x-www-form-urlencoded:
formParameters:
name:
description: name on account
type: string
example: Naruto Uzumaki
gender:
enum: ["male", "female"]
Is the equivalent in raml 1.0 to:
body:
application/x-www-form-urlencoded:
properties:
name:
description: name on account
type: string
example: Naruto Uzumaki
gender:
enum: ["male", "female"]
So what it changes is the formParameters attribute to the properties one.
#Pedro has covered option 2, so here is option 1. Based on the discussion in the comments, it seems the encoding used is application/x-www-form-urlencoded.
You need to use formParameters.
Example:
post:
description: The POST operation adds an object to a specified bucket using HTML forms.
body:
application/x-www-form-urlencoded:
formParameters:
AWSAccessKeyId:
description: The AWS Access Key ID of the owner of the bucket who grants an Anonymous user access for a request that satisfies the set of constraints in the Policy.
type: string
acl:
description: Specifies an Amazon S3 access control list. If an invalid access control list is specified, an error is generated.
type: string
Reference: https://github.com/raml-org/raml-spec/blob/master/raml-0.8.md#web-forms
Post parameters can be expressed using JSON Schema
A simple RAML 0.8 example:
#%RAML 0.8
title: Api
baseUri: /
schemas:
- Invoice: |
{
"$schema": "http://json-schema.org/draft-03/schema",
"type": "object",
"properties": {
"Id": { "type": "integer"},
"Name": { "type": "string"},
"Total": { "type": "number"}
}
}
/invoices:
post:
body:
application/json:
schema: Invoice