Is it possible in the API Blueprint to define a set of possible responses for a given endpoint?
For example if I have a endpoint such as /movie/{id} I'd like to be able to define a set of movie records so that in the mock server I could GET /movie/1 or GET /movie/2 or GET /movie/3 and get the relevant record.
The examples I've seen all seem to define just one possible response.
You can add multiple request blocks, like this:
### Register [POST]
Registers an account
+ Request Already existing username
+ Body
{
"app": 3,
"username": "already-existing-username",
"password": "password"
}
+ Response 200 (application/json)
+ Body
{
"success": false,
"error": "The username specified is already registered to an account.",
"error_field": "username"
}
+ Request Invalid password
+ Body
{
"app": 3,
"username": "username",
"password": "password"
}
+ Response 200 (application/json)
+ Body
{
"success": false,
"error": "Please supply a valid password.",
"error_field": "password"
}
You can also find this in the official documentation
It's not possible to simulate this using a single action, but there is a workaround.
FORMAT: 1A
# Multi
## GET /movie/1
+ Response 200 (application/json)
{ "id": 1, "title": "First" }
## GET /movie/2
+ Response 200 (application/json)
{ "id": 2, "title": "Second" }
## GET /movie/3
+ Response 200 (application/json)
{ "id": 3, "title": "Third" }
## GET /movie/{id}
+ Parameters
+ id (required, number, `42`) ... Blah.
+ Response 200 (application/json)
{ "id": 42, "title": "First" }
Now, if you hit /movie/2, the mock server sends the appropriate response. Thanks.
this is how you provide multiple responses in your RAML file using Mulesoft(API designer) however if you're using mocking service for testing you'll always get the example response you set for testing
/{id}:
get:
headers:
Requester-Id:
required: true
responses:
200:
body:
application/json:
type: Account
example:
!include exapmle/AccountExample.raml
400:
body:
application/json:
example:
{"message":"Error retrieving account for the provided id"}
Related
rpc CreateBook(CreateBookRequest) returns (Book) {
option (google.api.http) = {
post: "/v1/{parent=publishers/*}/books"
body: "book"
};
}
message CreateBookRequest {
// The publisher who will publish this book.
// When using HTTP/JSON, this field is automatically populated based
// on the URI, because of the `{parent=publishers/*}` syntax.
string parent = 1 [
(google.api.field_behavior) = REQUIRED,
(google.api.resource_reference) = {
child_type: "library.googleapis.com/Book"
}];
Book book = 2 [(google.api.field_behavior) = REQUIRED];
string book_id = 3;
}
I don't understand post: "/v1/{parent=publishers/*}/books"
I thought publishers was a field in CreateBookRequest, then it populates to http, so it is something like this
post: "/v1/parent=publishers_field_value/books"
But publishers is not a field in CreateBookRequest
No, publishers is part of the expected value of the parent field. So suppose you have a protobuf request like this:
{
"parent": "publishers/pub1",
"book_id": "xyz"
"book": {
"author": "Stacy"
}
}
That can be transcoded by a client into an HTTP request with:
Method: POST
URI: /v1/publishers/pub1/books?bookId=xyz (with the appropriate host name)
Body:
{
"author": "Stacy"
}
If you try to specify a request with a parent that doesn't match publishers/*, I'd expect transcoding to fail.
That's in terms of transcoding from protobuf to HTTP, in the request. (That's the direction I'm most familiar with, having been coding it in C# just this week...)
In the server, it should just be the opposite - so given the HTTP request above, the server should come up with the original protobuf request including parent="publishers/pub1".
For a lot more information on all of this, see the proto defining HttpRule.
Using Gmail API to read my mailbox. The message reading process is working as expected but I want to change the label of reading messages just for acknowledgment purposes so that I can have track of the reading messages list in my Gmail inbox only. Tried given two methods to change the label but non of them worked for me. Need suggestion on the same
Methods:
Codebase is written in Golang (as a backend)
Tried with Google API Explorer
(METHOD 1) -
Go sample code:
gmsg: = gmail.ModifyMessageRequest {
RemoveLabelIds: [] string {
"INBOX". //system defined label
},
AddLabelIds: [] string {
"INBOXING" //my custom label. created through Gmail
},
}
_, errDelete: = gService.Users.Messages.Modify("me", messageid, &gmsg).Do()
if (errDelete != nil) {
logs.Error("GMAIL SERVICE ERROR:: for [", accountEmail, "] while moving message to [INBOXING] folder ", errDelete.Error())
}
Got below error :
{"level":"error","msg":"GMAIL SERVICE ERROR:: for [sample#gmail.com] while moving message to [INBOXING] folder googleapi: Error 400: Invalid label: INBOXING, invalidArgument","time":"2021-08-09 20:05:13"}
(METHOD 1) -
Gmail Modify API
Payload
{
"addLabelIds": [
"INBOXING"
],
"removeLabelIds": [
"INBOX"
]
}
Response from Google API -
{
"error": {
"code": 400,
"message": "Invalid label: INBOXING",
"errors": [
{
"message": "Invalid label: INBOXING",
"domain": "global",
"reason": "invalidArgument"
}
],
"status": "INVALID_ARGUMENT"
}
}
Observation - *
On modifying message with custom label's Gmail API return's 400 bad
request, but if we request with system labels it allows us to modify
the label.
You are using the label name instead of label id. To obtain the label id, you have to use the Method: users.labels.list
Response:
Once you have the ID, you can now use it in Method: users.messages.modify
Request body:
Response:
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'
So I'm trying to use the Youtube Data API with Kotlin + Spring Boot and I've been struggling a bit.
For now, I'm using hardcoded values for the api_key and the access_token for test purposes.
I'm trying to send a request to list my playlists but I keep getting this error:
"error": {
"code": 403,
"message": "The request is missing a valid API key.",
"errors": [
{
"message": "The request is missing a valid API key.",
"domain": "global",
"reason": "forbidden"
}
],
"status": "PERMISSION_DENIED"
Here's my code:
Controller:
#RestController
class PlaylistController(
private val youtubeService: YoutubeService
) {
#GetMapping("/playlists")
fun getPlaylists() : YoutubePlaylistResponse {
return youtubeService.getPlaylists()
}
}
Service:
#Service
class YoutubeService(
private val youtubeClient: YoutubeClient
) {
fun getPlaylists() = youtubeClient.getPlaylists(
access_token = "[ACCESS_TOKEN]",
api_key = "[API_KEY]"
)
}
Client
#FeignClient(name = "youtube", url = "https://www.googleapis.com/youtube/v3")
interface YoutubeClient {
#GetMapping("/playlists")
fun getPlaylists(
#RequestHeader("Authorization", required = true) access_token: String,
#RequestParam("api_key") api_key: String,
#RequestParam("part") part: String = "snippet",
#RequestParam("mine") mine: Boolean = true
): YoutubePlaylistResponse
}
Any thoughts on what I'm doing wrong?
PS: I'm getting the acess_token through the OAuth 2.0 Playground
Edit:
I was calling api_key but it's actually only key.
But now I'm getting a new problem:
"error": {
"code": 401,
"message": "The request uses the \u003ccode\u003emine\u003c/code\u003e parameter but is not properly authorized.",
"errors": [
{
"message": "The r... (464 bytes)]] with root cause
Apparently, it's because I'm trying to access my playlists and it says that I don't have the permission, but when I do the same request using cURL I get an appropriate response. Any thoughts on this?
According to the API documentation, the parameter should be called key rather than api_key:
Every request must either specify an API key (with the key parameter) or provide an OAuth 2.0 token. Your API key is available in the Developer Console's API Access pane for your project.
Source: https://developers.google.com/youtube/v3/docs
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.