This message was deleted.
# general
s
This message was deleted.
l
const api = new awsx.apigateway.API("macs-dev", {
routes: [
{
path: "/",
method: "GET",
eventHandler: new aws.lambda.CallbackFunction("get-handler", {
memorySize: 128,
layers: [
"arn:aws:lambda:us-east-2:485017757204:layer:data-api-layer:1",
],
callback: async (event) => {
const AWS = require("aws-sdk");
exports.handler = async function (event, context, callback) {
const data = require("data-api-client")({
secretArn: secret.arn,
resourceArn: db.arn,
database: "macsgameboard", // default database
region: "us-east-2",
});
`return data.query(
call getmiscdata();
);`
};
},
}),
apiKeyRequired: true,
},
],
apiKeySource: "HEADER",
});
const apikeys = awsx.apigateway.createAssociatedAPIKeys("my-api-keys", {
usagePlan: {
apiStages: [
{
apiId: api.restAPI.id,
stage: api.stage.stageName,
},
],
},
apiKeys: [{ name: "marketJS" }, { name: "macs-api-key" }],
});
the created lambda looks like:
exports.handler = __f0;
var __exports = {};
var __secret = {};
var __secret_arn_proto = {};
__f1.prototype = __secret_arn_proto;
Object.defineProperty(__secret_arn_proto, "constructor", { configurable: true, writable: true, value: __f1 });
Object.defineProperty(__secret_arn_proto, "apply", { configurable: true, writable: true, value: __f2 });
Object.defineProperty(__secret_arn_proto, "get", { configurable: true, writable: true, value: __f3 });
var __secret_arn = Object.create(__secret_arn_proto);
__secret_arn.value = "arn:aws:secretsmanager:us-east-2:485017757204:secret:macsdb_password-d28f2fe-bTXrLB";
__secret.arn = __secret_arn;
var __db = {};
var __db_arn = Object.create(__secret_arn_proto);
__db_arn.value = "arn:aws:rds:us-east-2:485017757204:cluster:tf-20200709154701156900000001";
__db.arn = __db_arn;
function __f1(__0) {
return (function() {
with({  }) {
return function /*constructor*/(value) {
this.value = value;
};
}
}).apply(undefined, undefined).apply(this, arguments);
}
function __f2(__0) {
return (function() {
with({  }) {
return function /*apply*/(func) {
throw new Error("'apply' is not allowed from inside a cloud-callback. Use 'get' to retrieve the value of this Output directly.");
};
}
}).apply(undefined, undefined).apply(this, arguments);
}
function __f3() {
return (function() {
with({  }) {
return function /*get*/() {
return this.value;
};
}
}).apply(undefined, undefined).apply(this, arguments);
}
function __f0(__0) {
return (function() {
with({ exports: __exports, secret: __secret, db: __db }) {
return async (event) => {
const AWS = require("aws-sdk");
exports.handler = async function (event, context, callback) {
const data = require("data-api-client")({
secretArn: secret.arn,
resourceArn: db.arn,
database: "macsgameboard", // default database
region: "us-east-2",
});
`return data.query(
call getmiscdata();
);`
};
};
}
}).apply(undefined, undefined).apply(this, arguments);
}
strange, right?
not sure why its adding all the extra crap, @faint-motherboard-95438
f
That’s weird indeed. Didn’t use Pulumi to make lambdas yet, but that’s definitely Pulumi adding code that seems useless to your handler. No idea why. Someone else here should be able to help you.
Feels like related to `Input`/`Output` again though. What’s the types of
secret.arn
and
db.arn
?
l
both strings
f
just
string
, not
pulumi.Output<string>
?
l
hmm
how do i find out?
im hard coding right now and same thing:
secretArn: "arnawssecretsmanagerus east 2485017757204㊙️rds-db-credentials/cluster-OIUG4EBTF7YZQN73YWXAOI3A64/admin-99EhqD", resourceArn: "arnawsrdsus east 2485017757204clustertf-20200709154701156900000001",
f
Ok, if hardcoded you get the same problem, we can exclude an
Output
issue
l
makes sense
ultimately this is the entirity of the lambda code:
const data = require("data-api-client")({ secretArn: "arnawssecretsmanagerus east 2485017757204㊙️rds-db-credentials/cluster-OIUG4EBTF7YZQN73YWXAOI3A64/admin-99EhqD", resourceArn: "arnawsrdsus east 2485017757204clustertf-20200709154701156900000001", database: "macsgameboard", // default database region: "us-east-2", }); return data.query(
call getmiscdata();
);
its really s trange
f
There’s a bunch of extra ‘stuff’ in the generated code due to the way serialization of closures works. I haven’t tried to run your example yet, but I do note that your callback includes a definition of an
exports.handler
— but note that the callback your providing is the handler
l
hmm
f
So you shouldn’t need to that additional export. I would expect to see that read more like:
Copy code
callback: async (event) => {
          const AWS = require("aws-sdk");
          const data = require("data-api-client")({
              secretArn: secret.arn.get(),
              resourceArn: db.arn.get(),
              database: "macsgameboard", // default database
              region: "us-east-2",
          });
         return data.query(`call getmiscdata();`);
          };
        },
