why i get apikit:router not found 404 error in mule eventhough it requests an already running and deployed api - mule

i have created 2 apis: system and experience for a project. The system had already been deployed into the cloudhub and running successfully. The experience api needs to invoke the system api through a router using the URL:
http://demo-insurance-system-api.us-e2.cloudhub.io
and uriparam is :customer
and queryparams are:?fname=James&lname=Butt
Its working perfectly fine.
but when i want to hit the same url from experience api's requester it gives me
ERROR 2020-05-18 01:58:15,217 [[muleinsurance-exp-api].http.requester.requestConfig.04 SelectorRunner] [event: ] org.mule.runtime.core.internal.exception.OnErrorPropagateHandler:
********************************************************************************
Message : HTTP OPTIONS on resource 'http://demo-insurance-system-api.us-e2.cloudhub.io' failed: not found (404).
Error type : HTTP:NOT_FOUND
Element : muleinsurance-experience-api-main/processors/0 # muleinsurance-exp-api:muleinsurance-experience-api.xml:17
Element XML : <apikit:router config-ref="muleinsurance-experience-api-config"></apikit:router>
(set debug level logging or '-Dmule.verbose.exceptions=true' for everything)
the onpremise testing is done in using postman and the experience url is:
http://localhost:8081/demoapi/customer?fname=James
the experience api raml is:
#%RAML 1.0
title: muleinsurance-experience-api
version: 1.0.0
traits:
client-id-required:
headers:
client_id:
type: string
client_secret:
type: string
responses:
401:
description: Unauthorized, The client_id or client_secret are not valid or the client does not have access.
404:
description: No Record found.
429:
description: The client used all of it's request quota for the current period.
500:
description: Server error ocurred
503:
description: Contracts Information Unreachable.
/demoapi:
/{searchString}:
get:
description: invokes either customer or policy request
queryParameters:
fname:
description: first name
example: Sumitra
required: false
type: string
lname:
description: Last name
example: Ojha
required: false
type: string
dob:
description: Date of Birth
example: 1/2/2003
required: false
type: string
customerID:
description: CustomerID
example: BU79786
required: false
type: string
policytype:
description: type of policy taken
example: Personal Auto
required: false
type: string
responses:
200:
body:
application/json:
example:
{"message": "connecting to System API"}
here i am adding the HTTP request xml snippet:
<choice doc:name="Choice" doc:id="444a5cd6-4aee-441a-8736-2d2fff681e2e" >
<when expression="#[attributes.uriParams.searchString == 'customer']">
<http:request method="GET" doc:name="Request" doc:id="30958d05-467a-41b1-bef3-83426359f2aa" url="http://demo-insurance-system-api.us-e2.cloudhub.io"><http:uri-params ><![CDATA[#[output application/java
---
{ customer : attributes.uriParams.searchString}]]]></http:uri-params>
<http:query-params ><![CDATA[#[output application/java
---
{ fname : vars.fname,
lname : vars.lname,
dob : vars.dob}]]]>
kindly point out where i need to improve. thanks in advance.

The problem is indicated in the error message: HTTP OPTIONS on resource 'http://demo-insurance-system-api.us-e2.cloudhub.io' failed: not found (404).
It looks like your HTTP Request in the experience API is using the HTTP Options method. The RAML indicates that the API only accepts the HTTP GET method. In the XML it should look like: <http:request method="GET" ...

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.

GCP API Gateway: Path parameters are being passed as query params

