Is It possible repass client header to sqs with API Gateway without lambda?
For example:
In Mapping templates -> Generate templates I access the body with $input.json('$').
I'm trying access
Action=SendMessage&MessageBody={"body": $util.escapeJavaScript($input.json('$')), "header": "$util.escapeJavaScript($input.params('header')"}
to repass header but it doesn't working because the header always comes empty.
Does Anybody knows how can I resolve this problem without lambda?
I resolved my problem changing the mapping templates to:
{
"method": "$context.httpMethod",
"body" : $input.json('$'),
"headers": {
#foreach($param in $input.params().header.keySet())
"$param": "$util.escapeJavaScript($input.params().header.get($param))"
#if($foreach.hasNext),#end
#end
}
}
Related
I need to customize the method handler for a REST API endpoint and point it to SQS instead of a Lambda function. I'm stalling out trying to get a full reference to the RestApi object...
The RestApi object I get back from RestApi.fromRestApiId is incomplete; I can't do this:
const restApi = apigateway.RestApi.fromRestApiId(this, 'RestApi', dependencies.api.rest.ApiId);
const queueResource = restApi.root.resourceForPath('/webhooks');
...without getting this error:
Error: root is not configured when imported using fromRestApiId(). Use fromRestApiAttributes() API instead.
I can't use RestApi.fromRestApiAttributes as that requires the rootResourceId -- which I can't seem to find a reference to. The documentation for RestApi.fromRestApiAttributes shows this, but I don't have props:
const api = RestApi.fromRestApiAttributes(this, 'RestApi', {
restApiId: props.restApiId,
rootResourceId: props.rootResourceId,
});
Does anyone know how to access the rootResourceId?
The root resource (/) id is a alphanumeric string like 4cfzeywftb, which can be found in the console breadcrumbs:
APIs > API (076t2zozc0) > Resources> / (4cfzeywftb)
or by calling get-resources:
aws apigateway get-resources --rest-api-id 076t2zozc0
{
"items": [
{
"id": "4cfzeywftb",
"path": "/",
"resourceMethods": {
"ANY": {}
}
},
{
"id": "36g7tq",
"parentId": "4cfzeywftb",
"pathPart": "{proxy+}",
"path": "/{proxy+}",
"resourceMethods": {
"ANY": {}
}
}
]
}
I'm having the following issues:
I need to allow CORS only on a specific domain
I need to make sure that secure cookies are sent along with the cross-origin request.
API Gateway specifies '*' as the Access-Control-Allow-Origin header and I need to only allow "example.com".
I found that I can do this by adding the following in override.ts in the rest API resource folder:
import { AmplifyApiRestResourceStackTemplate } from '#aws-amplify/cli-extensibility-helper';
export function override(resources: AmplifyApiRestResourceStackTemplate) {
// Change the default CORS response header Access-Control-Allow-Origin from "'*'" to the API's domain
resources.restApi.body.paths['/v1'].options['x-amazon-apigateway-integration'].responses.default.responseParameters['method.response.header.Access-Control-Allow-Origin'] = {
'Fn::Sub': "'https://www.example.com'"
};
}
This seems unreasonably hacky, but whatever.
But I can't seem to solve for the Access-Control-Allow-Credentials header... This doesn't work:
import { AmplifyApiRestResourceStackTemplate } from '#aws-amplify/cli-extensibility-helper';
export function override(resources: AmplifyApiRestResourceStackTemplate) {
// Change the default CORS response header Access-Control-Allow-Origin from "'*'" to the API's domain
resources.restApi.body.paths['/v1'].options['x-amazon-apigateway-integration'].responses.default.responseParameters['method.response.header.Access-Control-Allow-Origin'] = {
'Fn::Sub': "'https://www.example.com'"
};
// ADDING THIS ...
resources.restApi.body.paths['/v1'].options['x-amazon-apigateway-integration'].responses.default.responseParameters['method.response.header.Access-Control-Allow-Credentials'] = "true";
}
I get multiple errors, but it's basically complaining with this error for each of my REST endpoints:
Unable to put integration response on 'OPTIONS' for resource at path '/oauth/hubspot': Invalid mapping expression specified: Validation Result: warnings : [], errors : [Invalid mapping expression specified: true]
I get similar errors if I try any of the following:
// with quotes inside quotes
resources.restApi.body.paths['/v1'].options['x-amazon-apigateway-integration'].responses.default.responseParameters['method.response.header.Access-Control-Allow-Credentials'] = "'true'";
// this structure
resources.restApi.body.paths['/v1'].options['x-amazon-apigateway-integration'].responses.default.responseParameters['method.response.header.Access-Control-Allow-Credentials'] = {
'Fn::Sub': "'true'"
};
The thing is, I could easily do all this myself if Amplify would just let me override how I handle the OPTIONS request, and send it to my lambda function....
VICTORY!
I'd still be interested in any suggested approaches, but this worked for me:
// This file is used to override the REST API resources configuration
import { AmplifyApiRestResourceStackTemplate } from '#aws-amplify/cli-extensibility-helper';
export function override(resources: AmplifyApiRestResourceStackTemplate) {
delete resources.restApi.body.paths['/v1'].options;
delete resources.restApi.body.paths['/v1/{proxy+}'].options;
}
Basically, it's me telling Amplify to get out of the way and let me handle the OPTIONS request myself. So this leaves the "ANY" method on the endpoint so that the OPTIONS request flows through to the LAMBDA that's already configured. I already had the code in there to handle OPTIONS requests, so... It. Just. Worked.
I know it's been six months since you asked this question, but it's something i've just been smashing my head against now.
I had exactly the same issue:
I need CORS across several domains, but I can't use Access-Control-Allow-Origin: '*' because in the browser I am calling the api with { withCredentials: true }.
My Api's back into a lambda function which correctly handles the CORS preflight on the OPTION request. (ie. it returns Access-Control-Allow-Origin: https://permitted.origin.goes.here and associated headers).
But I get CORS errors because the AWS API Gateway response for OPTION is using a predefined MOCK response, which does not allow any other value for Access-Control-Allow-Origin than '*'.
(this was all generated by Amplify).
Anyways, I tried your method of deleting the OPTION handler, but that still did not work for me.
Eventually, after configuring it correctly in the AWS API Gateway interface, exporting the swagger and replicating that in the override.ts file, I finally got it cleanly working.
this was my code in override.ts:
import { AmplifyApiRestResourceStackTemplate } from '#aws-amplify/cli-extensibility-helper';
export function override(resources: AmplifyApiRestResourceStackTemplate) {
const { paths } = resources.restApi.body;
Object.keys(paths).forEach((path) => {
if (!!paths[path].options) {
const uri = paths[path]['x-amazon-apigateway-any-method']['x-amazon-apigateway-integration'].uri;
paths[path].options = {
"responses" : {
"200" : {
"description" : "200 response",
"headers" : {
"Access-Control-Allow-Credentials" : {
"type" : "string"
},
"Access-Control-Allow-Origin" : {
"type" : "string"
},
"Access-Control-Allow-Methods" : {
"type" : "string"
},
"Access-Control-Allow-Headers" : {
"type" : "string"
}
}
}
},
"x-amazon-apigateway-integration" : {
"httpMethod" : "POST",
"uri" : uri,
"responses" : {
"default" : {
"statusCode" : "200"
}
},
"passthroughBehavior" : "when_no_match",
"contentHandling" : "CONVERT_TO_TEXT",
"type" : "aws_proxy"
}
}
}
});
}
Now I can return secure cookies to a browser app from the AWS REST API without choking on CORS errors.
good luck!
"paths": {
"/{x}/{y}": {
"put": {
x-amazon-apigateway-integration": {
"uri": "https://hostname/contextpath/{x}/{y}"
"httpMethod": "PUT",
"type": "http"
}
}
}
If I do have query params for that resource then my invoke url would be https://apiid.execute-api.us-east-2.amazonaws.com/dev/x/y?queryparam1=value1&queryparam2=value2&queryparam3=value3&queryparam4=value4.
what would be my uri type in the integration request for my http backend?
How do I pass query params when calling it https://hostname/contextpath/{x}/{y}?=&= ? Can anyone suggest me how to pass query params when integrating with http type?
I use WireMock to stub responses from remote server.
In folder wiremock/mappings I have many files that set rules for stub.
Here example:
{
"request": {
"method": "GET",
"url": "/merchant"
},
"response": {
"headers": {
"Content-Type": "application/json"
},
"status": 200,
"fixedDelayMilliseconds": 3000,
"bodyFileName": "stub_response.json"
}
}
As you can see, when I get request /merchant then return stubbed response from file stub_response.json.
Nice. It's work fine.
But suppose I need temporary to disable this response. Is it possible to turn off stub response and return real response from remote server?
I need flag like this : "disable" : true
Is it possible in WireMock?
You can disable a rule by using the WireMock admin interface. If you invoke a DELETE command against http://.../__admin/mappings/<guid>, then the mapping will be deleted.
You can get a list of all the guids by performing a GET command against http://.../__admin/
If you want to replace a mapping with a real response instead of a stubbed response, you can add a second mapping that has a lower priority than the stubbed response and that proxies through to the real address. Normally, your stubbed response ill be used, but when you then delete the stubbed response, Wiremock will proxy through to the real address as it will then become the highest priority mapping.
To add a proxy, add the following into the response mapping
"proxyBaseUrl" : "http://otherhost.com",
To set the priority of mappings, add the following into your mapping JSON.
"priority": 1,
There presently isn't a disable attribute to a rule. You can remove a wiremock rule and re-add it later, which would functionally be the same.
If you running Wiremock as a Stand-alone process, you can set it into recording mode, effectively this will proxy all requests to a target URL.
Open http://localhost:8080/__admin/recorder (assuming you started WireMock on the default port of 8080), on that screen you have to enter the target URL and click the "Record" button. Until you stop recording it will make this Wiremock acting as a proxy.
If the recording is not something you actually need, you can dismiss recorded stub mappings. Although this is slightly misusing the Recorder feature it is a fast and easy way to temporarily turn your Wiremock into a proxy, instead of responding with stubs.
Wiremock can select which fixture to use depending on in which state of a scenario it runs. In different states, even for same request, it can have different mappings thus behave in different ways (in one of the states can respond from a fixture, and in another state it can be forwarded to the real API).
Say if there is a Proxy scenario, which is currently in the default Started state, then all the queries will be served by stubs that are not specific to any scenario state.
But if the Proxy scenario is in Active state, and we have a mapping that is specific for that scenario state (requiredScenarioState property), and it has a higher priority than default, then this mapping will be executed for any URI to this Wiremock instance:
{
"scenarioName": "Proxy",
"requiredScenarioState": "Activated",
"priority": 2,
"request": {
"urlPattern": ".*"
},
"response": {
"proxyBaseUrl": "https://swapi.dev/api"
}
}
The value for response.proxyBaseUrl, will make all the requests to be forwarded to provided host, when this mapping is matched.
Now we need to be able to toggle Proxy scenario to Activated state and back,
so we define two more mappings for POST /proxy endpoint on this Wiremock. They will have even higher priority - 1, so they can override even the mapping for "urlPattern": ".*".
Drop a JSON file containing all three mappings to your wiremock/mappings:
{
"mappings": [
{
"scenarioName": "Proxy",
"requiredScenarioState": "Activated",
"priority": 2,
"request": {
"urlPattern": ".*"
},
"response": {
"proxyBaseUrl": "https://swapi.dev/api"
}
},
{
"priority": 1,
"scenarioName": "Proxy",
"requiredScenarioState": "Activated",
"newScenarioState": "Started",
"request": { "method": "POST", "url": "/proxy"},
"response": { "status": 201, "body": "Proxy De-activated (Stubs will be used)" }
},
{
"priority": 1,
"scenarioName": "Proxy",
"requiredScenarioState": "Started",
"newScenarioState": "Activated",
"request": { "method": "POST", "url": "/proxy" },
"response": { "status": 201, "body": "Proxy Activated (Will forward all requests)" }
}
]
}
Now you can switch the Proxy scenario to Activated state,
and after switch it back to Started state with:
curl http://localhost:8080/proxy -XPOST
Good thing about this approach that this mapping file can be copied from project to project with minimal changes.
I'm using Wiremock to stub out some API calls made by mobile clients.
Struggling to get templated filename from request.
Can anyone confirm if this should even work with Wiremock standalone pls?
"response": { "status": 200, "bodyFileName": "Account-{{jsonPath request.body '$.user.identity'}}.json”}
I've got static files to be returned fine, just seems like bodyFileName doesn't like templating.
Example:
I configure wm with following Json:
{
"request": {
"method" : "GET",
"url": "/users/D8428899330"
},
"response": {
"status": 200,
"bodyFileName": "user-{{request.path.[1]}}.json"
}
}
Then when I attempt the url, http://localhost:9696/users/D8428899330 I get the following error,
HTTP ERROR: 500 Problem accessing /users/D8428899330. Reason:
java.lang.RuntimeException: java.io.FileNotFoundException:
/Users/iainframe/Documents/__files/user-{{request.path.[1]}}.json (No
such file or directory)
Should indicate that the file user-D8428899330.json resides in the correct location as I've hard coded it and it returns it ok. The command to start WireMock is:
java -jar ~/Documents/wm.jar --port 9696 --global-response-templating --verbose --root-dir /Users/iainframe/Documents/
The following response definition works fine for me with Wiremock 2.25.1 using the response template transformer.
"response": {
"transformers": ["response-template"],
"status": 200,
"bodyFileName": "user-{{jsonPath request.body '$.userid'}}.json" }
and this also works fine:
"response": {
"transformers": ["response-template"],
"status": 200,
"bodyFileName": "user-{{request.path.[3]}}.json" }
Interesting, I was running into the same problem and was able to solve it by starting WireMock with --global-response-templating. But you are using that parameter too... If it matters, I'm using WireMock 2.33.2.