l
Thank you, @faint-table-42725, I will try that.
f
Though, you technically shouldn’t need that require(‘aws-sdk’) either… I don’t see it getting used? If you need to reference the AWS sdk, you can always use
aws.sdk
assuming you’ve imported as
import * as aws from "@pulumi/aws"
l
ok
@faint-table-42725 is it easy to implement cors on a rest api gateway?
f
It should be relatively so — happy to help if you run into issues. There was an issue recently where I replied with the basics on how to do it: https://github.com/pulumi/pulumi-awsx/issues/533
l
@faint-table-42725 that doesnt make sense to me. I have tried that code.
Normally there would just be an enable_cors option in cf
f
@pulumi/awsx
uses API Gateway v1, where for enabling CORS, you end up having to do what’s described in https://docs.aws.amazon.com/apigateway/latest/developerguide/how-to-cors.html — which includes setting up mock integration for
OPTIONS
, etc. Unfortunately, there’s no built-in “enableCors” style functionality that abstracts that away in awsx.
As you point out, in Cloudformation, it’s easier since you can use SAM templates, which simply exposes a
CorsConfiguration
If you wanted to work with the HTTP API type in API Gateway, you could alternatively use https://www.pulumi.com/docs/reference/pkg/aws/apigatewayv2/api/
l
rest api is what I am trying to do not the http api
so its apigatewayv1
f
https://github.com/pulumi/pulumi-awsx/pull/331 shows how you might setup
OPTIONS
to enable CORS. You can either do it the way shown there (though, slightly more costly/inefficient) since it’s using a lambda to return the appropriate response, or configure a mock integration response per the AWS docs
l
can you do mock integrations with pulumi?
@faint-table-42725
f
Yes
l
how do you do it?
I am lost @faint-table-42725, i have everything else working but cors
f
I linked a pull request earlier — that’s a good starting point
I would try to at least get that working since the lambda it providers on
OPTIONS
is simply what the mock integration would typicall do
And then if you look at https://www.pulumi.com/docs/reference/pkg/aws/apigateway/integration/#example-usage — there’s a MOCK integration example at the end
The example in the integration response doc has and end-to-end 200 mock integration response
l
@faint-table-42725 is that what you would build out for cors?
f
No, the example isn’t for CORS
You would need to cross-ref the doc I pointed out earlier around the appropriate CORS headers to return
l
that would be a LOT of development if i have to build out one of those for EVERY endpoint on my rest api
f
I haven’t spent the time looking at this closely, but couldn’t you build it as a component resource and then reuse it?
l
hmm im new to pulumi, not sure how to do that. Sorry I ask so much. I love pulumi though.
f
It’s one of the nice things about pulumi is the ability to abstract things. A
ComponentResource
is simply an abstraction to represent a ‘thing’ that is made up of multiple underlying custom resources
You could also simply use code abstractions, too, to do what you want, e.g. create a function in your code like
makeCorsPath(…)
which takes the appropriate args and generates all the relevant resources
l
a lambda?
f
No, just regular code in your Pulumi program
so something like:
Copy code
function enableCorsForPath(path: string) {
    new Method(`${name}-options`), { ... });
    new Integration(`${name}-integration`, { ... });
    ...
}
and then anything you want to enable cors on a path, just include
enableCorsForPath("/api/path")
in your Pulumi program
l
oh ok
so basically build out what they have on that example but for cors
which is just headers that it needs to return
f
Yup
l
im not sure why im still lost, it seems like it should make sense, just to return the basic cors headers
but i cant wrap my head around it even though you skeletoned it out
f
API Gateway is a pretty complicated shape. It has a bunch of different resources you have to care about to ultimately make a response.
In the case we’re talking about, the way I think about it is this: I have some Lambda callback integration (e.g.
GET
on some path
/foo
) and so my Lambda must return the appropriate headers in its response. This is easy. I just have to include that in my response, e.g.
Copy code
return {
    statusCode: 200,
    headers: {
        "Access-Control-Allow-Headers" : "Content-Type",
        "Access-Control-Allow-Origin": "<https://www.example.com>",
        "Access-Control-Allow-Methods": "OPTIONS,POST,GET"
    },
    body: JSON.stringify(resp),
}
Now, the harder part, we have to account for the preflight
OPTIONS
request that a browser is going to send us
We could do it the easy way (what I suggested getting working first)
Which is to simply add another Lambda callback integration on
OPTIONS
which simply returns the headers with an empty body
So it would look very similar to your actual function, sans any business logic and returning an empty body.
Now, if you want to do it the “API Gateway Way (tm)” you can do what we just discussed — setup a mock integration for
OPTIONS
on that path instead, which involves having the
IntegrationResponse
etc.
If you’re going to be setting up multiple paths with CORS, it’s easiest to wrap that either in a
ComponentResource
or some other abstraction (say a function in your program itself) that will give you back a thing that encapsulates what we just talked about — either creating the simple Lambda callback that returns the headers w/ empty body or the set of various API Gateway resources (Resource, Integration, Method, IntegrationResponse) to give back the correct mock response.
l
i got that to work
the lambda that is
f
Great!
l
how does this look?
function enableCorsForPath(name) {
`const resource = new aws.apigateway.Resource(
"${name}-resource
, {`
parentId: api.rootResourceId,
pathPart: name,
restApi: api.id,
});
`const method = new aws.apigateway.Method(
"${name}-method"
, {`
authorization: "NONE",
httpMethod: "GET",
resourceId: resource.id,
restApi: api.id,
});
`const integration = new aws.apigateway.Integration(
${name}-integration
, {`
httpMethod: method.httpMethod,
resourceId: resource.id,
restApi: api.id,
type: "MOCK",
});
const response200 = new aws.apigateway.MethodResponse("response_200", {
httpMethod: method.httpMethod,
resourceId: resource.id,
restApi: api.id,
statusCode: "200",
});
const integrationresponse = new aws.apigateway.IntegrationResponse(
``${name}-integrationresponse`,`
{
httpMethod: method.httpMethod,
resourceId: resource.id,
// Transforms the backend JSON response to XML
responseTemplates: {},
restApi: api.id,
statusCode: response200.statusCode,
headers: {
"Access-Control-Allow-Headers": "Content-Type",
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods": "OPTIONS,POST,GET",
},
}
);
}
my resource says its missing parentId but its right there
f
I think you’re on the right track. You’ll probably want to parameterize some more of this like the
api
and parenting and some other things.
But I can totally see something along the lines of the above working and then you can totally refactor more and more later
One thing you’ll probably want to take a look at is either
ComponentResource
or
parent
so that you can nest some resources of these w/in your resource graph
l
my problem is that I built my api with aws.apigateway and they are using awsx