I'm trying to use GCP API Gateway to create a single endpoint for a couple of my backend services (A,B,C,D), each with their own path structure. I have the Gateway configured for one of the services as follows:
swagger: '2.0'
info:
title: <TITLE>
description: <DESC>
version: 1.0.0
schemes:
- https
produces:
- application/json
paths:
/service_a/match/{id_}:
get:
summary: <SUMMARY>
description: <DESC>
operationId: match_id_
parameters:
- required: true
type: string
name: id_
in: path
- required: true
type: boolean
default: false
name: bool_first
in: query
- required: false
type: boolean
default: false
name: bool_Second
in: query
x-google-backend:
address: <cloud_run_url>/match/{id_}
deadline: 60.0
responses:
'200':
description: Successful Response
'422':
description: Validation
This deploys just fine. But when I hit the endpoint gateway_url/service_a/match/123, it gets routed to cloud_run_url/match/%7Bid_%7D?id_=123 instead of cloud_run_url/match/123.
How can I fix this?
Editing my answer as I misunderstood the issue.
It seems like the { are getting leaked from your configuration as ASCII code, so when you call
x-google-backend:
address: <cloud_run_url>/match/{id_}
deadline: 60.0
it doesn't show the correct ID.
So this should be a leak issue from your yaml file and you can approach this the same way as in this thread about using path params

Google API gateway Cors Headers Use options request

After implementing an api gateway in front of my app engine instances I got a problem stating that the request was blocked because of the CORS header. After searching online I found out that API gateway doesn't provide a way to set the CORS policy, however it also "overwrite" the header sent by my single back-end application. Does I need to implement a load balancer to set an additional Header or there is a way to avoid the overwrite?
Example of API:
paths:
"/login":
post:
description: "Login into the service"
operationId: "login"
x-google-backend:
address: https://project-id.oa.r.appspot.com/api/v1/login
produces:
- "application/json"
responses:
200:
description: "Projects retrieved successfully"
schema:
$ref: "#/definitions/access_token"
401:
description: "Wrong password"
schema:
type: "string"
404:
description: "User not exists"
schema:
type: "string"
parameters:
- in: body
name: user
description: The user to create.
schema:
type: object
required:
- userName
properties:
userName:
type: string
firstName:
type: string
lastName:
type: string
After a lot of trials, I found a simpler solution than implementing a load balancer in front of the gateway:
To use the CORS headers provided by the back-end application it is enough to add a OPTIONS request to the API to avoid headers being overwritten. So, given the login API I just need to add the request like this:
paths:
"/login":
post:
description: "Login into the service"
operationId: "login"
x-google-backend:
address: https://project-id.oa.r.appspot.com/api/v1/login
produces:
- "application/json"
responses:
200:
description: "Projects retrieved successfully"
schema:
$ref: "#/definitions/access_token"
401:
description: "Wrong password"
schema:
type: "string"
404:
description: "User not exists"
schema:
type: "string"
parameters:
- in: body
name: user
description: The user to create.
schema:
type: object
required:
- userName
properties:
userName:
type: string
firstName:
type: string
lastName:
type: string
options:
description: "Cors associated request to login"
operationId: "login cors"
x-google-backend:
address: https://project-id.oa.r.appspot.com/api/v1/login
responses:
200:
description: "Allow"
401:
description: "Cors not allowed"

How to check if ID in the URI is null and throw an error?

I tried to check if a specific URL doesn't specified an ID
This are what I've got so far:
http://localhost:8082/orders/1234 ---- Success!
http://localhost:8082/orders/ ---- URL not found!
I want to throw and error something like "Id not specified!" not a URL not found.
Look at Validation Module
Look at the "Matches Regex" section down.
I have not tried it
Hope it may help
the believe the best approach for you is to start using RAML. you can design the endpoint in RAML and specify what error to be thrown if the request is not a match.
/{order_id}:
get:
description: Get a Order by id
responses:
200:
body:
application/json:
type: Foo
example: !include examples/Order.json
400:
body:
application/json:
type: Error
example: !include examples/Error.json
in the above RAML definition if there is no orders/{order_id} you will get a http 400 returned.
Define a custom error handling and that will return error description alongside other required data
set status code 400 in a variable, map fields as per below example.
ex:
{
"error": {
"errorCode": "NOT_FOUND",
"errorDateTime": "2019-10-10T14:00:46",
"errorMessage": "The requested API cannot be found, ID is not specified",
"errorDescription": "Description: /orders"
}
}

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
}