From 5323163a732e2c62cee938e901a2dc02092359a3 Mon Sep 17 00:00:00 2001 From: Sebastian Korfmann Date: Tue, 13 Jun 2023 18:04:21 +0200 Subject: [PATCH 01/47] API SDK: Add Cors This is an intial WIP commit, targeting the Simulator only and still WIP --- examples/tests/valid/website_with_api.w | 11 ++++- libs/wingsdk/src/cloud/api.ts | 43 ++++++++++++++++++- libs/wingsdk/src/target-sim/api.inflight.ts | 30 +++++++++++++ libs/wingsdk/src/target-sim/api.ts | 10 +++++ .../src/target-sim/schema-resources.ts | 3 +- 5 files changed, 93 insertions(+), 4 deletions(-) diff --git a/examples/tests/valid/website_with_api.w b/examples/tests/valid/website_with_api.w index 6388eb71bb0..d95b8594b41 100644 --- a/examples/tests/valid/website_with_api.w +++ b/examples/tests/valid/website_with_api.w @@ -1,7 +1,15 @@ bring cloud; //needs to be written before the website (so the website will be able to use it's url on sim env) -let api = new cloud.Api(); +let api = new cloud.Api( + cors: cloud.ApiCorsProps { + origins: ["*"], + methods: [cloud.HttpMethod.GET, cloud.HttpMethod.POST, cloud.HttpMethod.OPTIONS], + headers: ["Content-Type"], + allowCredentials: false, + exposedHeaders: ["Content-Type"] + } +); let website = new cloud.Website(path: "./website_with_api"); @@ -52,6 +60,5 @@ let optionsHandler = inflight(req: cloud.ApiRequest): cloud.ApiResponse => { api.get("/users", getHandler); api.post("/users", postHandler); -api.options("/users", optionsHandler); website.addJson("config.json", { apiUrl: api.url }); diff --git a/libs/wingsdk/src/cloud/api.ts b/libs/wingsdk/src/cloud/api.ts index 2399181de81..82275038437 100644 --- a/libs/wingsdk/src/cloud/api.ts +++ b/libs/wingsdk/src/cloud/api.ts @@ -9,11 +9,52 @@ import { IResource, Resource } from "../std/resource"; export const API_FQN = fqnForType("cloud.Api"); +/** + * Cors Properties for `Api`. + */ +export interface ApiCorsProps { + /** + * The list of allowed origins. + * @example ["https://example.com"] + */ + readonly origins: Array; + + /** + * The list of allowed methods. + * @example [HttpMethod.GET, HttpMethod.POST] + */ + readonly methods: Array; + + /** + * The list of allowed headers. + * @example ["Content-Type"] + */ + readonly headers?: Array; + + /** + * The list of exposed headers. + * @example ["Content-Type"] + */ + readonly exposedHeaders: Array; + + /** + * Whether to allow credentials. + */ + readonly allowCredentials: boolean; +} + /** * Properties for `Api`. */ -export interface ApiProps {} +export interface ApiProps { + /** + * Options for configuring the API's CORS behavior across all routes. + * Options can also be overridden on a per-route basis. (not yet implemented) + * @default - CORS is disabled + */ + readonly cors?: ApiCorsProps; +} /** * The OpenAPI spec. */ diff --git a/libs/wingsdk/src/target-sim/api.inflight.ts b/libs/wingsdk/src/target-sim/api.inflight.ts index 3fbc7b68741..0ab070b7b61 100644 --- a/libs/wingsdk/src/target-sim/api.inflight.ts +++ b/libs/wingsdk/src/target-sim/api.inflight.ts @@ -38,6 +38,7 @@ export class Api props; this.routes = []; this.context = context; + const { cors } = props; // Set up an express server that handles the routes. this.app = express(); @@ -46,6 +47,35 @@ export class Api // matching the limit to aws api gateway's payload size limit: // https://docs.aws.amazon.com/apigateway/latest/developerguide/limits.html this.app.use(express.text({ limit: "10mb", type: "*/*" })); + + // Set up CORS headers for options requests. + if (cors) { + // inspired by https://github.com/expressjs/cors/blob/f038e7722838fd83935674aa8c5bf452766741fb/lib/index.js#L159-L190 + const { origins, methods, headers, exposedHeaders } = cors; + this.app.use((req, res, next) => { + const responseHeaders: Record = {}; + const method = req.method && req.method.toUpperCase && req.method.toUpperCase(); + + responseHeaders["Access-Control-Allow-Origin"] = origins.join(","); + responseHeaders["Access-Control-Allow-Methods"] = methods.join(","); + responseHeaders["Access-Control-Expose-Headers"] = exposedHeaders.join(","); + + if (method === 'OPTIONS') { + if (headers) { + responseHeaders["Access-Control-Allow-Headers"] = headers.join(","); + } + for (const key of Object.keys(responseHeaders)) { + res.setHeader(key, responseHeaders[key]); + } + res.status(204).send(); + } else { + for (const key of Object.keys(responseHeaders)) { + res.setHeader(key, responseHeaders[key]); + } + next(); + } + }); + } } public async addEventSubscription( diff --git a/libs/wingsdk/src/target-sim/api.ts b/libs/wingsdk/src/target-sim/api.ts index 5cc155c7b73..d277d494610 100644 --- a/libs/wingsdk/src/target-sim/api.ts +++ b/libs/wingsdk/src/target-sim/api.ts @@ -8,6 +8,7 @@ import * as cloud from "../cloud"; import * as core from "../core"; import { IInflightHost, Resource } from "../std"; import { BaseResourceSchema } from "../testing/simulator"; +import { Construct } from "constructs"; /** * Simulator implementation of `cloud.Api`. @@ -15,8 +16,16 @@ import { BaseResourceSchema } from "../testing/simulator"; * @inflight `@winglang/sdk.cloud.IApiClient` */ export class Api extends cloud.Api implements ISimulatorResource { + public readonly cors?: cloud.ApiCorsProps; + private eventMappings: { [key: string]: EventMapping } = {}; + constructor(scope: Construct, id: string, props: cloud.ApiProps = {}) { + super(scope, id, props); + + this.cors = props.cors + } + public get url(): string { return simulatorAttrToken(this, "url"); } @@ -201,6 +210,7 @@ export class Api extends cloud.Api implements ISimulatorResource { path: this.node.path, props: { openApiSpec: this._getApiSpec(), + cors: this.cors, }, attrs: {} as any, }; diff --git a/libs/wingsdk/src/target-sim/schema-resources.ts b/libs/wingsdk/src/target-sim/schema-resources.ts index e09119e0be0..ecc6bb9de67 100644 --- a/libs/wingsdk/src/target-sim/schema-resources.ts +++ b/libs/wingsdk/src/target-sim/schema-resources.ts @@ -1,4 +1,4 @@ -import { ColumnType, HttpMethod, OpenApiSpec } from "../cloud"; +import { ApiCorsProps, ColumnType, HttpMethod, OpenApiSpec } from "../cloud"; import { Json } from "../std"; import { BaseResourceAttributes, @@ -29,6 +29,7 @@ export interface ApiSchema extends BaseResourceSchema { readonly type: typeof API_TYPE; readonly props: { openApiSpec: OpenApiSpec; + cors?: ApiCorsProps; }; readonly attrs: ApiAttributes & BaseResourceAttributes; } From 446d43c249735d81ab44d8eb79b025e73999b8c3 Mon Sep 17 00:00:00 2001 From: Sebastian Korfmann Date: Tue, 13 Jun 2023 22:23:08 +0200 Subject: [PATCH 02/47] Test correct headers are returned --- examples/tests/valid/website_with_api.js | 28 +++++++++++++++ examples/tests/valid/website_with_api.w | 38 ++++++++++++++------- libs/wingsdk/src/target-sim/api.inflight.ts | 5 +-- 3 files changed, 57 insertions(+), 14 deletions(-) create mode 100644 examples/tests/valid/website_with_api.js diff --git a/examples/tests/valid/website_with_api.js b/examples/tests/valid/website_with_api.js new file mode 100644 index 00000000000..f853285c028 --- /dev/null +++ b/examples/tests/valid/website_with_api.js @@ -0,0 +1,28 @@ +import internalFetch from "node-fetch"; + +export async function fetch(url, method, headers, body) { + const response = await internalFetch(url, { + method, + body, + headers, + }); + + const responseStatus = response.status, + responseHeaders = [...response.headers.entries()].reduce((acc, [key, value]) => { + acc[key] = value; + return acc; + }, {}); + + if (method === "OPTIONS") { + return { + status: responseStatus, + headers: responseHeaders, + body: null + }} else { + return { + status: responseStatus, + headers: responseHeaders, + body: response.json(), + } + } +} \ No newline at end of file diff --git a/examples/tests/valid/website_with_api.w b/examples/tests/valid/website_with_api.w index d95b8594b41..a00edb15f87 100644 --- a/examples/tests/valid/website_with_api.w +++ b/examples/tests/valid/website_with_api.w @@ -1,5 +1,9 @@ bring cloud; +class TestUtils { + extern "./website_with_api.js" static inflight fetch(url: str, method: str, headers: Json?, body: str?): Json; +} + //needs to be written before the website (so the website will be able to use it's url on sim env) let api = new cloud.Api( cors: cloud.ApiCorsProps { @@ -46,19 +50,29 @@ let postHandler = inflight (req: cloud.ApiRequest): cloud.ApiResponse => { }; }; - // responsible for the CORS - https://docs.aws.amazon.com/apigateway/latest/developerguide/how-to-cors.html -let optionsHandler = inflight(req: cloud.ApiRequest): cloud.ApiResponse => { - return cloud.ApiResponse { - headers: { - "Access-Control-Allow-Headers" : "Content-Type", - "Access-Control-Allow-Origin": "*", - "Access-Control-Allow-Methods": "OPTIONS,POST,GET" - }, - status: 204 - }; -}; - api.get("/users", getHandler); api.post("/users", postHandler); website.addJson("config.json", { apiUrl: api.url }); + +test "GET /users" { + let res = TestUtils.fetch(api.url + "/users", "GET"); + assert(num.fromJson(res.get("status")) == 200); + + assert(res.get("headers").get("access-control-allow-origin") == "*"); + assert(res.get("headers").get("access-control-expose-headers") == "Content-Type"); + assert(res.get("headers").get("access-control-allow-credentials") == "false"); + + assert(res.get("headers").get("access-control-allow-headers") == nil); + assert(res.get("headers").get("access-control-allow-methods") == nil); +} + +test "OPTIONS /users" { + let res = TestUtils.fetch(api.url + "/users", "OPTIONS"); + assert(num.fromJson(res.get("status")) == 204); + assert(res.get("headers").get("access-control-allow-origin") == "*"); + assert(res.get("headers").get("access-control-allow-methods") == "GET,POST,OPTIONS"); + assert(res.get("headers").get("access-control-allow-headers") == "Content-Type"); + assert(res.get("headers").get("access-control-expose-headers") == "Content-Type"); + assert(res.get("headers").get("access-control-allow-credentials") == "false"); +} \ No newline at end of file diff --git a/libs/wingsdk/src/target-sim/api.inflight.ts b/libs/wingsdk/src/target-sim/api.inflight.ts index 0ab070b7b61..fcbb63b7198 100644 --- a/libs/wingsdk/src/target-sim/api.inflight.ts +++ b/libs/wingsdk/src/target-sim/api.inflight.ts @@ -51,19 +51,20 @@ export class Api // Set up CORS headers for options requests. if (cors) { // inspired by https://github.com/expressjs/cors/blob/f038e7722838fd83935674aa8c5bf452766741fb/lib/index.js#L159-L190 - const { origins, methods, headers, exposedHeaders } = cors; + const { origins, methods, headers, exposedHeaders, allowCredentials } = cors; this.app.use((req, res, next) => { const responseHeaders: Record = {}; const method = req.method && req.method.toUpperCase && req.method.toUpperCase(); responseHeaders["Access-Control-Allow-Origin"] = origins.join(","); - responseHeaders["Access-Control-Allow-Methods"] = methods.join(","); responseHeaders["Access-Control-Expose-Headers"] = exposedHeaders.join(","); + responseHeaders["Access-Control-Allow-Credentials"] = allowCredentials ? "true" : "false"; if (method === 'OPTIONS') { if (headers) { responseHeaders["Access-Control-Allow-Headers"] = headers.join(","); } + responseHeaders["Access-Control-Allow-Methods"] = methods.join(","); for (const key of Object.keys(responseHeaders)) { res.setHeader(key, responseHeaders[key]); } From 74741984dd64deb4a718fafe06bc104b13c2fafd Mon Sep 17 00:00:00 2001 From: Sebastian Korfmann Date: Tue, 22 Aug 2023 16:58:42 +0200 Subject: [PATCH 03/47] fix test --- examples/tests/valid/website_with_api.js | 28 --------- examples/tests/valid/website_with_api.w | 77 ++++++++++++++++++------ 2 files changed, 58 insertions(+), 47 deletions(-) delete mode 100644 examples/tests/valid/website_with_api.js diff --git a/examples/tests/valid/website_with_api.js b/examples/tests/valid/website_with_api.js deleted file mode 100644 index f853285c028..00000000000 --- a/examples/tests/valid/website_with_api.js +++ /dev/null @@ -1,28 +0,0 @@ -import internalFetch from "node-fetch"; - -export async function fetch(url, method, headers, body) { - const response = await internalFetch(url, { - method, - body, - headers, - }); - - const responseStatus = response.status, - responseHeaders = [...response.headers.entries()].reduce((acc, [key, value]) => { - acc[key] = value; - return acc; - }, {}); - - if (method === "OPTIONS") { - return { - status: responseStatus, - headers: responseHeaders, - body: null - }} else { - return { - status: responseStatus, - headers: responseHeaders, - body: response.json(), - } - } -} \ No newline at end of file diff --git a/examples/tests/valid/website_with_api.w b/examples/tests/valid/website_with_api.w index 935f4cf2722..3ca9e225c7c 100644 --- a/examples/tests/valid/website_with_api.w +++ b/examples/tests/valid/website_with_api.w @@ -1,9 +1,6 @@ bring cloud; bring ex; - -class TestUtils { - extern "./website_with_api.js" static inflight fetch(url: str, method: str, headers: Json?, body: str?): Json; -} +bring http; //needs to be written before the website (so the website will be able to use it's url on sim env) let api = new cloud.Api( @@ -28,7 +25,6 @@ let usersTable = new ex.Table( } ); - let getHandler = inflight (req: cloud.ApiRequest): cloud.ApiResponse => { return cloud.ApiResponse { body: Json.stringify({ users: usersTable.list() }), @@ -56,24 +52,67 @@ api.post("/users", postHandler); website.addJson("config.json", { apiUrl: api.url }); +inflight class Assert { + static equalStr(a: str, b: str): bool { + try { + assert(a == b); + } catch e { + throw("expected: ${b} got: ${a}"); + } + } + + static isNil(a: str?): bool { + try { + assert(a == nil); + } catch e { + throw("expected ${a} to be nil"); + } + } + + static equalNum(a: num, b: num): bool{ + try { + assert(a == b); + } catch e { + log(e); + throw("expected: ${b} got: ${a}"); + } + } +} + test "GET /users" { - let res = TestUtils.fetch(api.url + "/users", "GET"); - assert(num.fromJson(res.get("status")) == 200); + let response = http.fetch(api.url + "/users", { + method: http.HttpMethod.GET, + headers: { + "Content-Type": "text/json" + } + }); - assert(res.get("headers").get("access-control-allow-origin") == "*"); - assert(res.get("headers").get("access-control-expose-headers") == "Content-Type"); - assert(res.get("headers").get("access-control-allow-credentials") == "false"); +let headers = response.headers; + Assert.equalNum(response.status, 200); - assert(res.get("headers").get("access-control-allow-headers") == nil); - assert(res.get("headers").get("access-control-allow-methods") == nil); + log(Json.stringify(headers)); + + Assert.equalStr(headers.get("access-control-allow-origin"), "*"); + Assert.equalStr(headers.get("access-control-expose-headers"), "Content-Type"); + Assert.equalStr(headers.get("access-control-allow-credentials"), "false"); + + Assert.isNil(headers.get("access-control-allow-headers")); + Assert.isNil(headers.get("access-control-allow-methods")); } test "OPTIONS /users" { - let res = TestUtils.fetch(api.url + "/users", "OPTIONS"); - assert(num.fromJson(res.get("status")) == 204); - assert(res.get("headers").get("access-control-allow-origin") == "*"); - assert(res.get("headers").get("access-control-allow-methods") == "GET,POST,OPTIONS"); - assert(res.get("headers").get("access-control-allow-headers") == "Content-Type"); - assert(res.get("headers").get("access-control-expose-headers") == "Content-Type"); - assert(res.get("headers").get("access-control-allow-credentials") == "false"); + let response = http.fetch(api.url + "/users", { + method: http.HttpMethod.OPTIONS, + headers: { + "Content-Type": "text/json" + } + }); + + let headers = response.headers; + Assert.equalNum(response.status, 204); + Assert.equalStr(headers.get("access-control-allow-origin"), "*"); + Assert.equalStr(headers.get("access-control-allow-methods"), "GET,POST,OPTIONS"); + Assert.equalStr(headers.get("access-control-allow-headers"), "Content-Type"); + Assert.equalStr(headers.get("access-control-expose-headers"), "Content-Type"); + Assert.equalStr(headers.get("access-control-allow-credentials"), "false"); } \ No newline at end of file From 58f42ef0933d0d60dd5bc4cbb6fdd11c6fa02a89 Mon Sep 17 00:00:00 2001 From: Sebastian Korfmann Date: Tue, 22 Aug 2023 20:04:07 +0200 Subject: [PATCH 04/47] More tests --- examples/tests/valid/assertions.w | 27 +++++ examples/tests/valid/cors_api.w | 113 ++++++++++++++++++++ examples/tests/valid/website_with_api.w | 52 +++------ libs/wingsdk/src/cloud/api.ts | 16 ++- libs/wingsdk/src/target-sim/api.inflight.ts | 22 +++- 5 files changed, 182 insertions(+), 48 deletions(-) create mode 100644 examples/tests/valid/assertions.w create mode 100644 examples/tests/valid/cors_api.w diff --git a/examples/tests/valid/assertions.w b/examples/tests/valid/assertions.w new file mode 100644 index 00000000000..d406d818bee --- /dev/null +++ b/examples/tests/valid/assertions.w @@ -0,0 +1,27 @@ +inflight class Assert { + static equalStr(a: str, b: str): bool { + try { + assert(a == b); + } catch e { + throw("expected: ${b} got: ${a}"); + } + } + + static isNil(a: str?): bool { + try { + assert(a == nil); + } catch e { + log(e); + throw("expected '${a}' to be nil"); + } + } + + static equalNum(a: num, b: num): bool{ + try { + assert(a == b); + } catch e { + log(e); + throw("expected: ${b} got: ${a}"); + } + } +} \ No newline at end of file diff --git a/examples/tests/valid/cors_api.w b/examples/tests/valid/cors_api.w new file mode 100644 index 00000000000..519a18316cf --- /dev/null +++ b/examples/tests/valid/cors_api.w @@ -0,0 +1,113 @@ +bring cloud; +bring ex; +bring http; +bring "./assertions.w" as t; + + +// DEFAULT CORS +// ============ +// without specifying cors options, the default is to allow all origins +let apiDefaultCors = new cloud.Api( + cors: {} +); + +let getHandler = inflight (req) => { + return { + body: "hello world", + status: 200 + }; +}; + +apiDefaultCors.get("/users", getHandler); + +test "GET /users has default cors headers" { + let response = http.fetch(apiDefaultCors.url + "/users", { + method: http.HttpMethod.GET, + headers: { + "Content-Type": "text/json" + } + }); + +let headers = response.headers; + t.Assert.equalNum(response.status, 200); + t.Assert.equalStr(headers.get("access-control-allow-origin"), "*"); + t.Assert.equalStr(headers.get("access-control-allow-credentials"), "false"); + t.Assert.isNil(headers.get("access-control-allow-headers")); + + t.Assert.isNil(headers.get("access-control-expose-headers")); + t.Assert.isNil(headers.get("access-control-allow-methods")); +} + +test "OPTIONS /users has default cors headers" { + let response = http.fetch(apiDefaultCors.url + "/users", { + method: http.HttpMethod.OPTIONS, + headers: { + "Content-Type": "text/json" + } + }); + + let headers = response.headers; + t.Assert.equalNum(response.status, 204); + t.Assert.equalStr(headers.get("access-control-allow-origin"), "*"); + t.Assert.equalStr(headers.get("access-control-allow-methods"), "GET,HEAD,PUT,PATCH,POST,DELETE"); + t.Assert.equalStr(headers.get("access-control-allow-headers"), "Content-Type,Authorization"); + t.Assert.equalStr(headers.get("access-control-allow-credentials"), "false"); + t.Assert.isNil(headers.get("access-control-expose-headers")); +} + +// Custom CORS +// ============ +let apiCustomCors = new cloud.Api( + cors: { + origins: ["winglang.io"], + methods: [cloud.HttpMethod.GET, cloud.HttpMethod.POST, cloud.HttpMethod.OPTIONS], + headers: ["Content-Type", "Authorization", "X-Custom-Header"], + allowCredentials: true, + exposedHeaders: ["Content-Type"] + } +) as "apiCustomCors"; + +let getCustomHandler = inflight (req) => { + return { + body: "hello world", + status: 200 + }; +}; + +apiCustomCors.get("/users", getCustomHandler); + +test "GET /users has cors headers" { + let response = http.fetch(apiCustomCors.url + "/users", { + method: http.HttpMethod.GET, + headers: { + "Content-Type": "text/json" + } + }); + +let headers = response.headers; + t.Assert.equalNum(response.status, 200); + + t.Assert.equalStr(headers.get("access-control-allow-origin"), "winglang.io"); + t.Assert.equalStr(headers.get("access-control-allow-credentials"), "true"); + t.Assert.equalStr(headers.get("access-control-expose-headers"), "Content-Type"); + + t.Assert.isNil(headers.get("access-control-allow-headers")); + t.Assert.isNil(headers.get("access-control-allow-methods")); +} + +test "OPTIONS /users has cors headers" { + let response = http.fetch(apiCustomCors.url + "/users", { + method: http.HttpMethod.OPTIONS, + headers: { + "Content-Type": "text/json" + } + }); + + let headers = response.headers; + t.Assert.equalNum(response.status, 204); + t.Assert.equalStr(headers.get("access-control-allow-origin"), "winglang.io"); + t.Assert.equalStr(headers.get("access-control-allow-methods"), "GET,POST,OPTIONS"); + t.Assert.equalStr(headers.get("access-control-allow-headers"), "Content-Type,Authorization,X-Custom-Header"); + t.Assert.equalStr(headers.get("access-control-allow-credentials"), "true"); + t.Assert.equalStr(headers.get("access-control-expose-headers"), "Content-Type"); +} \ No newline at end of file diff --git a/examples/tests/valid/website_with_api.w b/examples/tests/valid/website_with_api.w index 3ca9e225c7c..fdc76be7736 100644 --- a/examples/tests/valid/website_with_api.w +++ b/examples/tests/valid/website_with_api.w @@ -1,6 +1,7 @@ bring cloud; bring ex; bring http; +bring "./assertions.w" as t; //needs to be written before the website (so the website will be able to use it's url on sim env) let api = new cloud.Api( @@ -52,33 +53,6 @@ api.post("/users", postHandler); website.addJson("config.json", { apiUrl: api.url }); -inflight class Assert { - static equalStr(a: str, b: str): bool { - try { - assert(a == b); - } catch e { - throw("expected: ${b} got: ${a}"); - } - } - - static isNil(a: str?): bool { - try { - assert(a == nil); - } catch e { - throw("expected ${a} to be nil"); - } - } - - static equalNum(a: num, b: num): bool{ - try { - assert(a == b); - } catch e { - log(e); - throw("expected: ${b} got: ${a}"); - } - } -} - test "GET /users" { let response = http.fetch(api.url + "/users", { method: http.HttpMethod.GET, @@ -88,16 +62,16 @@ test "GET /users" { }); let headers = response.headers; - Assert.equalNum(response.status, 200); + t.Assert.equalNum(response.status, 200); log(Json.stringify(headers)); - Assert.equalStr(headers.get("access-control-allow-origin"), "*"); - Assert.equalStr(headers.get("access-control-expose-headers"), "Content-Type"); - Assert.equalStr(headers.get("access-control-allow-credentials"), "false"); + t.Assert.equalStr(headers.get("access-control-allow-origin"), "*"); + t.Assert.equalStr(headers.get("access-control-expose-headers"), "Content-Type"); + t.Assert.equalStr(headers.get("access-control-allow-credentials"), "false"); - Assert.isNil(headers.get("access-control-allow-headers")); - Assert.isNil(headers.get("access-control-allow-methods")); + t.Assert.isNil(headers.get("access-control-allow-headers")); + t.Assert.isNil(headers.get("access-control-allow-methods")); } test "OPTIONS /users" { @@ -109,10 +83,10 @@ test "OPTIONS /users" { }); let headers = response.headers; - Assert.equalNum(response.status, 204); - Assert.equalStr(headers.get("access-control-allow-origin"), "*"); - Assert.equalStr(headers.get("access-control-allow-methods"), "GET,POST,OPTIONS"); - Assert.equalStr(headers.get("access-control-allow-headers"), "Content-Type"); - Assert.equalStr(headers.get("access-control-expose-headers"), "Content-Type"); - Assert.equalStr(headers.get("access-control-allow-credentials"), "false"); + t.Assert.equalNum(response.status, 204); + t.Assert.equalStr(headers.get("access-control-allow-origin"), "*"); + t.Assert.equalStr(headers.get("access-control-allow-methods"), "GET,POST,OPTIONS"); + t.Assert.equalStr(headers.get("access-control-allow-headers"), "Content-Type"); + t.Assert.equalStr(headers.get("access-control-expose-headers"), "Content-Type"); + t.Assert.equalStr(headers.get("access-control-allow-credentials"), "false"); } \ No newline at end of file diff --git a/libs/wingsdk/src/cloud/api.ts b/libs/wingsdk/src/cloud/api.ts index 4d84d4acf69..e04733bea1b 100644 --- a/libs/wingsdk/src/cloud/api.ts +++ b/libs/wingsdk/src/cloud/api.ts @@ -16,31 +16,36 @@ export interface ApiCorsProps { /** * The list of allowed origins. * @example ["https://example.com"] + * @default - ["*"] */ - readonly origins: Array; + readonly origins?: Array; /** * The list of allowed methods. * @example [HttpMethod.GET, HttpMethod.POST] + * @default - [HttpMethod.GET, HttpMethod.POST, HttpMethod.PUT, HttpMethod.PATCH, HttpMethod.DELETE, HttpMethod.HEAD, HttpMethod.OPTIONS] */ - readonly methods: Array; + readonly methods?: Array; /** * The list of allowed headers. * @example ["Content-Type"] + * @default - ["Content-Type", "Authorization"] */ readonly headers?: Array; /** * The list of exposed headers. * @example ["Content-Type"] + * @default - [] */ - readonly exposedHeaders: Array; + readonly exposedHeaders?: Array; /** * Whether to allow credentials. + * @default - false */ - readonly allowCredentials: boolean; + readonly allowCredentials?: boolean; } /** @@ -51,6 +56,9 @@ export interface ApiProps { /** * Options for configuring the API's CORS behavior across all routes. * Options can also be overridden on a per-route basis. (not yet implemented) + * Can be set to to an empty Struct to enable CORS with default options. + * @example { origins: ["https://example.com"] } + * @example {} * @default - CORS is disabled */ readonly cors?: ApiCorsProps; diff --git a/libs/wingsdk/src/target-sim/api.inflight.ts b/libs/wingsdk/src/target-sim/api.inflight.ts index 7e09e7e1701..062c01086a5 100644 --- a/libs/wingsdk/src/target-sim/api.inflight.ts +++ b/libs/wingsdk/src/target-sim/api.inflight.ts @@ -51,20 +51,32 @@ export class Api // Set up CORS headers for options requests. if (cors) { // inspired by https://github.com/expressjs/cors/blob/f038e7722838fd83935674aa8c5bf452766741fb/lib/index.js#L159-L190 - const { origins, methods, headers, exposedHeaders, allowCredentials } = cors; + const { + origins = ["*"], + methods = ["GET", "HEAD", "PUT", "PATCH", "POST", "DELETE"], + headers = ["Content-Type", "Authorization"], + exposedHeaders = [], + allowCredentials = false, + } = cors; this.app.use((req, res, next) => { const responseHeaders: Record = {}; const method = req.method && req.method.toUpperCase && req.method.toUpperCase(); - responseHeaders["Access-Control-Allow-Origin"] = origins.join(","); - responseHeaders["Access-Control-Expose-Headers"] = exposedHeaders.join(","); + if (origins && origins.length > 0) { + responseHeaders["Access-Control-Allow-Origin"] = origins.join(","); + } + if (exposedHeaders && exposedHeaders.length > 0) { + responseHeaders["Access-Control-Expose-Headers"] = exposedHeaders.join(","); + } responseHeaders["Access-Control-Allow-Credentials"] = allowCredentials ? "true" : "false"; if (method === 'OPTIONS') { - if (headers) { + if (headers && headers.length > 0) { responseHeaders["Access-Control-Allow-Headers"] = headers.join(","); } - responseHeaders["Access-Control-Allow-Methods"] = methods.join(","); + if (methods && methods.length > 0) { + responseHeaders["Access-Control-Allow-Methods"] = methods.join(","); + } for (const key of Object.keys(responseHeaders)) { res.setHeader(key, responseHeaders[key]); } From 4db182752f3591818e0e02c1dd0e304568aac59a Mon Sep 17 00:00:00 2001 From: Sebastian Korfmann Date: Tue, 22 Aug 2023 20:24:19 +0200 Subject: [PATCH 05/47] Test setting headers --- examples/tests/valid/cors_api.w | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/examples/tests/valid/cors_api.w b/examples/tests/valid/cors_api.w index 519a18316cf..5b54437a160 100644 --- a/examples/tests/valid/cors_api.w +++ b/examples/tests/valid/cors_api.w @@ -103,6 +103,25 @@ test "OPTIONS /users has cors headers" { } }); + let headers = response.headers; + t.Assert.equalNum(response.status, 204); + t.Assert.equalStr(headers.get("access-control-allow-origin"), "winglang.io"); + t.Assert.equalStr(headers.get("access-control-allow-methods"), "GET,POST,OPTIONS"); + t.Assert.equalStr(headers.get("access-control-allow-headers"), "Content-Type,Authorization,X-Custom-Header"); + t.Assert.equalStr(headers.get("access-control-allow-credentials"), "true"); + t.Assert.equalStr(headers.get("access-control-expose-headers"), "Content-Type"); +} + +test "OPTIONS /users responds with proper headers for requested" { + let response = http.fetch(apiCustomCors.url + "/users", { + method: http.HttpMethod.OPTIONS, + headers: { + "Content-Type": "text/json", + "Access-Control-Request-Method": "PUT", + "Access-Control-Request-Headers": "Content-Type,Authorization,X-Custom-Foo" + } + }); + let headers = response.headers; t.Assert.equalNum(response.status, 204); t.Assert.equalStr(headers.get("access-control-allow-origin"), "winglang.io"); From 049ae6e79d730ad2d0369acb1d8878042e2ebeab Mon Sep 17 00:00:00 2001 From: "monada-bot[bot]" Date: Tue, 22 Aug 2023 18:43:13 +0000 Subject: [PATCH 06/47] chore: self mutation Signed-off-by: monada-bot[bot] --- docs/docs/04-standard-library/01-cloud/api.md | 147 +++ .../src/jsify/snapshots/json_object.snap | 2 +- .../incomplete_inflight_namespace.snap | 8 +- .../snapshots/completions/json_statics.snap | 4 +- .../completions/namespace_middle_dot.snap | 8 +- .../static_completions_after_expression.snap | 4 +- .../static_json_after_expression.snap | 4 +- ...tatic_json_after_expression_statement.snap | 4 +- .../variable_type_annotation_namespace.snap | 8 +- .../hovers/static_stdtype_method.snap | 2 +- .../valid/assertions.w_compile_tf-aws.md | 112 ++ .../valid/assertions.w_test_sim.md | 12 + .../valid/cors_api.w_compile_tf-aws.md | 1123 +++++++++++++++++ .../test_corpus/valid/cors_api.w_test_sim.md | 16 + .../website_with_api.w_compile_tf-aws.md | 380 ++++-- .../valid/website_with_api.w_test_sim.md | 6 +- 16 files changed, 1738 insertions(+), 102 deletions(-) create mode 100644 tools/hangar/__snapshots__/test_corpus/valid/assertions.w_compile_tf-aws.md create mode 100644 tools/hangar/__snapshots__/test_corpus/valid/assertions.w_test_sim.md create mode 100644 tools/hangar/__snapshots__/test_corpus/valid/cors_api.w_compile_tf-aws.md create mode 100644 tools/hangar/__snapshots__/test_corpus/valid/cors_api.w_test_sim.md diff --git a/docs/docs/04-standard-library/01-cloud/api.md b/docs/docs/04-standard-library/01-cloud/api.md index 0a203867f91..9f4b59a39c3 100644 --- a/docs/docs/04-standard-library/01-cloud/api.md +++ b/docs/docs/04-standard-library/01-cloud/api.md @@ -480,6 +480,123 @@ let ApiConnectProps = cloud.ApiConnectProps{ ... }; ``` +### ApiCorsProps + +Cors Options for `Api`. + +#### Initializer + +```wing +bring cloud; + +let ApiCorsProps = cloud.ApiCorsProps{ ... }; +``` + +#### Properties + +| **Name** | **Type** | **Description** | +| --- | --- | --- | +| allowCredentials | bool | Whether to allow credentials. | +| exposedHeaders | MutArray<str> | The list of exposed headers. | +| headers | MutArray<str> | The list of allowed headers. | +| methods | MutArray<HttpMethod> | The list of allowed methods. | +| origins | MutArray<str> | The list of allowed origins. | + +--- + +##### `allowCredentials`Optional + +```wing +allowCredentials: bool; +``` + +- *Type:* bool +- *Default:* false + +Whether to allow credentials. + +--- + +##### `exposedHeaders`Optional + +```wing +exposedHeaders: MutArray; +``` + +- *Type:* MutArray<str> +- *Default:* [] + +The list of exposed headers. + +--- + +*Example* + +```wing +["Content-Type"] +``` + + +##### `headers`Optional + +```wing +headers: MutArray; +``` + +- *Type:* MutArray<str> +- *Default:* ["Content-Type", "Authorization"] + +The list of allowed headers. + +--- + +*Example* + +```wing +["Content-Type"] +``` + + +##### `methods`Optional + +```wing +methods: MutArray; +``` + +- *Type:* MutArray<HttpMethod> +- *Default:* [HttpMethod.GET, HttpMethod.POST, HttpMethod.PUT, HttpMethod.PATCH, HttpMethod.DELETE, HttpMethod.HEAD, HttpMethod.OPTIONS] + +The list of allowed methods. + +--- + +*Example* + +```wing +[HttpMethod.GET, HttpMethod.POST] +``` + + +##### `origins`Optional + +```wing +origins: MutArray; +``` + +- *Type:* MutArray<str> +- *Default:* ["*"] + +The list of allowed origins. + +--- + +*Example* + +```wing +["https://example.com"] +``` + + ### ApiDeleteProps Options for Api put endpoint. @@ -570,6 +687,36 @@ bring cloud; let ApiProps = cloud.ApiProps{ ... }; ``` +#### Properties + +| **Name** | **Type** | **Description** | +| --- | --- | --- | +| cors | ApiCorsProps | Options for configuring the API's CORS behavior across all routes. | + +--- + +##### `cors`Optional + +```wing +cors: ApiCorsProps; +``` + +- *Type:* ApiCorsProps +- *Default:* CORS is disabled + +Options for configuring the API's CORS behavior across all routes. + +Options can also be overridden on a per-route basis. (not yet implemented) +Can be set to to an empty Struct to enable CORS with default options. + +--- + +*Example* + +```wing +{} +``` + ### ApiPutProps diff --git a/libs/wingc/src/jsify/snapshots/json_object.snap b/libs/wingc/src/jsify/snapshots/json_object.snap index 6ca8447c3a3..1e1d952344e 100644 --- a/libs/wingc/src/jsify/snapshots/json_object.snap +++ b/libs/wingc/src/jsify/snapshots/json_object.snap @@ -24,7 +24,7 @@ module.exports = function({ $jsonObj1, $std_Json }) { return $obj; } async handle() { - {console.log(((args) => { return JSON.stringify(args[0], null, args[1]) })([$jsonObj1]))}; + {console.log(((args) => { return JSON.stringify(args[0], null, args[1]?.indent) })([$jsonObj1]))}; } } return $Closure1; diff --git a/libs/wingc/src/lsp/snapshots/completions/incomplete_inflight_namespace.snap b/libs/wingc/src/lsp/snapshots/completions/incomplete_inflight_namespace.snap index b63d35d2642..ad74fabdb33 100644 --- a/libs/wingc/src/lsp/snapshots/completions/incomplete_inflight_namespace.snap +++ b/libs/wingc/src/lsp/snapshots/completions/incomplete_inflight_namespace.snap @@ -73,6 +73,12 @@ source: libs/wingc/src/lsp/completions.rs kind: markdown value: "```wing\nstruct ApiConnectProps\n```\n---\nOptions for Api patch endpoint." sortText: hh|ApiConnectProps +- label: ApiCorsProps + kind: 22 + documentation: + kind: markdown + value: "```wing\nstruct ApiCorsProps\n```\n---\nCors Options for `Api`.\n### Fields\n- `allowCredentials?` — Whether to allow credentials.\n- `exposedHeaders?` — The list of exposed headers.\n- `headers?` — The list of allowed headers.\n- `methods?` — The list of allowed methods.\n- `origins?` — The list of allowed origins." + sortText: hh|ApiCorsProps - label: ApiDeleteProps kind: 22 documentation: @@ -113,7 +119,7 @@ source: libs/wingc/src/lsp/completions.rs kind: 22 documentation: kind: markdown - value: "```wing\nstruct ApiProps\n```\n---\nOptions for `Api`." + value: "```wing\nstruct ApiProps\n```\n---\nOptions for `Api`.\n### Fields\n- `cors?` — Options for configuring the API's CORS behavior across all routes." sortText: hh|ApiProps - label: ApiPutProps kind: 22 diff --git a/libs/wingc/src/lsp/snapshots/completions/json_statics.snap b/libs/wingc/src/lsp/snapshots/completions/json_statics.snap index 434d36a6f11..71d42d4ff90 100644 --- a/libs/wingc/src/lsp/snapshots/completions/json_statics.snap +++ b/libs/wingc/src/lsp/snapshots/completions/json_statics.snap @@ -75,10 +75,10 @@ source: libs/wingc/src/lsp/completions.rs command: editor.action.triggerParameterHints - label: stringify kind: 2 - detail: "(json: any, indent: num?): str" + detail: "(json: any, options: JsonStringifyOptions?): str" documentation: kind: markdown - value: "```wing\nstatic stringify: (json: any, indent: num?): str\n```\n---\nFormats Json as string.\n\n\n### Returns\nstring representation of the Json\n\n### Remarks\n(JSON.stringify($args$))" + value: "```wing\nstatic stringify: (json: any, options: JsonStringifyOptions?): str\n```\n---\nFormats Json as string.\n\n\n### Returns\nstring representation of the Json" sortText: ff|stringify insertText: stringify($0) insertTextFormat: 2 diff --git a/libs/wingc/src/lsp/snapshots/completions/namespace_middle_dot.snap b/libs/wingc/src/lsp/snapshots/completions/namespace_middle_dot.snap index b63d35d2642..ad74fabdb33 100644 --- a/libs/wingc/src/lsp/snapshots/completions/namespace_middle_dot.snap +++ b/libs/wingc/src/lsp/snapshots/completions/namespace_middle_dot.snap @@ -73,6 +73,12 @@ source: libs/wingc/src/lsp/completions.rs kind: markdown value: "```wing\nstruct ApiConnectProps\n```\n---\nOptions for Api patch endpoint." sortText: hh|ApiConnectProps +- label: ApiCorsProps + kind: 22 + documentation: + kind: markdown + value: "```wing\nstruct ApiCorsProps\n```\n---\nCors Options for `Api`.\n### Fields\n- `allowCredentials?` — Whether to allow credentials.\n- `exposedHeaders?` — The list of exposed headers.\n- `headers?` — The list of allowed headers.\n- `methods?` — The list of allowed methods.\n- `origins?` — The list of allowed origins." + sortText: hh|ApiCorsProps - label: ApiDeleteProps kind: 22 documentation: @@ -113,7 +119,7 @@ source: libs/wingc/src/lsp/completions.rs kind: 22 documentation: kind: markdown - value: "```wing\nstruct ApiProps\n```\n---\nOptions for `Api`." + value: "```wing\nstruct ApiProps\n```\n---\nOptions for `Api`.\n### Fields\n- `cors?` — Options for configuring the API's CORS behavior across all routes." sortText: hh|ApiProps - label: ApiPutProps kind: 22 diff --git a/libs/wingc/src/lsp/snapshots/completions/static_completions_after_expression.snap b/libs/wingc/src/lsp/snapshots/completions/static_completions_after_expression.snap index 434d36a6f11..71d42d4ff90 100644 --- a/libs/wingc/src/lsp/snapshots/completions/static_completions_after_expression.snap +++ b/libs/wingc/src/lsp/snapshots/completions/static_completions_after_expression.snap @@ -75,10 +75,10 @@ source: libs/wingc/src/lsp/completions.rs command: editor.action.triggerParameterHints - label: stringify kind: 2 - detail: "(json: any, indent: num?): str" + detail: "(json: any, options: JsonStringifyOptions?): str" documentation: kind: markdown - value: "```wing\nstatic stringify: (json: any, indent: num?): str\n```\n---\nFormats Json as string.\n\n\n### Returns\nstring representation of the Json\n\n### Remarks\n(JSON.stringify($args$))" + value: "```wing\nstatic stringify: (json: any, options: JsonStringifyOptions?): str\n```\n---\nFormats Json as string.\n\n\n### Returns\nstring representation of the Json" sortText: ff|stringify insertText: stringify($0) insertTextFormat: 2 diff --git a/libs/wingc/src/lsp/snapshots/completions/static_json_after_expression.snap b/libs/wingc/src/lsp/snapshots/completions/static_json_after_expression.snap index 434d36a6f11..71d42d4ff90 100644 --- a/libs/wingc/src/lsp/snapshots/completions/static_json_after_expression.snap +++ b/libs/wingc/src/lsp/snapshots/completions/static_json_after_expression.snap @@ -75,10 +75,10 @@ source: libs/wingc/src/lsp/completions.rs command: editor.action.triggerParameterHints - label: stringify kind: 2 - detail: "(json: any, indent: num?): str" + detail: "(json: any, options: JsonStringifyOptions?): str" documentation: kind: markdown - value: "```wing\nstatic stringify: (json: any, indent: num?): str\n```\n---\nFormats Json as string.\n\n\n### Returns\nstring representation of the Json\n\n### Remarks\n(JSON.stringify($args$))" + value: "```wing\nstatic stringify: (json: any, options: JsonStringifyOptions?): str\n```\n---\nFormats Json as string.\n\n\n### Returns\nstring representation of the Json" sortText: ff|stringify insertText: stringify($0) insertTextFormat: 2 diff --git a/libs/wingc/src/lsp/snapshots/completions/static_json_after_expression_statement.snap b/libs/wingc/src/lsp/snapshots/completions/static_json_after_expression_statement.snap index 434d36a6f11..71d42d4ff90 100644 --- a/libs/wingc/src/lsp/snapshots/completions/static_json_after_expression_statement.snap +++ b/libs/wingc/src/lsp/snapshots/completions/static_json_after_expression_statement.snap @@ -75,10 +75,10 @@ source: libs/wingc/src/lsp/completions.rs command: editor.action.triggerParameterHints - label: stringify kind: 2 - detail: "(json: any, indent: num?): str" + detail: "(json: any, options: JsonStringifyOptions?): str" documentation: kind: markdown - value: "```wing\nstatic stringify: (json: any, indent: num?): str\n```\n---\nFormats Json as string.\n\n\n### Returns\nstring representation of the Json\n\n### Remarks\n(JSON.stringify($args$))" + value: "```wing\nstatic stringify: (json: any, options: JsonStringifyOptions?): str\n```\n---\nFormats Json as string.\n\n\n### Returns\nstring representation of the Json" sortText: ff|stringify insertText: stringify($0) insertTextFormat: 2 diff --git a/libs/wingc/src/lsp/snapshots/completions/variable_type_annotation_namespace.snap b/libs/wingc/src/lsp/snapshots/completions/variable_type_annotation_namespace.snap index b63d35d2642..ad74fabdb33 100644 --- a/libs/wingc/src/lsp/snapshots/completions/variable_type_annotation_namespace.snap +++ b/libs/wingc/src/lsp/snapshots/completions/variable_type_annotation_namespace.snap @@ -73,6 +73,12 @@ source: libs/wingc/src/lsp/completions.rs kind: markdown value: "```wing\nstruct ApiConnectProps\n```\n---\nOptions for Api patch endpoint." sortText: hh|ApiConnectProps +- label: ApiCorsProps + kind: 22 + documentation: + kind: markdown + value: "```wing\nstruct ApiCorsProps\n```\n---\nCors Options for `Api`.\n### Fields\n- `allowCredentials?` — Whether to allow credentials.\n- `exposedHeaders?` — The list of exposed headers.\n- `headers?` — The list of allowed headers.\n- `methods?` — The list of allowed methods.\n- `origins?` — The list of allowed origins." + sortText: hh|ApiCorsProps - label: ApiDeleteProps kind: 22 documentation: @@ -113,7 +119,7 @@ source: libs/wingc/src/lsp/completions.rs kind: 22 documentation: kind: markdown - value: "```wing\nstruct ApiProps\n```\n---\nOptions for `Api`." + value: "```wing\nstruct ApiProps\n```\n---\nOptions for `Api`.\n### Fields\n- `cors?` — Options for configuring the API's CORS behavior across all routes." sortText: hh|ApiProps - label: ApiPutProps kind: 22 diff --git a/libs/wingc/src/lsp/snapshots/hovers/static_stdtype_method.snap b/libs/wingc/src/lsp/snapshots/hovers/static_stdtype_method.snap index 1fa5948be80..d457f74d5ed 100644 --- a/libs/wingc/src/lsp/snapshots/hovers/static_stdtype_method.snap +++ b/libs/wingc/src/lsp/snapshots/hovers/static_stdtype_method.snap @@ -3,7 +3,7 @@ source: libs/wingc/src/lsp/hover.rs --- contents: kind: markdown - value: "```wing\nstatic stringify: (json: any, indent: num?): str\n```\n---\nFormats Json as string.\n\n\n### Returns\nstring representation of the Json\n\n### Remarks\n(JSON.stringify($args$))" + value: "```wing\nstatic stringify: (json: any, options: JsonStringifyOptions?): str\n```\n---\nFormats Json as string.\n\n\n### Returns\nstring representation of the Json" range: start: line: 1 diff --git a/tools/hangar/__snapshots__/test_corpus/valid/assertions.w_compile_tf-aws.md b/tools/hangar/__snapshots__/test_corpus/valid/assertions.w_compile_tf-aws.md new file mode 100644 index 00000000000..1461d316c68 --- /dev/null +++ b/tools/hangar/__snapshots__/test_corpus/valid/assertions.w_compile_tf-aws.md @@ -0,0 +1,112 @@ +# [assertions.w](../../../../../examples/tests/valid/assertions.w) | compile | tf-aws + +## inflight.Assert-1.js +```js +module.exports = function({ }) { + class Assert { + static async equalStr(a, b) { + try { + {((cond) => {if (!cond) throw new Error("assertion failed: a == b")})((((a,b) => { try { return require('assert').deepStrictEqual(a,b) === undefined; } catch { return false; } })(a,b)))}; + } + catch ($error_e) { + const e = $error_e.message; + {((msg) => {throw new Error(msg)})(String.raw({ raw: ["expected: ", " got: ", ""] }, b, a))}; + } + } + static async isNil(a) { + try { + {((cond) => {if (!cond) throw new Error("assertion failed: a == nil")})((((a,b) => { try { return require('assert').deepStrictEqual(a,b) === undefined; } catch { return false; } })(a,undefined)))}; + } + catch ($error_e) { + const e = $error_e.message; + {console.log(e)}; + {((msg) => {throw new Error(msg)})(String.raw({ raw: ["expected '", "' to be nil"] }, a))}; + } + } + static async equalNum(a, b) { + try { + {((cond) => {if (!cond) throw new Error("assertion failed: a == b")})((((a,b) => { try { return require('assert').deepStrictEqual(a,b) === undefined; } catch { return false; } })(a,b)))}; + } + catch ($error_e) { + const e = $error_e.message; + {console.log(e)}; + {((msg) => {throw new Error(msg)})(String.raw({ raw: ["expected: ", " got: ", ""] }, b, a))}; + } + } + } + return Assert; +} + +``` + +## main.tf.json +```json +{ + "//": { + "metadata": { + "backend": "local", + "stackName": "root", + "version": "0.17.0" + }, + "outputs": { + "root": { + "Default": { + "cloud.TestRunner": { + "TestFunctionArns": "WING_TEST_RUNNER_FUNCTION_ARNS" + } + } + } + } + }, + "output": { + "WING_TEST_RUNNER_FUNCTION_ARNS": { + "value": "[]" + } + }, + "provider": { + "aws": [ + {} + ] + } +} +``` + +## preflight.js +```js +const $stdlib = require('@winglang/sdk'); +const $outdir = process.env.WING_SYNTH_DIR ?? "."; +const $wing_is_test = process.env.WING_IS_TEST === "true"; +const std = $stdlib.std; +class $Root extends $stdlib.std.Resource { + constructor(scope, id) { + super(scope, id); + class Assert extends $stdlib.std.Resource { + constructor(scope, id, ) { + super(scope, id); + this._addInflightOps("equalStr", "isNil", "equalNum", "$inflight_init"); + } + static _toInflightType(context) { + return $stdlib.core.NodeJsCode.fromInline(` + require("./inflight.Assert-1.js")({ + }) + `); + } + _toInflight() { + return $stdlib.core.NodeJsCode.fromInline(` + (await (async () => { + const AssertClient = ${Assert._toInflightType(this).text}; + const client = new AssertClient({ + }); + if (client.$inflight_init) { await client.$inflight_init(); } + return client; + })()) + `); + } + } + } +} +const $App = $stdlib.core.App.for(process.env.WING_TARGET); +new $App({ outdir: $outdir, name: "assertions", rootConstruct: $Root, plugins: $plugins, isTestEnvironment: $wing_is_test, entrypointDir: process.env['WING_SOURCE_DIR'], rootId: process.env['WING_ROOT_ID'] }).synth(); + +``` + diff --git a/tools/hangar/__snapshots__/test_corpus/valid/assertions.w_test_sim.md b/tools/hangar/__snapshots__/test_corpus/valid/assertions.w_test_sim.md new file mode 100644 index 00000000000..8b323291d09 --- /dev/null +++ b/tools/hangar/__snapshots__/test_corpus/valid/assertions.w_test_sim.md @@ -0,0 +1,12 @@ +# [assertions.w](../../../../../examples/tests/valid/assertions.w) | test | sim + +## stdout.log +```log +pass ─ assertions.wsim (no tests) + + +Tests 1 passed (1) +Test Files 1 passed (1) +Duration +``` + diff --git a/tools/hangar/__snapshots__/test_corpus/valid/cors_api.w_compile_tf-aws.md b/tools/hangar/__snapshots__/test_corpus/valid/cors_api.w_compile_tf-aws.md new file mode 100644 index 00000000000..f122fea1769 --- /dev/null +++ b/tools/hangar/__snapshots__/test_corpus/valid/cors_api.w_compile_tf-aws.md @@ -0,0 +1,1123 @@ +# [cors_api.w](../../../../../examples/tests/valid/cors_api.w) | compile | tf-aws + +## inflight.$Closure1-2.js +```js +module.exports = function({ }) { + class $Closure1 { + constructor({ }) { + const $obj = (...args) => this.handle(...args); + Object.setPrototypeOf($obj, this); + return $obj; + } + async handle(req) { + return ({"body": "hello world","status": 200}); + } + } + return $Closure1; +} + +``` + +## inflight.$Closure2-2.js +```js +module.exports = function({ $apiDefaultCors_url, $http_HttpMethod, $http_Util, $t_Assert }) { + class $Closure2 { + constructor({ }) { + const $obj = (...args) => this.handle(...args); + Object.setPrototypeOf($obj, this); + return $obj; + } + async handle() { + const response = (await $http_Util.fetch(($apiDefaultCors_url + "/users"),({"method": $http_HttpMethod.GET,"headers": ({"Content-Type": "text/json"})}))); + const headers = response.headers; + (await $t_Assert.equalNum(response.status,200)); + (await $t_Assert.equalStr((headers)["access-control-allow-origin"],"*")); + (await $t_Assert.equalStr((headers)["access-control-allow-credentials"],"false")); + (await $t_Assert.isNil((headers)["access-control-allow-headers"])); + (await $t_Assert.isNil((headers)["access-control-expose-headers"])); + (await $t_Assert.isNil((headers)["access-control-allow-methods"])); + } + } + return $Closure2; +} + +``` + +## inflight.$Closure3-2.js +```js +module.exports = function({ $apiDefaultCors_url, $http_HttpMethod, $http_Util, $t_Assert }) { + class $Closure3 { + constructor({ }) { + const $obj = (...args) => this.handle(...args); + Object.setPrototypeOf($obj, this); + return $obj; + } + async handle() { + const response = (await $http_Util.fetch(($apiDefaultCors_url + "/users"),({"method": $http_HttpMethod.OPTIONS,"headers": ({"Content-Type": "text/json"})}))); + const headers = response.headers; + (await $t_Assert.equalNum(response.status,204)); + (await $t_Assert.equalStr((headers)["access-control-allow-origin"],"*")); + (await $t_Assert.equalStr((headers)["access-control-allow-methods"],"GET,HEAD,PUT,PATCH,POST,DELETE")); + (await $t_Assert.equalStr((headers)["access-control-allow-headers"],"Content-Type,Authorization")); + (await $t_Assert.equalStr((headers)["access-control-allow-credentials"],"false")); + (await $t_Assert.isNil((headers)["access-control-expose-headers"])); + } + } + return $Closure3; +} + +``` + +## inflight.$Closure4-2.js +```js +module.exports = function({ }) { + class $Closure4 { + constructor({ }) { + const $obj = (...args) => this.handle(...args); + Object.setPrototypeOf($obj, this); + return $obj; + } + async handle(req) { + return ({"body": "hello world","status": 200}); + } + } + return $Closure4; +} + +``` + +## inflight.$Closure5-2.js +```js +module.exports = function({ $apiCustomCors_url, $http_HttpMethod, $http_Util, $t_Assert }) { + class $Closure5 { + constructor({ }) { + const $obj = (...args) => this.handle(...args); + Object.setPrototypeOf($obj, this); + return $obj; + } + async handle() { + const response = (await $http_Util.fetch(($apiCustomCors_url + "/users"),({"method": $http_HttpMethod.GET,"headers": ({"Content-Type": "text/json"})}))); + const headers = response.headers; + (await $t_Assert.equalNum(response.status,200)); + (await $t_Assert.equalStr((headers)["access-control-allow-origin"],"winglang.io")); + (await $t_Assert.equalStr((headers)["access-control-allow-credentials"],"true")); + (await $t_Assert.equalStr((headers)["access-control-expose-headers"],"Content-Type")); + (await $t_Assert.isNil((headers)["access-control-allow-headers"])); + (await $t_Assert.isNil((headers)["access-control-allow-methods"])); + } + } + return $Closure5; +} + +``` + +## inflight.$Closure6-2.js +```js +module.exports = function({ $apiCustomCors_url, $http_HttpMethod, $http_Util, $t_Assert }) { + class $Closure6 { + constructor({ }) { + const $obj = (...args) => this.handle(...args); + Object.setPrototypeOf($obj, this); + return $obj; + } + async handle() { + const response = (await $http_Util.fetch(($apiCustomCors_url + "/users"),({"method": $http_HttpMethod.OPTIONS,"headers": ({"Content-Type": "text/json"})}))); + const headers = response.headers; + (await $t_Assert.equalNum(response.status,204)); + (await $t_Assert.equalStr((headers)["access-control-allow-origin"],"winglang.io")); + (await $t_Assert.equalStr((headers)["access-control-allow-methods"],"GET,POST,OPTIONS")); + (await $t_Assert.equalStr((headers)["access-control-allow-headers"],"Content-Type,Authorization,X-Custom-Header")); + (await $t_Assert.equalStr((headers)["access-control-allow-credentials"],"true")); + (await $t_Assert.equalStr((headers)["access-control-expose-headers"],"Content-Type")); + } + } + return $Closure6; +} + +``` + +## inflight.$Closure7-2.js +```js +module.exports = function({ $apiCustomCors_url, $http_HttpMethod, $http_Util, $t_Assert }) { + class $Closure7 { + constructor({ }) { + const $obj = (...args) => this.handle(...args); + Object.setPrototypeOf($obj, this); + return $obj; + } + async handle() { + const response = (await $http_Util.fetch(($apiCustomCors_url + "/users"),({"method": $http_HttpMethod.OPTIONS,"headers": ({"Content-Type": "text/json","Access-Control-Request-Method": "PUT","Access-Control-Request-Headers": "Content-Type,Authorization,X-Custom-Foo"})}))); + const headers = response.headers; + (await $t_Assert.equalNum(response.status,204)); + (await $t_Assert.equalStr((headers)["access-control-allow-origin"],"winglang.io")); + (await $t_Assert.equalStr((headers)["access-control-allow-methods"],"GET,POST,OPTIONS")); + (await $t_Assert.equalStr((headers)["access-control-allow-headers"],"Content-Type,Authorization,X-Custom-Header")); + (await $t_Assert.equalStr((headers)["access-control-allow-credentials"],"true")); + (await $t_Assert.equalStr((headers)["access-control-expose-headers"],"Content-Type")); + } + } + return $Closure7; +} + +``` + +## inflight.Assert-1.js +```js +module.exports = function({ }) { + class Assert { + static async equalStr(a, b) { + try { + {((cond) => {if (!cond) throw new Error("assertion failed: a == b")})((((a,b) => { try { return require('assert').deepStrictEqual(a,b) === undefined; } catch { return false; } })(a,b)))}; + } + catch ($error_e) { + const e = $error_e.message; + {((msg) => {throw new Error(msg)})(String.raw({ raw: ["expected: ", " got: ", ""] }, b, a))}; + } + } + static async isNil(a) { + try { + {((cond) => {if (!cond) throw new Error("assertion failed: a == nil")})((((a,b) => { try { return require('assert').deepStrictEqual(a,b) === undefined; } catch { return false; } })(a,undefined)))}; + } + catch ($error_e) { + const e = $error_e.message; + {console.log(e)}; + {((msg) => {throw new Error(msg)})(String.raw({ raw: ["expected '", "' to be nil"] }, a))}; + } + } + static async equalNum(a, b) { + try { + {((cond) => {if (!cond) throw new Error("assertion failed: a == b")})((((a,b) => { try { return require('assert').deepStrictEqual(a,b) === undefined; } catch { return false; } })(a,b)))}; + } + catch ($error_e) { + const e = $error_e.message; + {console.log(e)}; + {((msg) => {throw new Error(msg)})(String.raw({ raw: ["expected: ", " got: ", ""] }, b, a))}; + } + } + } + return Assert; +} + +``` + +## main.tf.json +```json +{ + "//": { + "metadata": { + "backend": "local", + "stackName": "root", + "version": "0.17.0" + }, + "outputs": { + "root": { + "Default": { + "cloud.TestRunner": { + "TestFunctionArns": "WING_TEST_RUNNER_FUNCTION_ARNS" + } + } + } + } + }, + "data": { + "aws_region": { + "Region": { + "//": { + "metadata": { + "path": "root/Default/Region", + "uniqueId": "Region" + } + } + } + } + }, + "output": { + "WING_TEST_RUNNER_FUNCTION_ARNS": { + "value": "[[\"root/Default/Default/test:GET --users has default cors headers\",\"${aws_lambda_function.testGET--usershasdefaultcorsheaders_Handler_1182379A.arn}\"],[\"root/Default/Default/test:OPTIONS --users has default cors headers\",\"${aws_lambda_function.testOPTIONS--usershasdefaultcorsheaders_Handler_D03A1BFF.arn}\"],[\"root/Default/Default/test:GET --users has cors headers\",\"${aws_lambda_function.testGET--usershascorsheaders_Handler_E0F337CB.arn}\"],[\"root/Default/Default/test:OPTIONS --users has cors headers\",\"${aws_lambda_function.testOPTIONS--usershascorsheaders_Handler_3A565385.arn}\"],[\"root/Default/Default/test:OPTIONS --users responds with proper headers for requested\",\"${aws_lambda_function.testOPTIONS--usersrespondswithproperheadersforrequested_Handler_0A2AB662.arn}\"]]" + } + }, + "provider": { + "aws": [ + {} + ] + }, + "resource": { + "aws_api_gateway_deployment": { + "apiCustomCors_api_deployment_05CB4F30": { + "//": { + "metadata": { + "path": "root/Default/Default/apiCustomCors/api/deployment", + "uniqueId": "apiCustomCors_api_deployment_05CB4F30" + } + }, + "lifecycle": { + "create_before_destroy": true + }, + "rest_api_id": "${aws_api_gateway_rest_api.apiCustomCors_api_D8B9CD90.id}", + "triggers": { + "redeployment": "c01de636f4118b7353903b8f0e757abf473c0518" + } + }, + "cloudApi_api_deployment_545514BF": { + "//": { + "metadata": { + "path": "root/Default/Default/cloud.Api/api/deployment", + "uniqueId": "cloudApi_api_deployment_545514BF" + } + }, + "lifecycle": { + "create_before_destroy": true + }, + "rest_api_id": "${aws_api_gateway_rest_api.cloudApi_api_2B334D75.id}", + "triggers": { + "redeployment": "b86a15a13275d206adddc5360e513fd5d0f527e6" + } + } + }, + "aws_api_gateway_rest_api": { + "apiCustomCors_api_D8B9CD90": { + "//": { + "metadata": { + "path": "root/Default/Default/apiCustomCors/api/api", + "uniqueId": "apiCustomCors_api_D8B9CD90" + } + }, + "body": "{\"openapi\":\"3.0.3\",\"paths\":{\"/users\":{\"get\":{\"operationId\":\"get-users\",\"responses\":{\"200\":{\"description\":\"200 response\",\"content\":{}}},\"parameters\":[],\"x-amazon-apigateway-integration\":{\"uri\":\"arn:aws:apigateway:${data.aws_region.Region.name}:lambda:path/2015-03-31/functions/${aws_lambda_function.apiCustomCors_apiCustomCors-OnRequest-52bc3c17_E34A40CA.arn}/invocations\",\"type\":\"aws_proxy\",\"httpMethod\":\"POST\",\"responses\":{\"default\":{\"statusCode\":\"200\"}},\"passthroughBehavior\":\"when_no_match\",\"contentHandling\":\"CONVERT_TO_TEXT\"}}}}}", + "name": "api-c8efe62e" + }, + "cloudApi_api_2B334D75": { + "//": { + "metadata": { + "path": "root/Default/Default/cloud.Api/api/api", + "uniqueId": "cloudApi_api_2B334D75" + } + }, + "body": "{\"openapi\":\"3.0.3\",\"paths\":{\"/users\":{\"get\":{\"operationId\":\"get-users\",\"responses\":{\"200\":{\"description\":\"200 response\",\"content\":{}}},\"parameters\":[],\"x-amazon-apigateway-integration\":{\"uri\":\"arn:aws:apigateway:${data.aws_region.Region.name}:lambda:path/2015-03-31/functions/${aws_lambda_function.cloudApi_cloudApi-OnRequest-cdafee6e_A6C8366F.arn}/invocations\",\"type\":\"aws_proxy\",\"httpMethod\":\"POST\",\"responses\":{\"default\":{\"statusCode\":\"200\"}},\"passthroughBehavior\":\"when_no_match\",\"contentHandling\":\"CONVERT_TO_TEXT\"}}}}}", + "name": "api-c895068c" + } + }, + "aws_api_gateway_stage": { + "apiCustomCors_api_stage_F5D92865": { + "//": { + "metadata": { + "path": "root/Default/Default/apiCustomCors/api/stage", + "uniqueId": "apiCustomCors_api_stage_F5D92865" + } + }, + "deployment_id": "${aws_api_gateway_deployment.apiCustomCors_api_deployment_05CB4F30.id}", + "rest_api_id": "${aws_api_gateway_rest_api.apiCustomCors_api_D8B9CD90.id}", + "stage_name": "prod" + }, + "cloudApi_api_stage_BBB283E4": { + "//": { + "metadata": { + "path": "root/Default/Default/cloud.Api/api/stage", + "uniqueId": "cloudApi_api_stage_BBB283E4" + } + }, + "deployment_id": "${aws_api_gateway_deployment.cloudApi_api_deployment_545514BF.id}", + "rest_api_id": "${aws_api_gateway_rest_api.cloudApi_api_2B334D75.id}", + "stage_name": "prod" + } + }, + "aws_iam_role": { + "apiCustomCors_apiCustomCors-OnRequest-52bc3c17_IamRole_692EAD85": { + "//": { + "metadata": { + "path": "root/Default/Default/apiCustomCors/apiCustomCors-OnRequest-52bc3c17/IamRole", + "uniqueId": "apiCustomCors_apiCustomCors-OnRequest-52bc3c17_IamRole_692EAD85" + } + }, + "assume_role_policy": "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Action\":\"sts:AssumeRole\",\"Principal\":{\"Service\":\"lambda.amazonaws.com\"},\"Effect\":\"Allow\"}]}" + }, + "cloudApi_cloudApi-OnRequest-cdafee6e_IamRole_4382C442": { + "//": { + "metadata": { + "path": "root/Default/Default/cloud.Api/cloud.Api-OnRequest-cdafee6e/IamRole", + "uniqueId": "cloudApi_cloudApi-OnRequest-cdafee6e_IamRole_4382C442" + } + }, + "assume_role_policy": "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Action\":\"sts:AssumeRole\",\"Principal\":{\"Service\":\"lambda.amazonaws.com\"},\"Effect\":\"Allow\"}]}" + }, + "testGET--usershascorsheaders_Handler_IamRole_6841C3FF": { + "//": { + "metadata": { + "path": "root/Default/Default/test:GET --users has cors headers/Handler/IamRole", + "uniqueId": "testGET--usershascorsheaders_Handler_IamRole_6841C3FF" + } + }, + "assume_role_policy": "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Action\":\"sts:AssumeRole\",\"Principal\":{\"Service\":\"lambda.amazonaws.com\"},\"Effect\":\"Allow\"}]}" + }, + "testGET--usershasdefaultcorsheaders_Handler_IamRole_17E5D7FC": { + "//": { + "metadata": { + "path": "root/Default/Default/test:GET --users has default cors headers/Handler/IamRole", + "uniqueId": "testGET--usershasdefaultcorsheaders_Handler_IamRole_17E5D7FC" + } + }, + "assume_role_policy": "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Action\":\"sts:AssumeRole\",\"Principal\":{\"Service\":\"lambda.amazonaws.com\"},\"Effect\":\"Allow\"}]}" + }, + "testOPTIONS--usershascorsheaders_Handler_IamRole_0EFF66BD": { + "//": { + "metadata": { + "path": "root/Default/Default/test:OPTIONS --users has cors headers/Handler/IamRole", + "uniqueId": "testOPTIONS--usershascorsheaders_Handler_IamRole_0EFF66BD" + } + }, + "assume_role_policy": "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Action\":\"sts:AssumeRole\",\"Principal\":{\"Service\":\"lambda.amazonaws.com\"},\"Effect\":\"Allow\"}]}" + }, + "testOPTIONS--usershasdefaultcorsheaders_Handler_IamRole_E58DF921": { + "//": { + "metadata": { + "path": "root/Default/Default/test:OPTIONS --users has default cors headers/Handler/IamRole", + "uniqueId": "testOPTIONS--usershasdefaultcorsheaders_Handler_IamRole_E58DF921" + } + }, + "assume_role_policy": "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Action\":\"sts:AssumeRole\",\"Principal\":{\"Service\":\"lambda.amazonaws.com\"},\"Effect\":\"Allow\"}]}" + }, + "testOPTIONS--usersrespondswithproperheadersforrequested_Handler_IamRole_4AB06A0F": { + "//": { + "metadata": { + "path": "root/Default/Default/test:OPTIONS --users responds with proper headers for requested/Handler/IamRole", + "uniqueId": "testOPTIONS--usersrespondswithproperheadersforrequested_Handler_IamRole_4AB06A0F" + } + }, + "assume_role_policy": "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Action\":\"sts:AssumeRole\",\"Principal\":{\"Service\":\"lambda.amazonaws.com\"},\"Effect\":\"Allow\"}]}" + } + }, + "aws_iam_role_policy": { + "apiCustomCors_apiCustomCors-OnRequest-52bc3c17_IamRolePolicy_92D18911": { + "//": { + "metadata": { + "path": "root/Default/Default/apiCustomCors/apiCustomCors-OnRequest-52bc3c17/IamRolePolicy", + "uniqueId": "apiCustomCors_apiCustomCors-OnRequest-52bc3c17_IamRolePolicy_92D18911" + } + }, + "policy": "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Action\":\"none:null\",\"Resource\":\"*\"}]}", + "role": "${aws_iam_role.apiCustomCors_apiCustomCors-OnRequest-52bc3c17_IamRole_692EAD85.name}" + }, + "cloudApi_cloudApi-OnRequest-cdafee6e_IamRolePolicy_8BF9C89F": { + "//": { + "metadata": { + "path": "root/Default/Default/cloud.Api/cloud.Api-OnRequest-cdafee6e/IamRolePolicy", + "uniqueId": "cloudApi_cloudApi-OnRequest-cdafee6e_IamRolePolicy_8BF9C89F" + } + }, + "policy": "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Action\":\"none:null\",\"Resource\":\"*\"}]}", + "role": "${aws_iam_role.cloudApi_cloudApi-OnRequest-cdafee6e_IamRole_4382C442.name}" + }, + "testGET--usershascorsheaders_Handler_IamRolePolicy_BEF25776": { + "//": { + "metadata": { + "path": "root/Default/Default/test:GET --users has cors headers/Handler/IamRolePolicy", + "uniqueId": "testGET--usershascorsheaders_Handler_IamRolePolicy_BEF25776" + } + }, + "policy": "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Action\":\"none:null\",\"Resource\":\"*\"}]}", + "role": "${aws_iam_role.testGET--usershascorsheaders_Handler_IamRole_6841C3FF.name}" + }, + "testGET--usershasdefaultcorsheaders_Handler_IamRolePolicy_F382BF6B": { + "//": { + "metadata": { + "path": "root/Default/Default/test:GET --users has default cors headers/Handler/IamRolePolicy", + "uniqueId": "testGET--usershasdefaultcorsheaders_Handler_IamRolePolicy_F382BF6B" + } + }, + "policy": "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Action\":\"none:null\",\"Resource\":\"*\"}]}", + "role": "${aws_iam_role.testGET--usershasdefaultcorsheaders_Handler_IamRole_17E5D7FC.name}" + }, + "testOPTIONS--usershascorsheaders_Handler_IamRolePolicy_F6912B4F": { + "//": { + "metadata": { + "path": "root/Default/Default/test:OPTIONS --users has cors headers/Handler/IamRolePolicy", + "uniqueId": "testOPTIONS--usershascorsheaders_Handler_IamRolePolicy_F6912B4F" + } + }, + "policy": "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Action\":\"none:null\",\"Resource\":\"*\"}]}", + "role": "${aws_iam_role.testOPTIONS--usershascorsheaders_Handler_IamRole_0EFF66BD.name}" + }, + "testOPTIONS--usershasdefaultcorsheaders_Handler_IamRolePolicy_C496A648": { + "//": { + "metadata": { + "path": "root/Default/Default/test:OPTIONS --users has default cors headers/Handler/IamRolePolicy", + "uniqueId": "testOPTIONS--usershasdefaultcorsheaders_Handler_IamRolePolicy_C496A648" + } + }, + "policy": "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Action\":\"none:null\",\"Resource\":\"*\"}]}", + "role": "${aws_iam_role.testOPTIONS--usershasdefaultcorsheaders_Handler_IamRole_E58DF921.name}" + }, + "testOPTIONS--usersrespondswithproperheadersforrequested_Handler_IamRolePolicy_00E727F2": { + "//": { + "metadata": { + "path": "root/Default/Default/test:OPTIONS --users responds with proper headers for requested/Handler/IamRolePolicy", + "uniqueId": "testOPTIONS--usersrespondswithproperheadersforrequested_Handler_IamRolePolicy_00E727F2" + } + }, + "policy": "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Action\":\"none:null\",\"Resource\":\"*\"}]}", + "role": "${aws_iam_role.testOPTIONS--usersrespondswithproperheadersforrequested_Handler_IamRole_4AB06A0F.name}" + } + }, + "aws_iam_role_policy_attachment": { + "apiCustomCors_apiCustomCors-OnRequest-52bc3c17_IamRolePolicyAttachment_9133AE8C": { + "//": { + "metadata": { + "path": "root/Default/Default/apiCustomCors/apiCustomCors-OnRequest-52bc3c17/IamRolePolicyAttachment", + "uniqueId": "apiCustomCors_apiCustomCors-OnRequest-52bc3c17_IamRolePolicyAttachment_9133AE8C" + } + }, + "policy_arn": "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole", + "role": "${aws_iam_role.apiCustomCors_apiCustomCors-OnRequest-52bc3c17_IamRole_692EAD85.name}" + }, + "cloudApi_cloudApi-OnRequest-cdafee6e_IamRolePolicyAttachment_5383D6A2": { + "//": { + "metadata": { + "path": "root/Default/Default/cloud.Api/cloud.Api-OnRequest-cdafee6e/IamRolePolicyAttachment", + "uniqueId": "cloudApi_cloudApi-OnRequest-cdafee6e_IamRolePolicyAttachment_5383D6A2" + } + }, + "policy_arn": "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole", + "role": "${aws_iam_role.cloudApi_cloudApi-OnRequest-cdafee6e_IamRole_4382C442.name}" + }, + "testGET--usershascorsheaders_Handler_IamRolePolicyAttachment_54A08CCC": { + "//": { + "metadata": { + "path": "root/Default/Default/test:GET --users has cors headers/Handler/IamRolePolicyAttachment", + "uniqueId": "testGET--usershascorsheaders_Handler_IamRolePolicyAttachment_54A08CCC" + } + }, + "policy_arn": "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole", + "role": "${aws_iam_role.testGET--usershascorsheaders_Handler_IamRole_6841C3FF.name}" + }, + "testGET--usershasdefaultcorsheaders_Handler_IamRolePolicyAttachment_50A99B49": { + "//": { + "metadata": { + "path": "root/Default/Default/test:GET --users has default cors headers/Handler/IamRolePolicyAttachment", + "uniqueId": "testGET--usershasdefaultcorsheaders_Handler_IamRolePolicyAttachment_50A99B49" + } + }, + "policy_arn": "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole", + "role": "${aws_iam_role.testGET--usershasdefaultcorsheaders_Handler_IamRole_17E5D7FC.name}" + }, + "testOPTIONS--usershascorsheaders_Handler_IamRolePolicyAttachment_CA58727C": { + "//": { + "metadata": { + "path": "root/Default/Default/test:OPTIONS --users has cors headers/Handler/IamRolePolicyAttachment", + "uniqueId": "testOPTIONS--usershascorsheaders_Handler_IamRolePolicyAttachment_CA58727C" + } + }, + "policy_arn": "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole", + "role": "${aws_iam_role.testOPTIONS--usershascorsheaders_Handler_IamRole_0EFF66BD.name}" + }, + "testOPTIONS--usershasdefaultcorsheaders_Handler_IamRolePolicyAttachment_CF666F56": { + "//": { + "metadata": { + "path": "root/Default/Default/test:OPTIONS --users has default cors headers/Handler/IamRolePolicyAttachment", + "uniqueId": "testOPTIONS--usershasdefaultcorsheaders_Handler_IamRolePolicyAttachment_CF666F56" + } + }, + "policy_arn": "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole", + "role": "${aws_iam_role.testOPTIONS--usershasdefaultcorsheaders_Handler_IamRole_E58DF921.name}" + }, + "testOPTIONS--usersrespondswithproperheadersforrequested_Handler_IamRolePolicyAttachment_F702A7D9": { + "//": { + "metadata": { + "path": "root/Default/Default/test:OPTIONS --users responds with proper headers for requested/Handler/IamRolePolicyAttachment", + "uniqueId": "testOPTIONS--usersrespondswithproperheadersforrequested_Handler_IamRolePolicyAttachment_F702A7D9" + } + }, + "policy_arn": "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole", + "role": "${aws_iam_role.testOPTIONS--usersrespondswithproperheadersforrequested_Handler_IamRole_4AB06A0F.name}" + } + }, + "aws_lambda_function": { + "apiCustomCors_apiCustomCors-OnRequest-52bc3c17_E34A40CA": { + "//": { + "metadata": { + "path": "root/Default/Default/apiCustomCors/apiCustomCors-OnRequest-52bc3c17/Default", + "uniqueId": "apiCustomCors_apiCustomCors-OnRequest-52bc3c17_E34A40CA" + } + }, + "environment": { + "variables": { + "WING_FUNCTION_NAME": "apiCustomCors-OnRequest-52bc3c17-c85c85c9", + "WING_TARGET": "tf-aws" + } + }, + "function_name": "apiCustomCors-OnRequest-52bc3c17-c85c85c9", + "handler": "index.handler", + "publish": true, + "role": "${aws_iam_role.apiCustomCors_apiCustomCors-OnRequest-52bc3c17_IamRole_692EAD85.arn}", + "runtime": "nodejs18.x", + "s3_bucket": "${aws_s3_bucket.Code.bucket}", + "s3_key": "${aws_s3_object.apiCustomCors_apiCustomCors-OnRequest-52bc3c17_S3Object_40199F8B.key}", + "timeout": 30, + "vpc_config": { + "security_group_ids": [], + "subnet_ids": [] + } + }, + "cloudApi_cloudApi-OnRequest-cdafee6e_A6C8366F": { + "//": { + "metadata": { + "path": "root/Default/Default/cloud.Api/cloud.Api-OnRequest-cdafee6e/Default", + "uniqueId": "cloudApi_cloudApi-OnRequest-cdafee6e_A6C8366F" + } + }, + "environment": { + "variables": { + "WING_FUNCTION_NAME": "cloud-Api-OnRequest-cdafee6e-c8147384", + "WING_TARGET": "tf-aws" + } + }, + "function_name": "cloud-Api-OnRequest-cdafee6e-c8147384", + "handler": "index.handler", + "publish": true, + "role": "${aws_iam_role.cloudApi_cloudApi-OnRequest-cdafee6e_IamRole_4382C442.arn}", + "runtime": "nodejs18.x", + "s3_bucket": "${aws_s3_bucket.Code.bucket}", + "s3_key": "${aws_s3_object.cloudApi_cloudApi-OnRequest-cdafee6e_S3Object_5DAAA0EF.key}", + "timeout": 30, + "vpc_config": { + "security_group_ids": [], + "subnet_ids": [] + } + }, + "testGET--usershascorsheaders_Handler_E0F337CB": { + "//": { + "metadata": { + "path": "root/Default/Default/test:GET --users has cors headers/Handler/Default", + "uniqueId": "testGET--usershascorsheaders_Handler_E0F337CB" + } + }, + "environment": { + "variables": { + "WING_FUNCTION_NAME": "Handler-c8d51aba", + "WING_TARGET": "tf-aws", + "WING_TOKEN_TFTOKEN_TOKEN_54": "${jsonencode(aws_api_gateway_stage.apiCustomCors_api_stage_F5D92865.invoke_url)}" + } + }, + "function_name": "Handler-c8d51aba", + "handler": "index.handler", + "publish": true, + "role": "${aws_iam_role.testGET--usershascorsheaders_Handler_IamRole_6841C3FF.arn}", + "runtime": "nodejs18.x", + "s3_bucket": "${aws_s3_bucket.Code.bucket}", + "s3_key": "${aws_s3_object.testGET--usershascorsheaders_Handler_S3Object_A63B28D3.key}", + "timeout": 30, + "vpc_config": { + "security_group_ids": [], + "subnet_ids": [] + } + }, + "testGET--usershasdefaultcorsheaders_Handler_1182379A": { + "//": { + "metadata": { + "path": "root/Default/Default/test:GET --users has default cors headers/Handler/Default", + "uniqueId": "testGET--usershasdefaultcorsheaders_Handler_1182379A" + } + }, + "environment": { + "variables": { + "WING_FUNCTION_NAME": "Handler-c80c888e", + "WING_TARGET": "tf-aws", + "WING_TOKEN_TFTOKEN_TOKEN_7": "${jsonencode(aws_api_gateway_stage.cloudApi_api_stage_BBB283E4.invoke_url)}" + } + }, + "function_name": "Handler-c80c888e", + "handler": "index.handler", + "publish": true, + "role": "${aws_iam_role.testGET--usershasdefaultcorsheaders_Handler_IamRole_17E5D7FC.arn}", + "runtime": "nodejs18.x", + "s3_bucket": "${aws_s3_bucket.Code.bucket}", + "s3_key": "${aws_s3_object.testGET--usershasdefaultcorsheaders_Handler_S3Object_ADAE18A8.key}", + "timeout": 30, + "vpc_config": { + "security_group_ids": [], + "subnet_ids": [] + } + }, + "testOPTIONS--usershascorsheaders_Handler_3A565385": { + "//": { + "metadata": { + "path": "root/Default/Default/test:OPTIONS --users has cors headers/Handler/Default", + "uniqueId": "testOPTIONS--usershascorsheaders_Handler_3A565385" + } + }, + "environment": { + "variables": { + "WING_FUNCTION_NAME": "Handler-c81c750d", + "WING_TARGET": "tf-aws", + "WING_TOKEN_TFTOKEN_TOKEN_54": "${jsonencode(aws_api_gateway_stage.apiCustomCors_api_stage_F5D92865.invoke_url)}" + } + }, + "function_name": "Handler-c81c750d", + "handler": "index.handler", + "publish": true, + "role": "${aws_iam_role.testOPTIONS--usershascorsheaders_Handler_IamRole_0EFF66BD.arn}", + "runtime": "nodejs18.x", + "s3_bucket": "${aws_s3_bucket.Code.bucket}", + "s3_key": "${aws_s3_object.testOPTIONS--usershascorsheaders_Handler_S3Object_7EC6E95C.key}", + "timeout": 30, + "vpc_config": { + "security_group_ids": [], + "subnet_ids": [] + } + }, + "testOPTIONS--usershasdefaultcorsheaders_Handler_D03A1BFF": { + "//": { + "metadata": { + "path": "root/Default/Default/test:OPTIONS --users has default cors headers/Handler/Default", + "uniqueId": "testOPTIONS--usershasdefaultcorsheaders_Handler_D03A1BFF" + } + }, + "environment": { + "variables": { + "WING_FUNCTION_NAME": "Handler-c82f7728", + "WING_TARGET": "tf-aws", + "WING_TOKEN_TFTOKEN_TOKEN_7": "${jsonencode(aws_api_gateway_stage.cloudApi_api_stage_BBB283E4.invoke_url)}" + } + }, + "function_name": "Handler-c82f7728", + "handler": "index.handler", + "publish": true, + "role": "${aws_iam_role.testOPTIONS--usershasdefaultcorsheaders_Handler_IamRole_E58DF921.arn}", + "runtime": "nodejs18.x", + "s3_bucket": "${aws_s3_bucket.Code.bucket}", + "s3_key": "${aws_s3_object.testOPTIONS--usershasdefaultcorsheaders_Handler_S3Object_2F99FE7D.key}", + "timeout": 30, + "vpc_config": { + "security_group_ids": [], + "subnet_ids": [] + } + }, + "testOPTIONS--usersrespondswithproperheadersforrequested_Handler_0A2AB662": { + "//": { + "metadata": { + "path": "root/Default/Default/test:OPTIONS --users responds with proper headers for requested/Handler/Default", + "uniqueId": "testOPTIONS--usersrespondswithproperheadersforrequested_Handler_0A2AB662" + } + }, + "environment": { + "variables": { + "WING_FUNCTION_NAME": "Handler-c8aef6d3", + "WING_TARGET": "tf-aws", + "WING_TOKEN_TFTOKEN_TOKEN_54": "${jsonencode(aws_api_gateway_stage.apiCustomCors_api_stage_F5D92865.invoke_url)}" + } + }, + "function_name": "Handler-c8aef6d3", + "handler": "index.handler", + "publish": true, + "role": "${aws_iam_role.testOPTIONS--usersrespondswithproperheadersforrequested_Handler_IamRole_4AB06A0F.arn}", + "runtime": "nodejs18.x", + "s3_bucket": "${aws_s3_bucket.Code.bucket}", + "s3_key": "${aws_s3_object.testOPTIONS--usersrespondswithproperheadersforrequested_Handler_S3Object_0954ACCC.key}", + "timeout": 30, + "vpc_config": { + "security_group_ids": [], + "subnet_ids": [] + } + } + }, + "aws_lambda_permission": { + "apiCustomCors_api_permission-GET-41f0e61d_B09839FA": { + "//": { + "metadata": { + "path": "root/Default/Default/apiCustomCors/api/permission-GET-41f0e61d", + "uniqueId": "apiCustomCors_api_permission-GET-41f0e61d_B09839FA" + } + }, + "action": "lambda:InvokeFunction", + "function_name": "${aws_lambda_function.apiCustomCors_apiCustomCors-OnRequest-52bc3c17_E34A40CA.function_name}", + "principal": "apigateway.amazonaws.com", + "source_arn": "${aws_api_gateway_rest_api.apiCustomCors_api_D8B9CD90.execution_arn}/*/GET/users", + "statement_id": "AllowExecutionFromAPIGateway-GET-41f0e61d" + }, + "cloudApi_api_permission-GET-41f0e61d_DD9B4FD0": { + "//": { + "metadata": { + "path": "root/Default/Default/cloud.Api/api/permission-GET-41f0e61d", + "uniqueId": "cloudApi_api_permission-GET-41f0e61d_DD9B4FD0" + } + }, + "action": "lambda:InvokeFunction", + "function_name": "${aws_lambda_function.cloudApi_cloudApi-OnRequest-cdafee6e_A6C8366F.function_name}", + "principal": "apigateway.amazonaws.com", + "source_arn": "${aws_api_gateway_rest_api.cloudApi_api_2B334D75.execution_arn}/*/GET/users", + "statement_id": "AllowExecutionFromAPIGateway-GET-41f0e61d" + } + }, + "aws_s3_bucket": { + "Code": { + "//": { + "metadata": { + "path": "root/Default/Code", + "uniqueId": "Code" + } + }, + "bucket_prefix": "code-c84a50b1-" + } + }, + "aws_s3_object": { + "apiCustomCors_apiCustomCors-OnRequest-52bc3c17_S3Object_40199F8B": { + "//": { + "metadata": { + "path": "root/Default/Default/apiCustomCors/apiCustomCors-OnRequest-52bc3c17/S3Object", + "uniqueId": "apiCustomCors_apiCustomCors-OnRequest-52bc3c17_S3Object_40199F8B" + } + }, + "bucket": "${aws_s3_bucket.Code.bucket}", + "key": "", + "source": "" + }, + "cloudApi_cloudApi-OnRequest-cdafee6e_S3Object_5DAAA0EF": { + "//": { + "metadata": { + "path": "root/Default/Default/cloud.Api/cloud.Api-OnRequest-cdafee6e/S3Object", + "uniqueId": "cloudApi_cloudApi-OnRequest-cdafee6e_S3Object_5DAAA0EF" + } + }, + "bucket": "${aws_s3_bucket.Code.bucket}", + "key": "", + "source": "" + }, + "testGET--usershascorsheaders_Handler_S3Object_A63B28D3": { + "//": { + "metadata": { + "path": "root/Default/Default/test:GET --users has cors headers/Handler/S3Object", + "uniqueId": "testGET--usershascorsheaders_Handler_S3Object_A63B28D3" + } + }, + "bucket": "${aws_s3_bucket.Code.bucket}", + "key": "", + "source": "" + }, + "testGET--usershasdefaultcorsheaders_Handler_S3Object_ADAE18A8": { + "//": { + "metadata": { + "path": "root/Default/Default/test:GET --users has default cors headers/Handler/S3Object", + "uniqueId": "testGET--usershasdefaultcorsheaders_Handler_S3Object_ADAE18A8" + } + }, + "bucket": "${aws_s3_bucket.Code.bucket}", + "key": "", + "source": "" + }, + "testOPTIONS--usershascorsheaders_Handler_S3Object_7EC6E95C": { + "//": { + "metadata": { + "path": "root/Default/Default/test:OPTIONS --users has cors headers/Handler/S3Object", + "uniqueId": "testOPTIONS--usershascorsheaders_Handler_S3Object_7EC6E95C" + } + }, + "bucket": "${aws_s3_bucket.Code.bucket}", + "key": "", + "source": "" + }, + "testOPTIONS--usershasdefaultcorsheaders_Handler_S3Object_2F99FE7D": { + "//": { + "metadata": { + "path": "root/Default/Default/test:OPTIONS --users has default cors headers/Handler/S3Object", + "uniqueId": "testOPTIONS--usershasdefaultcorsheaders_Handler_S3Object_2F99FE7D" + } + }, + "bucket": "${aws_s3_bucket.Code.bucket}", + "key": "", + "source": "" + }, + "testOPTIONS--usersrespondswithproperheadersforrequested_Handler_S3Object_0954ACCC": { + "//": { + "metadata": { + "path": "root/Default/Default/test:OPTIONS --users responds with proper headers for requested/Handler/S3Object", + "uniqueId": "testOPTIONS--usersrespondswithproperheadersforrequested_Handler_S3Object_0954ACCC" + } + }, + "bucket": "${aws_s3_bucket.Code.bucket}", + "key": "", + "source": "" + } + } + } +} +``` + +## preflight.assertions-1.js +```js +module.exports = function({ $stdlib }) { + const std = $stdlib.std; + class Assert extends $stdlib.std.Resource { + constructor(scope, id, ) { + super(scope, id); + this._addInflightOps("equalStr", "isNil", "equalNum", "$inflight_init"); + } + static _toInflightType(context) { + return $stdlib.core.NodeJsCode.fromInline(` + require("./inflight.Assert-1.js")({ + }) + `); + } + _toInflight() { + return $stdlib.core.NodeJsCode.fromInline(` + (await (async () => { + const AssertClient = ${Assert._toInflightType(this).text}; + const client = new AssertClient({ + }); + if (client.$inflight_init) { await client.$inflight_init(); } + return client; + })()) + `); + } + } + return { Assert }; +}; + +``` + +## preflight.js +```js +const $stdlib = require('@winglang/sdk'); +const $outdir = process.env.WING_SYNTH_DIR ?? "."; +const $wing_is_test = process.env.WING_IS_TEST === "true"; +const std = $stdlib.std; +const cloud = $stdlib.cloud; +const ex = $stdlib.ex; +const http = $stdlib.http; +const t = require("./preflight.assertions-1.js")({ $stdlib }); +class $Root extends $stdlib.std.Resource { + constructor(scope, id) { + super(scope, id); + class $Closure1 extends $stdlib.std.Resource { + constructor(scope, id, ) { + super(scope, id); + this._addInflightOps("handle", "$inflight_init"); + this.display.hidden = true; + } + static _toInflightType(context) { + return $stdlib.core.NodeJsCode.fromInline(` + require("./inflight.$Closure1-2.js")({ + }) + `); + } + _toInflight() { + return $stdlib.core.NodeJsCode.fromInline(` + (await (async () => { + const $Closure1Client = ${$Closure1._toInflightType(this).text}; + const client = new $Closure1Client({ + }); + if (client.$inflight_init) { await client.$inflight_init(); } + return client; + })()) + `); + } + } + class $Closure2 extends $stdlib.std.Resource { + constructor(scope, id, ) { + super(scope, id); + this._addInflightOps("handle", "$inflight_init"); + this.display.hidden = true; + } + static _toInflightType(context) { + return $stdlib.core.NodeJsCode.fromInline(` + require("./inflight.$Closure2-2.js")({ + $apiDefaultCors_url: ${context._lift(apiDefaultCors.url)}, + $http_HttpMethod: ${context._lift(http.HttpMethod)}, + $http_Util: ${context._lift(http.Util)}, + $t_Assert: ${context._lift(t.Assert)}, + }) + `); + } + _toInflight() { + return $stdlib.core.NodeJsCode.fromInline(` + (await (async () => { + const $Closure2Client = ${$Closure2._toInflightType(this).text}; + const client = new $Closure2Client({ + }); + if (client.$inflight_init) { await client.$inflight_init(); } + return client; + })()) + `); + } + _registerBind(host, ops) { + if (ops.includes("handle")) { + $Closure2._registerBindObject(apiDefaultCors.url, host, []); + } + super._registerBind(host, ops); + } + } + class $Closure3 extends $stdlib.std.Resource { + constructor(scope, id, ) { + super(scope, id); + this._addInflightOps("handle", "$inflight_init"); + this.display.hidden = true; + } + static _toInflightType(context) { + return $stdlib.core.NodeJsCode.fromInline(` + require("./inflight.$Closure3-2.js")({ + $apiDefaultCors_url: ${context._lift(apiDefaultCors.url)}, + $http_HttpMethod: ${context._lift(http.HttpMethod)}, + $http_Util: ${context._lift(http.Util)}, + $t_Assert: ${context._lift(t.Assert)}, + }) + `); + } + _toInflight() { + return $stdlib.core.NodeJsCode.fromInline(` + (await (async () => { + const $Closure3Client = ${$Closure3._toInflightType(this).text}; + const client = new $Closure3Client({ + }); + if (client.$inflight_init) { await client.$inflight_init(); } + return client; + })()) + `); + } + _registerBind(host, ops) { + if (ops.includes("handle")) { + $Closure3._registerBindObject(apiDefaultCors.url, host, []); + } + super._registerBind(host, ops); + } + } + class $Closure4 extends $stdlib.std.Resource { + constructor(scope, id, ) { + super(scope, id); + this._addInflightOps("handle", "$inflight_init"); + this.display.hidden = true; + } + static _toInflightType(context) { + return $stdlib.core.NodeJsCode.fromInline(` + require("./inflight.$Closure4-2.js")({ + }) + `); + } + _toInflight() { + return $stdlib.core.NodeJsCode.fromInline(` + (await (async () => { + const $Closure4Client = ${$Closure4._toInflightType(this).text}; + const client = new $Closure4Client({ + }); + if (client.$inflight_init) { await client.$inflight_init(); } + return client; + })()) + `); + } + } + class $Closure5 extends $stdlib.std.Resource { + constructor(scope, id, ) { + super(scope, id); + this._addInflightOps("handle", "$inflight_init"); + this.display.hidden = true; + } + static _toInflightType(context) { + return $stdlib.core.NodeJsCode.fromInline(` + require("./inflight.$Closure5-2.js")({ + $apiCustomCors_url: ${context._lift(apiCustomCors.url)}, + $http_HttpMethod: ${context._lift(http.HttpMethod)}, + $http_Util: ${context._lift(http.Util)}, + $t_Assert: ${context._lift(t.Assert)}, + }) + `); + } + _toInflight() { + return $stdlib.core.NodeJsCode.fromInline(` + (await (async () => { + const $Closure5Client = ${$Closure5._toInflightType(this).text}; + const client = new $Closure5Client({ + }); + if (client.$inflight_init) { await client.$inflight_init(); } + return client; + })()) + `); + } + _registerBind(host, ops) { + if (ops.includes("handle")) { + $Closure5._registerBindObject(apiCustomCors.url, host, []); + } + super._registerBind(host, ops); + } + } + class $Closure6 extends $stdlib.std.Resource { + constructor(scope, id, ) { + super(scope, id); + this._addInflightOps("handle", "$inflight_init"); + this.display.hidden = true; + } + static _toInflightType(context) { + return $stdlib.core.NodeJsCode.fromInline(` + require("./inflight.$Closure6-2.js")({ + $apiCustomCors_url: ${context._lift(apiCustomCors.url)}, + $http_HttpMethod: ${context._lift(http.HttpMethod)}, + $http_Util: ${context._lift(http.Util)}, + $t_Assert: ${context._lift(t.Assert)}, + }) + `); + } + _toInflight() { + return $stdlib.core.NodeJsCode.fromInline(` + (await (async () => { + const $Closure6Client = ${$Closure6._toInflightType(this).text}; + const client = new $Closure6Client({ + }); + if (client.$inflight_init) { await client.$inflight_init(); } + return client; + })()) + `); + } + _registerBind(host, ops) { + if (ops.includes("handle")) { + $Closure6._registerBindObject(apiCustomCors.url, host, []); + } + super._registerBind(host, ops); + } + } + class $Closure7 extends $stdlib.std.Resource { + constructor(scope, id, ) { + super(scope, id); + this._addInflightOps("handle", "$inflight_init"); + this.display.hidden = true; + } + static _toInflightType(context) { + return $stdlib.core.NodeJsCode.fromInline(` + require("./inflight.$Closure7-2.js")({ + $apiCustomCors_url: ${context._lift(apiCustomCors.url)}, + $http_HttpMethod: ${context._lift(http.HttpMethod)}, + $http_Util: ${context._lift(http.Util)}, + $t_Assert: ${context._lift(t.Assert)}, + }) + `); + } + _toInflight() { + return $stdlib.core.NodeJsCode.fromInline(` + (await (async () => { + const $Closure7Client = ${$Closure7._toInflightType(this).text}; + const client = new $Closure7Client({ + }); + if (client.$inflight_init) { await client.$inflight_init(); } + return client; + })()) + `); + } + _registerBind(host, ops) { + if (ops.includes("handle")) { + $Closure7._registerBindObject(apiCustomCors.url, host, []); + } + super._registerBind(host, ops); + } + } + const apiDefaultCors = this.node.root.newAbstract("@winglang/sdk.cloud.Api",this,"cloud.Api",{ cors: ({}) }); + const getHandler = new $Closure1(this,"$Closure1"); + (apiDefaultCors.get("/users",getHandler)); + this.node.root.new("@winglang/sdk.std.Test",std.Test,this,"test:GET /users has default cors headers",new $Closure2(this,"$Closure2")); + this.node.root.new("@winglang/sdk.std.Test",std.Test,this,"test:OPTIONS /users has default cors headers",new $Closure3(this,"$Closure3")); + const apiCustomCors = this.node.root.newAbstract("@winglang/sdk.cloud.Api",this,"apiCustomCors",{ cors: ({"origins": ["winglang.io"],"methods": [cloud.HttpMethod.GET, cloud.HttpMethod.POST, cloud.HttpMethod.OPTIONS],"headers": ["Content-Type", "Authorization", "X-Custom-Header"],"allowCredentials": true,"exposedHeaders": ["Content-Type"]}) }); + const getCustomHandler = new $Closure4(this,"$Closure4"); + (apiCustomCors.get("/users",getCustomHandler)); + this.node.root.new("@winglang/sdk.std.Test",std.Test,this,"test:GET /users has cors headers",new $Closure5(this,"$Closure5")); + this.node.root.new("@winglang/sdk.std.Test",std.Test,this,"test:OPTIONS /users has cors headers",new $Closure6(this,"$Closure6")); + this.node.root.new("@winglang/sdk.std.Test",std.Test,this,"test:OPTIONS /users responds with proper headers for requested",new $Closure7(this,"$Closure7")); + } +} +const $App = $stdlib.core.App.for(process.env.WING_TARGET); +new $App({ outdir: $outdir, name: "cors_api", rootConstruct: $Root, plugins: $plugins, isTestEnvironment: $wing_is_test, entrypointDir: process.env['WING_SOURCE_DIR'], rootId: process.env['WING_ROOT_ID'] }).synth(); + +``` + diff --git a/tools/hangar/__snapshots__/test_corpus/valid/cors_api.w_test_sim.md b/tools/hangar/__snapshots__/test_corpus/valid/cors_api.w_test_sim.md new file mode 100644 index 00000000000..41b41114956 --- /dev/null +++ b/tools/hangar/__snapshots__/test_corpus/valid/cors_api.w_test_sim.md @@ -0,0 +1,16 @@ +# [cors_api.w](../../../../../examples/tests/valid/cors_api.w) | test | sim + +## stdout.log +```log +pass ─ cors_api.wsim » root/env0/test:GET --users has default cors headers +pass ─ cors_api.wsim » root/env1/test:OPTIONS --users has default cors headers +pass ─ cors_api.wsim » root/env2/test:GET --users has cors headers +pass ─ cors_api.wsim » root/env3/test:OPTIONS --users has cors headers +pass ─ cors_api.wsim » root/env4/test:OPTIONS --users responds with proper headers for requested + + +Tests 5 passed (5) +Test Files 1 passed (1) +Duration +``` + diff --git a/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_compile_tf-aws.md b/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_compile_tf-aws.md index a97c260ea86..ece7a0bfab0 100644 --- a/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_compile_tf-aws.md +++ b/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_compile_tf-aws.md @@ -1,6 +1,6 @@ # [website_with_api.w](../../../../../examples/tests/valid/website_with_api.w) | compile | tf-aws -## inflight.$Closure1-1.js +## inflight.$Closure1-2.js ```js module.exports = function({ $std_Json, $usersTable }) { class $Closure1 { @@ -18,7 +18,7 @@ module.exports = function({ $std_Json, $usersTable }) { ``` -## inflight.$Closure2-1.js +## inflight.$Closure2-2.js ```js module.exports = function({ $std_Json, $usersTable }) { class $Closure2 { @@ -41,17 +41,25 @@ module.exports = function({ $std_Json, $usersTable }) { ``` -## inflight.$Closure3-1.js +## inflight.$Closure3-2.js ```js -module.exports = function({ }) { +module.exports = function({ $api_url, $http_HttpMethod, $http_Util, $std_Json, $t_Assert }) { class $Closure3 { constructor({ }) { const $obj = (...args) => this.handle(...args); Object.setPrototypeOf($obj, this); return $obj; } - async handle(req) { - return ({"headers": ({"Access-Control-Allow-Headers": "Content-Type","Access-Control-Allow-Origin": "*","Access-Control-Allow-Methods": "OPTIONS,POST,GET"}),"status": 204}); + async handle() { + const response = (await $http_Util.fetch(($api_url + "/users"),({"method": $http_HttpMethod.GET,"headers": ({"Content-Type": "text/json"})}))); + const headers = response.headers; + (await $t_Assert.equalNum(response.status,200)); + {console.log(((args) => { return JSON.stringify(args[0], null, args[1]?.indent) })([headers]))}; + (await $t_Assert.equalStr((headers)["access-control-allow-origin"],"*")); + (await $t_Assert.equalStr((headers)["access-control-expose-headers"],"Content-Type")); + (await $t_Assert.equalStr((headers)["access-control-allow-credentials"],"false")); + (await $t_Assert.isNil((headers)["access-control-allow-headers"])); + (await $t_Assert.isNil((headers)["access-control-allow-methods"])); } } return $Closure3; @@ -59,6 +67,70 @@ module.exports = function({ }) { ``` +## inflight.$Closure4-2.js +```js +module.exports = function({ $api_url, $http_HttpMethod, $http_Util, $t_Assert }) { + class $Closure4 { + constructor({ }) { + const $obj = (...args) => this.handle(...args); + Object.setPrototypeOf($obj, this); + return $obj; + } + async handle() { + const response = (await $http_Util.fetch(($api_url + "/users"),({"method": $http_HttpMethod.OPTIONS,"headers": ({"Content-Type": "text/json"})}))); + const headers = response.headers; + (await $t_Assert.equalNum(response.status,204)); + (await $t_Assert.equalStr((headers)["access-control-allow-origin"],"*")); + (await $t_Assert.equalStr((headers)["access-control-allow-methods"],"GET,POST,OPTIONS")); + (await $t_Assert.equalStr((headers)["access-control-allow-headers"],"Content-Type")); + (await $t_Assert.equalStr((headers)["access-control-expose-headers"],"Content-Type")); + (await $t_Assert.equalStr((headers)["access-control-allow-credentials"],"false")); + } + } + return $Closure4; +} + +``` + +## inflight.Assert-1.js +```js +module.exports = function({ }) { + class Assert { + static async equalStr(a, b) { + try { + {((cond) => {if (!cond) throw new Error("assertion failed: a == b")})((((a,b) => { try { return require('assert').deepStrictEqual(a,b) === undefined; } catch { return false; } })(a,b)))}; + } + catch ($error_e) { + const e = $error_e.message; + {((msg) => {throw new Error(msg)})(String.raw({ raw: ["expected: ", " got: ", ""] }, b, a))}; + } + } + static async isNil(a) { + try { + {((cond) => {if (!cond) throw new Error("assertion failed: a == nil")})((((a,b) => { try { return require('assert').deepStrictEqual(a,b) === undefined; } catch { return false; } })(a,undefined)))}; + } + catch ($error_e) { + const e = $error_e.message; + {console.log(e)}; + {((msg) => {throw new Error(msg)})(String.raw({ raw: ["expected '", "' to be nil"] }, a))}; + } + } + static async equalNum(a, b) { + try { + {((cond) => {if (!cond) throw new Error("assertion failed: a == b")})((((a,b) => { try { return require('assert').deepStrictEqual(a,b) === undefined; } catch { return false; } })(a,b)))}; + } + catch ($error_e) { + const e = $error_e.message; + {console.log(e)}; + {((msg) => {throw new Error(msg)})(String.raw({ raw: ["expected: ", " got: ", ""] }, b, a))}; + } + } + } + return Assert; +} + +``` + ## main.tf.json ```json { @@ -129,7 +201,7 @@ module.exports = function({ }) { }, "output": { "WING_TEST_RUNNER_FUNCTION_ARNS": { - "value": "[]" + "value": "[[\"root/Default/Default/test:GET --users\",\"${aws_lambda_function.testGET--users_Handler_5E592AA6.arn}\"],[\"root/Default/Default/test:OPTIONS --users\",\"${aws_lambda_function.testOPTIONS--users_Handler_01361C41.arn}\"]]" } }, "provider": { @@ -151,7 +223,7 @@ module.exports = function({ }) { }, "rest_api_id": "${aws_api_gateway_rest_api.cloudApi_api_2B334D75.id}", "triggers": { - "redeployment": "968b3a7209054cfcc312829735646935481807e4" + "redeployment": "6cabf6d04679bca394eb3265feb1ff765b56abb1" } } }, @@ -163,7 +235,7 @@ module.exports = function({ }) { "uniqueId": "cloudApi_api_2B334D75" } }, - "body": "{\"openapi\":\"3.0.3\",\"paths\":{\"/users\":{\"get\":{\"operationId\":\"get-users\",\"responses\":{\"200\":{\"description\":\"200 response\",\"content\":{}}},\"parameters\":[],\"x-amazon-apigateway-integration\":{\"uri\":\"arn:aws:apigateway:${data.aws_region.Region.name}:lambda:path/2015-03-31/functions/${aws_lambda_function.cloudApi_cloudApi-OnRequest-cdafee6e_A6C8366F.arn}/invocations\",\"type\":\"aws_proxy\",\"httpMethod\":\"POST\",\"responses\":{\"default\":{\"statusCode\":\"200\"}},\"passthroughBehavior\":\"when_no_match\",\"contentHandling\":\"CONVERT_TO_TEXT\"}},\"post\":{\"operationId\":\"post-users\",\"responses\":{\"200\":{\"description\":\"200 response\",\"content\":{}}},\"parameters\":[],\"x-amazon-apigateway-integration\":{\"uri\":\"arn:aws:apigateway:${data.aws_region.Region.name}:lambda:path/2015-03-31/functions/${aws_lambda_function.cloudApi_cloudApi-OnRequest-86898773_701F5CA7.arn}/invocations\",\"type\":\"aws_proxy\",\"httpMethod\":\"POST\",\"responses\":{\"default\":{\"statusCode\":\"200\"}},\"passthroughBehavior\":\"when_no_match\",\"contentHandling\":\"CONVERT_TO_TEXT\"}},\"options\":{\"operationId\":\"options-users\",\"responses\":{\"200\":{\"description\":\"200 response\",\"content\":{}}},\"parameters\":[],\"x-amazon-apigateway-integration\":{\"uri\":\"arn:aws:apigateway:${data.aws_region.Region.name}:lambda:path/2015-03-31/functions/${aws_lambda_function.cloudApi_cloudApi-OnRequest-3fc9280c_5DA20E7A.arn}/invocations\",\"type\":\"aws_proxy\",\"httpMethod\":\"POST\",\"responses\":{\"default\":{\"statusCode\":\"200\"}},\"passthroughBehavior\":\"when_no_match\",\"contentHandling\":\"CONVERT_TO_TEXT\"}}}}}", + "body": "{\"openapi\":\"3.0.3\",\"paths\":{\"/users\":{\"get\":{\"operationId\":\"get-users\",\"responses\":{\"200\":{\"description\":\"200 response\",\"content\":{}}},\"parameters\":[],\"x-amazon-apigateway-integration\":{\"uri\":\"arn:aws:apigateway:${data.aws_region.Region.name}:lambda:path/2015-03-31/functions/${aws_lambda_function.cloudApi_cloudApi-OnRequest-cdafee6e_A6C8366F.arn}/invocations\",\"type\":\"aws_proxy\",\"httpMethod\":\"POST\",\"responses\":{\"default\":{\"statusCode\":\"200\"}},\"passthroughBehavior\":\"when_no_match\",\"contentHandling\":\"CONVERT_TO_TEXT\"}},\"post\":{\"operationId\":\"post-users\",\"responses\":{\"200\":{\"description\":\"200 response\",\"content\":{}}},\"parameters\":[],\"x-amazon-apigateway-integration\":{\"uri\":\"arn:aws:apigateway:${data.aws_region.Region.name}:lambda:path/2015-03-31/functions/${aws_lambda_function.cloudApi_cloudApi-OnRequest-86898773_701F5CA7.arn}/invocations\",\"type\":\"aws_proxy\",\"httpMethod\":\"POST\",\"responses\":{\"default\":{\"statusCode\":\"200\"}},\"passthroughBehavior\":\"when_no_match\",\"contentHandling\":\"CONVERT_TO_TEXT\"}}}}}", "name": "api-c895068c" } }, @@ -265,15 +337,6 @@ module.exports = function({ }) { } }, "aws_iam_role": { - "cloudApi_cloudApi-OnRequest-3fc9280c_IamRole_EBC99528": { - "//": { - "metadata": { - "path": "root/Default/Default/cloud.Api/cloud.Api-OnRequest-3fc9280c/IamRole", - "uniqueId": "cloudApi_cloudApi-OnRequest-3fc9280c_IamRole_EBC99528" - } - }, - "assume_role_policy": "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Action\":\"sts:AssumeRole\",\"Principal\":{\"Service\":\"lambda.amazonaws.com\"},\"Effect\":\"Allow\"}]}" - }, "cloudApi_cloudApi-OnRequest-86898773_IamRole_6300C24F": { "//": { "metadata": { @@ -291,19 +354,27 @@ module.exports = function({ }) { } }, "assume_role_policy": "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Action\":\"sts:AssumeRole\",\"Principal\":{\"Service\":\"lambda.amazonaws.com\"},\"Effect\":\"Allow\"}]}" - } - }, - "aws_iam_role_policy": { - "cloudApi_cloudApi-OnRequest-3fc9280c_IamRolePolicy_1552D6FE": { + }, + "testGET--users_Handler_IamRole_3C6B1A52": { "//": { "metadata": { - "path": "root/Default/Default/cloud.Api/cloud.Api-OnRequest-3fc9280c/IamRolePolicy", - "uniqueId": "cloudApi_cloudApi-OnRequest-3fc9280c_IamRolePolicy_1552D6FE" + "path": "root/Default/Default/test:GET --users/Handler/IamRole", + "uniqueId": "testGET--users_Handler_IamRole_3C6B1A52" } }, - "policy": "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Action\":\"none:null\",\"Resource\":\"*\"}]}", - "role": "${aws_iam_role.cloudApi_cloudApi-OnRequest-3fc9280c_IamRole_EBC99528.name}" + "assume_role_policy": "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Action\":\"sts:AssumeRole\",\"Principal\":{\"Service\":\"lambda.amazonaws.com\"},\"Effect\":\"Allow\"}]}" }, + "testOPTIONS--users_Handler_IamRole_F9362AB0": { + "//": { + "metadata": { + "path": "root/Default/Default/test:OPTIONS --users/Handler/IamRole", + "uniqueId": "testOPTIONS--users_Handler_IamRole_F9362AB0" + } + }, + "assume_role_policy": "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Action\":\"sts:AssumeRole\",\"Principal\":{\"Service\":\"lambda.amazonaws.com\"},\"Effect\":\"Allow\"}]}" + } + }, + "aws_iam_role_policy": { "cloudApi_cloudApi-OnRequest-86898773_IamRolePolicy_DAC639E5": { "//": { "metadata": { @@ -323,19 +394,29 @@ module.exports = function({ }) { }, "policy": "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Action\":[\"dynamodb:Scan\"],\"Resource\":[\"${aws_dynamodb_table.exTable.arn}\"],\"Effect\":\"Allow\"}]}", "role": "${aws_iam_role.cloudApi_cloudApi-OnRequest-cdafee6e_IamRole_4382C442.name}" - } - }, - "aws_iam_role_policy_attachment": { - "cloudApi_cloudApi-OnRequest-3fc9280c_IamRolePolicyAttachment_A988C18B": { + }, + "testGET--users_Handler_IamRolePolicy_70F076F2": { "//": { "metadata": { - "path": "root/Default/Default/cloud.Api/cloud.Api-OnRequest-3fc9280c/IamRolePolicyAttachment", - "uniqueId": "cloudApi_cloudApi-OnRequest-3fc9280c_IamRolePolicyAttachment_A988C18B" + "path": "root/Default/Default/test:GET --users/Handler/IamRolePolicy", + "uniqueId": "testGET--users_Handler_IamRolePolicy_70F076F2" } }, - "policy_arn": "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole", - "role": "${aws_iam_role.cloudApi_cloudApi-OnRequest-3fc9280c_IamRole_EBC99528.name}" + "policy": "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Action\":\"none:null\",\"Resource\":\"*\"}]}", + "role": "${aws_iam_role.testGET--users_Handler_IamRole_3C6B1A52.name}" }, + "testOPTIONS--users_Handler_IamRolePolicy_E1FFA7C9": { + "//": { + "metadata": { + "path": "root/Default/Default/test:OPTIONS --users/Handler/IamRolePolicy", + "uniqueId": "testOPTIONS--users_Handler_IamRolePolicy_E1FFA7C9" + } + }, + "policy": "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Action\":\"none:null\",\"Resource\":\"*\"}]}", + "role": "${aws_iam_role.testOPTIONS--users_Handler_IamRole_F9362AB0.name}" + } + }, + "aws_iam_role_policy_attachment": { "cloudApi_cloudApi-OnRequest-86898773_IamRolePolicyAttachment_6E485A17": { "//": { "metadata": { @@ -355,35 +436,29 @@ module.exports = function({ }) { }, "policy_arn": "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole", "role": "${aws_iam_role.cloudApi_cloudApi-OnRequest-cdafee6e_IamRole_4382C442.name}" - } - }, - "aws_lambda_function": { - "cloudApi_cloudApi-OnRequest-3fc9280c_5DA20E7A": { + }, + "testGET--users_Handler_IamRolePolicyAttachment_CE57C035": { "//": { "metadata": { - "path": "root/Default/Default/cloud.Api/cloud.Api-OnRequest-3fc9280c/Default", - "uniqueId": "cloudApi_cloudApi-OnRequest-3fc9280c_5DA20E7A" + "path": "root/Default/Default/test:GET --users/Handler/IamRolePolicyAttachment", + "uniqueId": "testGET--users_Handler_IamRolePolicyAttachment_CE57C035" } }, - "environment": { - "variables": { - "WING_FUNCTION_NAME": "cloud-Api-OnRequest-3fc9280c-c8d3ecf9", - "WING_TARGET": "tf-aws" + "policy_arn": "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole", + "role": "${aws_iam_role.testGET--users_Handler_IamRole_3C6B1A52.name}" + }, + "testOPTIONS--users_Handler_IamRolePolicyAttachment_41F75288": { + "//": { + "metadata": { + "path": "root/Default/Default/test:OPTIONS --users/Handler/IamRolePolicyAttachment", + "uniqueId": "testOPTIONS--users_Handler_IamRolePolicyAttachment_41F75288" } }, - "function_name": "cloud-Api-OnRequest-3fc9280c-c8d3ecf9", - "handler": "index.handler", - "publish": true, - "role": "${aws_iam_role.cloudApi_cloudApi-OnRequest-3fc9280c_IamRole_EBC99528.arn}", - "runtime": "nodejs18.x", - "s3_bucket": "${aws_s3_bucket.Code.bucket}", - "s3_key": "${aws_s3_object.cloudApi_cloudApi-OnRequest-3fc9280c_S3Object_EC722CF3.key}", - "timeout": 30, - "vpc_config": { - "security_group_ids": [], - "subnet_ids": [] - } - }, + "policy_arn": "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole", + "role": "${aws_iam_role.testOPTIONS--users_Handler_IamRole_F9362AB0.name}" + } + }, + "aws_lambda_function": { "cloudApi_cloudApi-OnRequest-86898773_701F5CA7": { "//": { "metadata": { @@ -441,6 +516,60 @@ module.exports = function({ }) { "security_group_ids": [], "subnet_ids": [] } + }, + "testGET--users_Handler_5E592AA6": { + "//": { + "metadata": { + "path": "root/Default/Default/test:GET --users/Handler/Default", + "uniqueId": "testGET--users_Handler_5E592AA6" + } + }, + "environment": { + "variables": { + "WING_FUNCTION_NAME": "Handler-c86bda55", + "WING_TARGET": "tf-aws", + "WING_TOKEN_TFTOKEN_TOKEN_7": "${jsonencode(aws_api_gateway_stage.cloudApi_api_stage_BBB283E4.invoke_url)}" + } + }, + "function_name": "Handler-c86bda55", + "handler": "index.handler", + "publish": true, + "role": "${aws_iam_role.testGET--users_Handler_IamRole_3C6B1A52.arn}", + "runtime": "nodejs18.x", + "s3_bucket": "${aws_s3_bucket.Code.bucket}", + "s3_key": "${aws_s3_object.testGET--users_Handler_S3Object_8248DE4B.key}", + "timeout": 30, + "vpc_config": { + "security_group_ids": [], + "subnet_ids": [] + } + }, + "testOPTIONS--users_Handler_01361C41": { + "//": { + "metadata": { + "path": "root/Default/Default/test:OPTIONS --users/Handler/Default", + "uniqueId": "testOPTIONS--users_Handler_01361C41" + } + }, + "environment": { + "variables": { + "WING_FUNCTION_NAME": "Handler-c8b7642e", + "WING_TARGET": "tf-aws", + "WING_TOKEN_TFTOKEN_TOKEN_7": "${jsonencode(aws_api_gateway_stage.cloudApi_api_stage_BBB283E4.invoke_url)}" + } + }, + "function_name": "Handler-c8b7642e", + "handler": "index.handler", + "publish": true, + "role": "${aws_iam_role.testOPTIONS--users_Handler_IamRole_F9362AB0.arn}", + "runtime": "nodejs18.x", + "s3_bucket": "${aws_s3_bucket.Code.bucket}", + "s3_key": "${aws_s3_object.testOPTIONS--users_Handler_S3Object_C0C4A75C.key}", + "timeout": 30, + "vpc_config": { + "security_group_ids": [], + "subnet_ids": [] + } } }, "aws_lambda_permission": { @@ -457,19 +586,6 @@ module.exports = function({ }) { "source_arn": "${aws_api_gateway_rest_api.cloudApi_api_2B334D75.execution_arn}/*/GET/users", "statement_id": "AllowExecutionFromAPIGateway-GET-41f0e61d" }, - "cloudApi_api_permission-OPTIONS-41f0e61d_12224EFF": { - "//": { - "metadata": { - "path": "root/Default/Default/cloud.Api/api/permission-OPTIONS-41f0e61d", - "uniqueId": "cloudApi_api_permission-OPTIONS-41f0e61d_12224EFF" - } - }, - "action": "lambda:InvokeFunction", - "function_name": "${aws_lambda_function.cloudApi_cloudApi-OnRequest-3fc9280c_5DA20E7A.function_name}", - "principal": "apigateway.amazonaws.com", - "source_arn": "${aws_api_gateway_rest_api.cloudApi_api_2B334D75.execution_arn}/*/OPTIONS/users", - "statement_id": "AllowExecutionFromAPIGateway-OPTIONS-41f0e61d" - }, "cloudApi_api_permission-POST-41f0e61d_743604B6": { "//": { "metadata": { @@ -565,17 +681,6 @@ module.exports = function({ }) { } }, "aws_s3_object": { - "cloudApi_cloudApi-OnRequest-3fc9280c_S3Object_EC722CF3": { - "//": { - "metadata": { - "path": "root/Default/Default/cloud.Api/cloud.Api-OnRequest-3fc9280c/S3Object", - "uniqueId": "cloudApi_cloudApi-OnRequest-3fc9280c_S3Object_EC722CF3" - } - }, - "bucket": "${aws_s3_bucket.Code.bucket}", - "key": "", - "source": "" - }, "cloudApi_cloudApi-OnRequest-86898773_S3Object_12D28469": { "//": { "metadata": { @@ -628,12 +733,66 @@ module.exports = function({ }) { "aws_s3_bucket.cloudWebsite_WebsiteBucket_EB03D355" ], "key": "config.json" + }, + "testGET--users_Handler_S3Object_8248DE4B": { + "//": { + "metadata": { + "path": "root/Default/Default/test:GET --users/Handler/S3Object", + "uniqueId": "testGET--users_Handler_S3Object_8248DE4B" + } + }, + "bucket": "${aws_s3_bucket.Code.bucket}", + "key": "", + "source": "" + }, + "testOPTIONS--users_Handler_S3Object_C0C4A75C": { + "//": { + "metadata": { + "path": "root/Default/Default/test:OPTIONS --users/Handler/S3Object", + "uniqueId": "testOPTIONS--users_Handler_S3Object_C0C4A75C" + } + }, + "bucket": "${aws_s3_bucket.Code.bucket}", + "key": "", + "source": "" } } } } ``` +## preflight.assertions-1.js +```js +module.exports = function({ $stdlib }) { + const std = $stdlib.std; + class Assert extends $stdlib.std.Resource { + constructor(scope, id, ) { + super(scope, id); + this._addInflightOps("equalStr", "isNil", "equalNum", "$inflight_init"); + } + static _toInflightType(context) { + return $stdlib.core.NodeJsCode.fromInline(` + require("./inflight.Assert-1.js")({ + }) + `); + } + _toInflight() { + return $stdlib.core.NodeJsCode.fromInline(` + (await (async () => { + const AssertClient = ${Assert._toInflightType(this).text}; + const client = new AssertClient({ + }); + if (client.$inflight_init) { await client.$inflight_init(); } + return client; + })()) + `); + } + } + return { Assert }; +}; + +``` + ## preflight.js ```js const $stdlib = require('@winglang/sdk'); @@ -642,6 +801,8 @@ const $wing_is_test = process.env.WING_IS_TEST === "true"; const std = $stdlib.std; const cloud = $stdlib.cloud; const ex = $stdlib.ex; +const http = $stdlib.http; +const t = require("./preflight.assertions-1.js")({ $stdlib }); class $Root extends $stdlib.std.Resource { constructor(scope, id) { super(scope, id); @@ -653,7 +814,7 @@ class $Root extends $stdlib.std.Resource { } static _toInflightType(context) { return $stdlib.core.NodeJsCode.fromInline(` - require("./inflight.$Closure1-1.js")({ + require("./inflight.$Closure1-2.js")({ $std_Json: ${context._lift(std.Json)}, $usersTable: ${context._lift(usersTable)}, }) @@ -685,7 +846,7 @@ class $Root extends $stdlib.std.Resource { } static _toInflightType(context) { return $stdlib.core.NodeJsCode.fromInline(` - require("./inflight.$Closure2-1.js")({ + require("./inflight.$Closure2-2.js")({ $std_Json: ${context._lift(std.Json)}, $usersTable: ${context._lift(usersTable)}, }) @@ -717,7 +878,12 @@ class $Root extends $stdlib.std.Resource { } static _toInflightType(context) { return $stdlib.core.NodeJsCode.fromInline(` - require("./inflight.$Closure3-1.js")({ + require("./inflight.$Closure3-2.js")({ + $api_url: ${context._lift(api.url)}, + $http_HttpMethod: ${context._lift(http.HttpMethod)}, + $http_Util: ${context._lift(http.Util)}, + $std_Json: ${context._lift(std.Json)}, + $t_Assert: ${context._lift(t.Assert)}, }) `); } @@ -732,17 +898,57 @@ class $Root extends $stdlib.std.Resource { })()) `); } + _registerBind(host, ops) { + if (ops.includes("handle")) { + $Closure3._registerBindObject(api.url, host, []); + } + super._registerBind(host, ops); + } + } + class $Closure4 extends $stdlib.std.Resource { + constructor(scope, id, ) { + super(scope, id); + this._addInflightOps("handle", "$inflight_init"); + this.display.hidden = true; + } + static _toInflightType(context) { + return $stdlib.core.NodeJsCode.fromInline(` + require("./inflight.$Closure4-2.js")({ + $api_url: ${context._lift(api.url)}, + $http_HttpMethod: ${context._lift(http.HttpMethod)}, + $http_Util: ${context._lift(http.Util)}, + $t_Assert: ${context._lift(t.Assert)}, + }) + `); + } + _toInflight() { + return $stdlib.core.NodeJsCode.fromInline(` + (await (async () => { + const $Closure4Client = ${$Closure4._toInflightType(this).text}; + const client = new $Closure4Client({ + }); + if (client.$inflight_init) { await client.$inflight_init(); } + return client; + })()) + `); + } + _registerBind(host, ops) { + if (ops.includes("handle")) { + $Closure4._registerBindObject(api.url, host, []); + } + super._registerBind(host, ops); + } } - const api = this.node.root.newAbstract("@winglang/sdk.cloud.Api",this,"cloud.Api"); + const api = this.node.root.newAbstract("@winglang/sdk.cloud.Api",this,"cloud.Api",{ cors: ({"origins": ["*"],"methods": [cloud.HttpMethod.GET, cloud.HttpMethod.POST, cloud.HttpMethod.OPTIONS],"headers": ["Content-Type"],"allowCredentials": false,"exposedHeaders": ["Content-Type"]}) }); const website = this.node.root.newAbstract("@winglang/sdk.cloud.Website",this,"cloud.Website",{ path: "./website_with_api" }); const usersTable = this.node.root.newAbstract("@winglang/sdk.ex.Table",this,"ex.Table",{ name: "users-table", primaryKey: "id", columns: ({"id": ex.ColumnType.STRING,"name": ex.ColumnType.STRING,"age": ex.ColumnType.NUMBER}) }); const getHandler = new $Closure1(this,"$Closure1"); const postHandler = new $Closure2(this,"$Closure2"); - const optionsHandler = new $Closure3(this,"$Closure3"); (api.get("/users",getHandler)); (api.post("/users",postHandler)); - (api.options("/users",optionsHandler)); (website.addJson("config.json",({"apiUrl": api.url}))); + this.node.root.new("@winglang/sdk.std.Test",std.Test,this,"test:GET /users",new $Closure3(this,"$Closure3")); + this.node.root.new("@winglang/sdk.std.Test",std.Test,this,"test:OPTIONS /users",new $Closure4(this,"$Closure4")); } } const $App = $stdlib.core.App.for(process.env.WING_TARGET); diff --git a/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_test_sim.md b/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_test_sim.md index d722e2edca8..8c128f76838 100644 --- a/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_test_sim.md +++ b/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_test_sim.md @@ -2,10 +2,12 @@ ## stdout.log ```log -pass ─ website_with_api.wsim (no tests) +pass ┌ website_with_api.wsim » root/env0/test:GET --users + └ {"access-control-allow-credentials":"false","access-control-allow-origin":"*","access-control-expose-headers":"Content-Type","connection":"keep-alive","content-length":"12","content-type":"text/html; charset=utf-8","date":"Tue, 22 Aug 2023 18:33:36 GMT","etag":"W/\"c-1bl/ybs8pYaLwWPVCd0mBRAdBaY\"","keep-alive":"timeout=5","x-powered-by":"Express"} +pass ─ website_with_api.wsim » root/env1/test:OPTIONS --users -Tests 1 passed (1) +Tests 2 passed (2) Test Files 1 passed (1) Duration ``` From 40bfc45124f0347b3032a2ef31006c04c7209deb Mon Sep 17 00:00:00 2001 From: "monada-bot[bot]" Date: Tue, 22 Aug 2023 19:04:45 +0000 Subject: [PATCH 07/47] chore: self mutation Signed-off-by: monada-bot[bot] --- .../test_corpus/valid/website_with_api.w_test_sim.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_test_sim.md b/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_test_sim.md index 8c128f76838..184c57b14a1 100644 --- a/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_test_sim.md +++ b/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_test_sim.md @@ -3,7 +3,7 @@ ## stdout.log ```log pass ┌ website_with_api.wsim » root/env0/test:GET --users - └ {"access-control-allow-credentials":"false","access-control-allow-origin":"*","access-control-expose-headers":"Content-Type","connection":"keep-alive","content-length":"12","content-type":"text/html; charset=utf-8","date":"Tue, 22 Aug 2023 18:33:36 GMT","etag":"W/\"c-1bl/ybs8pYaLwWPVCd0mBRAdBaY\"","keep-alive":"timeout=5","x-powered-by":"Express"} + └ {"access-control-allow-credentials":"false","access-control-allow-origin":"*","access-control-expose-headers":"Content-Type","connection":"keep-alive","content-length":"12","content-type":"text/html; charset=utf-8","date":"Tue, 22 Aug 2023 18:58:25 GMT","etag":"W/\"c-1bl/ybs8pYaLwWPVCd0mBRAdBaY\"","keep-alive":"timeout=5","x-powered-by":"Express"} pass ─ website_with_api.wsim » root/env1/test:OPTIONS --users From 06ad9d92ffa2ac19eea4725cc4af33bdfa96f25f Mon Sep 17 00:00:00 2001 From: "monada-bot[bot]" Date: Tue, 22 Aug 2023 19:19:33 +0000 Subject: [PATCH 08/47] chore: self mutation Signed-off-by: monada-bot[bot] --- .../test_corpus/valid/website_with_api.w_test_sim.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_test_sim.md b/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_test_sim.md index 184c57b14a1..9fc0baadf2a 100644 --- a/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_test_sim.md +++ b/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_test_sim.md @@ -3,7 +3,7 @@ ## stdout.log ```log pass ┌ website_with_api.wsim » root/env0/test:GET --users - └ {"access-control-allow-credentials":"false","access-control-allow-origin":"*","access-control-expose-headers":"Content-Type","connection":"keep-alive","content-length":"12","content-type":"text/html; charset=utf-8","date":"Tue, 22 Aug 2023 18:58:25 GMT","etag":"W/\"c-1bl/ybs8pYaLwWPVCd0mBRAdBaY\"","keep-alive":"timeout=5","x-powered-by":"Express"} + └ {"access-control-allow-credentials":"false","access-control-allow-origin":"*","access-control-expose-headers":"Content-Type","connection":"keep-alive","content-length":"12","content-type":"text/html; charset=utf-8","date":"Tue, 22 Aug 2023 19:13:18 GMT","etag":"W/\"c-1bl/ybs8pYaLwWPVCd0mBRAdBaY\"","keep-alive":"timeout=5","x-powered-by":"Express"} pass ─ website_with_api.wsim » root/env1/test:OPTIONS --users From 21c0374391833ad4bcdc80c24683ef4d6d848a74 Mon Sep 17 00:00:00 2001 From: "monada-bot[bot]" Date: Tue, 22 Aug 2023 19:36:40 +0000 Subject: [PATCH 09/47] chore: self mutation Signed-off-by: monada-bot[bot] --- .../test_corpus/valid/website_with_api.w_test_sim.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_test_sim.md b/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_test_sim.md index 9fc0baadf2a..769d2665aa8 100644 --- a/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_test_sim.md +++ b/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_test_sim.md @@ -3,7 +3,7 @@ ## stdout.log ```log pass ┌ website_with_api.wsim » root/env0/test:GET --users - └ {"access-control-allow-credentials":"false","access-control-allow-origin":"*","access-control-expose-headers":"Content-Type","connection":"keep-alive","content-length":"12","content-type":"text/html; charset=utf-8","date":"Tue, 22 Aug 2023 19:13:18 GMT","etag":"W/\"c-1bl/ybs8pYaLwWPVCd0mBRAdBaY\"","keep-alive":"timeout=5","x-powered-by":"Express"} + └ {"access-control-allow-credentials":"false","access-control-allow-origin":"*","access-control-expose-headers":"Content-Type","connection":"keep-alive","content-length":"12","content-type":"text/html; charset=utf-8","date":"Tue, 22 Aug 2023 19:29:40 GMT","etag":"W/\"c-1bl/ybs8pYaLwWPVCd0mBRAdBaY\"","keep-alive":"timeout=5","x-powered-by":"Express"} pass ─ website_with_api.wsim » root/env1/test:OPTIONS --users From 021dd25f24d30d309fe5d3f0bda71179b0f9b428 Mon Sep 17 00:00:00 2001 From: "monada-bot[bot]" Date: Tue, 22 Aug 2023 19:53:21 +0000 Subject: [PATCH 10/47] chore: self mutation Signed-off-by: monada-bot[bot] --- .../test_corpus/valid/website_with_api.w_test_sim.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_test_sim.md b/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_test_sim.md index 769d2665aa8..5c26ba5d6fa 100644 --- a/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_test_sim.md +++ b/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_test_sim.md @@ -3,7 +3,7 @@ ## stdout.log ```log pass ┌ website_with_api.wsim » root/env0/test:GET --users - └ {"access-control-allow-credentials":"false","access-control-allow-origin":"*","access-control-expose-headers":"Content-Type","connection":"keep-alive","content-length":"12","content-type":"text/html; charset=utf-8","date":"Tue, 22 Aug 2023 19:29:40 GMT","etag":"W/\"c-1bl/ybs8pYaLwWPVCd0mBRAdBaY\"","keep-alive":"timeout=5","x-powered-by":"Express"} + └ {"access-control-allow-credentials":"false","access-control-allow-origin":"*","access-control-expose-headers":"Content-Type","connection":"keep-alive","content-length":"12","content-type":"text/html; charset=utf-8","date":"Tue, 22 Aug 2023 19:46:56 GMT","etag":"W/\"c-1bl/ybs8pYaLwWPVCd0mBRAdBaY\"","keep-alive":"timeout=5","x-powered-by":"Express"} pass ─ website_with_api.wsim » root/env1/test:OPTIONS --users From c2b8617f04836ed1512b25fbbe1bea7836c29393 Mon Sep 17 00:00:00 2001 From: "monada-bot[bot]" Date: Tue, 22 Aug 2023 20:09:15 +0000 Subject: [PATCH 11/47] chore: self mutation Signed-off-by: monada-bot[bot] --- .../test_corpus/valid/website_with_api.w_test_sim.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_test_sim.md b/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_test_sim.md index 5c26ba5d6fa..3080408312a 100644 --- a/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_test_sim.md +++ b/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_test_sim.md @@ -3,7 +3,7 @@ ## stdout.log ```log pass ┌ website_with_api.wsim » root/env0/test:GET --users - └ {"access-control-allow-credentials":"false","access-control-allow-origin":"*","access-control-expose-headers":"Content-Type","connection":"keep-alive","content-length":"12","content-type":"text/html; charset=utf-8","date":"Tue, 22 Aug 2023 19:46:56 GMT","etag":"W/\"c-1bl/ybs8pYaLwWPVCd0mBRAdBaY\"","keep-alive":"timeout=5","x-powered-by":"Express"} + └ {"access-control-allow-credentials":"false","access-control-allow-origin":"*","access-control-expose-headers":"Content-Type","connection":"keep-alive","content-length":"12","content-type":"text/html; charset=utf-8","date":"Tue, 22 Aug 2023 20:03:06 GMT","etag":"W/\"c-1bl/ybs8pYaLwWPVCd0mBRAdBaY\"","keep-alive":"timeout=5","x-powered-by":"Express"} pass ─ website_with_api.wsim » root/env1/test:OPTIONS --users From 54cb8e484ff55f3af61834d9abae05c35af0187b Mon Sep 17 00:00:00 2001 From: "monada-bot[bot]" Date: Tue, 22 Aug 2023 20:26:12 +0000 Subject: [PATCH 12/47] chore: self mutation Signed-off-by: monada-bot[bot] --- .../test_corpus/valid/website_with_api.w_test_sim.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_test_sim.md b/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_test_sim.md index 3080408312a..a6085317cdb 100644 --- a/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_test_sim.md +++ b/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_test_sim.md @@ -3,7 +3,7 @@ ## stdout.log ```log pass ┌ website_with_api.wsim » root/env0/test:GET --users - └ {"access-control-allow-credentials":"false","access-control-allow-origin":"*","access-control-expose-headers":"Content-Type","connection":"keep-alive","content-length":"12","content-type":"text/html; charset=utf-8","date":"Tue, 22 Aug 2023 20:03:06 GMT","etag":"W/\"c-1bl/ybs8pYaLwWPVCd0mBRAdBaY\"","keep-alive":"timeout=5","x-powered-by":"Express"} + └ {"access-control-allow-credentials":"false","access-control-allow-origin":"*","access-control-expose-headers":"Content-Type","connection":"keep-alive","content-length":"12","content-type":"text/html; charset=utf-8","date":"Tue, 22 Aug 2023 20:19:38 GMT","etag":"W/\"c-1bl/ybs8pYaLwWPVCd0mBRAdBaY\"","keep-alive":"timeout=5","x-powered-by":"Express"} pass ─ website_with_api.wsim » root/env1/test:OPTIONS --users From 3394c84cbd3880d33d5868cf1b8ded67c287aeef Mon Sep 17 00:00:00 2001 From: "monada-bot[bot]" Date: Tue, 22 Aug 2023 20:43:56 +0000 Subject: [PATCH 13/47] chore: self mutation Signed-off-by: monada-bot[bot] --- .../test_corpus/valid/website_with_api.w_test_sim.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_test_sim.md b/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_test_sim.md index a6085317cdb..175738b0430 100644 --- a/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_test_sim.md +++ b/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_test_sim.md @@ -3,7 +3,7 @@ ## stdout.log ```log pass ┌ website_with_api.wsim » root/env0/test:GET --users - └ {"access-control-allow-credentials":"false","access-control-allow-origin":"*","access-control-expose-headers":"Content-Type","connection":"keep-alive","content-length":"12","content-type":"text/html; charset=utf-8","date":"Tue, 22 Aug 2023 20:19:38 GMT","etag":"W/\"c-1bl/ybs8pYaLwWPVCd0mBRAdBaY\"","keep-alive":"timeout=5","x-powered-by":"Express"} + └ {"access-control-allow-credentials":"false","access-control-allow-origin":"*","access-control-expose-headers":"Content-Type","connection":"keep-alive","content-length":"12","content-type":"text/html; charset=utf-8","date":"Tue, 22 Aug 2023 20:35:21 GMT","etag":"W/\"c-1bl/ybs8pYaLwWPVCd0mBRAdBaY\"","keep-alive":"timeout=5","x-powered-by":"Express"} pass ─ website_with_api.wsim » root/env1/test:OPTIONS --users From 9661e0673e4e2e8c7da6f28449ce1f9aec58e57d Mon Sep 17 00:00:00 2001 From: "monada-bot[bot]" Date: Tue, 22 Aug 2023 21:01:22 +0000 Subject: [PATCH 14/47] chore: self mutation Signed-off-by: monada-bot[bot] --- .../test_corpus/valid/website_with_api.w_test_sim.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_test_sim.md b/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_test_sim.md index 175738b0430..ec0aa693967 100644 --- a/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_test_sim.md +++ b/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_test_sim.md @@ -3,7 +3,7 @@ ## stdout.log ```log pass ┌ website_with_api.wsim » root/env0/test:GET --users - └ {"access-control-allow-credentials":"false","access-control-allow-origin":"*","access-control-expose-headers":"Content-Type","connection":"keep-alive","content-length":"12","content-type":"text/html; charset=utf-8","date":"Tue, 22 Aug 2023 20:35:21 GMT","etag":"W/\"c-1bl/ybs8pYaLwWPVCd0mBRAdBaY\"","keep-alive":"timeout=5","x-powered-by":"Express"} + └ {"access-control-allow-credentials":"false","access-control-allow-origin":"*","access-control-expose-headers":"Content-Type","connection":"keep-alive","content-length":"12","content-type":"text/html; charset=utf-8","date":"Tue, 22 Aug 2023 20:54:45 GMT","etag":"W/\"c-1bl/ybs8pYaLwWPVCd0mBRAdBaY\"","keep-alive":"timeout=5","x-powered-by":"Express"} pass ─ website_with_api.wsim » root/env1/test:OPTIONS --users From 196fae5b4431469807e583f7bb53c65201655966 Mon Sep 17 00:00:00 2001 From: "monada-bot[bot]" Date: Tue, 22 Aug 2023 21:16:59 +0000 Subject: [PATCH 15/47] chore: self mutation Signed-off-by: monada-bot[bot] --- .../test_corpus/valid/website_with_api.w_test_sim.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_test_sim.md b/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_test_sim.md index ec0aa693967..dece920d6b8 100644 --- a/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_test_sim.md +++ b/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_test_sim.md @@ -3,7 +3,7 @@ ## stdout.log ```log pass ┌ website_with_api.wsim » root/env0/test:GET --users - └ {"access-control-allow-credentials":"false","access-control-allow-origin":"*","access-control-expose-headers":"Content-Type","connection":"keep-alive","content-length":"12","content-type":"text/html; charset=utf-8","date":"Tue, 22 Aug 2023 20:54:45 GMT","etag":"W/\"c-1bl/ybs8pYaLwWPVCd0mBRAdBaY\"","keep-alive":"timeout=5","x-powered-by":"Express"} + └ {"access-control-allow-credentials":"false","access-control-allow-origin":"*","access-control-expose-headers":"Content-Type","connection":"keep-alive","content-length":"12","content-type":"text/html; charset=utf-8","date":"Tue, 22 Aug 2023 21:11:07 GMT","etag":"W/\"c-1bl/ybs8pYaLwWPVCd0mBRAdBaY\"","keep-alive":"timeout=5","x-powered-by":"Express"} pass ─ website_with_api.wsim » root/env1/test:OPTIONS --users From ffb57d01e7924f4534588c4975d7899fc72927b1 Mon Sep 17 00:00:00 2001 From: "monada-bot[bot]" Date: Tue, 22 Aug 2023 21:32:06 +0000 Subject: [PATCH 16/47] chore: self mutation Signed-off-by: monada-bot[bot] --- .../test_corpus/valid/website_with_api.w_test_sim.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_test_sim.md b/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_test_sim.md index dece920d6b8..8c414fc6b0f 100644 --- a/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_test_sim.md +++ b/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_test_sim.md @@ -3,7 +3,7 @@ ## stdout.log ```log pass ┌ website_with_api.wsim » root/env0/test:GET --users - └ {"access-control-allow-credentials":"false","access-control-allow-origin":"*","access-control-expose-headers":"Content-Type","connection":"keep-alive","content-length":"12","content-type":"text/html; charset=utf-8","date":"Tue, 22 Aug 2023 21:11:07 GMT","etag":"W/\"c-1bl/ybs8pYaLwWPVCd0mBRAdBaY\"","keep-alive":"timeout=5","x-powered-by":"Express"} + └ {"access-control-allow-credentials":"false","access-control-allow-origin":"*","access-control-expose-headers":"Content-Type","connection":"keep-alive","content-length":"12","content-type":"text/html; charset=utf-8","date":"Tue, 22 Aug 2023 21:25:14 GMT","etag":"W/\"c-1bl/ybs8pYaLwWPVCd0mBRAdBaY\"","keep-alive":"timeout=5","x-powered-by":"Express"} pass ─ website_with_api.wsim » root/env1/test:OPTIONS --users From a8b36c6971a0529c70aa63b684a29bceae9fd605 Mon Sep 17 00:00:00 2001 From: "monada-bot[bot]" Date: Tue, 22 Aug 2023 21:47:28 +0000 Subject: [PATCH 17/47] chore: self mutation Signed-off-by: monada-bot[bot] --- .../test_corpus/valid/website_with_api.w_test_sim.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_test_sim.md b/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_test_sim.md index 8c414fc6b0f..891b998c726 100644 --- a/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_test_sim.md +++ b/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_test_sim.md @@ -3,7 +3,7 @@ ## stdout.log ```log pass ┌ website_with_api.wsim » root/env0/test:GET --users - └ {"access-control-allow-credentials":"false","access-control-allow-origin":"*","access-control-expose-headers":"Content-Type","connection":"keep-alive","content-length":"12","content-type":"text/html; charset=utf-8","date":"Tue, 22 Aug 2023 21:25:14 GMT","etag":"W/\"c-1bl/ybs8pYaLwWPVCd0mBRAdBaY\"","keep-alive":"timeout=5","x-powered-by":"Express"} + └ {"access-control-allow-credentials":"false","access-control-allow-origin":"*","access-control-expose-headers":"Content-Type","connection":"keep-alive","content-length":"12","content-type":"text/html; charset=utf-8","date":"Tue, 22 Aug 2023 21:40:59 GMT","etag":"W/\"c-1bl/ybs8pYaLwWPVCd0mBRAdBaY\"","keep-alive":"timeout=5","x-powered-by":"Express"} pass ─ website_with_api.wsim » root/env1/test:OPTIONS --users From 7262e26d7f81c65c0c164cdc69fdf243f5bdf639 Mon Sep 17 00:00:00 2001 From: "monada-bot[bot]" Date: Tue, 22 Aug 2023 22:04:54 +0000 Subject: [PATCH 18/47] chore: self mutation Signed-off-by: monada-bot[bot] --- .../test_corpus/valid/website_with_api.w_test_sim.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_test_sim.md b/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_test_sim.md index 891b998c726..a1380e8a644 100644 --- a/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_test_sim.md +++ b/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_test_sim.md @@ -3,7 +3,7 @@ ## stdout.log ```log pass ┌ website_with_api.wsim » root/env0/test:GET --users - └ {"access-control-allow-credentials":"false","access-control-allow-origin":"*","access-control-expose-headers":"Content-Type","connection":"keep-alive","content-length":"12","content-type":"text/html; charset=utf-8","date":"Tue, 22 Aug 2023 21:40:59 GMT","etag":"W/\"c-1bl/ybs8pYaLwWPVCd0mBRAdBaY\"","keep-alive":"timeout=5","x-powered-by":"Express"} + └ {"access-control-allow-credentials":"false","access-control-allow-origin":"*","access-control-expose-headers":"Content-Type","connection":"keep-alive","content-length":"12","content-type":"text/html; charset=utf-8","date":"Tue, 22 Aug 2023 21:57:20 GMT","etag":"W/\"c-1bl/ybs8pYaLwWPVCd0mBRAdBaY\"","keep-alive":"timeout=5","x-powered-by":"Express"} pass ─ website_with_api.wsim » root/env1/test:OPTIONS --users From d46a3a1ab18c022098e02b9b84f4b8381d8bd0e3 Mon Sep 17 00:00:00 2001 From: "monada-bot[bot]" Date: Tue, 22 Aug 2023 22:20:53 +0000 Subject: [PATCH 19/47] chore: self mutation Signed-off-by: monada-bot[bot] --- .../test_corpus/valid/website_with_api.w_test_sim.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_test_sim.md b/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_test_sim.md index a1380e8a644..86dc7e6da53 100644 --- a/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_test_sim.md +++ b/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_test_sim.md @@ -3,7 +3,7 @@ ## stdout.log ```log pass ┌ website_with_api.wsim » root/env0/test:GET --users - └ {"access-control-allow-credentials":"false","access-control-allow-origin":"*","access-control-expose-headers":"Content-Type","connection":"keep-alive","content-length":"12","content-type":"text/html; charset=utf-8","date":"Tue, 22 Aug 2023 21:57:20 GMT","etag":"W/\"c-1bl/ybs8pYaLwWPVCd0mBRAdBaY\"","keep-alive":"timeout=5","x-powered-by":"Express"} + └ {"access-control-allow-credentials":"false","access-control-allow-origin":"*","access-control-expose-headers":"Content-Type","connection":"keep-alive","content-length":"12","content-type":"text/html; charset=utf-8","date":"Tue, 22 Aug 2023 22:13:29 GMT","etag":"W/\"c-1bl/ybs8pYaLwWPVCd0mBRAdBaY\"","keep-alive":"timeout=5","x-powered-by":"Express"} pass ─ website_with_api.wsim » root/env1/test:OPTIONS --users From d11dc42619e379c817e5bd17f1c583c823d7e0da Mon Sep 17 00:00:00 2001 From: "monada-bot[bot]" Date: Tue, 22 Aug 2023 22:36:37 +0000 Subject: [PATCH 20/47] chore: self mutation Signed-off-by: monada-bot[bot] --- .../test_corpus/valid/website_with_api.w_test_sim.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_test_sim.md b/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_test_sim.md index 86dc7e6da53..258edd4defe 100644 --- a/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_test_sim.md +++ b/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_test_sim.md @@ -3,7 +3,7 @@ ## stdout.log ```log pass ┌ website_with_api.wsim » root/env0/test:GET --users - └ {"access-control-allow-credentials":"false","access-control-allow-origin":"*","access-control-expose-headers":"Content-Type","connection":"keep-alive","content-length":"12","content-type":"text/html; charset=utf-8","date":"Tue, 22 Aug 2023 22:13:29 GMT","etag":"W/\"c-1bl/ybs8pYaLwWPVCd0mBRAdBaY\"","keep-alive":"timeout=5","x-powered-by":"Express"} + └ {"access-control-allow-credentials":"false","access-control-allow-origin":"*","access-control-expose-headers":"Content-Type","connection":"keep-alive","content-length":"12","content-type":"text/html; charset=utf-8","date":"Tue, 22 Aug 2023 22:30:53 GMT","etag":"W/\"c-1bl/ybs8pYaLwWPVCd0mBRAdBaY\"","keep-alive":"timeout=5","x-powered-by":"Express"} pass ─ website_with_api.wsim » root/env1/test:OPTIONS --users From 326eadcdbcf32c91bd328799c77589fa25be38d8 Mon Sep 17 00:00:00 2001 From: "monada-bot[bot]" Date: Tue, 22 Aug 2023 22:52:09 +0000 Subject: [PATCH 21/47] chore: self mutation Signed-off-by: monada-bot[bot] --- .../test_corpus/valid/website_with_api.w_test_sim.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_test_sim.md b/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_test_sim.md index 258edd4defe..aca09b789fa 100644 --- a/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_test_sim.md +++ b/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_test_sim.md @@ -3,7 +3,7 @@ ## stdout.log ```log pass ┌ website_with_api.wsim » root/env0/test:GET --users - └ {"access-control-allow-credentials":"false","access-control-allow-origin":"*","access-control-expose-headers":"Content-Type","connection":"keep-alive","content-length":"12","content-type":"text/html; charset=utf-8","date":"Tue, 22 Aug 2023 22:30:53 GMT","etag":"W/\"c-1bl/ybs8pYaLwWPVCd0mBRAdBaY\"","keep-alive":"timeout=5","x-powered-by":"Express"} + └ {"access-control-allow-credentials":"false","access-control-allow-origin":"*","access-control-expose-headers":"Content-Type","connection":"keep-alive","content-length":"12","content-type":"text/html; charset=utf-8","date":"Tue, 22 Aug 2023 22:44:39 GMT","etag":"W/\"c-1bl/ybs8pYaLwWPVCd0mBRAdBaY\"","keep-alive":"timeout=5","x-powered-by":"Express"} pass ─ website_with_api.wsim » root/env1/test:OPTIONS --users From 713bd969cb6cedd4ba71017daa10f6e9eeba07dc Mon Sep 17 00:00:00 2001 From: "monada-bot[bot]" Date: Thu, 24 Aug 2023 07:40:43 +0000 Subject: [PATCH 22/47] chore: self mutation (../patches/build.diff/build.diff) Signed-off-by: monada-bot[bot] --- libs/wingsdk/src/target-sim/api.inflight.ts | 12 ++++++++---- libs/wingsdk/src/target-sim/api.ts | 4 ++-- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/libs/wingsdk/src/target-sim/api.inflight.ts b/libs/wingsdk/src/target-sim/api.inflight.ts index 062c01086a5..a086ca88449 100644 --- a/libs/wingsdk/src/target-sim/api.inflight.ts +++ b/libs/wingsdk/src/target-sim/api.inflight.ts @@ -60,17 +60,21 @@ export class Api } = cors; this.app.use((req, res, next) => { const responseHeaders: Record = {}; - const method = req.method && req.method.toUpperCase && req.method.toUpperCase(); + const method = + req.method && req.method.toUpperCase && req.method.toUpperCase(); if (origins && origins.length > 0) { responseHeaders["Access-Control-Allow-Origin"] = origins.join(","); } if (exposedHeaders && exposedHeaders.length > 0) { - responseHeaders["Access-Control-Expose-Headers"] = exposedHeaders.join(","); + responseHeaders["Access-Control-Expose-Headers"] = + exposedHeaders.join(","); } - responseHeaders["Access-Control-Allow-Credentials"] = allowCredentials ? "true" : "false"; + responseHeaders["Access-Control-Allow-Credentials"] = allowCredentials + ? "true" + : "false"; - if (method === 'OPTIONS') { + if (method === "OPTIONS") { if (headers && headers.length > 0) { responseHeaders["Access-Control-Allow-Headers"] = headers.join(","); } diff --git a/libs/wingsdk/src/target-sim/api.ts b/libs/wingsdk/src/target-sim/api.ts index baca451494e..29bdafd1506 100644 --- a/libs/wingsdk/src/target-sim/api.ts +++ b/libs/wingsdk/src/target-sim/api.ts @@ -1,3 +1,4 @@ +import { Construct } from "constructs"; import { EventMapping } from "./event-mapping"; import { Function } from "./function"; import { ISimulatorResource } from "./resource"; @@ -9,7 +10,6 @@ import * as core from "../core"; import { Connections } from "../core"; import { Display, IInflightHost } from "../std"; import { BaseResourceSchema } from "../testing/simulator"; -import { Construct } from "constructs"; /** * Simulator implementation of `cloud.Api`. @@ -24,7 +24,7 @@ export class Api extends cloud.Api implements ISimulatorResource { constructor(scope: Construct, id: string, props: cloud.ApiProps = {}) { super(scope, id, props); - this.cors = props.cors + this.cors = props.cors; } public get url(): string { From 5562e7ef9895080570cf5bc9730592b8a530d826 Mon Sep 17 00:00:00 2001 From: "monada-bot[bot]" Date: Thu, 24 Aug 2023 07:40:43 +0000 Subject: [PATCH 23/47] chore: self mutation (../patches/e2e-2of2.diff/e2e-2of2.diff) Signed-off-by: monada-bot[bot] --- .../website_with_api.w_compile_tf-aws.md | 380 ++++++++++++++---- .../valid/website_with_api.w_test_sim.md | 2 +- 2 files changed, 294 insertions(+), 88 deletions(-) diff --git a/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_compile_tf-aws.md b/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_compile_tf-aws.md index 57248cb7170..b5dfcc25220 100644 --- a/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_compile_tf-aws.md +++ b/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_compile_tf-aws.md @@ -1,6 +1,6 @@ # [website_with_api.w](../../../../../examples/tests/valid/website_with_api.w) | compile | tf-aws -## inflight.$Closure1-1.js +## inflight.$Closure1-2.js ```js module.exports = function({ $std_Json, $usersTable }) { class $Closure1 { @@ -18,7 +18,7 @@ module.exports = function({ $std_Json, $usersTable }) { ``` -## inflight.$Closure2-1.js +## inflight.$Closure2-2.js ```js module.exports = function({ $std_Json, $usersTable }) { class $Closure2 { @@ -41,17 +41,25 @@ module.exports = function({ $std_Json, $usersTable }) { ``` -## inflight.$Closure3-1.js +## inflight.$Closure3-2.js ```js -module.exports = function({ }) { +module.exports = function({ $api_url, $http_HttpMethod, $http_Util, $std_Json, $t_Assert }) { class $Closure3 { constructor({ }) { const $obj = (...args) => this.handle(...args); Object.setPrototypeOf($obj, this); return $obj; } - async handle(req) { - return ({"headers": ({"Access-Control-Allow-Headers": "Content-Type","Access-Control-Allow-Origin": "*","Access-Control-Allow-Methods": "OPTIONS,POST,GET"}),"status": 204}); + async handle() { + const response = (await $http_Util.fetch(($api_url + "/users"),({"method": $http_HttpMethod.GET,"headers": ({"Content-Type": "text/json"})}))); + const headers = response.headers; + (await $t_Assert.equalNum(response.status,200)); + {console.log(((args) => { return JSON.stringify(args[0], null, args[1]?.indent) })([headers]))}; + (await $t_Assert.equalStr((headers)["access-control-allow-origin"],"*")); + (await $t_Assert.equalStr((headers)["access-control-expose-headers"],"Content-Type")); + (await $t_Assert.equalStr((headers)["access-control-allow-credentials"],"false")); + (await $t_Assert.isNil((headers)["access-control-allow-headers"])); + (await $t_Assert.isNil((headers)["access-control-allow-methods"])); } } return $Closure3; @@ -59,6 +67,70 @@ module.exports = function({ }) { ``` +## inflight.$Closure4-2.js +```js +module.exports = function({ $api_url, $http_HttpMethod, $http_Util, $t_Assert }) { + class $Closure4 { + constructor({ }) { + const $obj = (...args) => this.handle(...args); + Object.setPrototypeOf($obj, this); + return $obj; + } + async handle() { + const response = (await $http_Util.fetch(($api_url + "/users"),({"method": $http_HttpMethod.OPTIONS,"headers": ({"Content-Type": "text/json"})}))); + const headers = response.headers; + (await $t_Assert.equalNum(response.status,204)); + (await $t_Assert.equalStr((headers)["access-control-allow-origin"],"*")); + (await $t_Assert.equalStr((headers)["access-control-allow-methods"],"GET,POST,OPTIONS")); + (await $t_Assert.equalStr((headers)["access-control-allow-headers"],"Content-Type")); + (await $t_Assert.equalStr((headers)["access-control-expose-headers"],"Content-Type")); + (await $t_Assert.equalStr((headers)["access-control-allow-credentials"],"false")); + } + } + return $Closure4; +} + +``` + +## inflight.Assert-1.js +```js +module.exports = function({ }) { + class Assert { + static async equalStr(a, b) { + try { + {((cond) => {if (!cond) throw new Error("assertion failed: a == b")})((((a,b) => { try { return require('assert').deepStrictEqual(a,b) === undefined; } catch { return false; } })(a,b)))}; + } + catch ($error_e) { + const e = $error_e.message; + {((msg) => {throw new Error(msg)})(String.raw({ raw: ["expected: ", " got: ", ""] }, b, a))}; + } + } + static async isNil(a) { + try { + {((cond) => {if (!cond) throw new Error("assertion failed: a == nil")})((((a,b) => { try { return require('assert').deepStrictEqual(a,b) === undefined; } catch { return false; } })(a,undefined)))}; + } + catch ($error_e) { + const e = $error_e.message; + {console.log(e)}; + {((msg) => {throw new Error(msg)})(String.raw({ raw: ["expected '", "' to be nil"] }, a))}; + } + } + static async equalNum(a, b) { + try { + {((cond) => {if (!cond) throw new Error("assertion failed: a == b")})((((a,b) => { try { return require('assert').deepStrictEqual(a,b) === undefined; } catch { return false; } })(a,b)))}; + } + catch ($error_e) { + const e = $error_e.message; + {console.log(e)}; + {((msg) => {throw new Error(msg)})(String.raw({ raw: ["expected: ", " got: ", ""] }, b, a))}; + } + } + } + return Assert; +} + +``` + ## main.tf.json ```json { @@ -129,7 +201,7 @@ module.exports = function({ }) { }, "output": { "WING_TEST_RUNNER_FUNCTION_ARNS": { - "value": "[]" + "value": "[[\"root/Default/Default/test:GET --users\",\"${aws_lambda_function.testGET--users_Handler_5E592AA6.arn}\"],[\"root/Default/Default/test:OPTIONS --users\",\"${aws_lambda_function.testOPTIONS--users_Handler_01361C41.arn}\"]]" } }, "provider": { @@ -151,7 +223,7 @@ module.exports = function({ }) { }, "rest_api_id": "${aws_api_gateway_rest_api.cloudApi_api_2B334D75.id}", "triggers": { - "redeployment": "53d34a77ebc006529cbee88240e663619396c753" + "redeployment": "d5cfebcc5973474c59bd3c32a8bef5db1de2c86c" } } }, @@ -163,7 +235,7 @@ module.exports = function({ }) { "uniqueId": "cloudApi_api_2B334D75" } }, - "body": "{\"openapi\":\"3.0.3\",\"paths\":{\"/users\":{\"get\":{\"operationId\":\"get-users\",\"responses\":{\"200\":{\"description\":\"200 response\",\"content\":{}}},\"parameters\":[],\"x-amazon-apigateway-integration\":{\"uri\":\"arn:aws:apigateway:${data.aws_region.Region.name}:lambda:path/2015-03-31/functions/${aws_lambda_function.cloudApi_cloudApi-OnRequest-cdafee6e_A6C8366F.arn}/invocations\",\"type\":\"aws_proxy\",\"httpMethod\":\"POST\",\"responses\":{\"default\":{\"statusCode\":\"200\"}},\"passthroughBehavior\":\"when_no_match\",\"contentHandling\":\"CONVERT_TO_TEXT\"}},\"post\":{\"operationId\":\"post-users\",\"responses\":{\"200\":{\"description\":\"200 response\",\"content\":{}}},\"parameters\":[],\"x-amazon-apigateway-integration\":{\"uri\":\"arn:aws:apigateway:${data.aws_region.Region.name}:lambda:path/2015-03-31/functions/${aws_lambda_function.cloudApi_cloudApi-OnRequest-86898773_701F5CA7.arn}/invocations\",\"type\":\"aws_proxy\",\"httpMethod\":\"POST\",\"responses\":{\"default\":{\"statusCode\":\"200\"}},\"passthroughBehavior\":\"when_no_match\",\"contentHandling\":\"CONVERT_TO_TEXT\"}},\"options\":{\"operationId\":\"options-users\",\"responses\":{\"200\":{\"description\":\"200 response\",\"content\":{}}},\"parameters\":[],\"x-amazon-apigateway-integration\":{\"uri\":\"arn:aws:apigateway:${data.aws_region.Region.name}:lambda:path/2015-03-31/functions/${aws_lambda_function.cloudApi_cloudApi-OnRequest-3fc9280c_5DA20E7A.arn}/invocations\",\"type\":\"aws_proxy\",\"httpMethod\":\"POST\",\"responses\":{\"default\":{\"statusCode\":\"200\"}},\"passthroughBehavior\":\"when_no_match\",\"contentHandling\":\"CONVERT_TO_TEXT\"}}}}}", + "body": "{\"openapi\":\"3.0.3\",\"paths\":{\"/users\":{\"get\":{\"operationId\":\"get-users\",\"responses\":{\"200\":{\"description\":\"200 response\",\"content\":{}}},\"parameters\":[],\"x-amazon-apigateway-integration\":{\"uri\":\"arn:aws:apigateway:${data.aws_region.Region.name}:lambda:path/2015-03-31/functions/${aws_lambda_function.cloudApi_cloudApi-OnRequest-cdafee6e_A6C8366F.arn}/invocations\",\"type\":\"aws_proxy\",\"httpMethod\":\"POST\",\"responses\":{\"default\":{\"statusCode\":\"200\"}},\"passthroughBehavior\":\"when_no_match\",\"contentHandling\":\"CONVERT_TO_TEXT\"}},\"post\":{\"operationId\":\"post-users\",\"responses\":{\"200\":{\"description\":\"200 response\",\"content\":{}}},\"parameters\":[],\"x-amazon-apigateway-integration\":{\"uri\":\"arn:aws:apigateway:${data.aws_region.Region.name}:lambda:path/2015-03-31/functions/${aws_lambda_function.cloudApi_cloudApi-OnRequest-86898773_701F5CA7.arn}/invocations\",\"type\":\"aws_proxy\",\"httpMethod\":\"POST\",\"responses\":{\"default\":{\"statusCode\":\"200\"}},\"passthroughBehavior\":\"when_no_match\",\"contentHandling\":\"CONVERT_TO_TEXT\"}}}}}", "name": "api-c895068c" } }, @@ -265,15 +337,6 @@ module.exports = function({ }) { } }, "aws_iam_role": { - "cloudApi_cloudApi-OnRequest-3fc9280c_IamRole_EBC99528": { - "//": { - "metadata": { - "path": "root/Default/Default/cloud.Api/cloud.Api-OnRequest-3fc9280c/IamRole", - "uniqueId": "cloudApi_cloudApi-OnRequest-3fc9280c_IamRole_EBC99528" - } - }, - "assume_role_policy": "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Action\":\"sts:AssumeRole\",\"Principal\":{\"Service\":\"lambda.amazonaws.com\"},\"Effect\":\"Allow\"}]}" - }, "cloudApi_cloudApi-OnRequest-86898773_IamRole_6300C24F": { "//": { "metadata": { @@ -291,19 +354,27 @@ module.exports = function({ }) { } }, "assume_role_policy": "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Action\":\"sts:AssumeRole\",\"Principal\":{\"Service\":\"lambda.amazonaws.com\"},\"Effect\":\"Allow\"}]}" - } - }, - "aws_iam_role_policy": { - "cloudApi_cloudApi-OnRequest-3fc9280c_IamRolePolicy_1552D6FE": { + }, + "testGET--users_Handler_IamRole_3C6B1A52": { "//": { "metadata": { - "path": "root/Default/Default/cloud.Api/cloud.Api-OnRequest-3fc9280c/IamRolePolicy", - "uniqueId": "cloudApi_cloudApi-OnRequest-3fc9280c_IamRolePolicy_1552D6FE" + "path": "root/Default/Default/test:GET --users/Handler/IamRole", + "uniqueId": "testGET--users_Handler_IamRole_3C6B1A52" } }, - "policy": "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Action\":\"none:null\",\"Resource\":\"*\"}]}", - "role": "${aws_iam_role.cloudApi_cloudApi-OnRequest-3fc9280c_IamRole_EBC99528.name}" + "assume_role_policy": "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Action\":\"sts:AssumeRole\",\"Principal\":{\"Service\":\"lambda.amazonaws.com\"},\"Effect\":\"Allow\"}]}" }, + "testOPTIONS--users_Handler_IamRole_F9362AB0": { + "//": { + "metadata": { + "path": "root/Default/Default/test:OPTIONS --users/Handler/IamRole", + "uniqueId": "testOPTIONS--users_Handler_IamRole_F9362AB0" + } + }, + "assume_role_policy": "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Action\":\"sts:AssumeRole\",\"Principal\":{\"Service\":\"lambda.amazonaws.com\"},\"Effect\":\"Allow\"}]}" + } + }, + "aws_iam_role_policy": { "cloudApi_cloudApi-OnRequest-86898773_IamRolePolicy_DAC639E5": { "//": { "metadata": { @@ -323,19 +394,29 @@ module.exports = function({ }) { }, "policy": "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Action\":[\"dynamodb:Scan\"],\"Resource\":[\"${aws_dynamodb_table.exTable.arn}\"],\"Effect\":\"Allow\"}]}", "role": "${aws_iam_role.cloudApi_cloudApi-OnRequest-cdafee6e_IamRole_4382C442.name}" - } - }, - "aws_iam_role_policy_attachment": { - "cloudApi_cloudApi-OnRequest-3fc9280c_IamRolePolicyAttachment_A988C18B": { + }, + "testGET--users_Handler_IamRolePolicy_70F076F2": { "//": { "metadata": { - "path": "root/Default/Default/cloud.Api/cloud.Api-OnRequest-3fc9280c/IamRolePolicyAttachment", - "uniqueId": "cloudApi_cloudApi-OnRequest-3fc9280c_IamRolePolicyAttachment_A988C18B" + "path": "root/Default/Default/test:GET --users/Handler/IamRolePolicy", + "uniqueId": "testGET--users_Handler_IamRolePolicy_70F076F2" } }, - "policy_arn": "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole", - "role": "${aws_iam_role.cloudApi_cloudApi-OnRequest-3fc9280c_IamRole_EBC99528.name}" + "policy": "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Action\":\"none:null\",\"Resource\":\"*\"}]}", + "role": "${aws_iam_role.testGET--users_Handler_IamRole_3C6B1A52.name}" }, + "testOPTIONS--users_Handler_IamRolePolicy_E1FFA7C9": { + "//": { + "metadata": { + "path": "root/Default/Default/test:OPTIONS --users/Handler/IamRolePolicy", + "uniqueId": "testOPTIONS--users_Handler_IamRolePolicy_E1FFA7C9" + } + }, + "policy": "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Action\":\"none:null\",\"Resource\":\"*\"}]}", + "role": "${aws_iam_role.testOPTIONS--users_Handler_IamRole_F9362AB0.name}" + } + }, + "aws_iam_role_policy_attachment": { "cloudApi_cloudApi-OnRequest-86898773_IamRolePolicyAttachment_6E485A17": { "//": { "metadata": { @@ -355,35 +436,29 @@ module.exports = function({ }) { }, "policy_arn": "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole", "role": "${aws_iam_role.cloudApi_cloudApi-OnRequest-cdafee6e_IamRole_4382C442.name}" - } - }, - "aws_lambda_function": { - "cloudApi_cloudApi-OnRequest-3fc9280c_5DA20E7A": { + }, + "testGET--users_Handler_IamRolePolicyAttachment_CE57C035": { "//": { "metadata": { - "path": "root/Default/Default/cloud.Api/cloud.Api-OnRequest-3fc9280c/Default", - "uniqueId": "cloudApi_cloudApi-OnRequest-3fc9280c_5DA20E7A" + "path": "root/Default/Default/test:GET --users/Handler/IamRolePolicyAttachment", + "uniqueId": "testGET--users_Handler_IamRolePolicyAttachment_CE57C035" } }, - "environment": { - "variables": { - "WING_FUNCTION_NAME": "cloud-Api-OnRequest-3fc9280c-c8d3ecf9", - "WING_TARGET": "tf-aws" + "policy_arn": "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole", + "role": "${aws_iam_role.testGET--users_Handler_IamRole_3C6B1A52.name}" + }, + "testOPTIONS--users_Handler_IamRolePolicyAttachment_41F75288": { + "//": { + "metadata": { + "path": "root/Default/Default/test:OPTIONS --users/Handler/IamRolePolicyAttachment", + "uniqueId": "testOPTIONS--users_Handler_IamRolePolicyAttachment_41F75288" } }, - "function_name": "cloud-Api-OnRequest-3fc9280c-c8d3ecf9", - "handler": "index.handler", - "publish": true, - "role": "${aws_iam_role.cloudApi_cloudApi-OnRequest-3fc9280c_IamRole_EBC99528.arn}", - "runtime": "nodejs18.x", - "s3_bucket": "${aws_s3_bucket.Code.bucket}", - "s3_key": "${aws_s3_object.cloudApi_cloudApi-OnRequest-3fc9280c_S3Object_EC722CF3.key}", - "timeout": 30, - "vpc_config": { - "security_group_ids": [], - "subnet_ids": [] - } - }, + "policy_arn": "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole", + "role": "${aws_iam_role.testOPTIONS--users_Handler_IamRole_F9362AB0.name}" + } + }, + "aws_lambda_function": { "cloudApi_cloudApi-OnRequest-86898773_701F5CA7": { "//": { "metadata": { @@ -441,6 +516,60 @@ module.exports = function({ }) { "security_group_ids": [], "subnet_ids": [] } + }, + "testGET--users_Handler_5E592AA6": { + "//": { + "metadata": { + "path": "root/Default/Default/test:GET --users/Handler/Default", + "uniqueId": "testGET--users_Handler_5E592AA6" + } + }, + "environment": { + "variables": { + "WING_FUNCTION_NAME": "Handler-c86bda55", + "WING_TARGET": "tf-aws", + "WING_TOKEN_TFTOKEN_TOKEN_7": "${jsonencode(aws_api_gateway_stage.cloudApi_api_stage_BBB283E4.invoke_url)}" + } + }, + "function_name": "Handler-c86bda55", + "handler": "index.handler", + "publish": true, + "role": "${aws_iam_role.testGET--users_Handler_IamRole_3C6B1A52.arn}", + "runtime": "nodejs18.x", + "s3_bucket": "${aws_s3_bucket.Code.bucket}", + "s3_key": "${aws_s3_object.testGET--users_Handler_S3Object_8248DE4B.key}", + "timeout": 30, + "vpc_config": { + "security_group_ids": [], + "subnet_ids": [] + } + }, + "testOPTIONS--users_Handler_01361C41": { + "//": { + "metadata": { + "path": "root/Default/Default/test:OPTIONS --users/Handler/Default", + "uniqueId": "testOPTIONS--users_Handler_01361C41" + } + }, + "environment": { + "variables": { + "WING_FUNCTION_NAME": "Handler-c8b7642e", + "WING_TARGET": "tf-aws", + "WING_TOKEN_TFTOKEN_TOKEN_7": "${jsonencode(aws_api_gateway_stage.cloudApi_api_stage_BBB283E4.invoke_url)}" + } + }, + "function_name": "Handler-c8b7642e", + "handler": "index.handler", + "publish": true, + "role": "${aws_iam_role.testOPTIONS--users_Handler_IamRole_F9362AB0.arn}", + "runtime": "nodejs18.x", + "s3_bucket": "${aws_s3_bucket.Code.bucket}", + "s3_key": "${aws_s3_object.testOPTIONS--users_Handler_S3Object_C0C4A75C.key}", + "timeout": 30, + "vpc_config": { + "security_group_ids": [], + "subnet_ids": [] + } } }, "aws_lambda_permission": { @@ -457,19 +586,6 @@ module.exports = function({ }) { "source_arn": "${aws_api_gateway_rest_api.cloudApi_api_2B334D75.execution_arn}/*/GET/users", "statement_id": "AllowExecutionFromAPIGateway-GET-41f0e61d" }, - "cloudApi_api_permission-OPTIONS-41f0e61d_12224EFF": { - "//": { - "metadata": { - "path": "root/Default/Default/cloud.Api/api/permission-OPTIONS-41f0e61d", - "uniqueId": "cloudApi_api_permission-OPTIONS-41f0e61d_12224EFF" - } - }, - "action": "lambda:InvokeFunction", - "function_name": "${aws_lambda_function.cloudApi_cloudApi-OnRequest-3fc9280c_5DA20E7A.function_name}", - "principal": "apigateway.amazonaws.com", - "source_arn": "${aws_api_gateway_rest_api.cloudApi_api_2B334D75.execution_arn}/*/OPTIONS/users", - "statement_id": "AllowExecutionFromAPIGateway-OPTIONS-41f0e61d" - }, "cloudApi_api_permission-POST-41f0e61d_743604B6": { "//": { "metadata": { @@ -550,17 +666,6 @@ module.exports = function({ }) { } }, "aws_s3_object": { - "cloudApi_cloudApi-OnRequest-3fc9280c_S3Object_EC722CF3": { - "//": { - "metadata": { - "path": "root/Default/Default/cloud.Api/cloud.Api-OnRequest-3fc9280c/S3Object", - "uniqueId": "cloudApi_cloudApi-OnRequest-3fc9280c_S3Object_EC722CF3" - } - }, - "bucket": "${aws_s3_bucket.Code.bucket}", - "key": "", - "source": "" - }, "cloudApi_cloudApi-OnRequest-86898773_S3Object_12D28469": { "//": { "metadata": { @@ -613,12 +718,66 @@ module.exports = function({ }) { "aws_s3_bucket.cloudWebsite_WebsiteBucket_EB03D355" ], "key": "config.json" + }, + "testGET--users_Handler_S3Object_8248DE4B": { + "//": { + "metadata": { + "path": "root/Default/Default/test:GET --users/Handler/S3Object", + "uniqueId": "testGET--users_Handler_S3Object_8248DE4B" + } + }, + "bucket": "${aws_s3_bucket.Code.bucket}", + "key": "", + "source": "" + }, + "testOPTIONS--users_Handler_S3Object_C0C4A75C": { + "//": { + "metadata": { + "path": "root/Default/Default/test:OPTIONS --users/Handler/S3Object", + "uniqueId": "testOPTIONS--users_Handler_S3Object_C0C4A75C" + } + }, + "bucket": "${aws_s3_bucket.Code.bucket}", + "key": "", + "source": "" } } } } ``` +## preflight.assertions-1.js +```js +module.exports = function({ $stdlib }) { + const std = $stdlib.std; + class Assert extends $stdlib.std.Resource { + constructor(scope, id, ) { + super(scope, id); + this._addInflightOps("equalStr", "isNil", "equalNum", "$inflight_init"); + } + static _toInflightType(context) { + return $stdlib.core.NodeJsCode.fromInline(` + require("./inflight.Assert-1.js")({ + }) + `); + } + _toInflight() { + return $stdlib.core.NodeJsCode.fromInline(` + (await (async () => { + const AssertClient = ${Assert._toInflightType(this).text}; + const client = new AssertClient({ + }); + if (client.$inflight_init) { await client.$inflight_init(); } + return client; + })()) + `); + } + } + return { Assert }; +}; + +``` + ## preflight.js ```js const $stdlib = require('@winglang/sdk'); @@ -627,6 +786,8 @@ const $wing_is_test = process.env.WING_IS_TEST === "true"; const std = $stdlib.std; const cloud = $stdlib.cloud; const ex = $stdlib.ex; +const http = $stdlib.http; +const t = require("./preflight.assertions-1.js")({ $stdlib }); class $Root extends $stdlib.std.Resource { constructor(scope, id) { super(scope, id); @@ -638,7 +799,7 @@ class $Root extends $stdlib.std.Resource { } static _toInflightType(context) { return $stdlib.core.NodeJsCode.fromInline(` - require("./inflight.$Closure1-1.js")({ + require("./inflight.$Closure1-2.js")({ $std_Json: ${context._lift(std.Json)}, $usersTable: ${context._lift(usersTable)}, }) @@ -670,7 +831,7 @@ class $Root extends $stdlib.std.Resource { } static _toInflightType(context) { return $stdlib.core.NodeJsCode.fromInline(` - require("./inflight.$Closure2-1.js")({ + require("./inflight.$Closure2-2.js")({ $std_Json: ${context._lift(std.Json)}, $usersTable: ${context._lift(usersTable)}, }) @@ -702,7 +863,12 @@ class $Root extends $stdlib.std.Resource { } static _toInflightType(context) { return $stdlib.core.NodeJsCode.fromInline(` - require("./inflight.$Closure3-1.js")({ + require("./inflight.$Closure3-2.js")({ + $api_url: ${context._lift(api.url)}, + $http_HttpMethod: ${context._lift(http.HttpMethod)}, + $http_Util: ${context._lift(http.Util)}, + $std_Json: ${context._lift(std.Json)}, + $t_Assert: ${context._lift(t.Assert)}, }) `); } @@ -717,17 +883,57 @@ class $Root extends $stdlib.std.Resource { })()) `); } + _registerBind(host, ops) { + if (ops.includes("handle")) { + $Closure3._registerBindObject(api.url, host, []); + } + super._registerBind(host, ops); + } + } + class $Closure4 extends $stdlib.std.Resource { + constructor(scope, id, ) { + super(scope, id); + this._addInflightOps("handle", "$inflight_init"); + this.display.hidden = true; + } + static _toInflightType(context) { + return $stdlib.core.NodeJsCode.fromInline(` + require("./inflight.$Closure4-2.js")({ + $api_url: ${context._lift(api.url)}, + $http_HttpMethod: ${context._lift(http.HttpMethod)}, + $http_Util: ${context._lift(http.Util)}, + $t_Assert: ${context._lift(t.Assert)}, + }) + `); + } + _toInflight() { + return $stdlib.core.NodeJsCode.fromInline(` + (await (async () => { + const $Closure4Client = ${$Closure4._toInflightType(this).text}; + const client = new $Closure4Client({ + }); + if (client.$inflight_init) { await client.$inflight_init(); } + return client; + })()) + `); + } + _registerBind(host, ops) { + if (ops.includes("handle")) { + $Closure4._registerBindObject(api.url, host, []); + } + super._registerBind(host, ops); + } } - const api = this.node.root.newAbstract("@winglang/sdk.cloud.Api",this,"cloud.Api"); + const api = this.node.root.newAbstract("@winglang/sdk.cloud.Api",this,"cloud.Api",{ cors: ({"origins": ["*"],"methods": [cloud.HttpMethod.GET, cloud.HttpMethod.POST, cloud.HttpMethod.OPTIONS],"headers": ["Content-Type"],"allowCredentials": false,"exposedHeaders": ["Content-Type"]}) }); const website = this.node.root.newAbstract("@winglang/sdk.cloud.Website",this,"cloud.Website",{ path: "./website_with_api" }); const usersTable = this.node.root.newAbstract("@winglang/sdk.ex.Table",this,"ex.Table",{ name: "users-table", primaryKey: "id", columns: ({"id": ex.ColumnType.STRING,"name": ex.ColumnType.STRING,"age": ex.ColumnType.NUMBER}) }); const getHandler = new $Closure1(this,"$Closure1"); const postHandler = new $Closure2(this,"$Closure2"); - const optionsHandler = new $Closure3(this,"$Closure3"); (api.get("/users",getHandler)); (api.post("/users",postHandler)); - (api.options("/users",optionsHandler)); (website.addJson("config.json",({"apiUrl": api.url}))); + this.node.root.new("@winglang/sdk.std.Test",std.Test,this,"test:GET /users",new $Closure3(this,"$Closure3")); + this.node.root.new("@winglang/sdk.std.Test",std.Test,this,"test:OPTIONS /users",new $Closure4(this,"$Closure4")); } } const $App = $stdlib.core.App.for(process.env.WING_TARGET); diff --git a/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_test_sim.md b/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_test_sim.md index aca09b789fa..d7cfcf684a2 100644 --- a/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_test_sim.md +++ b/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_test_sim.md @@ -3,7 +3,7 @@ ## stdout.log ```log pass ┌ website_with_api.wsim » root/env0/test:GET --users - └ {"access-control-allow-credentials":"false","access-control-allow-origin":"*","access-control-expose-headers":"Content-Type","connection":"keep-alive","content-length":"12","content-type":"text/html; charset=utf-8","date":"Tue, 22 Aug 2023 22:44:39 GMT","etag":"W/\"c-1bl/ybs8pYaLwWPVCd0mBRAdBaY\"","keep-alive":"timeout=5","x-powered-by":"Express"} + └ {"access-control-allow-credentials":"false","access-control-allow-origin":"*","access-control-expose-headers":"Content-Type","connection":"keep-alive","content-length":"12","content-type":"text/html; charset=utf-8","date":"Thu, 24 Aug 2023 07:34:57 GMT","etag":"W/\"c-1bl/ybs8pYaLwWPVCd0mBRAdBaY\"","keep-alive":"timeout=5","x-powered-by":"Express"} pass ─ website_with_api.wsim » root/env1/test:OPTIONS --users From a7f61048d6794ea2d52fbc752197df422577df1f Mon Sep 17 00:00:00 2001 From: "monada-bot[bot]" Date: Thu, 24 Aug 2023 08:01:10 +0000 Subject: [PATCH 24/47] chore: self mutation (../patches/e2e-2of2.diff/e2e-2of2.diff) Signed-off-by: monada-bot[bot] --- .../test_corpus/valid/website_with_api.w_test_sim.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_test_sim.md b/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_test_sim.md index d7cfcf684a2..831444abf12 100644 --- a/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_test_sim.md +++ b/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_test_sim.md @@ -3,7 +3,7 @@ ## stdout.log ```log pass ┌ website_with_api.wsim » root/env0/test:GET --users - └ {"access-control-allow-credentials":"false","access-control-allow-origin":"*","access-control-expose-headers":"Content-Type","connection":"keep-alive","content-length":"12","content-type":"text/html; charset=utf-8","date":"Thu, 24 Aug 2023 07:34:57 GMT","etag":"W/\"c-1bl/ybs8pYaLwWPVCd0mBRAdBaY\"","keep-alive":"timeout=5","x-powered-by":"Express"} + └ {"access-control-allow-credentials":"false","access-control-allow-origin":"*","access-control-expose-headers":"Content-Type","connection":"keep-alive","content-length":"12","content-type":"text/html; charset=utf-8","date":"Thu, 24 Aug 2023 07:55:31 GMT","etag":"W/\"c-1bl/ybs8pYaLwWPVCd0mBRAdBaY\"","keep-alive":"timeout=5","x-powered-by":"Express"} pass ─ website_with_api.wsim » root/env1/test:OPTIONS --users From 1b1df98d1c0d2a1a4ccfb4be53f92f56533ad48e Mon Sep 17 00:00:00 2001 From: "monada-bot[bot]" Date: Thu, 24 Aug 2023 08:15:57 +0000 Subject: [PATCH 25/47] chore: self mutation (../patches/e2e-2of2.diff/e2e-2of2.diff) Signed-off-by: monada-bot[bot] --- .../test_corpus/valid/website_with_api.w_test_sim.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_test_sim.md b/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_test_sim.md index 831444abf12..b54e4743538 100644 --- a/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_test_sim.md +++ b/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_test_sim.md @@ -3,7 +3,7 @@ ## stdout.log ```log pass ┌ website_with_api.wsim » root/env0/test:GET --users - └ {"access-control-allow-credentials":"false","access-control-allow-origin":"*","access-control-expose-headers":"Content-Type","connection":"keep-alive","content-length":"12","content-type":"text/html; charset=utf-8","date":"Thu, 24 Aug 2023 07:55:31 GMT","etag":"W/\"c-1bl/ybs8pYaLwWPVCd0mBRAdBaY\"","keep-alive":"timeout=5","x-powered-by":"Express"} + └ {"access-control-allow-credentials":"false","access-control-allow-origin":"*","access-control-expose-headers":"Content-Type","connection":"keep-alive","content-length":"12","content-type":"text/html; charset=utf-8","date":"Thu, 24 Aug 2023 08:09:46 GMT","etag":"W/\"c-1bl/ybs8pYaLwWPVCd0mBRAdBaY\"","keep-alive":"timeout=5","x-powered-by":"Express"} pass ─ website_with_api.wsim » root/env1/test:OPTIONS --users From 63144f575fc0d01d734eabd8e6ad35806f0823a4 Mon Sep 17 00:00:00 2001 From: "monada-bot[bot]" Date: Thu, 24 Aug 2023 08:33:35 +0000 Subject: [PATCH 26/47] chore: self mutation (../patches/e2e-2of2.diff/e2e-2of2.diff) Signed-off-by: monada-bot[bot] --- .../test_corpus/valid/website_with_api.w_test_sim.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_test_sim.md b/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_test_sim.md index b54e4743538..52e9c979d76 100644 --- a/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_test_sim.md +++ b/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_test_sim.md @@ -3,7 +3,7 @@ ## stdout.log ```log pass ┌ website_with_api.wsim » root/env0/test:GET --users - └ {"access-control-allow-credentials":"false","access-control-allow-origin":"*","access-control-expose-headers":"Content-Type","connection":"keep-alive","content-length":"12","content-type":"text/html; charset=utf-8","date":"Thu, 24 Aug 2023 08:09:46 GMT","etag":"W/\"c-1bl/ybs8pYaLwWPVCd0mBRAdBaY\"","keep-alive":"timeout=5","x-powered-by":"Express"} + └ {"access-control-allow-credentials":"false","access-control-allow-origin":"*","access-control-expose-headers":"Content-Type","connection":"keep-alive","content-length":"12","content-type":"text/html; charset=utf-8","date":"Thu, 24 Aug 2023 08:24:11 GMT","etag":"W/\"c-1bl/ybs8pYaLwWPVCd0mBRAdBaY\"","keep-alive":"timeout=5","x-powered-by":"Express"} pass ─ website_with_api.wsim » root/env1/test:OPTIONS --users From 1f7d5962d9bacc6efd07f28424980a785eb5bd17 Mon Sep 17 00:00:00 2001 From: "monada-bot[bot]" Date: Thu, 24 Aug 2023 08:49:09 +0000 Subject: [PATCH 27/47] chore: self mutation (../patches/e2e-2of2.diff/e2e-2of2.diff) Signed-off-by: monada-bot[bot] --- .../test_corpus/valid/website_with_api.w_test_sim.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_test_sim.md b/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_test_sim.md index 52e9c979d76..78cf36a8276 100644 --- a/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_test_sim.md +++ b/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_test_sim.md @@ -3,7 +3,7 @@ ## stdout.log ```log pass ┌ website_with_api.wsim » root/env0/test:GET --users - └ {"access-control-allow-credentials":"false","access-control-allow-origin":"*","access-control-expose-headers":"Content-Type","connection":"keep-alive","content-length":"12","content-type":"text/html; charset=utf-8","date":"Thu, 24 Aug 2023 08:24:11 GMT","etag":"W/\"c-1bl/ybs8pYaLwWPVCd0mBRAdBaY\"","keep-alive":"timeout=5","x-powered-by":"Express"} + └ {"access-control-allow-credentials":"false","access-control-allow-origin":"*","access-control-expose-headers":"Content-Type","connection":"keep-alive","content-length":"12","content-type":"text/html; charset=utf-8","date":"Thu, 24 Aug 2023 08:42:10 GMT","etag":"W/\"c-1bl/ybs8pYaLwWPVCd0mBRAdBaY\"","keep-alive":"timeout=5","x-powered-by":"Express"} pass ─ website_with_api.wsim » root/env1/test:OPTIONS --users From 36c0b32a53b36d841c1a0b151928ea8ade611c36 Mon Sep 17 00:00:00 2001 From: "monada-bot[bot]" Date: Thu, 24 Aug 2023 09:08:20 +0000 Subject: [PATCH 28/47] chore: self mutation (../patches/e2e-2of2.diff/e2e-2of2.diff) Signed-off-by: monada-bot[bot] --- .../test_corpus/valid/website_with_api.w_test_sim.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_test_sim.md b/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_test_sim.md index 78cf36a8276..cbaa17ec000 100644 --- a/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_test_sim.md +++ b/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_test_sim.md @@ -3,7 +3,7 @@ ## stdout.log ```log pass ┌ website_with_api.wsim » root/env0/test:GET --users - └ {"access-control-allow-credentials":"false","access-control-allow-origin":"*","access-control-expose-headers":"Content-Type","connection":"keep-alive","content-length":"12","content-type":"text/html; charset=utf-8","date":"Thu, 24 Aug 2023 08:42:10 GMT","etag":"W/\"c-1bl/ybs8pYaLwWPVCd0mBRAdBaY\"","keep-alive":"timeout=5","x-powered-by":"Express"} + └ {"access-control-allow-credentials":"false","access-control-allow-origin":"*","access-control-expose-headers":"Content-Type","connection":"keep-alive","content-length":"12","content-type":"text/html; charset=utf-8","date":"Thu, 24 Aug 2023 08:57:33 GMT","etag":"W/\"c-1bl/ybs8pYaLwWPVCd0mBRAdBaY\"","keep-alive":"timeout=5","x-powered-by":"Express"} pass ─ website_with_api.wsim » root/env1/test:OPTIONS --users From bef29ce5f1b054fc5c2dd27a02a5b080810e3777 Mon Sep 17 00:00:00 2001 From: "monada-bot[bot]" Date: Thu, 24 Aug 2023 09:33:17 +0000 Subject: [PATCH 29/47] Merge branch 'main' into 2289-adding-cors Signed-off-by: monada-bot[bot] --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d9c6bd3f256..c69ebd23a24 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -352,7 +352,7 @@ jobs: PACKAGE_VERSION: ${{ needs.build.outputs.version }} run: | PACKAGES=("winglang-sdk" "winglang-compiler" "wingconsole-design-system" "wingconsole-ui" "wingconsole-server" "wingconsole-app") - for PACKAGE in $PACKAGES; do + for PACKAGE in "${PACKAGES[@]}"; do npm publish "$PACKAGE-$PACKAGE_VERSION.tgz" --access public done From 7440b9249fb230dee20a03200715c1a0e0207677 Mon Sep 17 00:00:00 2001 From: "monada-bot[bot]" Date: Thu, 24 Aug 2023 09:33:17 +0000 Subject: [PATCH 30/47] chore: self mutation (../patches/e2e-2of2.diff/e2e-2of2.diff) Signed-off-by: monada-bot[bot] --- .../test_corpus/valid/website_with_api.w_test_sim.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_test_sim.md b/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_test_sim.md index cbaa17ec000..2ad6045d27c 100644 --- a/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_test_sim.md +++ b/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_test_sim.md @@ -3,7 +3,7 @@ ## stdout.log ```log pass ┌ website_with_api.wsim » root/env0/test:GET --users - └ {"access-control-allow-credentials":"false","access-control-allow-origin":"*","access-control-expose-headers":"Content-Type","connection":"keep-alive","content-length":"12","content-type":"text/html; charset=utf-8","date":"Thu, 24 Aug 2023 08:57:33 GMT","etag":"W/\"c-1bl/ybs8pYaLwWPVCd0mBRAdBaY\"","keep-alive":"timeout=5","x-powered-by":"Express"} + └ {"access-control-allow-credentials":"false","access-control-allow-origin":"*","access-control-expose-headers":"Content-Type","connection":"keep-alive","content-length":"12","content-type":"text/html; charset=utf-8","date":"Thu, 24 Aug 2023 09:26:13 GMT","etag":"W/\"c-1bl/ybs8pYaLwWPVCd0mBRAdBaY\"","keep-alive":"timeout=5","x-powered-by":"Express"} pass ─ website_with_api.wsim » root/env1/test:OPTIONS --users From 077e7444317464af2befc94eea7162d8d7d59e1a Mon Sep 17 00:00:00 2001 From: "monada-bot[bot]" Date: Thu, 24 Aug 2023 09:53:42 +0000 Subject: [PATCH 31/47] chore: self mutation (../patches/e2e-2of2.diff/e2e-2of2.diff) Signed-off-by: monada-bot[bot] --- .../test_corpus/valid/website_with_api.w_test_sim.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_test_sim.md b/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_test_sim.md index 2ad6045d27c..eed97ed1a5b 100644 --- a/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_test_sim.md +++ b/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_test_sim.md @@ -3,7 +3,7 @@ ## stdout.log ```log pass ┌ website_with_api.wsim » root/env0/test:GET --users - └ {"access-control-allow-credentials":"false","access-control-allow-origin":"*","access-control-expose-headers":"Content-Type","connection":"keep-alive","content-length":"12","content-type":"text/html; charset=utf-8","date":"Thu, 24 Aug 2023 09:26:13 GMT","etag":"W/\"c-1bl/ybs8pYaLwWPVCd0mBRAdBaY\"","keep-alive":"timeout=5","x-powered-by":"Express"} + └ {"access-control-allow-credentials":"false","access-control-allow-origin":"*","access-control-expose-headers":"Content-Type","connection":"keep-alive","content-length":"12","content-type":"text/html; charset=utf-8","date":"Thu, 24 Aug 2023 09:41:39 GMT","etag":"W/\"c-1bl/ybs8pYaLwWPVCd0mBRAdBaY\"","keep-alive":"timeout=5","x-powered-by":"Express"} pass ─ website_with_api.wsim » root/env1/test:OPTIONS --users From 764702af81d86741f3d42f5feb112aeb863b7008 Mon Sep 17 00:00:00 2001 From: "monada-bot[bot]" Date: Thu, 24 Aug 2023 10:11:01 +0000 Subject: [PATCH 32/47] chore: self mutation (../patches/e2e-2of2.diff/e2e-2of2.diff) Signed-off-by: monada-bot[bot] --- .../test_corpus/valid/website_with_api.w_test_sim.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_test_sim.md b/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_test_sim.md index eed97ed1a5b..8f0a2e60a69 100644 --- a/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_test_sim.md +++ b/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_test_sim.md @@ -3,7 +3,7 @@ ## stdout.log ```log pass ┌ website_with_api.wsim » root/env0/test:GET --users - └ {"access-control-allow-credentials":"false","access-control-allow-origin":"*","access-control-expose-headers":"Content-Type","connection":"keep-alive","content-length":"12","content-type":"text/html; charset=utf-8","date":"Thu, 24 Aug 2023 09:41:39 GMT","etag":"W/\"c-1bl/ybs8pYaLwWPVCd0mBRAdBaY\"","keep-alive":"timeout=5","x-powered-by":"Express"} + └ {"access-control-allow-credentials":"false","access-control-allow-origin":"*","access-control-expose-headers":"Content-Type","connection":"keep-alive","content-length":"12","content-type":"text/html; charset=utf-8","date":"Thu, 24 Aug 2023 10:04:06 GMT","etag":"W/\"c-1bl/ybs8pYaLwWPVCd0mBRAdBaY\"","keep-alive":"timeout=5","x-powered-by":"Express"} pass ─ website_with_api.wsim » root/env1/test:OPTIONS --users From c194b11d2bf7e5c79bf49c2ad42724f3969dd261 Mon Sep 17 00:00:00 2001 From: "monada-bot[bot]" Date: Thu, 24 Aug 2023 10:28:39 +0000 Subject: [PATCH 33/47] chore: self mutation (../patches/e2e-2of2.diff/e2e-2of2.diff) Signed-off-by: monada-bot[bot] --- .../test_corpus/valid/website_with_api.w_test_sim.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_test_sim.md b/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_test_sim.md index 8f0a2e60a69..5580d69f814 100644 --- a/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_test_sim.md +++ b/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_test_sim.md @@ -3,7 +3,7 @@ ## stdout.log ```log pass ┌ website_with_api.wsim » root/env0/test:GET --users - └ {"access-control-allow-credentials":"false","access-control-allow-origin":"*","access-control-expose-headers":"Content-Type","connection":"keep-alive","content-length":"12","content-type":"text/html; charset=utf-8","date":"Thu, 24 Aug 2023 10:04:06 GMT","etag":"W/\"c-1bl/ybs8pYaLwWPVCd0mBRAdBaY\"","keep-alive":"timeout=5","x-powered-by":"Express"} + └ {"access-control-allow-credentials":"false","access-control-allow-origin":"*","access-control-expose-headers":"Content-Type","connection":"keep-alive","content-length":"12","content-type":"text/html; charset=utf-8","date":"Thu, 24 Aug 2023 10:21:14 GMT","etag":"W/\"c-1bl/ybs8pYaLwWPVCd0mBRAdBaY\"","keep-alive":"timeout=5","x-powered-by":"Express"} pass ─ website_with_api.wsim » root/env1/test:OPTIONS --users From 162ba322b06b440457451c77b1b36b594ef2f42d Mon Sep 17 00:00:00 2001 From: "monada-bot[bot]" Date: Thu, 24 Aug 2023 10:43:34 +0000 Subject: [PATCH 34/47] chore: self mutation (../patches/e2e-2of2.diff/e2e-2of2.diff) Signed-off-by: monada-bot[bot] --- .../test_corpus/valid/website_with_api.w_test_sim.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_test_sim.md b/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_test_sim.md index 5580d69f814..997d8455de7 100644 --- a/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_test_sim.md +++ b/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_test_sim.md @@ -3,7 +3,7 @@ ## stdout.log ```log pass ┌ website_with_api.wsim » root/env0/test:GET --users - └ {"access-control-allow-credentials":"false","access-control-allow-origin":"*","access-control-expose-headers":"Content-Type","connection":"keep-alive","content-length":"12","content-type":"text/html; charset=utf-8","date":"Thu, 24 Aug 2023 10:21:14 GMT","etag":"W/\"c-1bl/ybs8pYaLwWPVCd0mBRAdBaY\"","keep-alive":"timeout=5","x-powered-by":"Express"} + └ {"access-control-allow-credentials":"false","access-control-allow-origin":"*","access-control-expose-headers":"Content-Type","connection":"keep-alive","content-length":"12","content-type":"text/html; charset=utf-8","date":"Thu, 24 Aug 2023 10:37:05 GMT","etag":"W/\"c-1bl/ybs8pYaLwWPVCd0mBRAdBaY\"","keep-alive":"timeout=5","x-powered-by":"Express"} pass ─ website_with_api.wsim » root/env1/test:OPTIONS --users From 28d12b1ed74d6e252e579f416149d61a9242fcb7 Mon Sep 17 00:00:00 2001 From: Sebastian Korfmann Date: Thu, 24 Aug 2023 14:14:44 +0200 Subject: [PATCH 35/47] Update snapshots --- .../__snapshots__/api.test.ts.snap | 58 +++++++++++++++++++ .../__snapshots__/tokens.test.ts.snap | 4 +- 2 files changed, 60 insertions(+), 2 deletions(-) diff --git a/libs/wingsdk/test/target-tf-aws/__snapshots__/api.test.ts.snap b/libs/wingsdk/test/target-tf-aws/__snapshots__/api.test.ts.snap index efcff9a9242..b93c088794e 100644 --- a/libs/wingsdk/test/target-tf-aws/__snapshots__/api.test.ts.snap +++ b/libs/wingsdk/test/target-tf-aws/__snapshots__/api.test.ts.snap @@ -1,5 +1,42 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html +exports[`api configured for cors 1`] = ` +{ + "openapi": "3.0.3", + "paths": { + "/": { + "get": { + "operationId": "get", + "parameters": [], + "responses": { + "200": { + "content": {}, + "description": "200 response", + }, + }, + "x-amazon-apigateway-integration": { + "contentHandling": "CONVERT_TO_TEXT", + "httpMethod": "POST", + "passthroughBehavior": "when_no_match", + "responses": { + "default": { + "responseParameters": { + "method.response.header.Access-Control-Allow-Headers": "'Content-Type,Authorization'", + "method.response.header.Access-Control-Allow-Methods": "'GET,POST,PUT,DELETE,HEAD,OPTIONS'", + "method.response.header.Access-Control-Allow-Origin": "'*'", + }, + "statusCode": "200", + }, + }, + "type": "aws_proxy", + "uri": "arn:aws:apigateway:\${data.aws_region.Region.name}:lambda:path/2015-03-31/functions/\${aws_lambda_function.Api_Api-OnRequest-c5395e41_37F21C2B.arn}/invocations", + }, + }, + }, + }, +} +`; + exports[`api with CONNECT route 1`] = ` { "openapi": "3.0.3", @@ -20,6 +57,7 @@ exports[`api with CONNECT route 1`] = ` "passthroughBehavior": "when_no_match", "responses": { "default": { + "responseParameters": {}, "statusCode": "200", }, }, @@ -52,6 +90,7 @@ exports[`api with DELETE route 1`] = ` "passthroughBehavior": "when_no_match", "responses": { "default": { + "responseParameters": {}, "statusCode": "200", }, }, @@ -84,6 +123,7 @@ exports[`api with GET route at root 1`] = ` "passthroughBehavior": "when_no_match", "responses": { "default": { + "responseParameters": {}, "statusCode": "200", }, }, @@ -116,6 +156,7 @@ exports[`api with GET routes with common prefix 1`] = ` "passthroughBehavior": "when_no_match", "responses": { "default": { + "responseParameters": {}, "statusCode": "200", }, }, @@ -140,6 +181,7 @@ exports[`api with GET routes with common prefix 1`] = ` "passthroughBehavior": "when_no_match", "responses": { "default": { + "responseParameters": {}, "statusCode": "200", }, }, @@ -172,6 +214,7 @@ exports[`api with GET routes with different prefix 1`] = ` "passthroughBehavior": "when_no_match", "responses": { "default": { + "responseParameters": {}, "statusCode": "200", }, }, @@ -196,6 +239,7 @@ exports[`api with GET routes with different prefix 1`] = ` "passthroughBehavior": "when_no_match", "responses": { "default": { + "responseParameters": {}, "statusCode": "200", }, }, @@ -228,6 +272,7 @@ exports[`api with HEAD route 1`] = ` "passthroughBehavior": "when_no_match", "responses": { "default": { + "responseParameters": {}, "statusCode": "200", }, }, @@ -260,6 +305,7 @@ exports[`api with OPTIONS route 1`] = ` "passthroughBehavior": "when_no_match", "responses": { "default": { + "responseParameters": {}, "statusCode": "200", }, }, @@ -292,6 +338,7 @@ exports[`api with PATCH route 1`] = ` "passthroughBehavior": "when_no_match", "responses": { "default": { + "responseParameters": {}, "statusCode": "200", }, }, @@ -324,6 +371,7 @@ exports[`api with POST route 1`] = ` "passthroughBehavior": "when_no_match", "responses": { "default": { + "responseParameters": {}, "statusCode": "200", }, }, @@ -356,6 +404,7 @@ exports[`api with PUT route 1`] = ` "passthroughBehavior": "when_no_match", "responses": { "default": { + "responseParameters": {}, "statusCode": "200", }, }, @@ -388,6 +437,7 @@ exports[`api with multiple GET route and one lambda 1`] = ` "passthroughBehavior": "when_no_match", "responses": { "default": { + "responseParameters": {}, "statusCode": "200", }, }, @@ -412,6 +462,7 @@ exports[`api with multiple GET route and one lambda 1`] = ` "passthroughBehavior": "when_no_match", "responses": { "default": { + "responseParameters": {}, "statusCode": "200", }, }, @@ -444,6 +495,7 @@ exports[`api with multiple methods and multiple lambda 1`] = ` "passthroughBehavior": "when_no_match", "responses": { "default": { + "responseParameters": {}, "statusCode": "200", }, }, @@ -468,6 +520,7 @@ exports[`api with multiple methods and multiple lambda 1`] = ` "passthroughBehavior": "when_no_match", "responses": { "default": { + "responseParameters": {}, "statusCode": "200", }, }, @@ -500,6 +553,7 @@ exports[`api with multiple methods and one lambda 1`] = ` "passthroughBehavior": "when_no_match", "responses": { "default": { + "responseParameters": {}, "statusCode": "200", }, }, @@ -524,6 +578,7 @@ exports[`api with multiple methods and one lambda 1`] = ` "passthroughBehavior": "when_no_match", "responses": { "default": { + "responseParameters": {}, "statusCode": "200", }, }, @@ -556,6 +611,7 @@ exports[`api with multiple methods on same route 1`] = ` "passthroughBehavior": "when_no_match", "responses": { "default": { + "responseParameters": {}, "statusCode": "200", }, }, @@ -578,6 +634,7 @@ exports[`api with multiple methods on same route 1`] = ` "passthroughBehavior": "when_no_match", "responses": { "default": { + "responseParameters": {}, "statusCode": "200", }, }, @@ -619,6 +676,7 @@ exports[`api with path parameter 1`] = ` "passthroughBehavior": "when_no_match", "responses": { "default": { + "responseParameters": {}, "statusCode": "200", }, }, diff --git a/libs/wingsdk/test/target-tf-aws/__snapshots__/tokens.test.ts.snap b/libs/wingsdk/test/target-tf-aws/__snapshots__/tokens.test.ts.snap index 9079917bca1..361628301cb 100644 --- a/libs/wingsdk/test/target-tf-aws/__snapshots__/tokens.test.ts.snap +++ b/libs/wingsdk/test/target-tf-aws/__snapshots__/tokens.test.ts.snap @@ -39,13 +39,13 @@ exports[`captures tokens 2`] = ` }, "rest_api_id": "\${aws_api_gateway_rest_api.Api_api_91C07D84.id}", "triggers": { - "redeployment": "5c0f11f0478884e3d7859fa987b8b7ecf8f2f6bc", + "redeployment": "0c985ec4b2e80179d31281ba3e16595d5e75a9cd", }, }, }, "aws_api_gateway_rest_api": { "Api_api_91C07D84": { - "body": "{\\"openapi\\":\\"3.0.3\\",\\"paths\\":{\\"/\\":{\\"get\\":{\\"operationId\\":\\"get\\",\\"responses\\":{\\"200\\":{\\"description\\":\\"200 response\\",\\"content\\":{}}},\\"parameters\\":[],\\"x-amazon-apigateway-integration\\":{\\"uri\\":\\"arn:aws:apigateway:\${data.aws_region.Region.name}:lambda:path/2015-03-31/functions/\${aws_lambda_function.Api_Api-OnRequest-c5395e41_37F21C2B.arn}/invocations\\",\\"type\\":\\"aws_proxy\\",\\"httpMethod\\":\\"POST\\",\\"responses\\":{\\"default\\":{\\"statusCode\\":\\"200\\"}},\\"passthroughBehavior\\":\\"when_no_match\\",\\"contentHandling\\":\\"CONVERT_TO_TEXT\\"}}}}}", + "body": "{\\"openapi\\":\\"3.0.3\\",\\"paths\\":{\\"/\\":{\\"get\\":{\\"operationId\\":\\"get\\",\\"responses\\":{\\"200\\":{\\"description\\":\\"200 response\\",\\"content\\":{}}},\\"parameters\\":[],\\"x-amazon-apigateway-integration\\":{\\"uri\\":\\"arn:aws:apigateway:\${data.aws_region.Region.name}:lambda:path/2015-03-31/functions/\${aws_lambda_function.Api_Api-OnRequest-c5395e41_37F21C2B.arn}/invocations\\",\\"type\\":\\"aws_proxy\\",\\"httpMethod\\":\\"POST\\",\\"responses\\":{\\"default\\":{\\"statusCode\\":\\"200\\",\\"responseParameters\\":{}}},\\"passthroughBehavior\\":\\"when_no_match\\",\\"contentHandling\\":\\"CONVERT_TO_TEXT\\"}}}}}", "name": "api-c8f613f0", }, }, From ddf59e8f06b0c6f4508b043d8b5d00137bde5ba5 Mon Sep 17 00:00:00 2001 From: Sebastian Korfmann Date: Thu, 24 Aug 2023 14:15:14 +0200 Subject: [PATCH 36/47] Update vitest --- libs/wingsdk/package.json | 2 +- pnpm-lock.yaml | 96 +++++++++++++++++++++------------------ 2 files changed, 54 insertions(+), 44 deletions(-) diff --git a/libs/wingsdk/package.json b/libs/wingsdk/package.json index ea93690be0e..2c8ff960db4 100644 --- a/libs/wingsdk/package.json +++ b/libs/wingsdk/package.json @@ -71,7 +71,7 @@ "standard-version": "^9", "ts-node": "^10.9.1", "typescript": "^4.9.4", - "vitest": "^0.32.2", + "vitest": "^0.34.2", "wing-api-checker": "workspace:^" }, "peerDependencies": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 086a472068d..9feb588cbfd 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -464,7 +464,7 @@ importers: version: 4.0.0(vite@4.3.9) '@vitest/coverage-c8': specifier: ^0.31.4 - version: 0.31.4(vitest@0.32.2) + version: 0.31.4(vitest@0.34.2) '@wingconsole/eslint-plugin': specifier: workspace:^ version: link:../../tools/eslint-plugin @@ -1186,7 +1186,7 @@ importers: version: 5.59.11(eslint@8.42.0)(typescript@4.9.4) '@vitest/coverage-v8': specifier: ^0.32.2 - version: 0.32.2(vitest@0.32.2) + version: 0.32.2(vitest@0.34.2) '@winglang/jsii-docgen': specifier: workspace:^ version: link:../../apps/jsii-docgen @@ -1254,8 +1254,8 @@ importers: specifier: ^4.9.4 version: 4.9.4 vitest: - specifier: ^0.32.2 - version: 0.32.2 + specifier: ^0.34.2 + version: 0.34.2 wing-api-checker: specifier: workspace:^ version: link:../../apps/wing-api-checker @@ -9589,7 +9589,7 @@ packages: vitest: 0.31.4(happy-dom@9.20.3) dev: true - /@vitest/coverage-c8@0.31.4(vitest@0.32.2): + /@vitest/coverage-c8@0.31.4(vitest@0.34.2): resolution: {integrity: sha512-VPx368m4DTcpA/P0v3YdVxl4QOSh1DbUcXURLRvDShrIB5KxOgfzw4Bn2R8AhAe/GyiWW/FIsJ/OJdYXCCiC1w==} peerDependencies: vitest: '>=0.30.0 <1' @@ -9599,10 +9599,10 @@ packages: magic-string: 0.30.0 picocolors: 1.0.0 std-env: 3.3.3 - vitest: 0.32.2 + vitest: 0.34.2 dev: true - /@vitest/coverage-v8@0.32.2(vitest@0.32.2): + /@vitest/coverage-v8@0.32.2(vitest@0.34.2): resolution: {integrity: sha512-/+V3nB3fyeuuSeKxCfi6XmWjDIxpky7AWSkGVfaMjAk7di8igBwRsThLjultwIZdTDH1RAxpjmCXEfSqsMFZOA==} peerDependencies: vitest: '>=0.32.0 <1' @@ -9618,7 +9618,7 @@ packages: std-env: 3.3.3 test-exclude: 6.0.0 v8-to-istanbul: 9.1.0 - vitest: 0.32.2 + vitest: 0.34.2 transitivePeerDependencies: - supports-color dev: true @@ -9639,11 +9639,11 @@ packages: chai: 4.3.7 dev: true - /@vitest/expect@0.32.2: - resolution: {integrity: sha512-6q5yzweLnyEv5Zz1fqK5u5E83LU+gOMVBDuxBl2d2Jfx1BAp5M+rZgc5mlyqdnxquyoiOXpXmFNkcGcfFnFH3Q==} + /@vitest/expect@0.34.2: + resolution: {integrity: sha512-EZm2dMNlLyIfDMha17QHSQcg2KjeAZaXd65fpPzXY5bvnfx10Lcaz3N55uEe8PhF+w4pw+hmrlHLLlRn9vkBJg==} dependencies: - '@vitest/spy': 0.32.2 - '@vitest/utils': 0.32.2 + '@vitest/spy': 0.34.2 + '@vitest/utils': 0.34.2 chai: 4.3.7 dev: true @@ -9665,11 +9665,10 @@ packages: pathe: 1.1.1 dev: true - /@vitest/runner@0.32.2: - resolution: {integrity: sha512-06vEL0C1pomOEktGoLjzZw+1Fb+7RBRhmw/06WkDrd1akkT9i12su0ku+R/0QM69dfkIL/rAIDTG+CSuQVDcKw==} + /@vitest/runner@0.34.2: + resolution: {integrity: sha512-8ydGPACVX5tK3Dl0SUwxfdg02h+togDNeQX3iXVFYgzF5odxvaou7HnquALFZkyVuYskoaHUOqOyOLpOEj5XTA==} dependencies: - '@vitest/utils': 0.32.2 - concordance: 5.0.4 + '@vitest/utils': 0.34.2 p-limit: 4.0.0 pathe: 1.1.1 dev: true @@ -9677,7 +9676,7 @@ packages: /@vitest/snapshot@0.30.1: resolution: {integrity: sha512-fJZqKrE99zo27uoZA/azgWyWbFvM1rw2APS05yB0JaLwUIg9aUtvvnBf4q7JWhEcAHmSwbrxKFgyBUga6tq9Tw==} dependencies: - magic-string: 0.30.0 + magic-string: 0.30.3 pathe: 1.1.1 pretty-format: 27.5.1 dev: true @@ -9685,17 +9684,17 @@ packages: /@vitest/snapshot@0.31.4: resolution: {integrity: sha512-LemvNumL3NdWSmfVAMpXILGyaXPkZbG5tyl6+RQSdcHnTj6hvA49UAI8jzez9oQyE/FWLKRSNqTGzsHuk89LRA==} dependencies: - magic-string: 0.30.0 + magic-string: 0.30.3 pathe: 1.1.1 pretty-format: 27.5.1 dev: true - /@vitest/snapshot@0.32.2: - resolution: {integrity: sha512-JwhpeH/PPc7GJX38vEfCy9LtRzf9F4er7i4OsAJyV7sjPwjj+AIR8cUgpMTWK4S3TiamzopcTyLsZDMuldoi5A==} + /@vitest/snapshot@0.34.2: + resolution: {integrity: sha512-qhQ+xy3u4mwwLxltS4Pd4SR+XHv4EajiTPNY3jkIBLUApE6/ce72neJPSUQZ7bL3EBuKI+NhvzhGj3n5baRQUQ==} dependencies: - magic-string: 0.30.0 + magic-string: 0.30.3 pathe: 1.1.1 - pretty-format: 27.5.1 + pretty-format: 29.5.0 dev: true /@vitest/spy@0.30.1: @@ -9710,8 +9709,8 @@ packages: tinyspy: 2.1.1 dev: true - /@vitest/spy@0.32.2: - resolution: {integrity: sha512-Q/ZNILJ4ca/VzQbRM8ur3Si5Sardsh1HofatG9wsJY1RfEaw0XKP8IVax2lI1qnrk9YPuG9LA2LkZ0EI/3d4ug==} + /@vitest/spy@0.34.2: + resolution: {integrity: sha512-yd4L9OhfH6l0Av7iK3sPb3MykhtcRN5c5K5vm1nTbuN7gYn+yvUVVsyvzpHrjqS7EWqn9WsPJb7+0c3iuY60tA==} dependencies: tinyspy: 2.1.1 dev: true @@ -9732,12 +9731,12 @@ packages: pretty-format: 27.5.1 dev: true - /@vitest/utils@0.32.2: - resolution: {integrity: sha512-lnJ0T5i03j0IJaeW73hxe2AuVnZ/y1BhhCOuIcl9LIzXnbpXJT9Lrt6brwKHXLOiA7MZ6N5hSJjt0xE1dGNCzQ==} + /@vitest/utils@0.34.2: + resolution: {integrity: sha512-Lzw+kAsTPubhoQDp1uVAOP6DhNia1GMDsI9jgB0yMn+/nDaPieYQ88lKqz/gGjSHL4zwOItvpehec9OY+rS73w==} dependencies: diff-sequences: 29.4.3 loupe: 2.3.6 - pretty-format: 27.5.1 + pretty-format: 29.5.0 dev: true /@vscode/vsce@2.16.0: @@ -12473,7 +12472,7 @@ packages: dependencies: semver: 7.5.3 shelljs: 0.8.5 - typescript: 5.3.0-dev.20230823 + typescript: 5.3.0-dev.20230824 dev: true /dset@3.1.2: @@ -16962,6 +16961,13 @@ packages: '@jridgewell/sourcemap-codec': 1.4.15 dev: true + /magic-string@0.30.3: + resolution: {integrity: sha512-B7xGbll2fG/VjP+SWg4sX3JynwIU0mjoTc6MPpKNuIvftk6u6vqhDnk1R80b8C2GBR6ywqy+1DcKBrevBg+bmw==} + engines: {node: '>=12'} + dependencies: + '@jridgewell/sourcemap-codec': 1.4.15 + dev: true + /make-dir@2.1.0: resolution: {integrity: sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==} engines: {node: '>=6'} @@ -20787,6 +20793,11 @@ packages: engines: {node: '>=14.0.0'} dev: true + /tinypool@0.7.0: + resolution: {integrity: sha512-zSYNUlYSMhJ6Zdou4cJwo/p7w5nmAH17GRfU/ui3ctvjXFErXXkruT4MWW6poDeXgCaIBlGLrfU6TbTXxyGMww==} + engines: {node: '>=14.0.0'} + dev: true + /tinyspy@2.1.1: resolution: {integrity: sha512-XPJL2uSzcOyBMky6OFrusqWlzfFrXtE0hPuMgW8A2HmaqrPo4ZQHRN/V0QXN3FSjKxpsbRrFc5LI7KOwBsT1/w==} engines: {node: '>=14.0.0'} @@ -21392,8 +21403,8 @@ packages: engines: {node: '>=14.17'} hasBin: true - /typescript@5.3.0-dev.20230823: - resolution: {integrity: sha512-IEFfTl67UgTTJzf+Ma4+0txDKzxzxtjNjyFjTm7c+yP5sff+atKe44mAjhCxX8wGjwRrFuhAUeHz1udmzBZ4ow==} + /typescript@5.3.0-dev.20230824: + resolution: {integrity: sha512-iiUWxGibzrRHEBLDJfVymsvpPKflf3cMrw0oQTMQoguFS2ikNlVlfQWAsYeHqGpRQc77nSQkzsE9rAHNHqvIjw==} engines: {node: '>=14.17'} hasBin: true dev: true @@ -21794,8 +21805,8 @@ packages: - terser dev: true - /vite-node@0.32.2(@types/node@18.16.18): - resolution: {integrity: sha512-dTQ1DCLwl2aEseov7cfQ+kDMNJpM1ebpyMMMwWzBvLbis8Nla/6c9WQcqpPssTwS6Rp/+U6KwlIj8Eapw4bLdA==} + /vite-node@0.34.2(@types/node@18.16.18): + resolution: {integrity: sha512-JtW249Zm3FB+F7pQfH56uWSdlltCo1IOkZW5oHBzeQo0iX4jtC7o1t9aILMGd9kVekXBP2lfJBEQt9rBh07ebA==} engines: {node: '>=v14.18.0'} hasBin: true dependencies: @@ -21980,8 +21991,8 @@ packages: - terser dev: true - /vitest@0.32.2: - resolution: {integrity: sha512-hU8GNNuQfwuQmqTLfiKcqEhZY72Zxb7nnN07koCUNmntNxbKQnVbeIS6sqUgR3eXSlbOpit8+/gr1KpqoMgWCQ==} + /vitest@0.34.2: + resolution: {integrity: sha512-WgaIvBbjsSYMq/oiMlXUI7KflELmzM43BEvkdC/8b5CAod4ryAiY2z8uR6Crbi5Pjnu5oOmhKa9sy7uk6paBxQ==} engines: {node: '>=v14.18.0'} hasBin: true peerDependencies: @@ -22014,27 +22025,26 @@ packages: '@types/chai': 4.3.5 '@types/chai-subset': 1.3.3 '@types/node': 18.16.18 - '@vitest/expect': 0.32.2 - '@vitest/runner': 0.32.2 - '@vitest/snapshot': 0.32.2 - '@vitest/spy': 0.32.2 - '@vitest/utils': 0.32.2 + '@vitest/expect': 0.34.2 + '@vitest/runner': 0.34.2 + '@vitest/snapshot': 0.34.2 + '@vitest/spy': 0.34.2 + '@vitest/utils': 0.34.2 acorn: 8.9.0 acorn-walk: 8.2.0 cac: 6.7.14 chai: 4.3.7 - concordance: 5.0.4 debug: 4.3.4 local-pkg: 0.4.3 - magic-string: 0.30.0 + magic-string: 0.30.3 pathe: 1.1.1 picocolors: 1.0.0 std-env: 3.3.3 strip-literal: 1.0.1 tinybench: 2.5.0 - tinypool: 0.5.0 + tinypool: 0.7.0 vite: 4.3.9(@types/node@18.16.18) - vite-node: 0.32.2(@types/node@18.16.18) + vite-node: 0.34.2(@types/node@18.16.18) why-is-node-running: 2.2.2 transitivePeerDependencies: - less From 7de0620f7b1c83416a8f4984dabed0f2a7bf2710 Mon Sep 17 00:00:00 2001 From: Sebastian Korfmann Date: Thu, 24 Aug 2023 14:17:05 +0200 Subject: [PATCH 37/47] Don't run vitest by default with update There's an issue with vitest, where snapshots are detected as obsolete when running in a focused mode ('-t') Other than that, this should be a concious choice --- libs/wingsdk/vitest.config.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/libs/wingsdk/vitest.config.ts b/libs/wingsdk/vitest.config.ts index 275c28c9da4..1cb7948faf8 100644 --- a/libs/wingsdk/vitest.config.ts +++ b/libs/wingsdk/vitest.config.ts @@ -2,7 +2,6 @@ import { defineConfig } from "vitest/config"; export default defineConfig({ test: { - update: true, globalSetup: "test/global.setup.ts", testTimeout: 200_000, include: ["test/**/*.test.ts"], From b8df3a85d61b058221f8e4f885afcc7324ef1cda Mon Sep 17 00:00:00 2001 From: Sebastian Korfmann Date: Thu, 24 Aug 2023 14:21:33 +0200 Subject: [PATCH 38/47] Apply feedback and add tests --- examples/tests/valid/website_with_api.w | 3 +- libs/wingsdk/src/cloud/api.ts | 65 ++++++++++++++++++++++-- libs/wingsdk/src/target-sim/api.ts | 6 +-- libs/wingsdk/test/target-sim/api.test.ts | 64 +++++++++++++++++++++++ 4 files changed, 127 insertions(+), 11 deletions(-) diff --git a/examples/tests/valid/website_with_api.w b/examples/tests/valid/website_with_api.w index fdc76be7736..0b8bf6dd003 100644 --- a/examples/tests/valid/website_with_api.w +++ b/examples/tests/valid/website_with_api.w @@ -5,7 +5,8 @@ bring "./assertions.w" as t; //needs to be written before the website (so the website will be able to use it's url on sim env) let api = new cloud.Api( - cors: cloud.ApiCorsProps { + cors: true, + corsOptions: cloud.ApiCorsOptions { origins: ["*"], methods: [cloud.HttpMethod.GET, cloud.HttpMethod.POST, cloud.HttpMethod.OPTIONS], headers: ["Content-Type"], diff --git a/libs/wingsdk/src/cloud/api.ts b/libs/wingsdk/src/cloud/api.ts index e04733bea1b..16d459021b6 100644 --- a/libs/wingsdk/src/cloud/api.ts +++ b/libs/wingsdk/src/cloud/api.ts @@ -12,7 +12,7 @@ export const API_FQN = fqnForType("cloud.Api"); /** * Cors Options for `Api`. */ -export interface ApiCorsProps { +export interface ApiCorsOptions { /** * The list of allowed origins. * @example ["https://example.com"] @@ -56,12 +56,34 @@ export interface ApiProps { /** * Options for configuring the API's CORS behavior across all routes. * Options can also be overridden on a per-route basis. (not yet implemented) - * Can be set to to an empty Struct to enable CORS with default options. + * When enabled this will add CORS headers with default options. + * Can be customized by passing `corsOptions` + * @example true + * @default - false, CORS configuration is disabled + */ + readonly cors?: boolean; + + /** + * Options for configuring the API's CORS behavior across all routes. + * Options can also be overridden on a per-route basis. (not yet implemented) + * * @example { origins: ["https://example.com"] } - * @example {} - * @default - CORS is disabled + * @default - Default CORS options are applied when `cors` is set to `true` + * origins: ["*"], + * methods: [ + * HttpMethod.GET, + * HttpMethod.POST, + * HttpMethod.PUT, + * HttpMethod.DELETE, + * HttpMethod.HEAD, + * HttpMethod.OPTIONS, + * ], + * headers: ["Content-Type", "Authorization"], + * exposedHeaders: [], + * allowCredentials: false, + * */ - readonly cors?: ApiCorsProps; + readonly corsOptions?: ApiCorsOptions; } /** * The OpenAPI spec. @@ -104,11 +126,31 @@ export abstract class Api extends Resource { paths: {}, }; + private corsDefaultValues: ApiCorsOptions = { + origins: ["*"], + methods: [ + HttpMethod.GET, + HttpMethod.POST, + HttpMethod.PUT, + HttpMethod.DELETE, + HttpMethod.HEAD, + HttpMethod.OPTIONS, + ], + headers: ["Content-Type", "Authorization"], + exposedHeaders: [], + allowCredentials: false, + }; + + protected corsOptions: ApiCorsOptions; + protected corsEnabled: boolean; + constructor(scope: Construct, id: string, props: ApiProps = {}) { super(scope, id); props; + this.corsOptions = this._cors(props.corsOptions); + this.corsEnabled = props.cors ?? false; this.display.title = "Api"; this.display.description = "A REST API endpoint"; } @@ -222,6 +264,19 @@ export abstract class Api extends Resource { } } + /** + * Returns CORS configuration. If props are provided, they will have precedence over defaults. + * @param props + * @returns ApiCorsOptions + * @internal + */ + protected _cors(props?: ApiCorsOptions): ApiCorsOptions { + return { + ...this.corsDefaultValues, + ...props, + }; + } + /** * Add a route to the api spec. * @param path The path to add. diff --git a/libs/wingsdk/src/target-sim/api.ts b/libs/wingsdk/src/target-sim/api.ts index 29bdafd1506..b404b79cb81 100644 --- a/libs/wingsdk/src/target-sim/api.ts +++ b/libs/wingsdk/src/target-sim/api.ts @@ -17,14 +17,10 @@ import { BaseResourceSchema } from "../testing/simulator"; * @inflight `@winglang/sdk.cloud.IApiClient` */ export class Api extends cloud.Api implements ISimulatorResource { - public readonly cors?: cloud.ApiCorsProps; - private eventMappings: { [key: string]: EventMapping } = {}; constructor(scope: Construct, id: string, props: cloud.ApiProps = {}) { super(scope, id, props); - - this.cors = props.cors; } public get url(): string { @@ -213,7 +209,7 @@ export class Api extends cloud.Api implements ISimulatorResource { path: this.node.path, props: { openApiSpec: this._getApiSpec(), - cors: this.cors, + cors: this.corsEnabled ? this.corsOptions : undefined, }, attrs: {} as any, }; diff --git a/libs/wingsdk/test/target-sim/api.test.ts b/libs/wingsdk/test/target-sim/api.test.ts index c9501304304..a259a02a93f 100644 --- a/libs/wingsdk/test/target-sim/api.test.ts +++ b/libs/wingsdk/test/target-sim/api.test.ts @@ -513,3 +513,67 @@ test("no response body", async () => { expect(response.status).toEqual(200); expect(response.bodyUsed).toBeFalsy(); }); + +test("api with CORS defaults", async () => { + // GIVEN + const ROUTE = "/hello"; + const RESPONSE = "boom"; + + const app = new SimApp(); + const api = cloud.Api._newApi(app, "my_api", { cors: true }); + const inflight = Testing.makeHandler(app, "Handler", INFLIGHT_CODE(RESPONSE)); + api.get(ROUTE, inflight); + + // WHEN + const s = await app.startSimulator(); + const apiUrl = getApiUrl(s, "/my_api"); + const response = await fetch(apiUrl + ROUTE, { method: "GET" }); + + // THEN + await s.stop(); + + expect(response.status).toEqual(200); + expect(await response.text()).toEqual(RESPONSE); + expect(response.headers.get("access-control-allow-origin")).toEqual("*"); + expect(response.headers.get("access-control-allow-credentials")).toEqual( + "false" + ); +}); + +test("api with custom CORS settings", async () => { + // GIVEN + const ROUTE = "/hello"; + const RESPONSE = "boom"; + + const app = new SimApp(); + const api = cloud.Api._newApi(app, "my_api", { + cors: true, + corsOptions: { + origins: ["https://example.com"], + allowCredentials: true, + exposedHeaders: ["x-wingnuts"], + }, + }); + const inflight = Testing.makeHandler(app, "Handler", INFLIGHT_CODE(RESPONSE)); + api.get(ROUTE, inflight); + + // WHEN + const s = await app.startSimulator(); + const apiUrl = getApiUrl(s, "/my_api"); + const response = await fetch(apiUrl + ROUTE, { method: "GET" }); + + // THEN + await s.stop(); + + expect(response.status).toEqual(200); + expect(await response.text()).toEqual(RESPONSE); + expect(response.headers.get("access-control-allow-origin")).toEqual( + "https://example.com" + ); + expect(response.headers.get("access-control-allow-credentials")).toEqual( + "true" + ); + expect(response.headers.get("access-control-expose-headers")).toEqual( + "x-wingnuts" + ); +}); From 6776cea91771520c6d31b8124329925b28bfe851 Mon Sep 17 00:00:00 2001 From: Sebastian Korfmann Date: Thu, 24 Aug 2023 17:14:33 +0200 Subject: [PATCH 39/47] sim & aws --- libs/wingsdk/src/cloud/api.ts | 22 +++++++++++---- libs/wingsdk/src/target-sim/api.inflight.ts | 13 ++++----- libs/wingsdk/src/target-sim/api.ts | 4 +-- .../src/target-sim/schema-resources.ts | 4 +-- libs/wingsdk/src/target-tf-aws/api.ts | 28 ++++++++++++++----- .../__snapshots__/api.test.ts.snap | 5 ++++ libs/wingsdk/test/target-tf-aws/api.test.ts | 17 +++++++++++ 7 files changed, 70 insertions(+), 23 deletions(-) diff --git a/libs/wingsdk/src/cloud/api.ts b/libs/wingsdk/src/cloud/api.ts index 16d459021b6..cd6a5cbd424 100644 --- a/libs/wingsdk/src/cloud/api.ts +++ b/libs/wingsdk/src/cloud/api.ts @@ -141,16 +141,14 @@ export abstract class Api extends Resource { allowCredentials: false, }; - protected corsOptions: ApiCorsOptions; - protected corsEnabled: boolean; + protected corsOptions?: ApiCorsOptions; constructor(scope: Construct, id: string, props: ApiProps = {}) { super(scope, id); props; - this.corsOptions = this._cors(props.corsOptions); - this.corsEnabled = props.cors ?? false; + this.corsOptions = props.cors ? this._cors(props.corsOptions) : undefined; this.display.title = "Api"; this.display.description = "A REST API endpoint"; } @@ -288,7 +286,8 @@ export abstract class Api extends Resource { public _addToSpec( path: string, method: string, - apiSpecExtension: OpenApiSpecExtension + apiSpecExtension: OpenApiSpecExtension, + corsOptions?: ApiCorsOptions ) { if (this.apiSpec.paths[path]?.[method.toLowerCase()]) { throw new Error( @@ -320,6 +319,19 @@ export abstract class Api extends Resource { "200": { description: "200 response", content: {}, + ...(corsOptions && { + headers: { + "Access-Control-Allow-Origin": `'${corsOptions.origins?.join( + "," + )}'`, + "Access-Control-Allow-Methods": `'${corsOptions.methods?.join( + "," + )}'`, + "Access-Control-Allow-Headers": `'${corsOptions.headers?.join( + "," + )}'`, + }, + }), }, }, parameters: pathParameters, diff --git a/libs/wingsdk/src/target-sim/api.inflight.ts b/libs/wingsdk/src/target-sim/api.inflight.ts index a086ca88449..3697d39ea97 100644 --- a/libs/wingsdk/src/target-sim/api.inflight.ts +++ b/libs/wingsdk/src/target-sim/api.inflight.ts @@ -38,7 +38,7 @@ export class Api props; this.routes = []; this.context = context; - const { cors } = props; + const { corsOptions } = props; // Set up an express server that handles the routes. this.app = express(); @@ -49,15 +49,14 @@ export class Api this.app.use(express.text({ limit: "10mb", type: "*/*" })); // Set up CORS headers for options requests. - if (cors) { - // inspired by https://github.com/expressjs/cors/blob/f038e7722838fd83935674aa8c5bf452766741fb/lib/index.js#L159-L190 + if (corsOptions) { const { - origins = ["*"], - methods = ["GET", "HEAD", "PUT", "PATCH", "POST", "DELETE"], - headers = ["Content-Type", "Authorization"], + origins = [], + headers = [], + methods = [], exposedHeaders = [], allowCredentials = false, - } = cors; + } = corsOptions; this.app.use((req, res, next) => { const responseHeaders: Record = {}; const method = diff --git a/libs/wingsdk/src/target-sim/api.ts b/libs/wingsdk/src/target-sim/api.ts index b404b79cb81..989617a4206 100644 --- a/libs/wingsdk/src/target-sim/api.ts +++ b/libs/wingsdk/src/target-sim/api.ts @@ -81,7 +81,7 @@ export class Api extends cloud.Api implements ISimulatorResource { ): void { this._validatePath(path); - this._addToSpec(path, method, undefined); + this._addToSpec(path, method, undefined, this.corsOptions); const fn = this.createOrGetFunction(inflight, props, path, method); Connections.of(this).add({ @@ -209,7 +209,7 @@ export class Api extends cloud.Api implements ISimulatorResource { path: this.node.path, props: { openApiSpec: this._getApiSpec(), - cors: this.corsEnabled ? this.corsOptions : undefined, + corsOptions: this.corsOptions, }, attrs: {} as any, }; diff --git a/libs/wingsdk/src/target-sim/schema-resources.ts b/libs/wingsdk/src/target-sim/schema-resources.ts index e1bc2b0b898..78068789d9a 100644 --- a/libs/wingsdk/src/target-sim/schema-resources.ts +++ b/libs/wingsdk/src/target-sim/schema-resources.ts @@ -1,4 +1,4 @@ -import { ApiCorsProps, HttpMethod, OpenApiSpec } from "../cloud"; +import { ApiCorsOptions, HttpMethod, OpenApiSpec } from "../cloud"; import { ColumnType } from "../ex"; import { Json } from "../std"; import { @@ -31,7 +31,7 @@ export interface ApiSchema extends BaseResourceSchema { readonly type: typeof API_TYPE; readonly props: { openApiSpec: OpenApiSpec; - cors?: ApiCorsProps; + corsOptions?: ApiCorsOptions; }; readonly attrs: ApiAttributes & BaseResourceAttributes; } diff --git a/libs/wingsdk/src/target-tf-aws/api.ts b/libs/wingsdk/src/target-tf-aws/api.ts index 40dd46cd00b..76ef7617ea2 100644 --- a/libs/wingsdk/src/target-tf-aws/api.ts +++ b/libs/wingsdk/src/target-tf-aws/api.ts @@ -40,10 +40,12 @@ const NAME_OPTS: NameOptions = { */ export class Api extends cloud.Api { private readonly api: WingRestApi; + constructor(scope: Construct, id: string, props: cloud.ApiProps = {}) { super(scope, id, props); this.api = new WingRestApi(this, "api", { apiSpec: this._getApiSpec(), + cors: this.corsOptions, }); } @@ -69,7 +71,7 @@ export class Api extends cloud.Api { const fn = this.addHandler(inflight); const apiSpecEndpoint = this.api.addEndpoint(path, "GET", fn); - this._addToSpec(path, "GET", apiSpecEndpoint); + this._addToSpec(path, "GET", apiSpecEndpoint, this.corsOptions); Connections.of(this).add({ source: this, @@ -96,7 +98,7 @@ export class Api extends cloud.Api { const fn = this.addHandler(inflight); const apiSpecEndpoint = this.api.addEndpoint(path, "POST", fn); - this._addToSpec(path, "POST", apiSpecEndpoint); + this._addToSpec(path, "POST", apiSpecEndpoint, this.corsOptions); Connections.of(this).add({ source: this, @@ -123,7 +125,7 @@ export class Api extends cloud.Api { const fn = this.addHandler(inflight); const apiSpecEndpoint = this.api.addEndpoint(path, "PUT", fn); - this._addToSpec(path, "PUT", apiSpecEndpoint); + this._addToSpec(path, "PUT", apiSpecEndpoint, this.corsOptions); Connections.of(this).add({ source: this, @@ -150,7 +152,7 @@ export class Api extends cloud.Api { const fn = this.addHandler(inflight); const apiSpecEndpoint = this.api.addEndpoint(path, "DELETE", fn); - this._addToSpec(path, "DELETE", apiSpecEndpoint); + this._addToSpec(path, "DELETE", apiSpecEndpoint, this.corsOptions); Connections.of(this).add({ source: this, @@ -177,7 +179,7 @@ export class Api extends cloud.Api { const fn = this.addHandler(inflight); const apiSpecEndpoint = this.api.addEndpoint(path, "PATCH", fn); - this._addToSpec(path, "PATCH", apiSpecEndpoint); + this._addToSpec(path, "PATCH", apiSpecEndpoint, this.corsOptions); Connections.of(this).add({ source: this, @@ -204,7 +206,7 @@ export class Api extends cloud.Api { const fn = this.addHandler(inflight); const apiSpecEndpoint = this.api.addEndpoint(path, "OPTIONS", fn); - this._addToSpec(path, "OPTIONS", apiSpecEndpoint); + this._addToSpec(path, "OPTIONS", apiSpecEndpoint, this.corsOptions); Connections.of(this).add({ source: this, @@ -231,7 +233,7 @@ export class Api extends cloud.Api { const fn = this.addHandler(inflight); const apiSpecEndpoint = this.api.addEndpoint(path, "HEAD", fn); - this._addToSpec(path, "HEAD", apiSpecEndpoint); + this._addToSpec(path, "HEAD", apiSpecEndpoint, this.corsOptions); Connections.of(this).add({ source: this, @@ -373,15 +375,19 @@ class WingRestApi extends Construct { public readonly stage: ApiGatewayStage; private readonly deployment: ApiGatewayDeployment; private readonly region: string; + private readonly cors?: cloud.ApiCorsOptions; + constructor( scope: Construct, id: string, props: { apiSpec: OpenApiSpec; + cors?: cloud.ApiCorsOptions; } ) { super(scope, id); + this.cors = props.cors; this.region = (App.of(this) as App).region; this.api = new ApiGatewayRestApi(this, "api", { name: ResourceNames.generateName(this, NAME_OPTS), @@ -440,6 +446,13 @@ class WingRestApi extends Construct { * @returns OpenApi extension object for the endpoint and handler */ private createApiSpecExtension(handler: Function) { + const cors = this.cors + ? { + "method.response.header.Access-Control-Allow-Origin": `'${this.cors.origins}'`, + "method.response.header.Access-Control-Allow-Methods": `'${this.cors.methods}'`, + "method.response.header.Access-Control-Allow-Headers": `'${this.cors.headers}'`, + } + : {}; const extension = { "x-amazon-apigateway-integration": { uri: `arn:aws:apigateway:${this.region}:lambda:path/2015-03-31/functions/${handler.arn}/invocations`, @@ -448,6 +461,7 @@ class WingRestApi extends Construct { responses: { default: { statusCode: "200", + responseParameters: cors, }, }, passthroughBehavior: "when_no_match", diff --git a/libs/wingsdk/test/target-tf-aws/__snapshots__/api.test.ts.snap b/libs/wingsdk/test/target-tf-aws/__snapshots__/api.test.ts.snap index b93c088794e..5bfd07b699f 100644 --- a/libs/wingsdk/test/target-tf-aws/__snapshots__/api.test.ts.snap +++ b/libs/wingsdk/test/target-tf-aws/__snapshots__/api.test.ts.snap @@ -12,6 +12,11 @@ exports[`api configured for cors 1`] = ` "200": { "content": {}, "description": "200 response", + "headers": { + "Access-Control-Allow-Headers": "'Content-Type,Authorization'", + "Access-Control-Allow-Methods": "'GET,POST,PUT,DELETE,HEAD,OPTIONS'", + "Access-Control-Allow-Origin": "'*'", + }, }, }, "x-amazon-apigateway-integration": { diff --git a/libs/wingsdk/test/target-tf-aws/api.test.ts b/libs/wingsdk/test/target-tf-aws/api.test.ts index 5a362fb1ee5..e012a7f15ae 100644 --- a/libs/wingsdk/test/target-tf-aws/api.test.ts +++ b/libs/wingsdk/test/target-tf-aws/api.test.ts @@ -316,3 +316,20 @@ test("api url can be used as environment variable", () => { tfConfig.resource.aws_lambda_function.Fn.environment.variables.API_URL ).toEqual("${aws_api_gateway_stage.Api_api_stage_E0FA39D6.invoke_url}"); }); + +test("api configured for cors", () => { + // GIVEN + const app = new tfaws.App({ outdir: mkdtemp() }); + const api = new Api(app, "Api", { cors: true }); + const handler = Testing.makeHandler(app, "Handler", INFLIGHT_CODE); + api.get("/", handler); + + const output = app.synth(); + + // THEN + const apiSpec = extractApiSpec(output); + expect(tfResourcesOfCount(output, "aws_api_gateway_rest_api")).toEqual(1); + expect(tfResourcesOfCount(output, "aws_lambda_function")).toEqual(1); + expect(Object.keys(apiSpec.paths["/"])).toStrictEqual(["get"]); + expect(apiSpec).toMatchSnapshot(); +}); From 19c968ee58e04f84d1b465c1407b979ac388516d Mon Sep 17 00:00:00 2001 From: Sebastian Korfmann Date: Fri, 25 Aug 2023 17:43:10 +0200 Subject: [PATCH 40/47] Refactor --- examples/tests/sdk_tests/api/cors.w | 22 +++ examples/tests/valid/api_cors_custom.w | 73 ++++++++ examples/tests/valid/api_cors_default.w | 50 ++++++ examples/tests/valid/cors_api.w | 132 --------------- libs/wingsdk/src/cloud/api.ts | 156 +++++++++++++++--- .../src/shared-aws/api.onrequest.inflight.ts | 18 +- libs/wingsdk/src/target-sim/api.inflight.ts | 41 ++--- libs/wingsdk/src/target-sim/api.ts | 2 +- .../src/target-sim/schema-resources.ts | 4 +- libs/wingsdk/src/target-tf-aws/api.ts | 17 +- libs/wingsdk/test/target-sim/api.test.ts | 4 +- 11 files changed, 309 insertions(+), 210 deletions(-) create mode 100644 examples/tests/sdk_tests/api/cors.w create mode 100644 examples/tests/valid/api_cors_custom.w create mode 100644 examples/tests/valid/api_cors_default.w delete mode 100644 examples/tests/valid/cors_api.w diff --git a/examples/tests/sdk_tests/api/cors.w b/examples/tests/sdk_tests/api/cors.w new file mode 100644 index 00000000000..9a3fe45378c --- /dev/null +++ b/examples/tests/sdk_tests/api/cors.w @@ -0,0 +1,22 @@ +bring cloud; +bring http; +bring util; + +let api = new cloud.Api({ + cors: true +}); +let body = "ok!"; + +api.get("/path", inflight (req) => { + return { + status: 200 + body: body + }; +}); + +test "http.get and http.fetch can preform a call to an api" { + let url = api.url + "/path"; + let response = http.get(url); + + assert(response.headers.get("access-control-allow-origin") == "*"); +} diff --git a/examples/tests/valid/api_cors_custom.w b/examples/tests/valid/api_cors_custom.w new file mode 100644 index 00000000000..1ac9986c817 --- /dev/null +++ b/examples/tests/valid/api_cors_custom.w @@ -0,0 +1,73 @@ +bring cloud; +bring ex; +bring http; +bring "./assertions.w" as t; + +let api = new cloud.Api( + cors: true, + corsOptions: { + allowOrigin: ["winglang.io"], + allowMethods: [cloud.HttpMethod.GET, cloud.HttpMethod.POST, cloud.HttpMethod.OPTIONS], + allowHeaders: ["Content-Type", "Authorization", "X-Custom-Header"], + allowCredentials: true, + exposeHeaders: ["Content-Type"] + } +) + +api.get("/users", inflight (req) => { + return { + body: "hello world", + status: 200 + }; +}); + +test "GET /users has cors headers" { + let response = http.get(api.url + "/users"); + + let headers = response.headers; + t.Assert.equalNum(response.status, 200); + + // GET cors headers are set + t.Assert.equalStr(headers.get("access-control-allow-origin"), "winglang.io"); + t.Assert.equalStr(headers.get("access-control-allow-credentials"), "true"); + t.Assert.equalStr(headers.get("access-control-expose-headers"), "Content-Type"); + + // OPTIONS cors headers are not set + t.Assert.isNil(headers.get("access-control-allow-headers")); + t.Assert.isNil(headers.get("access-control-allow-methods")); +} + +test "OPTIONS /users has cors headers" { + let response = http.fetch(apiCustomCors.url + "/users", { + method: http.HttpMethod.OPTIONS + }); + + let headers = response.headers; + + t.Assert.equalNum(response.status, 204); + + // OPTIONS cors headers are set + t.Assert.equalStr(headers.get("access-control-allow-methods"), "GET,POST,OPTIONS"); + t.Assert.equalStr(headers.get("access-control-allow-headers"), "Content-Type,Authorization,X-Custom-Header"); + + // Other cors headers are not set + t.Assert.equalStr(headers.get("access-control-allow-origin"), "winglang.io"); + t.Assert.equalStr(headers.get("access-control-expose-headers"), "Content-Type"); + t.Assert.equalStr(headers.get("access-control-allow-credentials"), "true"); +} + +test "OPTIONS /users responds with proper headers for requested" { + let response = http.fetch(apiCustomCors.url + "/users", { + method: http.HttpMethod.OPTIONS, + headers: { + "Content-Type": "text/json", + "Access-Control-Request-Method": "PUT", + "Access-Control-Request-Headers": "Content-Type,Authorization,X-Custom-Foo" + } + }); + + let headers = response.headers; + t.Assert.equalNum(response.status, 204); + t.Assert.equalStr(headers.get("access-control-allow-methods"), "GET,POST,OPTIONS"); + t.Assert.equalStr(headers.get("access-control-allow-headers"), "Content-Type,Authorization,X-Custom-Header"); +} \ No newline at end of file diff --git a/examples/tests/valid/api_cors_default.w b/examples/tests/valid/api_cors_default.w new file mode 100644 index 00000000000..05f7802f1c2 --- /dev/null +++ b/examples/tests/valid/api_cors_default.w @@ -0,0 +1,50 @@ +bring cloud; +bring ex; +bring http; +bring "./assertions.w" as t; + +let apiDefaultCors = new cloud.Api( + cors: true +); + +apiDefaultCors.get("/users", inflight (req) => { + return { + body: "hello world", + status: 200 + }; +}); + +test "GET /users has default cors headers" { + let response = http.get(apiDefaultCors.url + "/users"); + + let headers = response.headers; + t.Assert.equalNum(response.status, 200); + + // GET cors headers are set + t.Assert.equalStr(headers.get("access-control-allow-origin"), "*"); + t.Assert.equalStr(headers.get("access-control-allow-credentials"), "false"); + t.Assert.equalStr(headers.get("access-control-expose-headers"), ""); + + // OPTIONS headers are not set + t.Assert.isNil(headers.get("access-control-allow-headers")); + t.Assert.isNil(headers.get("access-control-allow-methods")); +} + +test "OPTIONS /users has default cors headers" { + let response = http.fetch(apiDefaultCors.url + "/users", { + method: http.HttpMethod.OPTIONS + }); + + let headers = response.headers; + t.Assert.equalNum(response.status, 204); + + // OPTIONS cors headers are set + t.Assert.equalStr(headers.get("access-control-allow-headers"), "Content-Type,Authorization"); + t.Assert.equalStr(headers.get("access-control-allow-methods"), "GET,POST,PUT,DELETE,HEAD,OPTIONS"); + + // Other headers are not set + t.Assert.isNil(headers.get("access-control-allow-origin")); + t.Assert.isNil(headers.get("access-control-allow-credentials")); + t.Assert.isNil(headers.get("access-control-expose-headers")); +} + diff --git a/examples/tests/valid/cors_api.w b/examples/tests/valid/cors_api.w deleted file mode 100644 index 5b54437a160..00000000000 --- a/examples/tests/valid/cors_api.w +++ /dev/null @@ -1,132 +0,0 @@ -bring cloud; -bring ex; -bring http; -bring "./assertions.w" as t; - - -// DEFAULT CORS -// ============ -// without specifying cors options, the default is to allow all origins -let apiDefaultCors = new cloud.Api( - cors: {} -); - -let getHandler = inflight (req) => { - return { - body: "hello world", - status: 200 - }; -}; - -apiDefaultCors.get("/users", getHandler); - -test "GET /users has default cors headers" { - let response = http.fetch(apiDefaultCors.url + "/users", { - method: http.HttpMethod.GET, - headers: { - "Content-Type": "text/json" - } - }); - -let headers = response.headers; - t.Assert.equalNum(response.status, 200); - t.Assert.equalStr(headers.get("access-control-allow-origin"), "*"); - t.Assert.equalStr(headers.get("access-control-allow-credentials"), "false"); - t.Assert.isNil(headers.get("access-control-allow-headers")); - - t.Assert.isNil(headers.get("access-control-expose-headers")); - t.Assert.isNil(headers.get("access-control-allow-methods")); -} - -test "OPTIONS /users has default cors headers" { - let response = http.fetch(apiDefaultCors.url + "/users", { - method: http.HttpMethod.OPTIONS, - headers: { - "Content-Type": "text/json" - } - }); - - let headers = response.headers; - t.Assert.equalNum(response.status, 204); - t.Assert.equalStr(headers.get("access-control-allow-origin"), "*"); - t.Assert.equalStr(headers.get("access-control-allow-methods"), "GET,HEAD,PUT,PATCH,POST,DELETE"); - t.Assert.equalStr(headers.get("access-control-allow-headers"), "Content-Type,Authorization"); - t.Assert.equalStr(headers.get("access-control-allow-credentials"), "false"); - t.Assert.isNil(headers.get("access-control-expose-headers")); -} - -// Custom CORS -// ============ -let apiCustomCors = new cloud.Api( - cors: { - origins: ["winglang.io"], - methods: [cloud.HttpMethod.GET, cloud.HttpMethod.POST, cloud.HttpMethod.OPTIONS], - headers: ["Content-Type", "Authorization", "X-Custom-Header"], - allowCredentials: true, - exposedHeaders: ["Content-Type"] - } -) as "apiCustomCors"; - -let getCustomHandler = inflight (req) => { - return { - body: "hello world", - status: 200 - }; -}; - -apiCustomCors.get("/users", getCustomHandler); - -test "GET /users has cors headers" { - let response = http.fetch(apiCustomCors.url + "/users", { - method: http.HttpMethod.GET, - headers: { - "Content-Type": "text/json" - } - }); - -let headers = response.headers; - t.Assert.equalNum(response.status, 200); - - t.Assert.equalStr(headers.get("access-control-allow-origin"), "winglang.io"); - t.Assert.equalStr(headers.get("access-control-allow-credentials"), "true"); - t.Assert.equalStr(headers.get("access-control-expose-headers"), "Content-Type"); - - t.Assert.isNil(headers.get("access-control-allow-headers")); - t.Assert.isNil(headers.get("access-control-allow-methods")); -} - -test "OPTIONS /users has cors headers" { - let response = http.fetch(apiCustomCors.url + "/users", { - method: http.HttpMethod.OPTIONS, - headers: { - "Content-Type": "text/json" - } - }); - - let headers = response.headers; - t.Assert.equalNum(response.status, 204); - t.Assert.equalStr(headers.get("access-control-allow-origin"), "winglang.io"); - t.Assert.equalStr(headers.get("access-control-allow-methods"), "GET,POST,OPTIONS"); - t.Assert.equalStr(headers.get("access-control-allow-headers"), "Content-Type,Authorization,X-Custom-Header"); - t.Assert.equalStr(headers.get("access-control-allow-credentials"), "true"); - t.Assert.equalStr(headers.get("access-control-expose-headers"), "Content-Type"); -} - -test "OPTIONS /users responds with proper headers for requested" { - let response = http.fetch(apiCustomCors.url + "/users", { - method: http.HttpMethod.OPTIONS, - headers: { - "Content-Type": "text/json", - "Access-Control-Request-Method": "PUT", - "Access-Control-Request-Headers": "Content-Type,Authorization,X-Custom-Foo" - } - }); - - let headers = response.headers; - t.Assert.equalNum(response.status, 204); - t.Assert.equalStr(headers.get("access-control-allow-origin"), "winglang.io"); - t.Assert.equalStr(headers.get("access-control-allow-methods"), "GET,POST,OPTIONS"); - t.Assert.equalStr(headers.get("access-control-allow-headers"), "Content-Type,Authorization,X-Custom-Header"); - t.Assert.equalStr(headers.get("access-control-allow-credentials"), "true"); - t.Assert.equalStr(headers.get("access-control-expose-headers"), "Content-Type"); -} \ No newline at end of file diff --git a/libs/wingsdk/src/cloud/api.ts b/libs/wingsdk/src/cloud/api.ts index cb570538da1..dfc53d4ffdd 100644 --- a/libs/wingsdk/src/cloud/api.ts +++ b/libs/wingsdk/src/cloud/api.ts @@ -14,32 +14,32 @@ export const API_FQN = fqnForType("cloud.Api"); */ export interface ApiCorsOptions { /** - * The list of allowed origins. + * The list of allowed allowOrigin. * @example ["https://example.com"] * @default - ["*"] */ - readonly origins?: Array; + readonly allowOrigin?: Array; /** * The list of allowed methods. * @example [HttpMethod.GET, HttpMethod.POST] * @default - [HttpMethod.GET, HttpMethod.POST, HttpMethod.PUT, HttpMethod.PATCH, HttpMethod.DELETE, HttpMethod.HEAD, HttpMethod.OPTIONS] */ - readonly methods?: Array; + readonly allowMethods?: Array; /** * The list of allowed headers. * @example ["Content-Type"] * @default - ["Content-Type", "Authorization"] */ - readonly headers?: Array; + readonly allowHeaders?: Array; /** * The list of exposed headers. * @example ["Content-Type"] * @default - [] */ - readonly exposedHeaders?: Array; + readonly exposeHeaders?: Array; /** * Whether to allow credentials. @@ -67,10 +67,10 @@ export interface ApiProps { * Options for configuring the API's CORS behavior across all routes. * Options can also be overridden on a per-route basis. (not yet implemented) * - * @example { origins: ["https://example.com"] } + * @example { allowOrigin: ["https://example.com"] } * @default - Default CORS options are applied when `cors` is set to `true` - * origins: ["*"], - * methods: [ + * allowOrigin: ["*"], + * allowMethods: [ * HttpMethod.GET, * HttpMethod.POST, * HttpMethod.PUT, @@ -78,8 +78,8 @@ export interface ApiProps { * HttpMethod.HEAD, * HttpMethod.OPTIONS, * ], - * headers: ["Content-Type", "Authorization"], - * exposedHeaders: [], + * allowHeaders: ["Content-Type", "Authorization"], + * exposeHeaders: [], * allowCredentials: false, * */ @@ -97,6 +97,59 @@ export type OpenApiSpec = any; * */ export type OpenApiSpecExtension = any; +/** + * The OpenAPI spec for CORS headers. + * */ +export type OpenApiCorsHeaders = Record; + +/** + * Type definition for default CORS headers. + */ +type CorsDefaultResponseHeaders = { + /** + * Specifies the origin that is allowed to access the resource. + * https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin + */ + "Access-Control-Allow-Origin": string; + + /** + * Lists the headers that the client can access. + * https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Expose-Headers + */ + "Access-Control-Expose-Headers": string; + + /** + * Indicates whether the response to the request can + * be exposed when the credentials flag is true. + * https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Credentials + */ + "Access-Control-Allow-Credentials": string; +}; + +/** + * Type definition for CORS option headers. + */ +type CorsOptionsResponseHeaders = { + /** + * Specifies the headers that are allowed in a request. + * https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Headers + */ + "Access-Control-Allow-Headers": string; + + /** + * Specifies the methods that are allowed in a request. + * https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Methods + */ + "Access-Control-Allow-Methods": string; +}; + +/** + * Type definition for CORS headers which includes default and options headers. + */ +export type CorsHeaders = { + defaultResponse: CorsDefaultResponseHeaders; // Default CORS headers for all requests. + optionsResponse: CorsOptionsResponseHeaders; // CORS option headers for OPTIONS requests. +}; /** * Functionality shared between all `Api` implementations. * @inflight `@winglang/sdk.cloud.IApiClient` @@ -127,8 +180,8 @@ export abstract class Api extends Resource { }; private corsDefaultValues: ApiCorsOptions = { - origins: ["*"], - methods: [ + allowOrigin: ["*"], + allowMethods: [ HttpMethod.GET, HttpMethod.POST, HttpMethod.PUT, @@ -136,8 +189,8 @@ export abstract class Api extends Resource { HttpMethod.HEAD, HttpMethod.OPTIONS, ], - headers: ["Content-Type", "Authorization"], - exposedHeaders: [], + allowHeaders: ["Content-Type", "Authorization", "X-Requested-With"], + exposeHeaders: [], allowCredentials: false, }; @@ -281,6 +334,64 @@ export abstract class Api extends Resource { }; } + /** + * Generates the OpenAPI schema for CORS headers based on the provided CORS options. + * @param corsOptions The CORS options to generate the schema from. + * @returns An object representing the OpenAPI schema for CORS headers. + */ + private _corsOpenApiSchema(corsOptions?: ApiCorsOptions): OpenApiCorsHeaders { + const corsHeaders: OpenApiCorsHeaders = {}; + if (corsOptions) { + const corsHeaderSchema = { + schema: { + type: "string", + }, + }; + corsHeaders["Access-Control-Allow-Origin"] = corsHeaderSchema; + corsHeaders["Access-Control-Allow-Methods"] = corsHeaderSchema; + corsHeaders["Access-Control-Allow-Headers"] = corsHeaderSchema; + } + return corsHeaders; + } + + /** + * Generates an object containing default CORS response headers and OPTIONS response headers. + * @param corsOptions The CORS options to generate the headers from. + * @returns An object containing default CORS response headers and OPTIONS response headers. + * @internal + */ + protected _generateCorsHeaders( + corsOptions?: ApiCorsOptions + ): CorsHeaders | undefined { + if (corsOptions == undefined) { + return; + } + + const { + allowOrigin = [], + allowHeaders = [], + allowMethods = [], + exposeHeaders = [], + allowCredentials = false, + } = corsOptions; + + const defaultHeaders: CorsDefaultResponseHeaders = { + "Access-Control-Allow-Origin": allowOrigin.join(",") || "", + "Access-Control-Expose-Headers": exposeHeaders.join(",") || "", + "Access-Control-Allow-Credentials": allowCredentials ? "true" : "false", + }; + + const optionsHeaders: CorsOptionsResponseHeaders = { + "Access-Control-Allow-Headers": allowHeaders.join(",") || "", + "Access-Control-Allow-Methods": allowMethods.join(",") || "", + }; + + return { + defaultResponse: defaultHeaders, + optionsResponse: optionsHeaders, + }; + } + /** * Add a route to the api spec. * @param path The path to add. @@ -318,6 +429,7 @@ export abstract class Api extends Resource { }); }); } + const corsOpenApiSchema = this._corsOpenApiSchema(corsOptions); const methodSpec = { [method.toLowerCase()]: { operationId: operationId, @@ -325,19 +437,9 @@ export abstract class Api extends Resource { "200": { description: "200 response", content: {}, - ...(corsOptions && { - headers: { - "Access-Control-Allow-Origin": `'${corsOptions.origins?.join( - "," - )}'`, - "Access-Control-Allow-Methods": `'${corsOptions.methods?.join( - "," - )}'`, - "Access-Control-Allow-Headers": `'${corsOptions.headers?.join( - "," - )}'`, - }, - }), + ...(Object.keys(corsOpenApiSchema).length > 0 + ? { headers: corsOpenApiSchema } + : {}), }, }, parameters: pathParameters, diff --git a/libs/wingsdk/src/shared-aws/api.onrequest.inflight.ts b/libs/wingsdk/src/shared-aws/api.onrequest.inflight.ts index ca24488382b..41568577a6b 100644 --- a/libs/wingsdk/src/shared-aws/api.onrequest.inflight.ts +++ b/libs/wingsdk/src/shared-aws/api.onrequest.inflight.ts @@ -9,8 +9,18 @@ import { export class ApiOnRequestHandlerClient { private readonly handler: IApiEndpointHandlerClient; - constructor({ handler }: { handler: IApiEndpointHandlerClient }) { + private readonly corsHeaders?: Record; + constructor({ + handler, + args, + }: { + handler: IApiEndpointHandlerClient; + args: { + corsHeaders?: Record; + }; + }) { this.handler = handler; + this.corsHeaders = args.corsHeaders; } public async handle( request: APIGatewayProxyEvent @@ -18,7 +28,7 @@ export class ApiOnRequestHandlerClient { const apiRequest: ApiRequest = mapApigatewayEventToCloudApiRequest(request); const apiResponse: ApiResponse = await this.handler.handle(apiRequest); const apiGatewayResponse: APIGatewayProxyResult = - mapCloudApiResponseToApigatewayResponse(apiResponse); + mapCloudApiResponseToApigatewayResponse(apiResponse, this.corsHeaders); return apiGatewayResponse; } } @@ -29,13 +39,15 @@ export class ApiOnRequestHandlerClient { * @returns API Gateway response */ function mapCloudApiResponseToApigatewayResponse( - resp: ApiResponse + resp: ApiResponse, + corsHeaders?: Record ): APIGatewayProxyResult { return { statusCode: resp.status, body: resp.body ?? "", headers: { "Content-Type": "application/json", + ...corsHeaders, ...resp.headers, }, }; diff --git a/libs/wingsdk/src/target-sim/api.inflight.ts b/libs/wingsdk/src/target-sim/api.inflight.ts index 3697d39ea97..d9913d50e79 100644 --- a/libs/wingsdk/src/target-sim/api.inflight.ts +++ b/libs/wingsdk/src/target-sim/api.inflight.ts @@ -38,7 +38,7 @@ export class Api props; this.routes = []; this.context = context; - const { corsOptions } = props; + const { corsHeaders } = props; // Set up an express server that handles the routes. this.app = express(); @@ -49,44 +49,23 @@ export class Api this.app.use(express.text({ limit: "10mb", type: "*/*" })); // Set up CORS headers for options requests. - if (corsOptions) { - const { - origins = [], - headers = [], - methods = [], - exposedHeaders = [], - allowCredentials = false, - } = corsOptions; + if (corsHeaders) { this.app.use((req, res, next) => { - const responseHeaders: Record = {}; const method = req.method && req.method.toUpperCase && req.method.toUpperCase(); - if (origins && origins.length > 0) { - responseHeaders["Access-Control-Allow-Origin"] = origins.join(","); - } - if (exposedHeaders && exposedHeaders.length > 0) { - responseHeaders["Access-Control-Expose-Headers"] = - exposedHeaders.join(","); - } - responseHeaders["Access-Control-Allow-Credentials"] = allowCredentials - ? "true" - : "false"; - if (method === "OPTIONS") { - if (headers && headers.length > 0) { - responseHeaders["Access-Control-Allow-Headers"] = headers.join(","); - } - if (methods && methods.length > 0) { - responseHeaders["Access-Control-Allow-Methods"] = methods.join(","); - } - for (const key of Object.keys(responseHeaders)) { - res.setHeader(key, responseHeaders[key]); + for (const [key, value] of Object.entries( + corsHeaders.optionsResponse + )) { + res.setHeader(key, value); } res.status(204).send(); } else { - for (const key of Object.keys(responseHeaders)) { - res.setHeader(key, responseHeaders[key]); + for (const [key, value] of Object.entries( + corsHeaders.defaultResponse + )) { + res.setHeader(key, value); } next(); } diff --git a/libs/wingsdk/src/target-sim/api.ts b/libs/wingsdk/src/target-sim/api.ts index 28981224a74..96bbb91287e 100644 --- a/libs/wingsdk/src/target-sim/api.ts +++ b/libs/wingsdk/src/target-sim/api.ts @@ -207,7 +207,7 @@ export class Api extends cloud.Api implements ISimulatorResource { path: this.node.path, props: { openApiSpec: this._getApiSpec(), - corsOptions: this.corsOptions, + corsHeaders: this._generateCorsHeaders(this.corsOptions), }, attrs: {} as any, }; diff --git a/libs/wingsdk/src/target-sim/schema-resources.ts b/libs/wingsdk/src/target-sim/schema-resources.ts index 78068789d9a..bd4f5a8e57f 100644 --- a/libs/wingsdk/src/target-sim/schema-resources.ts +++ b/libs/wingsdk/src/target-sim/schema-resources.ts @@ -1,4 +1,4 @@ -import { ApiCorsOptions, HttpMethod, OpenApiSpec } from "../cloud"; +import { CorsHeaders, HttpMethod, OpenApiSpec } from "../cloud"; import { ColumnType } from "../ex"; import { Json } from "../std"; import { @@ -31,7 +31,7 @@ export interface ApiSchema extends BaseResourceSchema { readonly type: typeof API_TYPE; readonly props: { openApiSpec: OpenApiSpec; - corsOptions?: ApiCorsOptions; + corsHeaders?: CorsHeaders; }; readonly attrs: ApiAttributes & BaseResourceAttributes; } diff --git a/libs/wingsdk/src/target-tf-aws/api.ts b/libs/wingsdk/src/target-tf-aws/api.ts index cb3ebe64f1e..14d385484e1 100644 --- a/libs/wingsdk/src/target-tf-aws/api.ts +++ b/libs/wingsdk/src/target-tf-aws/api.ts @@ -325,7 +325,11 @@ export class Api extends cloud.Api { __dirname.replace("target-tf-aws", "shared-aws"), "api.onrequest.inflight.js" ), - "ApiOnRequestHandlerClient" + "ApiOnRequestHandlerClient", + { + corsHeaders: this._generateCorsHeaders(this.corsOptions) + ?.defaultResponse, + } ); return Function._newFunction( this, @@ -373,7 +377,6 @@ class WingRestApi extends Construct { public readonly stage: ApiGatewayStage; private readonly deployment: ApiGatewayDeployment; private readonly region: string; - private readonly cors?: cloud.ApiCorsOptions; constructor( scope: Construct, @@ -384,8 +387,6 @@ class WingRestApi extends Construct { } ) { super(scope, id); - - this.cors = props.cors; this.region = (App.of(this) as App).region; this.api = new ApiGatewayRestApi(this, "api", { name: ResourceNames.generateName(this, NAME_OPTS), @@ -444,13 +445,6 @@ class WingRestApi extends Construct { * @returns OpenApi extension object for the endpoint and handler */ private createApiSpecExtension(handler: Function) { - const cors = this.cors - ? { - "method.response.header.Access-Control-Allow-Origin": `'${this.cors.origins}'`, - "method.response.header.Access-Control-Allow-Methods": `'${this.cors.methods}'`, - "method.response.header.Access-Control-Allow-Headers": `'${this.cors.headers}'`, - } - : {}; const extension = { "x-amazon-apigateway-integration": { uri: `arn:aws:apigateway:${this.region}:lambda:path/2015-03-31/functions/${handler.arn}/invocations`, @@ -459,7 +453,6 @@ class WingRestApi extends Construct { responses: { default: { statusCode: "200", - responseParameters: cors, }, }, passthroughBehavior: "when_no_match", diff --git a/libs/wingsdk/test/target-sim/api.test.ts b/libs/wingsdk/test/target-sim/api.test.ts index a259a02a93f..2e0ddcffcc1 100644 --- a/libs/wingsdk/test/target-sim/api.test.ts +++ b/libs/wingsdk/test/target-sim/api.test.ts @@ -549,9 +549,9 @@ test("api with custom CORS settings", async () => { const api = cloud.Api._newApi(app, "my_api", { cors: true, corsOptions: { - origins: ["https://example.com"], + allowOrigin: ["https://example.com"], allowCredentials: true, - exposedHeaders: ["x-wingnuts"], + exposeHeaders: ["x-wingnuts"], }, }); const inflight = Testing.makeHandler(app, "Handler", INFLIGHT_CODE(RESPONSE)); From dad395e9762603531658ad31faf819ab3d1620ba Mon Sep 17 00:00:00 2001 From: Sebastian Korfmann Date: Sat, 26 Aug 2023 10:18:48 +0200 Subject: [PATCH 41/47] Fix tests --- .../src/shared-aws/api.onrequest.inflight.ts | 4 +- .../shared-aws/api.onrequest.inflight.test.ts | 43 ++++++++++++++++++ libs/wingsdk/test/target-sim/api.test.ts | 26 +++++++++++ .../__snapshots__/api.test.ts.snap | 44 +++++++------------ .../__snapshots__/tokens.test.ts.snap | 4 +- 5 files changed, 88 insertions(+), 33 deletions(-) diff --git a/libs/wingsdk/src/shared-aws/api.onrequest.inflight.ts b/libs/wingsdk/src/shared-aws/api.onrequest.inflight.ts index 41568577a6b..3e42f414d23 100644 --- a/libs/wingsdk/src/shared-aws/api.onrequest.inflight.ts +++ b/libs/wingsdk/src/shared-aws/api.onrequest.inflight.ts @@ -15,12 +15,12 @@ export class ApiOnRequestHandlerClient { args, }: { handler: IApiEndpointHandlerClient; - args: { + args?: { corsHeaders?: Record; }; }) { this.handler = handler; - this.corsHeaders = args.corsHeaders; + this.corsHeaders = args?.corsHeaders; } public async handle( request: APIGatewayProxyEvent diff --git a/libs/wingsdk/test/shared-aws/api.onrequest.inflight.test.ts b/libs/wingsdk/test/shared-aws/api.onrequest.inflight.test.ts index 85fd6943640..108e3fcbd0f 100644 --- a/libs/wingsdk/test/shared-aws/api.onrequest.inflight.test.ts +++ b/libs/wingsdk/test/shared-aws/api.onrequest.inflight.test.ts @@ -157,6 +157,49 @@ describe("ApiResponseMapper", () => { }, }); }); + + test("inject cors response headers", async () => { + // GIVEN + const apiRequestEvent: Partial = { + body: JSON.stringify({}), + headers: {}, + path: "/", + httpMethod: "GET", + }; + + const handlerResponse: ApiResponse = { + status: 200, + body: JSON.stringify({ key: "value" }), + }; + const requestHandlerClient = new ApiOnRequestHandlerClient({ + handler: { + handle: async () => { + return handlerResponse; + }, + }, + args: { + corsHeaders: { + "Access-Control-Allow-Origin": "*", + }, + }, + }); + + // WHEN + const response = await requestHandlerClient.handle( + apiRequestEvent as APIGatewayProxyEvent + ); + + // THEN + + expect(response).toEqual({ + statusCode: 200, + body: JSON.stringify({ key: "value" }), + headers: { + "Content-Type": "application/json", + "Access-Control-Allow-Origin": "*", + }, + }); + }); }); describe("ApiRequest", () => { diff --git a/libs/wingsdk/test/target-sim/api.test.ts b/libs/wingsdk/test/target-sim/api.test.ts index 2e0ddcffcc1..ef177c2aa60 100644 --- a/libs/wingsdk/test/target-sim/api.test.ts +++ b/libs/wingsdk/test/target-sim/api.test.ts @@ -577,3 +577,29 @@ test("api with custom CORS settings", async () => { "x-wingnuts" ); }); + +test("api with CORS settings responds to OPTIONS request", async () => { + // GIVEN + const ROUTE = "/hello"; + + const app = new SimApp(); + const api = cloud.Api._newApi(app, "my_api", { + cors: true, + }); + + // WHEN + const s = await app.startSimulator(); + const apiUrl = getApiUrl(s, "/my_api"); + const response = await fetch(apiUrl + ROUTE, { method: "OPTIONS" }); + + // THEN + await s.stop(); + + expect(response.status).toEqual(204); + expect(response.headers.get("access-control-allow-headers")).toEqual( + "Content-Type,Authorization,X-Requested-With" + ); + expect(response.headers.get("access-control-allow-methods")).toEqual( + "GET,POST,PUT,DELETE,HEAD,OPTIONS" + ); +}); diff --git a/libs/wingsdk/test/target-tf-aws/__snapshots__/api.test.ts.snap b/libs/wingsdk/test/target-tf-aws/__snapshots__/api.test.ts.snap index 5bfd07b699f..ef72c3e046d 100644 --- a/libs/wingsdk/test/target-tf-aws/__snapshots__/api.test.ts.snap +++ b/libs/wingsdk/test/target-tf-aws/__snapshots__/api.test.ts.snap @@ -13,9 +13,21 @@ exports[`api configured for cors 1`] = ` "content": {}, "description": "200 response", "headers": { - "Access-Control-Allow-Headers": "'Content-Type,Authorization'", - "Access-Control-Allow-Methods": "'GET,POST,PUT,DELETE,HEAD,OPTIONS'", - "Access-Control-Allow-Origin": "'*'", + "Access-Control-Allow-Headers": { + "schema": { + "type": "string", + }, + }, + "Access-Control-Allow-Methods": { + "schema": { + "type": "string", + }, + }, + "Access-Control-Allow-Origin": { + "schema": { + "type": "string", + }, + }, }, }, }, @@ -25,11 +37,6 @@ exports[`api configured for cors 1`] = ` "passthroughBehavior": "when_no_match", "responses": { "default": { - "responseParameters": { - "method.response.header.Access-Control-Allow-Headers": "'Content-Type,Authorization'", - "method.response.header.Access-Control-Allow-Methods": "'GET,POST,PUT,DELETE,HEAD,OPTIONS'", - "method.response.header.Access-Control-Allow-Origin": "'*'", - }, "statusCode": "200", }, }, @@ -62,7 +69,6 @@ exports[`api with CONNECT route 1`] = ` "passthroughBehavior": "when_no_match", "responses": { "default": { - "responseParameters": {}, "statusCode": "200", }, }, @@ -95,7 +101,6 @@ exports[`api with DELETE route 1`] = ` "passthroughBehavior": "when_no_match", "responses": { "default": { - "responseParameters": {}, "statusCode": "200", }, }, @@ -128,7 +133,6 @@ exports[`api with GET route at root 1`] = ` "passthroughBehavior": "when_no_match", "responses": { "default": { - "responseParameters": {}, "statusCode": "200", }, }, @@ -161,7 +165,6 @@ exports[`api with GET routes with common prefix 1`] = ` "passthroughBehavior": "when_no_match", "responses": { "default": { - "responseParameters": {}, "statusCode": "200", }, }, @@ -186,7 +189,6 @@ exports[`api with GET routes with common prefix 1`] = ` "passthroughBehavior": "when_no_match", "responses": { "default": { - "responseParameters": {}, "statusCode": "200", }, }, @@ -219,7 +221,6 @@ exports[`api with GET routes with different prefix 1`] = ` "passthroughBehavior": "when_no_match", "responses": { "default": { - "responseParameters": {}, "statusCode": "200", }, }, @@ -244,7 +245,6 @@ exports[`api with GET routes with different prefix 1`] = ` "passthroughBehavior": "when_no_match", "responses": { "default": { - "responseParameters": {}, "statusCode": "200", }, }, @@ -277,7 +277,6 @@ exports[`api with HEAD route 1`] = ` "passthroughBehavior": "when_no_match", "responses": { "default": { - "responseParameters": {}, "statusCode": "200", }, }, @@ -310,7 +309,6 @@ exports[`api with OPTIONS route 1`] = ` "passthroughBehavior": "when_no_match", "responses": { "default": { - "responseParameters": {}, "statusCode": "200", }, }, @@ -343,7 +341,6 @@ exports[`api with PATCH route 1`] = ` "passthroughBehavior": "when_no_match", "responses": { "default": { - "responseParameters": {}, "statusCode": "200", }, }, @@ -376,7 +373,6 @@ exports[`api with POST route 1`] = ` "passthroughBehavior": "when_no_match", "responses": { "default": { - "responseParameters": {}, "statusCode": "200", }, }, @@ -409,7 +405,6 @@ exports[`api with PUT route 1`] = ` "passthroughBehavior": "when_no_match", "responses": { "default": { - "responseParameters": {}, "statusCode": "200", }, }, @@ -442,7 +437,6 @@ exports[`api with multiple GET route and one lambda 1`] = ` "passthroughBehavior": "when_no_match", "responses": { "default": { - "responseParameters": {}, "statusCode": "200", }, }, @@ -467,7 +461,6 @@ exports[`api with multiple GET route and one lambda 1`] = ` "passthroughBehavior": "when_no_match", "responses": { "default": { - "responseParameters": {}, "statusCode": "200", }, }, @@ -500,7 +493,6 @@ exports[`api with multiple methods and multiple lambda 1`] = ` "passthroughBehavior": "when_no_match", "responses": { "default": { - "responseParameters": {}, "statusCode": "200", }, }, @@ -525,7 +517,6 @@ exports[`api with multiple methods and multiple lambda 1`] = ` "passthroughBehavior": "when_no_match", "responses": { "default": { - "responseParameters": {}, "statusCode": "200", }, }, @@ -558,7 +549,6 @@ exports[`api with multiple methods and one lambda 1`] = ` "passthroughBehavior": "when_no_match", "responses": { "default": { - "responseParameters": {}, "statusCode": "200", }, }, @@ -583,7 +573,6 @@ exports[`api with multiple methods and one lambda 1`] = ` "passthroughBehavior": "when_no_match", "responses": { "default": { - "responseParameters": {}, "statusCode": "200", }, }, @@ -616,7 +605,6 @@ exports[`api with multiple methods on same route 1`] = ` "passthroughBehavior": "when_no_match", "responses": { "default": { - "responseParameters": {}, "statusCode": "200", }, }, @@ -639,7 +627,6 @@ exports[`api with multiple methods on same route 1`] = ` "passthroughBehavior": "when_no_match", "responses": { "default": { - "responseParameters": {}, "statusCode": "200", }, }, @@ -681,7 +668,6 @@ exports[`api with path parameter 1`] = ` "passthroughBehavior": "when_no_match", "responses": { "default": { - "responseParameters": {}, "statusCode": "200", }, }, diff --git a/libs/wingsdk/test/target-tf-aws/__snapshots__/tokens.test.ts.snap b/libs/wingsdk/test/target-tf-aws/__snapshots__/tokens.test.ts.snap index 361628301cb..9079917bca1 100644 --- a/libs/wingsdk/test/target-tf-aws/__snapshots__/tokens.test.ts.snap +++ b/libs/wingsdk/test/target-tf-aws/__snapshots__/tokens.test.ts.snap @@ -39,13 +39,13 @@ exports[`captures tokens 2`] = ` }, "rest_api_id": "\${aws_api_gateway_rest_api.Api_api_91C07D84.id}", "triggers": { - "redeployment": "0c985ec4b2e80179d31281ba3e16595d5e75a9cd", + "redeployment": "5c0f11f0478884e3d7859fa987b8b7ecf8f2f6bc", }, }, }, "aws_api_gateway_rest_api": { "Api_api_91C07D84": { - "body": "{\\"openapi\\":\\"3.0.3\\",\\"paths\\":{\\"/\\":{\\"get\\":{\\"operationId\\":\\"get\\",\\"responses\\":{\\"200\\":{\\"description\\":\\"200 response\\",\\"content\\":{}}},\\"parameters\\":[],\\"x-amazon-apigateway-integration\\":{\\"uri\\":\\"arn:aws:apigateway:\${data.aws_region.Region.name}:lambda:path/2015-03-31/functions/\${aws_lambda_function.Api_Api-OnRequest-c5395e41_37F21C2B.arn}/invocations\\",\\"type\\":\\"aws_proxy\\",\\"httpMethod\\":\\"POST\\",\\"responses\\":{\\"default\\":{\\"statusCode\\":\\"200\\",\\"responseParameters\\":{}}},\\"passthroughBehavior\\":\\"when_no_match\\",\\"contentHandling\\":\\"CONVERT_TO_TEXT\\"}}}}}", + "body": "{\\"openapi\\":\\"3.0.3\\",\\"paths\\":{\\"/\\":{\\"get\\":{\\"operationId\\":\\"get\\",\\"responses\\":{\\"200\\":{\\"description\\":\\"200 response\\",\\"content\\":{}}},\\"parameters\\":[],\\"x-amazon-apigateway-integration\\":{\\"uri\\":\\"arn:aws:apigateway:\${data.aws_region.Region.name}:lambda:path/2015-03-31/functions/\${aws_lambda_function.Api_Api-OnRequest-c5395e41_37F21C2B.arn}/invocations\\",\\"type\\":\\"aws_proxy\\",\\"httpMethod\\":\\"POST\\",\\"responses\\":{\\"default\\":{\\"statusCode\\":\\"200\\"}},\\"passthroughBehavior\\":\\"when_no_match\\",\\"contentHandling\\":\\"CONVERT_TO_TEXT\\"}}}}}", "name": "api-c8f613f0", }, }, From f38dad5fa9c5b7f10693ee0c29813e58b4620edc Mon Sep 17 00:00:00 2001 From: Sebastian Korfmann Date: Mon, 4 Sep 2023 13:37:50 +0000 Subject: [PATCH 42/47] Fix tests --- docs/docs/04-standard-library/01-cloud/api.md | 96 ++- examples/tests/valid/api_cors_custom.w | 12 +- examples/tests/valid/website_with_api.w | 11 +- .../incomplete_inflight_namespace.snap | 8 +- .../completions/namespace_middle_dot.snap | 8 +- .../variable_type_annotation_namespace.snap | 8 +- libs/wingsdk/src/cloud/api.ts | 3 + .../__snapshots__/api.test.ts.snap | 86 +++ .../valid/api_cors_custom.w_compile_tf-aws.md | 727 ++++++++++++++++++ .../valid/api_cors_custom.w_test_sim.md | 14 + .../api_cors_default.w_compile_tf-aws.md | 598 ++++++++++++++ .../valid/assertions.w_compile_tf-aws.md | 21 +- .../website_with_api.w_compile_tf-aws.md | 386 +++++++--- .../valid/website_with_api.w_test_sim.md | 2 +- 14 files changed, 1821 insertions(+), 159 deletions(-) create mode 100644 tools/hangar/__snapshots__/test_corpus/valid/api_cors_custom.w_compile_tf-aws.md create mode 100644 tools/hangar/__snapshots__/test_corpus/valid/api_cors_custom.w_test_sim.md create mode 100644 tools/hangar/__snapshots__/test_corpus/valid/api_cors_default.w_compile_tf-aws.md diff --git a/docs/docs/04-standard-library/01-cloud/api.md b/docs/docs/04-standard-library/01-cloud/api.md index c525141b6dd..1f3a0d77309 100644 --- a/docs/docs/04-standard-library/01-cloud/api.md +++ b/docs/docs/04-standard-library/01-cloud/api.md @@ -467,31 +467,31 @@ let ApiConnectProps = cloud.ApiConnectProps{ ... }; ``` -### ApiCorsProps +### ApiCorsOptions Cors Options for `Api`. -#### Initializer +#### Initializer ```wing bring cloud; -let ApiCorsProps = cloud.ApiCorsProps{ ... }; +let ApiCorsOptions = cloud.ApiCorsOptions{ ... }; ``` #### Properties | **Name** | **Type** | **Description** | | --- | --- | --- | -| allowCredentials | bool | Whether to allow credentials. | -| exposedHeaders | MutArray<str> | The list of exposed headers. | -| headers | MutArray<str> | The list of allowed headers. | -| methods | MutArray<HttpMethod> | The list of allowed methods. | -| origins | MutArray<str> | The list of allowed origins. | +| allowCredentials | bool | Whether to allow credentials. | +| allowHeaders | MutArray<str> | The list of allowed headers. | +| allowMethods | MutArray<HttpMethod> | The list of allowed methods. | +| allowOrigin | MutArray<str> | The list of allowed allowOrigin. | +| exposeHeaders | MutArray<str> | The list of exposed headers. | --- -##### `allowCredentials`Optional +##### `allowCredentials`Optional ```wing allowCredentials: bool; @@ -504,16 +504,16 @@ Whether to allow credentials. --- -##### `exposedHeaders`Optional +##### `allowHeaders`Optional ```wing -exposedHeaders: MutArray; +allowHeaders: MutArray; ``` - *Type:* MutArray<str> -- *Default:* [] +- *Default:* ["Content-Type", "Authorization"] -The list of exposed headers. +The list of allowed headers. --- @@ -524,63 +524,63 @@ The list of exposed headers. ``` -##### `headers`Optional +##### `allowMethods`Optional ```wing -headers: MutArray; +allowMethods: MutArray; ``` -- *Type:* MutArray<str> -- *Default:* ["Content-Type", "Authorization"] +- *Type:* MutArray<HttpMethod> +- *Default:* [HttpMethod.GET, HttpMethod.POST, HttpMethod.PUT, HttpMethod.PATCH, HttpMethod.DELETE, HttpMethod.HEAD, HttpMethod.OPTIONS] -The list of allowed headers. +The list of allowed methods. --- *Example* ```wing -["Content-Type"] +[HttpMethod.GET, HttpMethod.POST] ``` -##### `methods`Optional +##### `allowOrigin`Optional ```wing -methods: MutArray; +allowOrigin: MutArray; ``` -- *Type:* MutArray<HttpMethod> -- *Default:* [HttpMethod.GET, HttpMethod.POST, HttpMethod.PUT, HttpMethod.PATCH, HttpMethod.DELETE, HttpMethod.HEAD, HttpMethod.OPTIONS] +- *Type:* MutArray<str> +- *Default:* ["*"] -The list of allowed methods. +The list of allowed allowOrigin. --- *Example* ```wing -[HttpMethod.GET, HttpMethod.POST] +["https://example.com"] ``` -##### `origins`Optional +##### `exposeHeaders`Optional ```wing -origins: MutArray; +exposeHeaders: MutArray; ``` - *Type:* MutArray<str> -- *Default:* ["*"] +- *Default:* [] -The list of allowed origins. +The list of exposed headers. --- *Example* ```wing -["https://example.com"] +["Content-Type"] ``` @@ -678,30 +678,54 @@ let ApiProps = cloud.ApiProps{ ... }; | **Name** | **Type** | **Description** | | --- | --- | --- | -| cors | ApiCorsProps | Options for configuring the API's CORS behavior across all routes. | +| cors | bool | Options for configuring the API's CORS behavior across all routes. | +| corsOptions | ApiCorsOptions | Options for configuring the API's CORS behavior across all routes. | --- ##### `cors`Optional ```wing -cors: ApiCorsProps; +cors: bool; +``` + +- *Type:* bool +- *Default:* false, CORS configuration is disabled + +Options for configuring the API's CORS behavior across all routes. + +Options can also be overridden on a per-route basis. (not yet implemented) +When enabled this will add CORS headers with default options. +Can be customized by passing `corsOptions` + +--- + +*Example* + +```wing +true +``` + + +##### `corsOptions`Optional + +```wing +corsOptions: ApiCorsOptions; ``` -- *Type:* ApiCorsProps -- *Default:* CORS is disabled +- *Type:* ApiCorsOptions +- *Default:* Default CORS options are applied when `cors` is set to `true` allowOrigin: ["*"], allowMethods: [ HttpMethod.GET, HttpMethod.POST, HttpMethod.PUT, HttpMethod.DELETE, HttpMethod.HEAD, HttpMethod.OPTIONS, ], allowHeaders: ["Content-Type", "Authorization"], exposeHeaders: [], allowCredentials: false, Options for configuring the API's CORS behavior across all routes. Options can also be overridden on a per-route basis. (not yet implemented) -Can be set to to an empty Struct to enable CORS with default options. --- *Example* ```wing -{} +{ allowOrigin: ["https://example.com"] } ``` diff --git a/examples/tests/valid/api_cors_custom.w b/examples/tests/valid/api_cors_custom.w index 1ac9986c817..31fd81d8ddc 100644 --- a/examples/tests/valid/api_cors_custom.w +++ b/examples/tests/valid/api_cors_custom.w @@ -12,7 +12,7 @@ let api = new cloud.Api( allowCredentials: true, exposeHeaders: ["Content-Type"] } -) +); api.get("/users", inflight (req) => { return { @@ -38,7 +38,7 @@ test "GET /users has cors headers" { } test "OPTIONS /users has cors headers" { - let response = http.fetch(apiCustomCors.url + "/users", { + let response = http.fetch(api.url + "/users", { method: http.HttpMethod.OPTIONS }); @@ -51,13 +51,13 @@ test "OPTIONS /users has cors headers" { t.Assert.equalStr(headers.get("access-control-allow-headers"), "Content-Type,Authorization,X-Custom-Header"); // Other cors headers are not set - t.Assert.equalStr(headers.get("access-control-allow-origin"), "winglang.io"); - t.Assert.equalStr(headers.get("access-control-expose-headers"), "Content-Type"); - t.Assert.equalStr(headers.get("access-control-allow-credentials"), "true"); + t.Assert.isNil(headers.get("access-control-allow-origin")); + t.Assert.isNil(headers.get("access-control-expose-headers")); + t.Assert.isNil(headers.get("access-control-allow-credentials")); } test "OPTIONS /users responds with proper headers for requested" { - let response = http.fetch(apiCustomCors.url + "/users", { + let response = http.fetch(api.url + "/users", { method: http.HttpMethod.OPTIONS, headers: { "Content-Type": "text/json", diff --git a/examples/tests/valid/website_with_api.w b/examples/tests/valid/website_with_api.w index 0b8bf6dd003..36146c5cc7d 100644 --- a/examples/tests/valid/website_with_api.w +++ b/examples/tests/valid/website_with_api.w @@ -7,11 +7,11 @@ bring "./assertions.w" as t; let api = new cloud.Api( cors: true, corsOptions: cloud.ApiCorsOptions { - origins: ["*"], - methods: [cloud.HttpMethod.GET, cloud.HttpMethod.POST, cloud.HttpMethod.OPTIONS], - headers: ["Content-Type"], + allowOrigin: ["*"], + allowMethods: [cloud.HttpMethod.GET, cloud.HttpMethod.POST, cloud.HttpMethod.OPTIONS], + allowHeaders: ["Content-Type"], allowCredentials: false, - exposedHeaders: ["Content-Type"] + exposeHeaders: ["Content-Type"] } ); @@ -85,9 +85,6 @@ test "OPTIONS /users" { let headers = response.headers; t.Assert.equalNum(response.status, 204); - t.Assert.equalStr(headers.get("access-control-allow-origin"), "*"); t.Assert.equalStr(headers.get("access-control-allow-methods"), "GET,POST,OPTIONS"); t.Assert.equalStr(headers.get("access-control-allow-headers"), "Content-Type"); - t.Assert.equalStr(headers.get("access-control-expose-headers"), "Content-Type"); - t.Assert.equalStr(headers.get("access-control-allow-credentials"), "false"); } \ No newline at end of file diff --git a/libs/wingc/src/lsp/snapshots/completions/incomplete_inflight_namespace.snap b/libs/wingc/src/lsp/snapshots/completions/incomplete_inflight_namespace.snap index 7c8f1a51d78..bf598d3deb6 100644 --- a/libs/wingc/src/lsp/snapshots/completions/incomplete_inflight_namespace.snap +++ b/libs/wingc/src/lsp/snapshots/completions/incomplete_inflight_namespace.snap @@ -73,12 +73,12 @@ source: libs/wingc/src/lsp/completions.rs kind: markdown value: "```wing\nstruct ApiConnectProps\n```\n---\nOptions for Api patch endpoint." sortText: hh|ApiConnectProps -- label: ApiCorsProps +- label: ApiCorsOptions kind: 22 documentation: kind: markdown - value: "```wing\nstruct ApiCorsProps\n```\n---\nCors Options for `Api`.\n### Fields\n- `allowCredentials?` — Whether to allow credentials.\n- `exposedHeaders?` — The list of exposed headers.\n- `headers?` — The list of allowed headers.\n- `methods?` — The list of allowed methods.\n- `origins?` — The list of allowed origins." - sortText: hh|ApiCorsProps + value: "```wing\nstruct ApiCorsOptions\n```\n---\nCors Options for `Api`.\n### Fields\n- `allowCredentials?` — Whether to allow credentials.\n- `allowHeaders?` — The list of allowed headers.\n- `allowMethods?` — The list of allowed methods.\n- `allowOrigin?` — The list of allowed allowOrigin.\n- `exposeHeaders?` — The list of exposed headers." + sortText: hh|ApiCorsOptions - label: ApiDeleteProps kind: 22 documentation: @@ -119,7 +119,7 @@ source: libs/wingc/src/lsp/completions.rs kind: 22 documentation: kind: markdown - value: "```wing\nstruct ApiProps\n```\n---\nOptions for `Api`.\n### Fields\n- `cors?` — Options for configuring the API's CORS behavior across all routes." + value: "```wing\nstruct ApiProps\n```\n---\nOptions for `Api`.\n### Fields\n- `cors?` — Options for configuring the API's CORS behavior across all routes.\n- `corsOptions?` — Options for configuring the API's CORS behavior across all routes." sortText: hh|ApiProps - label: ApiPutProps kind: 22 diff --git a/libs/wingc/src/lsp/snapshots/completions/namespace_middle_dot.snap b/libs/wingc/src/lsp/snapshots/completions/namespace_middle_dot.snap index 7c8f1a51d78..bf598d3deb6 100644 --- a/libs/wingc/src/lsp/snapshots/completions/namespace_middle_dot.snap +++ b/libs/wingc/src/lsp/snapshots/completions/namespace_middle_dot.snap @@ -73,12 +73,12 @@ source: libs/wingc/src/lsp/completions.rs kind: markdown value: "```wing\nstruct ApiConnectProps\n```\n---\nOptions for Api patch endpoint." sortText: hh|ApiConnectProps -- label: ApiCorsProps +- label: ApiCorsOptions kind: 22 documentation: kind: markdown - value: "```wing\nstruct ApiCorsProps\n```\n---\nCors Options for `Api`.\n### Fields\n- `allowCredentials?` — Whether to allow credentials.\n- `exposedHeaders?` — The list of exposed headers.\n- `headers?` — The list of allowed headers.\n- `methods?` — The list of allowed methods.\n- `origins?` — The list of allowed origins." - sortText: hh|ApiCorsProps + value: "```wing\nstruct ApiCorsOptions\n```\n---\nCors Options for `Api`.\n### Fields\n- `allowCredentials?` — Whether to allow credentials.\n- `allowHeaders?` — The list of allowed headers.\n- `allowMethods?` — The list of allowed methods.\n- `allowOrigin?` — The list of allowed allowOrigin.\n- `exposeHeaders?` — The list of exposed headers." + sortText: hh|ApiCorsOptions - label: ApiDeleteProps kind: 22 documentation: @@ -119,7 +119,7 @@ source: libs/wingc/src/lsp/completions.rs kind: 22 documentation: kind: markdown - value: "```wing\nstruct ApiProps\n```\n---\nOptions for `Api`.\n### Fields\n- `cors?` — Options for configuring the API's CORS behavior across all routes." + value: "```wing\nstruct ApiProps\n```\n---\nOptions for `Api`.\n### Fields\n- `cors?` — Options for configuring the API's CORS behavior across all routes.\n- `corsOptions?` — Options for configuring the API's CORS behavior across all routes." sortText: hh|ApiProps - label: ApiPutProps kind: 22 diff --git a/libs/wingc/src/lsp/snapshots/completions/variable_type_annotation_namespace.snap b/libs/wingc/src/lsp/snapshots/completions/variable_type_annotation_namespace.snap index 7c8f1a51d78..bf598d3deb6 100644 --- a/libs/wingc/src/lsp/snapshots/completions/variable_type_annotation_namespace.snap +++ b/libs/wingc/src/lsp/snapshots/completions/variable_type_annotation_namespace.snap @@ -73,12 +73,12 @@ source: libs/wingc/src/lsp/completions.rs kind: markdown value: "```wing\nstruct ApiConnectProps\n```\n---\nOptions for Api patch endpoint." sortText: hh|ApiConnectProps -- label: ApiCorsProps +- label: ApiCorsOptions kind: 22 documentation: kind: markdown - value: "```wing\nstruct ApiCorsProps\n```\n---\nCors Options for `Api`.\n### Fields\n- `allowCredentials?` — Whether to allow credentials.\n- `exposedHeaders?` — The list of exposed headers.\n- `headers?` — The list of allowed headers.\n- `methods?` — The list of allowed methods.\n- `origins?` — The list of allowed origins." - sortText: hh|ApiCorsProps + value: "```wing\nstruct ApiCorsOptions\n```\n---\nCors Options for `Api`.\n### Fields\n- `allowCredentials?` — Whether to allow credentials.\n- `allowHeaders?` — The list of allowed headers.\n- `allowMethods?` — The list of allowed methods.\n- `allowOrigin?` — The list of allowed allowOrigin.\n- `exposeHeaders?` — The list of exposed headers." + sortText: hh|ApiCorsOptions - label: ApiDeleteProps kind: 22 documentation: @@ -119,7 +119,7 @@ source: libs/wingc/src/lsp/completions.rs kind: 22 documentation: kind: markdown - value: "```wing\nstruct ApiProps\n```\n---\nOptions for `Api`.\n### Fields\n- `cors?` — Options for configuring the API's CORS behavior across all routes." + value: "```wing\nstruct ApiProps\n```\n---\nOptions for `Api`.\n### Fields\n- `cors?` — Options for configuring the API's CORS behavior across all routes.\n- `corsOptions?` — Options for configuring the API's CORS behavior across all routes." sortText: hh|ApiProps - label: ApiPutProps kind: 22 diff --git a/libs/wingsdk/src/cloud/api.ts b/libs/wingsdk/src/cloud/api.ts index 1f820fd54b7..59e19804408 100644 --- a/libs/wingsdk/src/cloud/api.ts +++ b/libs/wingsdk/src/cloud/api.ts @@ -194,6 +194,9 @@ export abstract class Api extends Resource { allowCredentials: false, }; + /** + * CORS options for api + */ protected corsOptions?: ApiCorsOptions; constructor(scope: Construct, id: string, props: ApiProps = {}) { diff --git a/libs/wingsdk/test/target-tf-aws/__snapshots__/api.test.ts.snap b/libs/wingsdk/test/target-tf-aws/__snapshots__/api.test.ts.snap index 3dd2d4d766d..41aedb0a3b5 100644 --- a/libs/wingsdk/test/target-tf-aws/__snapshots__/api.test.ts.snap +++ b/libs/wingsdk/test/target-tf-aws/__snapshots__/api.test.ts.snap @@ -1,5 +1,91 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html +exports[`api configured for cors 1`] = ` +{ + "openapi": "3.0.3", + "paths": { + "/": { + "get": { + "operationId": "get", + "parameters": [], + "responses": { + "200": { + "content": {}, + "description": "200 response", + "headers": { + "Access-Control-Allow-Headers": { + "schema": { + "type": "string", + }, + }, + "Access-Control-Allow-Methods": { + "schema": { + "type": "string", + }, + }, + "Access-Control-Allow-Origin": { + "schema": { + "type": "string", + }, + }, + }, + }, + }, + "x-amazon-apigateway-integration": { + "contentHandling": "CONVERT_TO_TEXT", + "httpMethod": "POST", + "passthroughBehavior": "when_no_match", + "responses": { + "default": { + "statusCode": "200", + }, + }, + "type": "aws_proxy", + "uri": "arn:aws:apigateway:\${data.aws_region.Region.name}:lambda:path/2015-03-31/functions/\${aws_lambda_function.Api_Api-OnRequest-c5395e41_37F21C2B.arn}/invocations", + }, + }, + }, + "/{proxy+}": { + "x-amazon-apigateway-any-method": { + "consumes": [ + "application/json", + ], + "produces": [ + "application/json", + ], + "responses": { + "404": { + "description": "404 response", + "headers": { + "Content-Type": { + "type": "string", + }, + }, + }, + }, + "x-amazon-apigateway-integration": { + "requestTemplates": { + "application/json": "{\\"statusCode\\": 404}", + }, + "responses": { + "default": { + "responseParameters": { + "method.response.header.Content-Type": "'application/json'", + }, + "responseTemplates": { + "application/json": "{\\"statusCode: 404, \\"message\\": \\"Error: Resource not found\\"}", + }, + "statusCode": "404", + }, + }, + "type": "mock", + }, + }, + }, + }, +} +`; + exports[`api with 'name' & 'age' parameter 1`] = ` { "openapi": "3.0.3", diff --git a/tools/hangar/__snapshots__/test_corpus/valid/api_cors_custom.w_compile_tf-aws.md b/tools/hangar/__snapshots__/test_corpus/valid/api_cors_custom.w_compile_tf-aws.md new file mode 100644 index 00000000000..c56608b3d64 --- /dev/null +++ b/tools/hangar/__snapshots__/test_corpus/valid/api_cors_custom.w_compile_tf-aws.md @@ -0,0 +1,727 @@ +# [api_cors_custom.w](../../../../../examples/tests/valid/api_cors_custom.w) | compile | tf-aws + +## inflight.$Closure1-2.js +```js +module.exports = function({ }) { + class $Closure1 { + constructor({ }) { + const $obj = (...args) => this.handle(...args); + Object.setPrototypeOf($obj, this); + return $obj; + } + async handle(req) { + return ({"body": "hello world","status": 200}); + } + } + return $Closure1; +} + +``` + +## inflight.$Closure2-2.js +```js +module.exports = function({ $api_url, $http_Util, $t_Assert }) { + class $Closure2 { + constructor({ }) { + const $obj = (...args) => this.handle(...args); + Object.setPrototypeOf($obj, this); + return $obj; + } + async handle() { + const response = (await $http_Util.get(($api_url + "/users"))); + const headers = response.headers; + (await $t_Assert.equalNum(response.status,200)); + (await $t_Assert.equalStr((headers)["access-control-allow-origin"],"winglang.io")); + (await $t_Assert.equalStr((headers)["access-control-allow-credentials"],"true")); + (await $t_Assert.equalStr((headers)["access-control-expose-headers"],"Content-Type")); + (await $t_Assert.isNil((headers)["access-control-allow-headers"])); + (await $t_Assert.isNil((headers)["access-control-allow-methods"])); + } + } + return $Closure2; +} + +``` + +## inflight.$Closure3-2.js +```js +module.exports = function({ $api_url, $http_HttpMethod, $http_Util, $t_Assert }) { + class $Closure3 { + constructor({ }) { + const $obj = (...args) => this.handle(...args); + Object.setPrototypeOf($obj, this); + return $obj; + } + async handle() { + const response = (await $http_Util.fetch(($api_url + "/users"),({"method": $http_HttpMethod.OPTIONS}))); + const headers = response.headers; + (await $t_Assert.equalNum(response.status,204)); + (await $t_Assert.equalStr((headers)["access-control-allow-methods"],"GET,POST,OPTIONS")); + (await $t_Assert.equalStr((headers)["access-control-allow-headers"],"Content-Type,Authorization,X-Custom-Header")); + (await $t_Assert.isNil((headers)["access-control-allow-origin"])); + (await $t_Assert.isNil((headers)["access-control-expose-headers"])); + (await $t_Assert.isNil((headers)["access-control-allow-credentials"])); + } + } + return $Closure3; +} + +``` + +## inflight.$Closure4-2.js +```js +module.exports = function({ $api_url, $http_HttpMethod, $http_Util, $t_Assert }) { + class $Closure4 { + constructor({ }) { + const $obj = (...args) => this.handle(...args); + Object.setPrototypeOf($obj, this); + return $obj; + } + async handle() { + const response = (await $http_Util.fetch(($api_url + "/users"),({"method": $http_HttpMethod.OPTIONS,"headers": ({"Content-Type": "text/json","Access-Control-Request-Method": "PUT","Access-Control-Request-Headers": "Content-Type,Authorization,X-Custom-Foo"})}))); + const headers = response.headers; + (await $t_Assert.equalNum(response.status,204)); + (await $t_Assert.equalStr((headers)["access-control-allow-methods"],"GET,POST,OPTIONS")); + (await $t_Assert.equalStr((headers)["access-control-allow-headers"],"Content-Type,Authorization,X-Custom-Header")); + } + } + return $Closure4; +} + +``` + +## inflight.Assert-1.js +```js +module.exports = function({ }) { + class Assert { + static async equalStr(a, b) { + try { + {((cond) => {if (!cond) throw new Error("assertion failed: a == b")})((((a,b) => { try { return require('assert').deepStrictEqual(a,b) === undefined; } catch { return false; } })(a,b)))}; + } + catch ($error_e) { + const e = $error_e.message; + throw new Error(String.raw({ raw: ["expected: ", " got: ", ""] }, b, a)); + } + } + static async isNil(a) { + try { + {((cond) => {if (!cond) throw new Error("assertion failed: a == nil")})((((a,b) => { try { return require('assert').deepStrictEqual(a,b) === undefined; } catch { return false; } })(a,undefined)))}; + } + catch ($error_e) { + const e = $error_e.message; + {console.log(e)}; + throw new Error(String.raw({ raw: ["expected '", "' to be nil"] }, a)); + } + } + static async equalNum(a, b) { + try { + {((cond) => {if (!cond) throw new Error("assertion failed: a == b")})((((a,b) => { try { return require('assert').deepStrictEqual(a,b) === undefined; } catch { return false; } })(a,b)))}; + } + catch ($error_e) { + const e = $error_e.message; + {console.log(e)}; + throw new Error(String.raw({ raw: ["expected: ", " got: ", ""] }, b, a)); + } + } + } + return Assert; +} + +``` + +## main.tf.json +```json +{ + "//": { + "metadata": { + "backend": "local", + "stackName": "root", + "version": "0.17.0" + }, + "outputs": { + "root": { + "Default": { + "cloud.TestRunner": { + "TestFunctionArns": "WING_TEST_RUNNER_FUNCTION_ARNS" + } + } + } + } + }, + "data": { + "aws_region": { + "Region": { + "//": { + "metadata": { + "path": "root/Default/Region", + "uniqueId": "Region" + } + } + } + } + }, + "output": { + "WING_TEST_RUNNER_FUNCTION_ARNS": { + "value": "[[\"root/Default/Default/test:GET --users has cors headers\",\"${aws_lambda_function.testGET--usershascorsheaders_Handler_E0F337CB.arn}\"],[\"root/Default/Default/test:OPTIONS --users has cors headers\",\"${aws_lambda_function.testOPTIONS--usershascorsheaders_Handler_3A565385.arn}\"],[\"root/Default/Default/test:OPTIONS --users responds with proper headers for requested\",\"${aws_lambda_function.testOPTIONS--usersrespondswithproperheadersforrequested_Handler_0A2AB662.arn}\"]]" + } + }, + "provider": { + "aws": [ + {} + ] + }, + "resource": { + "aws_api_gateway_deployment": { + "cloudApi_api_deployment_545514BF": { + "//": { + "metadata": { + "path": "root/Default/Default/cloud.Api/api/deployment", + "uniqueId": "cloudApi_api_deployment_545514BF" + } + }, + "lifecycle": { + "create_before_destroy": true + }, + "rest_api_id": "${aws_api_gateway_rest_api.cloudApi_api_2B334D75.id}", + "triggers": { + "redeployment": "${sha256(aws_api_gateway_rest_api.cloudApi_api_2B334D75.body)}" + } + } + }, + "aws_api_gateway_rest_api": { + "cloudApi_api_2B334D75": { + "//": { + "metadata": { + "path": "root/Default/Default/cloud.Api/api/api", + "uniqueId": "cloudApi_api_2B334D75" + } + }, + "body": "{\"openapi\":\"3.0.3\",\"paths\":{\"/users\":{\"get\":{\"operationId\":\"get-users\",\"responses\":{\"200\":{\"description\":\"200 response\",\"content\":{},\"headers\":{\"Access-Control-Allow-Origin\":{\"schema\":{\"type\":\"string\"}},\"Access-Control-Allow-Methods\":{\"schema\":{\"type\":\"string\"}},\"Access-Control-Allow-Headers\":{\"schema\":{\"type\":\"string\"}}}}},\"parameters\":[],\"x-amazon-apigateway-integration\":{\"uri\":\"arn:aws:apigateway:${data.aws_region.Region.name}:lambda:path/2015-03-31/functions/${aws_lambda_function.cloudApi_cloudApi-OnRequest-cdafee6e_A6C8366F.arn}/invocations\",\"type\":\"aws_proxy\",\"httpMethod\":\"POST\",\"responses\":{\"default\":{\"statusCode\":\"200\"}},\"passthroughBehavior\":\"when_no_match\",\"contentHandling\":\"CONVERT_TO_TEXT\"}}},\"/{proxy+}\":{\"x-amazon-apigateway-any-method\":{\"produces\":[\"application/json\"],\"consumes\":[\"application/json\"],\"x-amazon-apigateway-integration\":{\"type\":\"mock\",\"requestTemplates\":{\"application/json\":\"{\\\"statusCode\\\": 404}\"},\"responses\":{\"default\":{\"statusCode\":\"404\",\"responseParameters\":{\"method.response.header.Content-Type\":\"'application/json'\"},\"responseTemplates\":{\"application/json\":\"{\\\"statusCode: 404, \\\"message\\\": \\\"Error: Resource not found\\\"}\"}}}},\"responses\":{\"404\":{\"description\":\"404 response\",\"headers\":{\"Content-Type\":{\"type\":\"string\"}}}}}}}}", + "name": "api-c895068c" + } + }, + "aws_api_gateway_stage": { + "cloudApi_api_stage_BBB283E4": { + "//": { + "metadata": { + "path": "root/Default/Default/cloud.Api/api/stage", + "uniqueId": "cloudApi_api_stage_BBB283E4" + } + }, + "deployment_id": "${aws_api_gateway_deployment.cloudApi_api_deployment_545514BF.id}", + "rest_api_id": "${aws_api_gateway_rest_api.cloudApi_api_2B334D75.id}", + "stage_name": "prod" + } + }, + "aws_iam_role": { + "cloudApi_cloudApi-OnRequest-cdafee6e_IamRole_4382C442": { + "//": { + "metadata": { + "path": "root/Default/Default/cloud.Api/cloud.Api-OnRequest-cdafee6e/IamRole", + "uniqueId": "cloudApi_cloudApi-OnRequest-cdafee6e_IamRole_4382C442" + } + }, + "assume_role_policy": "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Action\":\"sts:AssumeRole\",\"Principal\":{\"Service\":\"lambda.amazonaws.com\"},\"Effect\":\"Allow\"}]}" + }, + "testGET--usershascorsheaders_Handler_IamRole_6841C3FF": { + "//": { + "metadata": { + "path": "root/Default/Default/test:GET --users has cors headers/Handler/IamRole", + "uniqueId": "testGET--usershascorsheaders_Handler_IamRole_6841C3FF" + } + }, + "assume_role_policy": "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Action\":\"sts:AssumeRole\",\"Principal\":{\"Service\":\"lambda.amazonaws.com\"},\"Effect\":\"Allow\"}]}" + }, + "testOPTIONS--usershascorsheaders_Handler_IamRole_0EFF66BD": { + "//": { + "metadata": { + "path": "root/Default/Default/test:OPTIONS --users has cors headers/Handler/IamRole", + "uniqueId": "testOPTIONS--usershascorsheaders_Handler_IamRole_0EFF66BD" + } + }, + "assume_role_policy": "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Action\":\"sts:AssumeRole\",\"Principal\":{\"Service\":\"lambda.amazonaws.com\"},\"Effect\":\"Allow\"}]}" + }, + "testOPTIONS--usersrespondswithproperheadersforrequested_Handler_IamRole_4AB06A0F": { + "//": { + "metadata": { + "path": "root/Default/Default/test:OPTIONS --users responds with proper headers for requested/Handler/IamRole", + "uniqueId": "testOPTIONS--usersrespondswithproperheadersforrequested_Handler_IamRole_4AB06A0F" + } + }, + "assume_role_policy": "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Action\":\"sts:AssumeRole\",\"Principal\":{\"Service\":\"lambda.amazonaws.com\"},\"Effect\":\"Allow\"}]}" + } + }, + "aws_iam_role_policy": { + "cloudApi_cloudApi-OnRequest-cdafee6e_IamRolePolicy_8BF9C89F": { + "//": { + "metadata": { + "path": "root/Default/Default/cloud.Api/cloud.Api-OnRequest-cdafee6e/IamRolePolicy", + "uniqueId": "cloudApi_cloudApi-OnRequest-cdafee6e_IamRolePolicy_8BF9C89F" + } + }, + "policy": "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Action\":\"none:null\",\"Resource\":\"*\"}]}", + "role": "${aws_iam_role.cloudApi_cloudApi-OnRequest-cdafee6e_IamRole_4382C442.name}" + }, + "testGET--usershascorsheaders_Handler_IamRolePolicy_BEF25776": { + "//": { + "metadata": { + "path": "root/Default/Default/test:GET --users has cors headers/Handler/IamRolePolicy", + "uniqueId": "testGET--usershascorsheaders_Handler_IamRolePolicy_BEF25776" + } + }, + "policy": "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Action\":\"none:null\",\"Resource\":\"*\"}]}", + "role": "${aws_iam_role.testGET--usershascorsheaders_Handler_IamRole_6841C3FF.name}" + }, + "testOPTIONS--usershascorsheaders_Handler_IamRolePolicy_F6912B4F": { + "//": { + "metadata": { + "path": "root/Default/Default/test:OPTIONS --users has cors headers/Handler/IamRolePolicy", + "uniqueId": "testOPTIONS--usershascorsheaders_Handler_IamRolePolicy_F6912B4F" + } + }, + "policy": "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Action\":\"none:null\",\"Resource\":\"*\"}]}", + "role": "${aws_iam_role.testOPTIONS--usershascorsheaders_Handler_IamRole_0EFF66BD.name}" + }, + "testOPTIONS--usersrespondswithproperheadersforrequested_Handler_IamRolePolicy_00E727F2": { + "//": { + "metadata": { + "path": "root/Default/Default/test:OPTIONS --users responds with proper headers for requested/Handler/IamRolePolicy", + "uniqueId": "testOPTIONS--usersrespondswithproperheadersforrequested_Handler_IamRolePolicy_00E727F2" + } + }, + "policy": "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Action\":\"none:null\",\"Resource\":\"*\"}]}", + "role": "${aws_iam_role.testOPTIONS--usersrespondswithproperheadersforrequested_Handler_IamRole_4AB06A0F.name}" + } + }, + "aws_iam_role_policy_attachment": { + "cloudApi_cloudApi-OnRequest-cdafee6e_IamRolePolicyAttachment_5383D6A2": { + "//": { + "metadata": { + "path": "root/Default/Default/cloud.Api/cloud.Api-OnRequest-cdafee6e/IamRolePolicyAttachment", + "uniqueId": "cloudApi_cloudApi-OnRequest-cdafee6e_IamRolePolicyAttachment_5383D6A2" + } + }, + "policy_arn": "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole", + "role": "${aws_iam_role.cloudApi_cloudApi-OnRequest-cdafee6e_IamRole_4382C442.name}" + }, + "testGET--usershascorsheaders_Handler_IamRolePolicyAttachment_54A08CCC": { + "//": { + "metadata": { + "path": "root/Default/Default/test:GET --users has cors headers/Handler/IamRolePolicyAttachment", + "uniqueId": "testGET--usershascorsheaders_Handler_IamRolePolicyAttachment_54A08CCC" + } + }, + "policy_arn": "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole", + "role": "${aws_iam_role.testGET--usershascorsheaders_Handler_IamRole_6841C3FF.name}" + }, + "testOPTIONS--usershascorsheaders_Handler_IamRolePolicyAttachment_CA58727C": { + "//": { + "metadata": { + "path": "root/Default/Default/test:OPTIONS --users has cors headers/Handler/IamRolePolicyAttachment", + "uniqueId": "testOPTIONS--usershascorsheaders_Handler_IamRolePolicyAttachment_CA58727C" + } + }, + "policy_arn": "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole", + "role": "${aws_iam_role.testOPTIONS--usershascorsheaders_Handler_IamRole_0EFF66BD.name}" + }, + "testOPTIONS--usersrespondswithproperheadersforrequested_Handler_IamRolePolicyAttachment_F702A7D9": { + "//": { + "metadata": { + "path": "root/Default/Default/test:OPTIONS --users responds with proper headers for requested/Handler/IamRolePolicyAttachment", + "uniqueId": "testOPTIONS--usersrespondswithproperheadersforrequested_Handler_IamRolePolicyAttachment_F702A7D9" + } + }, + "policy_arn": "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole", + "role": "${aws_iam_role.testOPTIONS--usersrespondswithproperheadersforrequested_Handler_IamRole_4AB06A0F.name}" + } + }, + "aws_lambda_function": { + "cloudApi_cloudApi-OnRequest-cdafee6e_A6C8366F": { + "//": { + "metadata": { + "path": "root/Default/Default/cloud.Api/cloud.Api-OnRequest-cdafee6e/Default", + "uniqueId": "cloudApi_cloudApi-OnRequest-cdafee6e_A6C8366F" + } + }, + "architectures": [ + "arm64" + ], + "environment": { + "variables": { + "WING_FUNCTION_NAME": "cloud-Api-OnRequest-cdafee6e-c8147384", + "WING_TARGET": "tf-aws" + } + }, + "function_name": "cloud-Api-OnRequest-cdafee6e-c8147384", + "handler": "index.handler", + "publish": true, + "role": "${aws_iam_role.cloudApi_cloudApi-OnRequest-cdafee6e_IamRole_4382C442.arn}", + "runtime": "nodejs18.x", + "s3_bucket": "${aws_s3_bucket.Code.bucket}", + "s3_key": "${aws_s3_object.cloudApi_cloudApi-OnRequest-cdafee6e_S3Object_5DAAA0EF.key}", + "timeout": 30, + "vpc_config": { + "security_group_ids": [], + "subnet_ids": [] + } + }, + "testGET--usershascorsheaders_Handler_E0F337CB": { + "//": { + "metadata": { + "path": "root/Default/Default/test:GET --users has cors headers/Handler/Default", + "uniqueId": "testGET--usershascorsheaders_Handler_E0F337CB" + } + }, + "architectures": [ + "arm64" + ], + "environment": { + "variables": { + "WING_FUNCTION_NAME": "Handler-c8d51aba", + "WING_TARGET": "tf-aws", + "WING_TOKEN_TFTOKEN_TOKEN_8": "${jsonencode(aws_api_gateway_stage.cloudApi_api_stage_BBB283E4.invoke_url)}" + } + }, + "function_name": "Handler-c8d51aba", + "handler": "index.handler", + "publish": true, + "role": "${aws_iam_role.testGET--usershascorsheaders_Handler_IamRole_6841C3FF.arn}", + "runtime": "nodejs18.x", + "s3_bucket": "${aws_s3_bucket.Code.bucket}", + "s3_key": "${aws_s3_object.testGET--usershascorsheaders_Handler_S3Object_A63B28D3.key}", + "timeout": 30, + "vpc_config": { + "security_group_ids": [], + "subnet_ids": [] + } + }, + "testOPTIONS--usershascorsheaders_Handler_3A565385": { + "//": { + "metadata": { + "path": "root/Default/Default/test:OPTIONS --users has cors headers/Handler/Default", + "uniqueId": "testOPTIONS--usershascorsheaders_Handler_3A565385" + } + }, + "architectures": [ + "arm64" + ], + "environment": { + "variables": { + "WING_FUNCTION_NAME": "Handler-c81c750d", + "WING_TARGET": "tf-aws", + "WING_TOKEN_TFTOKEN_TOKEN_8": "${jsonencode(aws_api_gateway_stage.cloudApi_api_stage_BBB283E4.invoke_url)}" + } + }, + "function_name": "Handler-c81c750d", + "handler": "index.handler", + "publish": true, + "role": "${aws_iam_role.testOPTIONS--usershascorsheaders_Handler_IamRole_0EFF66BD.arn}", + "runtime": "nodejs18.x", + "s3_bucket": "${aws_s3_bucket.Code.bucket}", + "s3_key": "${aws_s3_object.testOPTIONS--usershascorsheaders_Handler_S3Object_7EC6E95C.key}", + "timeout": 30, + "vpc_config": { + "security_group_ids": [], + "subnet_ids": [] + } + }, + "testOPTIONS--usersrespondswithproperheadersforrequested_Handler_0A2AB662": { + "//": { + "metadata": { + "path": "root/Default/Default/test:OPTIONS --users responds with proper headers for requested/Handler/Default", + "uniqueId": "testOPTIONS--usersrespondswithproperheadersforrequested_Handler_0A2AB662" + } + }, + "architectures": [ + "arm64" + ], + "environment": { + "variables": { + "WING_FUNCTION_NAME": "Handler-c8aef6d3", + "WING_TARGET": "tf-aws", + "WING_TOKEN_TFTOKEN_TOKEN_8": "${jsonencode(aws_api_gateway_stage.cloudApi_api_stage_BBB283E4.invoke_url)}" + } + }, + "function_name": "Handler-c8aef6d3", + "handler": "index.handler", + "publish": true, + "role": "${aws_iam_role.testOPTIONS--usersrespondswithproperheadersforrequested_Handler_IamRole_4AB06A0F.arn}", + "runtime": "nodejs18.x", + "s3_bucket": "${aws_s3_bucket.Code.bucket}", + "s3_key": "${aws_s3_object.testOPTIONS--usersrespondswithproperheadersforrequested_Handler_S3Object_0954ACCC.key}", + "timeout": 30, + "vpc_config": { + "security_group_ids": [], + "subnet_ids": [] + } + } + }, + "aws_lambda_permission": { + "cloudApi_api_permission-GET-41f0e61d_DD9B4FD0": { + "//": { + "metadata": { + "path": "root/Default/Default/cloud.Api/api/permission-GET-41f0e61d", + "uniqueId": "cloudApi_api_permission-GET-41f0e61d_DD9B4FD0" + } + }, + "action": "lambda:InvokeFunction", + "function_name": "${aws_lambda_function.cloudApi_cloudApi-OnRequest-cdafee6e_A6C8366F.function_name}", + "principal": "apigateway.amazonaws.com", + "source_arn": "${aws_api_gateway_rest_api.cloudApi_api_2B334D75.execution_arn}/*/GET/users", + "statement_id": "AllowExecutionFromAPIGateway-GET-41f0e61d" + } + }, + "aws_s3_bucket": { + "Code": { + "//": { + "metadata": { + "path": "root/Default/Code", + "uniqueId": "Code" + } + }, + "bucket_prefix": "code-c84a50b1-" + } + }, + "aws_s3_object": { + "cloudApi_cloudApi-OnRequest-cdafee6e_S3Object_5DAAA0EF": { + "//": { + "metadata": { + "path": "root/Default/Default/cloud.Api/cloud.Api-OnRequest-cdafee6e/S3Object", + "uniqueId": "cloudApi_cloudApi-OnRequest-cdafee6e_S3Object_5DAAA0EF" + } + }, + "bucket": "${aws_s3_bucket.Code.bucket}", + "key": "", + "source": "" + }, + "testGET--usershascorsheaders_Handler_S3Object_A63B28D3": { + "//": { + "metadata": { + "path": "root/Default/Default/test:GET --users has cors headers/Handler/S3Object", + "uniqueId": "testGET--usershascorsheaders_Handler_S3Object_A63B28D3" + } + }, + "bucket": "${aws_s3_bucket.Code.bucket}", + "key": "", + "source": "" + }, + "testOPTIONS--usershascorsheaders_Handler_S3Object_7EC6E95C": { + "//": { + "metadata": { + "path": "root/Default/Default/test:OPTIONS --users has cors headers/Handler/S3Object", + "uniqueId": "testOPTIONS--usershascorsheaders_Handler_S3Object_7EC6E95C" + } + }, + "bucket": "${aws_s3_bucket.Code.bucket}", + "key": "", + "source": "" + }, + "testOPTIONS--usersrespondswithproperheadersforrequested_Handler_S3Object_0954ACCC": { + "//": { + "metadata": { + "path": "root/Default/Default/test:OPTIONS --users responds with proper headers for requested/Handler/S3Object", + "uniqueId": "testOPTIONS--usersrespondswithproperheadersforrequested_Handler_S3Object_0954ACCC" + } + }, + "bucket": "${aws_s3_bucket.Code.bucket}", + "key": "", + "source": "" + } + } + } +} +``` + +## preflight.assertions-1.js +```js +module.exports = function({ $stdlib }) { + const std = $stdlib.std; + class Assert extends $stdlib.std.Resource { + constructor(scope, id, ) { + super(scope, id); + } + static _toInflightType(context) { + return ` + require("./inflight.Assert-1.js")({ + }) + `; + } + _toInflight() { + return ` + (await (async () => { + const AssertClient = ${Assert._toInflightType(this)}; + const client = new AssertClient({ + }); + if (client.$inflight_init) { await client.$inflight_init(); } + return client; + })()) + `; + } + _getInflightOps() { + return ["equalStr", "isNil", "equalNum", "$inflight_init"]; + } + } + return { Assert }; +}; + +``` + +## preflight.js +```js +const $stdlib = require('@winglang/sdk'); +const $plugins = ((s) => !s ? [] : s.split(';'))(process.env.WING_PLUGIN_PATHS); +const $outdir = process.env.WING_SYNTH_DIR ?? "."; +const $wing_is_test = process.env.WING_IS_TEST === "true"; +const std = $stdlib.std; +const cloud = $stdlib.cloud; +const ex = $stdlib.ex; +const http = $stdlib.http; +const t = require("./preflight.assertions-1.js")({ $stdlib }); +class $Root extends $stdlib.std.Resource { + constructor(scope, id) { + super(scope, id); + class $Closure1 extends $stdlib.std.Resource { + constructor(scope, id, ) { + super(scope, id); + (std.Node.of(this)).hidden = true; + } + static _toInflightType(context) { + return ` + require("./inflight.$Closure1-2.js")({ + }) + `; + } + _toInflight() { + return ` + (await (async () => { + const $Closure1Client = ${$Closure1._toInflightType(this)}; + const client = new $Closure1Client({ + }); + if (client.$inflight_init) { await client.$inflight_init(); } + return client; + })()) + `; + } + _getInflightOps() { + return ["handle", "$inflight_init"]; + } + } + class $Closure2 extends $stdlib.std.Resource { + constructor(scope, id, ) { + super(scope, id); + (std.Node.of(this)).hidden = true; + } + static _toInflightType(context) { + return ` + require("./inflight.$Closure2-2.js")({ + $api_url: ${context._lift(api.url)}, + $http_Util: ${context._lift(http.Util)}, + $t_Assert: ${context._lift(t.Assert)}, + }) + `; + } + _toInflight() { + return ` + (await (async () => { + const $Closure2Client = ${$Closure2._toInflightType(this)}; + const client = new $Closure2Client({ + }); + if (client.$inflight_init) { await client.$inflight_init(); } + return client; + })()) + `; + } + _getInflightOps() { + return ["handle", "$inflight_init"]; + } + _registerBind(host, ops) { + if (ops.includes("handle")) { + $Closure2._registerBindObject(api.url, host, []); + } + super._registerBind(host, ops); + } + } + class $Closure3 extends $stdlib.std.Resource { + constructor(scope, id, ) { + super(scope, id); + (std.Node.of(this)).hidden = true; + } + static _toInflightType(context) { + return ` + require("./inflight.$Closure3-2.js")({ + $api_url: ${context._lift(api.url)}, + $http_HttpMethod: ${context._lift(http.HttpMethod)}, + $http_Util: ${context._lift(http.Util)}, + $t_Assert: ${context._lift(t.Assert)}, + }) + `; + } + _toInflight() { + return ` + (await (async () => { + const $Closure3Client = ${$Closure3._toInflightType(this)}; + const client = new $Closure3Client({ + }); + if (client.$inflight_init) { await client.$inflight_init(); } + return client; + })()) + `; + } + _getInflightOps() { + return ["handle", "$inflight_init"]; + } + _registerBind(host, ops) { + if (ops.includes("handle")) { + $Closure3._registerBindObject(api.url, host, []); + } + super._registerBind(host, ops); + } + } + class $Closure4 extends $stdlib.std.Resource { + constructor(scope, id, ) { + super(scope, id); + (std.Node.of(this)).hidden = true; + } + static _toInflightType(context) { + return ` + require("./inflight.$Closure4-2.js")({ + $api_url: ${context._lift(api.url)}, + $http_HttpMethod: ${context._lift(http.HttpMethod)}, + $http_Util: ${context._lift(http.Util)}, + $t_Assert: ${context._lift(t.Assert)}, + }) + `; + } + _toInflight() { + return ` + (await (async () => { + const $Closure4Client = ${$Closure4._toInflightType(this)}; + const client = new $Closure4Client({ + }); + if (client.$inflight_init) { await client.$inflight_init(); } + return client; + })()) + `; + } + _getInflightOps() { + return ["handle", "$inflight_init"]; + } + _registerBind(host, ops) { + if (ops.includes("handle")) { + $Closure4._registerBindObject(api.url, host, []); + } + super._registerBind(host, ops); + } + } + const api = this.node.root.newAbstract("@winglang/sdk.cloud.Api",this,"cloud.Api",{ cors: true, corsOptions: ({"allowOrigin": ["winglang.io"],"allowMethods": [cloud.HttpMethod.GET, cloud.HttpMethod.POST, cloud.HttpMethod.OPTIONS],"allowHeaders": ["Content-Type", "Authorization", "X-Custom-Header"],"allowCredentials": true,"exposeHeaders": ["Content-Type"]}) }); + (api.get("/users",new $Closure1(this,"$Closure1"))); + this.node.root.new("@winglang/sdk.std.Test",std.Test,this,"test:GET /users has cors headers",new $Closure2(this,"$Closure2")); + this.node.root.new("@winglang/sdk.std.Test",std.Test,this,"test:OPTIONS /users has cors headers",new $Closure3(this,"$Closure3")); + this.node.root.new("@winglang/sdk.std.Test",std.Test,this,"test:OPTIONS /users responds with proper headers for requested",new $Closure4(this,"$Closure4")); + } +} +const $App = $stdlib.core.App.for(process.env.WING_TARGET); +new $App({ outdir: $outdir, name: "api_cors_custom", rootConstruct: $Root, plugins: $plugins, isTestEnvironment: $wing_is_test, entrypointDir: process.env['WING_SOURCE_DIR'], rootId: process.env['WING_ROOT_ID'] }).synth(); + +``` + diff --git a/tools/hangar/__snapshots__/test_corpus/valid/api_cors_custom.w_test_sim.md b/tools/hangar/__snapshots__/test_corpus/valid/api_cors_custom.w_test_sim.md new file mode 100644 index 00000000000..99c8842c325 --- /dev/null +++ b/tools/hangar/__snapshots__/test_corpus/valid/api_cors_custom.w_test_sim.md @@ -0,0 +1,14 @@ +# [api_cors_custom.w](../../../../../examples/tests/valid/api_cors_custom.w) | test | sim + +## stdout.log +```log +pass ─ api_cors_custom.wsim » root/env0/test:GET --users has cors headers +pass ─ api_cors_custom.wsim » root/env1/test:OPTIONS --users has cors headers +pass ─ api_cors_custom.wsim » root/env2/test:OPTIONS --users responds with proper headers for requested + + +Tests 3 passed (3) +Test Files 1 passed (1) +Duration +``` + diff --git a/tools/hangar/__snapshots__/test_corpus/valid/api_cors_default.w_compile_tf-aws.md b/tools/hangar/__snapshots__/test_corpus/valid/api_cors_default.w_compile_tf-aws.md new file mode 100644 index 00000000000..08fe0e7ae47 --- /dev/null +++ b/tools/hangar/__snapshots__/test_corpus/valid/api_cors_default.w_compile_tf-aws.md @@ -0,0 +1,598 @@ +# [api_cors_default.w](../../../../../examples/tests/valid/api_cors_default.w) | compile | tf-aws + +## inflight.$Closure1-2.js +```js +module.exports = function({ }) { + class $Closure1 { + constructor({ }) { + const $obj = (...args) => this.handle(...args); + Object.setPrototypeOf($obj, this); + return $obj; + } + async handle(req) { + return ({"body": "hello world","status": 200}); + } + } + return $Closure1; +} + +``` + +## inflight.$Closure2-2.js +```js +module.exports = function({ $apiDefaultCors_url, $http_Util, $t_Assert }) { + class $Closure2 { + constructor({ }) { + const $obj = (...args) => this.handle(...args); + Object.setPrototypeOf($obj, this); + return $obj; + } + async handle() { + const response = (await $http_Util.get(($apiDefaultCors_url + "/users"))); + const headers = response.headers; + (await $t_Assert.equalNum(response.status,200)); + (await $t_Assert.equalStr((headers)["access-control-allow-origin"],"*")); + (await $t_Assert.equalStr((headers)["access-control-allow-credentials"],"false")); + (await $t_Assert.equalStr((headers)["access-control-expose-headers"],"")); + (await $t_Assert.isNil((headers)["access-control-allow-headers"])); + (await $t_Assert.isNil((headers)["access-control-allow-methods"])); + } + } + return $Closure2; +} + +``` + +## inflight.$Closure3-2.js +```js +module.exports = function({ $apiDefaultCors_url, $http_HttpMethod, $http_Util, $t_Assert }) { + class $Closure3 { + constructor({ }) { + const $obj = (...args) => this.handle(...args); + Object.setPrototypeOf($obj, this); + return $obj; + } + async handle() { + const response = (await $http_Util.fetch(($apiDefaultCors_url + "/users"),({"method": $http_HttpMethod.OPTIONS}))); + const headers = response.headers; + (await $t_Assert.equalNum(response.status,204)); + (await $t_Assert.equalStr((headers)["access-control-allow-headers"],"Content-Type,Authorization")); + (await $t_Assert.equalStr((headers)["access-control-allow-methods"],"GET,POST,PUT,DELETE,HEAD,OPTIONS")); + (await $t_Assert.isNil((headers)["access-control-allow-origin"])); + (await $t_Assert.isNil((headers)["access-control-allow-credentials"])); + (await $t_Assert.isNil((headers)["access-control-expose-headers"])); + } + } + return $Closure3; +} + +``` + +## inflight.Assert-1.js +```js +module.exports = function({ }) { + class Assert { + static async equalStr(a, b) { + try { + {((cond) => {if (!cond) throw new Error("assertion failed: a == b")})((((a,b) => { try { return require('assert').deepStrictEqual(a,b) === undefined; } catch { return false; } })(a,b)))}; + } + catch ($error_e) { + const e = $error_e.message; + throw new Error(String.raw({ raw: ["expected: ", " got: ", ""] }, b, a)); + } + } + static async isNil(a) { + try { + {((cond) => {if (!cond) throw new Error("assertion failed: a == nil")})((((a,b) => { try { return require('assert').deepStrictEqual(a,b) === undefined; } catch { return false; } })(a,undefined)))}; + } + catch ($error_e) { + const e = $error_e.message; + {console.log(e)}; + throw new Error(String.raw({ raw: ["expected '", "' to be nil"] }, a)); + } + } + static async equalNum(a, b) { + try { + {((cond) => {if (!cond) throw new Error("assertion failed: a == b")})((((a,b) => { try { return require('assert').deepStrictEqual(a,b) === undefined; } catch { return false; } })(a,b)))}; + } + catch ($error_e) { + const e = $error_e.message; + {console.log(e)}; + throw new Error(String.raw({ raw: ["expected: ", " got: ", ""] }, b, a)); + } + } + } + return Assert; +} + +``` + +## main.tf.json +```json +{ + "//": { + "metadata": { + "backend": "local", + "stackName": "root", + "version": "0.17.0" + }, + "outputs": { + "root": { + "Default": { + "cloud.TestRunner": { + "TestFunctionArns": "WING_TEST_RUNNER_FUNCTION_ARNS" + } + } + } + } + }, + "data": { + "aws_region": { + "Region": { + "//": { + "metadata": { + "path": "root/Default/Region", + "uniqueId": "Region" + } + } + } + } + }, + "output": { + "WING_TEST_RUNNER_FUNCTION_ARNS": { + "value": "[[\"root/Default/Default/test:GET --users has default cors headers\",\"${aws_lambda_function.testGET--usershasdefaultcorsheaders_Handler_1182379A.arn}\"],[\"root/Default/Default/test:OPTIONS --users has default cors headers\",\"${aws_lambda_function.testOPTIONS--usershasdefaultcorsheaders_Handler_D03A1BFF.arn}\"]]" + } + }, + "provider": { + "aws": [ + {} + ] + }, + "resource": { + "aws_api_gateway_deployment": { + "cloudApi_api_deployment_545514BF": { + "//": { + "metadata": { + "path": "root/Default/Default/cloud.Api/api/deployment", + "uniqueId": "cloudApi_api_deployment_545514BF" + } + }, + "lifecycle": { + "create_before_destroy": true + }, + "rest_api_id": "${aws_api_gateway_rest_api.cloudApi_api_2B334D75.id}", + "triggers": { + "redeployment": "${sha256(aws_api_gateway_rest_api.cloudApi_api_2B334D75.body)}" + } + } + }, + "aws_api_gateway_rest_api": { + "cloudApi_api_2B334D75": { + "//": { + "metadata": { + "path": "root/Default/Default/cloud.Api/api/api", + "uniqueId": "cloudApi_api_2B334D75" + } + }, + "body": "{\"openapi\":\"3.0.3\",\"paths\":{\"/users\":{\"get\":{\"operationId\":\"get-users\",\"responses\":{\"200\":{\"description\":\"200 response\",\"content\":{},\"headers\":{\"Access-Control-Allow-Origin\":{\"schema\":{\"type\":\"string\"}},\"Access-Control-Allow-Methods\":{\"schema\":{\"type\":\"string\"}},\"Access-Control-Allow-Headers\":{\"schema\":{\"type\":\"string\"}}}}},\"parameters\":[],\"x-amazon-apigateway-integration\":{\"uri\":\"arn:aws:apigateway:${data.aws_region.Region.name}:lambda:path/2015-03-31/functions/${aws_lambda_function.cloudApi_cloudApi-OnRequest-cdafee6e_A6C8366F.arn}/invocations\",\"type\":\"aws_proxy\",\"httpMethod\":\"POST\",\"responses\":{\"default\":{\"statusCode\":\"200\"}},\"passthroughBehavior\":\"when_no_match\",\"contentHandling\":\"CONVERT_TO_TEXT\"}}},\"/{proxy+}\":{\"x-amazon-apigateway-any-method\":{\"produces\":[\"application/json\"],\"consumes\":[\"application/json\"],\"x-amazon-apigateway-integration\":{\"type\":\"mock\",\"requestTemplates\":{\"application/json\":\"{\\\"statusCode\\\": 404}\"},\"responses\":{\"default\":{\"statusCode\":\"404\",\"responseParameters\":{\"method.response.header.Content-Type\":\"'application/json'\"},\"responseTemplates\":{\"application/json\":\"{\\\"statusCode: 404, \\\"message\\\": \\\"Error: Resource not found\\\"}\"}}}},\"responses\":{\"404\":{\"description\":\"404 response\",\"headers\":{\"Content-Type\":{\"type\":\"string\"}}}}}}}}", + "name": "api-c895068c" + } + }, + "aws_api_gateway_stage": { + "cloudApi_api_stage_BBB283E4": { + "//": { + "metadata": { + "path": "root/Default/Default/cloud.Api/api/stage", + "uniqueId": "cloudApi_api_stage_BBB283E4" + } + }, + "deployment_id": "${aws_api_gateway_deployment.cloudApi_api_deployment_545514BF.id}", + "rest_api_id": "${aws_api_gateway_rest_api.cloudApi_api_2B334D75.id}", + "stage_name": "prod" + } + }, + "aws_iam_role": { + "cloudApi_cloudApi-OnRequest-cdafee6e_IamRole_4382C442": { + "//": { + "metadata": { + "path": "root/Default/Default/cloud.Api/cloud.Api-OnRequest-cdafee6e/IamRole", + "uniqueId": "cloudApi_cloudApi-OnRequest-cdafee6e_IamRole_4382C442" + } + }, + "assume_role_policy": "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Action\":\"sts:AssumeRole\",\"Principal\":{\"Service\":\"lambda.amazonaws.com\"},\"Effect\":\"Allow\"}]}" + }, + "testGET--usershasdefaultcorsheaders_Handler_IamRole_17E5D7FC": { + "//": { + "metadata": { + "path": "root/Default/Default/test:GET --users has default cors headers/Handler/IamRole", + "uniqueId": "testGET--usershasdefaultcorsheaders_Handler_IamRole_17E5D7FC" + } + }, + "assume_role_policy": "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Action\":\"sts:AssumeRole\",\"Principal\":{\"Service\":\"lambda.amazonaws.com\"},\"Effect\":\"Allow\"}]}" + }, + "testOPTIONS--usershasdefaultcorsheaders_Handler_IamRole_E58DF921": { + "//": { + "metadata": { + "path": "root/Default/Default/test:OPTIONS --users has default cors headers/Handler/IamRole", + "uniqueId": "testOPTIONS--usershasdefaultcorsheaders_Handler_IamRole_E58DF921" + } + }, + "assume_role_policy": "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Action\":\"sts:AssumeRole\",\"Principal\":{\"Service\":\"lambda.amazonaws.com\"},\"Effect\":\"Allow\"}]}" + } + }, + "aws_iam_role_policy": { + "cloudApi_cloudApi-OnRequest-cdafee6e_IamRolePolicy_8BF9C89F": { + "//": { + "metadata": { + "path": "root/Default/Default/cloud.Api/cloud.Api-OnRequest-cdafee6e/IamRolePolicy", + "uniqueId": "cloudApi_cloudApi-OnRequest-cdafee6e_IamRolePolicy_8BF9C89F" + } + }, + "policy": "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Action\":\"none:null\",\"Resource\":\"*\"}]}", + "role": "${aws_iam_role.cloudApi_cloudApi-OnRequest-cdafee6e_IamRole_4382C442.name}" + }, + "testGET--usershasdefaultcorsheaders_Handler_IamRolePolicy_F382BF6B": { + "//": { + "metadata": { + "path": "root/Default/Default/test:GET --users has default cors headers/Handler/IamRolePolicy", + "uniqueId": "testGET--usershasdefaultcorsheaders_Handler_IamRolePolicy_F382BF6B" + } + }, + "policy": "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Action\":\"none:null\",\"Resource\":\"*\"}]}", + "role": "${aws_iam_role.testGET--usershasdefaultcorsheaders_Handler_IamRole_17E5D7FC.name}" + }, + "testOPTIONS--usershasdefaultcorsheaders_Handler_IamRolePolicy_C496A648": { + "//": { + "metadata": { + "path": "root/Default/Default/test:OPTIONS --users has default cors headers/Handler/IamRolePolicy", + "uniqueId": "testOPTIONS--usershasdefaultcorsheaders_Handler_IamRolePolicy_C496A648" + } + }, + "policy": "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Action\":\"none:null\",\"Resource\":\"*\"}]}", + "role": "${aws_iam_role.testOPTIONS--usershasdefaultcorsheaders_Handler_IamRole_E58DF921.name}" + } + }, + "aws_iam_role_policy_attachment": { + "cloudApi_cloudApi-OnRequest-cdafee6e_IamRolePolicyAttachment_5383D6A2": { + "//": { + "metadata": { + "path": "root/Default/Default/cloud.Api/cloud.Api-OnRequest-cdafee6e/IamRolePolicyAttachment", + "uniqueId": "cloudApi_cloudApi-OnRequest-cdafee6e_IamRolePolicyAttachment_5383D6A2" + } + }, + "policy_arn": "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole", + "role": "${aws_iam_role.cloudApi_cloudApi-OnRequest-cdafee6e_IamRole_4382C442.name}" + }, + "testGET--usershasdefaultcorsheaders_Handler_IamRolePolicyAttachment_50A99B49": { + "//": { + "metadata": { + "path": "root/Default/Default/test:GET --users has default cors headers/Handler/IamRolePolicyAttachment", + "uniqueId": "testGET--usershasdefaultcorsheaders_Handler_IamRolePolicyAttachment_50A99B49" + } + }, + "policy_arn": "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole", + "role": "${aws_iam_role.testGET--usershasdefaultcorsheaders_Handler_IamRole_17E5D7FC.name}" + }, + "testOPTIONS--usershasdefaultcorsheaders_Handler_IamRolePolicyAttachment_CF666F56": { + "//": { + "metadata": { + "path": "root/Default/Default/test:OPTIONS --users has default cors headers/Handler/IamRolePolicyAttachment", + "uniqueId": "testOPTIONS--usershasdefaultcorsheaders_Handler_IamRolePolicyAttachment_CF666F56" + } + }, + "policy_arn": "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole", + "role": "${aws_iam_role.testOPTIONS--usershasdefaultcorsheaders_Handler_IamRole_E58DF921.name}" + } + }, + "aws_lambda_function": { + "cloudApi_cloudApi-OnRequest-cdafee6e_A6C8366F": { + "//": { + "metadata": { + "path": "root/Default/Default/cloud.Api/cloud.Api-OnRequest-cdafee6e/Default", + "uniqueId": "cloudApi_cloudApi-OnRequest-cdafee6e_A6C8366F" + } + }, + "architectures": [ + "arm64" + ], + "environment": { + "variables": { + "WING_FUNCTION_NAME": "cloud-Api-OnRequest-cdafee6e-c8147384", + "WING_TARGET": "tf-aws" + } + }, + "function_name": "cloud-Api-OnRequest-cdafee6e-c8147384", + "handler": "index.handler", + "publish": true, + "role": "${aws_iam_role.cloudApi_cloudApi-OnRequest-cdafee6e_IamRole_4382C442.arn}", + "runtime": "nodejs18.x", + "s3_bucket": "${aws_s3_bucket.Code.bucket}", + "s3_key": "${aws_s3_object.cloudApi_cloudApi-OnRequest-cdafee6e_S3Object_5DAAA0EF.key}", + "timeout": 30, + "vpc_config": { + "security_group_ids": [], + "subnet_ids": [] + } + }, + "testGET--usershasdefaultcorsheaders_Handler_1182379A": { + "//": { + "metadata": { + "path": "root/Default/Default/test:GET --users has default cors headers/Handler/Default", + "uniqueId": "testGET--usershasdefaultcorsheaders_Handler_1182379A" + } + }, + "architectures": [ + "arm64" + ], + "environment": { + "variables": { + "WING_FUNCTION_NAME": "Handler-c80c888e", + "WING_TARGET": "tf-aws", + "WING_TOKEN_TFTOKEN_TOKEN_8": "${jsonencode(aws_api_gateway_stage.cloudApi_api_stage_BBB283E4.invoke_url)}" + } + }, + "function_name": "Handler-c80c888e", + "handler": "index.handler", + "publish": true, + "role": "${aws_iam_role.testGET--usershasdefaultcorsheaders_Handler_IamRole_17E5D7FC.arn}", + "runtime": "nodejs18.x", + "s3_bucket": "${aws_s3_bucket.Code.bucket}", + "s3_key": "${aws_s3_object.testGET--usershasdefaultcorsheaders_Handler_S3Object_ADAE18A8.key}", + "timeout": 30, + "vpc_config": { + "security_group_ids": [], + "subnet_ids": [] + } + }, + "testOPTIONS--usershasdefaultcorsheaders_Handler_D03A1BFF": { + "//": { + "metadata": { + "path": "root/Default/Default/test:OPTIONS --users has default cors headers/Handler/Default", + "uniqueId": "testOPTIONS--usershasdefaultcorsheaders_Handler_D03A1BFF" + } + }, + "architectures": [ + "arm64" + ], + "environment": { + "variables": { + "WING_FUNCTION_NAME": "Handler-c82f7728", + "WING_TARGET": "tf-aws", + "WING_TOKEN_TFTOKEN_TOKEN_8": "${jsonencode(aws_api_gateway_stage.cloudApi_api_stage_BBB283E4.invoke_url)}" + } + }, + "function_name": "Handler-c82f7728", + "handler": "index.handler", + "publish": true, + "role": "${aws_iam_role.testOPTIONS--usershasdefaultcorsheaders_Handler_IamRole_E58DF921.arn}", + "runtime": "nodejs18.x", + "s3_bucket": "${aws_s3_bucket.Code.bucket}", + "s3_key": "${aws_s3_object.testOPTIONS--usershasdefaultcorsheaders_Handler_S3Object_2F99FE7D.key}", + "timeout": 30, + "vpc_config": { + "security_group_ids": [], + "subnet_ids": [] + } + } + }, + "aws_lambda_permission": { + "cloudApi_api_permission-GET-41f0e61d_DD9B4FD0": { + "//": { + "metadata": { + "path": "root/Default/Default/cloud.Api/api/permission-GET-41f0e61d", + "uniqueId": "cloudApi_api_permission-GET-41f0e61d_DD9B4FD0" + } + }, + "action": "lambda:InvokeFunction", + "function_name": "${aws_lambda_function.cloudApi_cloudApi-OnRequest-cdafee6e_A6C8366F.function_name}", + "principal": "apigateway.amazonaws.com", + "source_arn": "${aws_api_gateway_rest_api.cloudApi_api_2B334D75.execution_arn}/*/GET/users", + "statement_id": "AllowExecutionFromAPIGateway-GET-41f0e61d" + } + }, + "aws_s3_bucket": { + "Code": { + "//": { + "metadata": { + "path": "root/Default/Code", + "uniqueId": "Code" + } + }, + "bucket_prefix": "code-c84a50b1-" + } + }, + "aws_s3_object": { + "cloudApi_cloudApi-OnRequest-cdafee6e_S3Object_5DAAA0EF": { + "//": { + "metadata": { + "path": "root/Default/Default/cloud.Api/cloud.Api-OnRequest-cdafee6e/S3Object", + "uniqueId": "cloudApi_cloudApi-OnRequest-cdafee6e_S3Object_5DAAA0EF" + } + }, + "bucket": "${aws_s3_bucket.Code.bucket}", + "key": "", + "source": "" + }, + "testGET--usershasdefaultcorsheaders_Handler_S3Object_ADAE18A8": { + "//": { + "metadata": { + "path": "root/Default/Default/test:GET --users has default cors headers/Handler/S3Object", + "uniqueId": "testGET--usershasdefaultcorsheaders_Handler_S3Object_ADAE18A8" + } + }, + "bucket": "${aws_s3_bucket.Code.bucket}", + "key": "", + "source": "" + }, + "testOPTIONS--usershasdefaultcorsheaders_Handler_S3Object_2F99FE7D": { + "//": { + "metadata": { + "path": "root/Default/Default/test:OPTIONS --users has default cors headers/Handler/S3Object", + "uniqueId": "testOPTIONS--usershasdefaultcorsheaders_Handler_S3Object_2F99FE7D" + } + }, + "bucket": "${aws_s3_bucket.Code.bucket}", + "key": "", + "source": "" + } + } + } +} +``` + +## preflight.assertions-1.js +```js +module.exports = function({ $stdlib }) { + const std = $stdlib.std; + class Assert extends $stdlib.std.Resource { + constructor(scope, id, ) { + super(scope, id); + } + static _toInflightType(context) { + return ` + require("./inflight.Assert-1.js")({ + }) + `; + } + _toInflight() { + return ` + (await (async () => { + const AssertClient = ${Assert._toInflightType(this)}; + const client = new AssertClient({ + }); + if (client.$inflight_init) { await client.$inflight_init(); } + return client; + })()) + `; + } + _getInflightOps() { + return ["equalStr", "isNil", "equalNum", "$inflight_init"]; + } + } + return { Assert }; +}; + +``` + +## preflight.js +```js +const $stdlib = require('@winglang/sdk'); +const $plugins = ((s) => !s ? [] : s.split(';'))(process.env.WING_PLUGIN_PATHS); +const $outdir = process.env.WING_SYNTH_DIR ?? "."; +const $wing_is_test = process.env.WING_IS_TEST === "true"; +const std = $stdlib.std; +const cloud = $stdlib.cloud; +const ex = $stdlib.ex; +const http = $stdlib.http; +const t = require("./preflight.assertions-1.js")({ $stdlib }); +class $Root extends $stdlib.std.Resource { + constructor(scope, id) { + super(scope, id); + class $Closure1 extends $stdlib.std.Resource { + constructor(scope, id, ) { + super(scope, id); + (std.Node.of(this)).hidden = true; + } + static _toInflightType(context) { + return ` + require("./inflight.$Closure1-2.js")({ + }) + `; + } + _toInflight() { + return ` + (await (async () => { + const $Closure1Client = ${$Closure1._toInflightType(this)}; + const client = new $Closure1Client({ + }); + if (client.$inflight_init) { await client.$inflight_init(); } + return client; + })()) + `; + } + _getInflightOps() { + return ["handle", "$inflight_init"]; + } + } + class $Closure2 extends $stdlib.std.Resource { + constructor(scope, id, ) { + super(scope, id); + (std.Node.of(this)).hidden = true; + } + static _toInflightType(context) { + return ` + require("./inflight.$Closure2-2.js")({ + $apiDefaultCors_url: ${context._lift(apiDefaultCors.url)}, + $http_Util: ${context._lift(http.Util)}, + $t_Assert: ${context._lift(t.Assert)}, + }) + `; + } + _toInflight() { + return ` + (await (async () => { + const $Closure2Client = ${$Closure2._toInflightType(this)}; + const client = new $Closure2Client({ + }); + if (client.$inflight_init) { await client.$inflight_init(); } + return client; + })()) + `; + } + _getInflightOps() { + return ["handle", "$inflight_init"]; + } + _registerBind(host, ops) { + if (ops.includes("handle")) { + $Closure2._registerBindObject(apiDefaultCors.url, host, []); + } + super._registerBind(host, ops); + } + } + class $Closure3 extends $stdlib.std.Resource { + constructor(scope, id, ) { + super(scope, id); + (std.Node.of(this)).hidden = true; + } + static _toInflightType(context) { + return ` + require("./inflight.$Closure3-2.js")({ + $apiDefaultCors_url: ${context._lift(apiDefaultCors.url)}, + $http_HttpMethod: ${context._lift(http.HttpMethod)}, + $http_Util: ${context._lift(http.Util)}, + $t_Assert: ${context._lift(t.Assert)}, + }) + `; + } + _toInflight() { + return ` + (await (async () => { + const $Closure3Client = ${$Closure3._toInflightType(this)}; + const client = new $Closure3Client({ + }); + if (client.$inflight_init) { await client.$inflight_init(); } + return client; + })()) + `; + } + _getInflightOps() { + return ["handle", "$inflight_init"]; + } + _registerBind(host, ops) { + if (ops.includes("handle")) { + $Closure3._registerBindObject(apiDefaultCors.url, host, []); + } + super._registerBind(host, ops); + } + } + const apiDefaultCors = this.node.root.newAbstract("@winglang/sdk.cloud.Api",this,"cloud.Api",{ cors: true }); + (apiDefaultCors.get("/users",new $Closure1(this,"$Closure1"))); + this.node.root.new("@winglang/sdk.std.Test",std.Test,this,"test:GET /users has default cors headers",new $Closure2(this,"$Closure2")); + this.node.root.new("@winglang/sdk.std.Test",std.Test,this,"test:OPTIONS /users has default cors headers",new $Closure3(this,"$Closure3")); + } +} +const $App = $stdlib.core.App.for(process.env.WING_TARGET); +new $App({ outdir: $outdir, name: "api_cors_default", rootConstruct: $Root, plugins: $plugins, isTestEnvironment: $wing_is_test, entrypointDir: process.env['WING_SOURCE_DIR'], rootId: process.env['WING_ROOT_ID'] }).synth(); + +``` + diff --git a/tools/hangar/__snapshots__/test_corpus/valid/assertions.w_compile_tf-aws.md b/tools/hangar/__snapshots__/test_corpus/valid/assertions.w_compile_tf-aws.md index 1461d316c68..20388291ca6 100644 --- a/tools/hangar/__snapshots__/test_corpus/valid/assertions.w_compile_tf-aws.md +++ b/tools/hangar/__snapshots__/test_corpus/valid/assertions.w_compile_tf-aws.md @@ -10,7 +10,7 @@ module.exports = function({ }) { } catch ($error_e) { const e = $error_e.message; - {((msg) => {throw new Error(msg)})(String.raw({ raw: ["expected: ", " got: ", ""] }, b, a))}; + throw new Error(String.raw({ raw: ["expected: ", " got: ", ""] }, b, a)); } } static async isNil(a) { @@ -20,7 +20,7 @@ module.exports = function({ }) { catch ($error_e) { const e = $error_e.message; {console.log(e)}; - {((msg) => {throw new Error(msg)})(String.raw({ raw: ["expected '", "' to be nil"] }, a))}; + throw new Error(String.raw({ raw: ["expected '", "' to be nil"] }, a)); } } static async equalNum(a, b) { @@ -30,7 +30,7 @@ module.exports = function({ }) { catch ($error_e) { const e = $error_e.message; {console.log(e)}; - {((msg) => {throw new Error(msg)})(String.raw({ raw: ["expected: ", " got: ", ""] }, b, a))}; + throw new Error(String.raw({ raw: ["expected: ", " got: ", ""] }, b, a)); } } } @@ -74,6 +74,7 @@ module.exports = function({ }) { ## preflight.js ```js const $stdlib = require('@winglang/sdk'); +const $plugins = ((s) => !s ? [] : s.split(';'))(process.env.WING_PLUGIN_PATHS); const $outdir = process.env.WING_SYNTH_DIR ?? "."; const $wing_is_test = process.env.WING_IS_TEST === "true"; const std = $stdlib.std; @@ -83,24 +84,26 @@ class $Root extends $stdlib.std.Resource { class Assert extends $stdlib.std.Resource { constructor(scope, id, ) { super(scope, id); - this._addInflightOps("equalStr", "isNil", "equalNum", "$inflight_init"); } static _toInflightType(context) { - return $stdlib.core.NodeJsCode.fromInline(` + return ` require("./inflight.Assert-1.js")({ }) - `); + `; } _toInflight() { - return $stdlib.core.NodeJsCode.fromInline(` + return ` (await (async () => { - const AssertClient = ${Assert._toInflightType(this).text}; + const AssertClient = ${Assert._toInflightType(this)}; const client = new AssertClient({ }); if (client.$inflight_init) { await client.$inflight_init(); } return client; })()) - `); + `; + } + _getInflightOps() { + return ["equalStr", "isNil", "equalNum", "$inflight_init"]; } } } diff --git a/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_compile_tf-aws.md b/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_compile_tf-aws.md index 0db07378045..09872ca6cdd 100644 --- a/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_compile_tf-aws.md +++ b/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_compile_tf-aws.md @@ -1,6 +1,6 @@ # [website_with_api.w](../../../../../examples/tests/valid/website_with_api.w) | compile | tf-aws -## inflight.$Closure1-1.js +## inflight.$Closure1-2.js ```js module.exports = function({ $std_Json, $usersTable }) { class $Closure1 { @@ -18,7 +18,7 @@ module.exports = function({ $std_Json, $usersTable }) { ``` -## inflight.$Closure2-1.js +## inflight.$Closure2-2.js ```js module.exports = function({ $std_Json, $usersTable }) { class $Closure2 { @@ -41,17 +41,25 @@ module.exports = function({ $std_Json, $usersTable }) { ``` -## inflight.$Closure3-1.js +## inflight.$Closure3-2.js ```js -module.exports = function({ }) { +module.exports = function({ $api_url, $http_HttpMethod, $http_Util, $std_Json, $t_Assert }) { class $Closure3 { constructor({ }) { const $obj = (...args) => this.handle(...args); Object.setPrototypeOf($obj, this); return $obj; } - async handle(req) { - return ({"headers": ({"Access-Control-Allow-Headers": "Content-Type","Access-Control-Allow-Origin": "*","Access-Control-Allow-Methods": "OPTIONS,POST,GET"}),"status": 204}); + async handle() { + const response = (await $http_Util.fetch(($api_url + "/users"),({"method": $http_HttpMethod.GET,"headers": ({"Content-Type": "text/json"})}))); + const headers = response.headers; + (await $t_Assert.equalNum(response.status,200)); + {console.log(((args) => { return JSON.stringify(args[0], null, args[1]?.indent) })([headers]))}; + (await $t_Assert.equalStr((headers)["access-control-allow-origin"],"*")); + (await $t_Assert.equalStr((headers)["access-control-expose-headers"],"Content-Type")); + (await $t_Assert.equalStr((headers)["access-control-allow-credentials"],"false")); + (await $t_Assert.isNil((headers)["access-control-allow-headers"])); + (await $t_Assert.isNil((headers)["access-control-allow-methods"])); } } return $Closure3; @@ -59,6 +67,67 @@ module.exports = function({ }) { ``` +## inflight.$Closure4-2.js +```js +module.exports = function({ $api_url, $http_HttpMethod, $http_Util, $t_Assert }) { + class $Closure4 { + constructor({ }) { + const $obj = (...args) => this.handle(...args); + Object.setPrototypeOf($obj, this); + return $obj; + } + async handle() { + const response = (await $http_Util.fetch(($api_url + "/users"),({"method": $http_HttpMethod.OPTIONS,"headers": ({"Content-Type": "text/json"})}))); + const headers = response.headers; + (await $t_Assert.equalNum(response.status,204)); + (await $t_Assert.equalStr((headers)["access-control-allow-methods"],"GET,POST,OPTIONS")); + (await $t_Assert.equalStr((headers)["access-control-allow-headers"],"Content-Type")); + } + } + return $Closure4; +} + +``` + +## inflight.Assert-1.js +```js +module.exports = function({ }) { + class Assert { + static async equalStr(a, b) { + try { + {((cond) => {if (!cond) throw new Error("assertion failed: a == b")})((((a,b) => { try { return require('assert').deepStrictEqual(a,b) === undefined; } catch { return false; } })(a,b)))}; + } + catch ($error_e) { + const e = $error_e.message; + throw new Error(String.raw({ raw: ["expected: ", " got: ", ""] }, b, a)); + } + } + static async isNil(a) { + try { + {((cond) => {if (!cond) throw new Error("assertion failed: a == nil")})((((a,b) => { try { return require('assert').deepStrictEqual(a,b) === undefined; } catch { return false; } })(a,undefined)))}; + } + catch ($error_e) { + const e = $error_e.message; + {console.log(e)}; + throw new Error(String.raw({ raw: ["expected '", "' to be nil"] }, a)); + } + } + static async equalNum(a, b) { + try { + {((cond) => {if (!cond) throw new Error("assertion failed: a == b")})((((a,b) => { try { return require('assert').deepStrictEqual(a,b) === undefined; } catch { return false; } })(a,b)))}; + } + catch ($error_e) { + const e = $error_e.message; + {console.log(e)}; + throw new Error(String.raw({ raw: ["expected: ", " got: ", ""] }, b, a)); + } + } + } + return Assert; +} + +``` + ## main.tf.json ```json { @@ -129,7 +198,7 @@ module.exports = function({ }) { }, "output": { "WING_TEST_RUNNER_FUNCTION_ARNS": { - "value": "[]" + "value": "[[\"root/Default/Default/test:GET --users\",\"${aws_lambda_function.testGET--users_Handler_5E592AA6.arn}\"],[\"root/Default/Default/test:OPTIONS --users\",\"${aws_lambda_function.testOPTIONS--users_Handler_01361C41.arn}\"]]" } }, "provider": { @@ -163,7 +232,7 @@ module.exports = function({ }) { "uniqueId": "cloudApi_api_2B334D75" } }, - "body": "{\"openapi\":\"3.0.3\",\"paths\":{\"/users\":{\"get\":{\"operationId\":\"get-users\",\"responses\":{\"200\":{\"description\":\"200 response\",\"content\":{}}},\"parameters\":[],\"x-amazon-apigateway-integration\":{\"uri\":\"arn:aws:apigateway:${data.aws_region.Region.name}:lambda:path/2015-03-31/functions/${aws_lambda_function.cloudApi_cloudApi-OnRequest-cdafee6e_A6C8366F.arn}/invocations\",\"type\":\"aws_proxy\",\"httpMethod\":\"POST\",\"responses\":{\"default\":{\"statusCode\":\"200\"}},\"passthroughBehavior\":\"when_no_match\",\"contentHandling\":\"CONVERT_TO_TEXT\"}},\"post\":{\"operationId\":\"post-users\",\"responses\":{\"200\":{\"description\":\"200 response\",\"content\":{}}},\"parameters\":[],\"x-amazon-apigateway-integration\":{\"uri\":\"arn:aws:apigateway:${data.aws_region.Region.name}:lambda:path/2015-03-31/functions/${aws_lambda_function.cloudApi_cloudApi-OnRequest-86898773_701F5CA7.arn}/invocations\",\"type\":\"aws_proxy\",\"httpMethod\":\"POST\",\"responses\":{\"default\":{\"statusCode\":\"200\"}},\"passthroughBehavior\":\"when_no_match\",\"contentHandling\":\"CONVERT_TO_TEXT\"}},\"options\":{\"operationId\":\"options-users\",\"responses\":{\"200\":{\"description\":\"200 response\",\"content\":{}}},\"parameters\":[],\"x-amazon-apigateway-integration\":{\"uri\":\"arn:aws:apigateway:${data.aws_region.Region.name}:lambda:path/2015-03-31/functions/${aws_lambda_function.cloudApi_cloudApi-OnRequest-3fc9280c_5DA20E7A.arn}/invocations\",\"type\":\"aws_proxy\",\"httpMethod\":\"POST\",\"responses\":{\"default\":{\"statusCode\":\"200\"}},\"passthroughBehavior\":\"when_no_match\",\"contentHandling\":\"CONVERT_TO_TEXT\"}}},\"/{proxy+}\":{\"x-amazon-apigateway-any-method\":{\"produces\":[\"application/json\"],\"consumes\":[\"application/json\"],\"x-amazon-apigateway-integration\":{\"type\":\"mock\",\"requestTemplates\":{\"application/json\":\"{\\\"statusCode\\\": 404}\"},\"responses\":{\"default\":{\"statusCode\":\"404\",\"responseParameters\":{\"method.response.header.Content-Type\":\"'application/json'\"},\"responseTemplates\":{\"application/json\":\"{\\\"statusCode: 404, \\\"message\\\": \\\"Error: Resource not found\\\"}\"}}}},\"responses\":{\"404\":{\"description\":\"404 response\",\"headers\":{\"Content-Type\":{\"type\":\"string\"}}}}}}}}", + "body": "{\"openapi\":\"3.0.3\",\"paths\":{\"/users\":{\"get\":{\"operationId\":\"get-users\",\"responses\":{\"200\":{\"description\":\"200 response\",\"content\":{},\"headers\":{\"Access-Control-Allow-Origin\":{\"schema\":{\"type\":\"string\"}},\"Access-Control-Allow-Methods\":{\"schema\":{\"type\":\"string\"}},\"Access-Control-Allow-Headers\":{\"schema\":{\"type\":\"string\"}}}}},\"parameters\":[],\"x-amazon-apigateway-integration\":{\"uri\":\"arn:aws:apigateway:${data.aws_region.Region.name}:lambda:path/2015-03-31/functions/${aws_lambda_function.cloudApi_cloudApi-OnRequest-cdafee6e_A6C8366F.arn}/invocations\",\"type\":\"aws_proxy\",\"httpMethod\":\"POST\",\"responses\":{\"default\":{\"statusCode\":\"200\"}},\"passthroughBehavior\":\"when_no_match\",\"contentHandling\":\"CONVERT_TO_TEXT\"}},\"post\":{\"operationId\":\"post-users\",\"responses\":{\"200\":{\"description\":\"200 response\",\"content\":{},\"headers\":{\"Access-Control-Allow-Origin\":{\"schema\":{\"type\":\"string\"}},\"Access-Control-Allow-Methods\":{\"schema\":{\"type\":\"string\"}},\"Access-Control-Allow-Headers\":{\"schema\":{\"type\":\"string\"}}}}},\"parameters\":[],\"x-amazon-apigateway-integration\":{\"uri\":\"arn:aws:apigateway:${data.aws_region.Region.name}:lambda:path/2015-03-31/functions/${aws_lambda_function.cloudApi_cloudApi-OnRequest-86898773_701F5CA7.arn}/invocations\",\"type\":\"aws_proxy\",\"httpMethod\":\"POST\",\"responses\":{\"default\":{\"statusCode\":\"200\"}},\"passthroughBehavior\":\"when_no_match\",\"contentHandling\":\"CONVERT_TO_TEXT\"}}},\"/{proxy+}\":{\"x-amazon-apigateway-any-method\":{\"produces\":[\"application/json\"],\"consumes\":[\"application/json\"],\"x-amazon-apigateway-integration\":{\"type\":\"mock\",\"requestTemplates\":{\"application/json\":\"{\\\"statusCode\\\": 404}\"},\"responses\":{\"default\":{\"statusCode\":\"404\",\"responseParameters\":{\"method.response.header.Content-Type\":\"'application/json'\"},\"responseTemplates\":{\"application/json\":\"{\\\"statusCode: 404, \\\"message\\\": \\\"Error: Resource not found\\\"}\"}}}},\"responses\":{\"404\":{\"description\":\"404 response\",\"headers\":{\"Content-Type\":{\"type\":\"string\"}}}}}}}}", "name": "api-c895068c" } }, @@ -265,15 +334,6 @@ module.exports = function({ }) { } }, "aws_iam_role": { - "cloudApi_cloudApi-OnRequest-3fc9280c_IamRole_EBC99528": { - "//": { - "metadata": { - "path": "root/Default/Default/cloud.Api/cloud.Api-OnRequest-3fc9280c/IamRole", - "uniqueId": "cloudApi_cloudApi-OnRequest-3fc9280c_IamRole_EBC99528" - } - }, - "assume_role_policy": "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Action\":\"sts:AssumeRole\",\"Principal\":{\"Service\":\"lambda.amazonaws.com\"},\"Effect\":\"Allow\"}]}" - }, "cloudApi_cloudApi-OnRequest-86898773_IamRole_6300C24F": { "//": { "metadata": { @@ -291,19 +351,27 @@ module.exports = function({ }) { } }, "assume_role_policy": "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Action\":\"sts:AssumeRole\",\"Principal\":{\"Service\":\"lambda.amazonaws.com\"},\"Effect\":\"Allow\"}]}" - } - }, - "aws_iam_role_policy": { - "cloudApi_cloudApi-OnRequest-3fc9280c_IamRolePolicy_1552D6FE": { + }, + "testGET--users_Handler_IamRole_3C6B1A52": { "//": { "metadata": { - "path": "root/Default/Default/cloud.Api/cloud.Api-OnRequest-3fc9280c/IamRolePolicy", - "uniqueId": "cloudApi_cloudApi-OnRequest-3fc9280c_IamRolePolicy_1552D6FE" + "path": "root/Default/Default/test:GET --users/Handler/IamRole", + "uniqueId": "testGET--users_Handler_IamRole_3C6B1A52" } }, - "policy": "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Action\":\"none:null\",\"Resource\":\"*\"}]}", - "role": "${aws_iam_role.cloudApi_cloudApi-OnRequest-3fc9280c_IamRole_EBC99528.name}" + "assume_role_policy": "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Action\":\"sts:AssumeRole\",\"Principal\":{\"Service\":\"lambda.amazonaws.com\"},\"Effect\":\"Allow\"}]}" }, + "testOPTIONS--users_Handler_IamRole_F9362AB0": { + "//": { + "metadata": { + "path": "root/Default/Default/test:OPTIONS --users/Handler/IamRole", + "uniqueId": "testOPTIONS--users_Handler_IamRole_F9362AB0" + } + }, + "assume_role_policy": "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Action\":\"sts:AssumeRole\",\"Principal\":{\"Service\":\"lambda.amazonaws.com\"},\"Effect\":\"Allow\"}]}" + } + }, + "aws_iam_role_policy": { "cloudApi_cloudApi-OnRequest-86898773_IamRolePolicy_DAC639E5": { "//": { "metadata": { @@ -323,19 +391,29 @@ module.exports = function({ }) { }, "policy": "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Action\":[\"dynamodb:Scan\"],\"Resource\":[\"${aws_dynamodb_table.exTable.arn}\"],\"Effect\":\"Allow\"}]}", "role": "${aws_iam_role.cloudApi_cloudApi-OnRequest-cdafee6e_IamRole_4382C442.name}" - } - }, - "aws_iam_role_policy_attachment": { - "cloudApi_cloudApi-OnRequest-3fc9280c_IamRolePolicyAttachment_A988C18B": { + }, + "testGET--users_Handler_IamRolePolicy_70F076F2": { "//": { "metadata": { - "path": "root/Default/Default/cloud.Api/cloud.Api-OnRequest-3fc9280c/IamRolePolicyAttachment", - "uniqueId": "cloudApi_cloudApi-OnRequest-3fc9280c_IamRolePolicyAttachment_A988C18B" + "path": "root/Default/Default/test:GET --users/Handler/IamRolePolicy", + "uniqueId": "testGET--users_Handler_IamRolePolicy_70F076F2" } }, - "policy_arn": "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole", - "role": "${aws_iam_role.cloudApi_cloudApi-OnRequest-3fc9280c_IamRole_EBC99528.name}" + "policy": "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Action\":\"none:null\",\"Resource\":\"*\"}]}", + "role": "${aws_iam_role.testGET--users_Handler_IamRole_3C6B1A52.name}" }, + "testOPTIONS--users_Handler_IamRolePolicy_E1FFA7C9": { + "//": { + "metadata": { + "path": "root/Default/Default/test:OPTIONS --users/Handler/IamRolePolicy", + "uniqueId": "testOPTIONS--users_Handler_IamRolePolicy_E1FFA7C9" + } + }, + "policy": "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Action\":\"none:null\",\"Resource\":\"*\"}]}", + "role": "${aws_iam_role.testOPTIONS--users_Handler_IamRole_F9362AB0.name}" + } + }, + "aws_iam_role_policy_attachment": { "cloudApi_cloudApi-OnRequest-86898773_IamRolePolicyAttachment_6E485A17": { "//": { "metadata": { @@ -355,14 +433,34 @@ module.exports = function({ }) { }, "policy_arn": "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole", "role": "${aws_iam_role.cloudApi_cloudApi-OnRequest-cdafee6e_IamRole_4382C442.name}" + }, + "testGET--users_Handler_IamRolePolicyAttachment_CE57C035": { + "//": { + "metadata": { + "path": "root/Default/Default/test:GET --users/Handler/IamRolePolicyAttachment", + "uniqueId": "testGET--users_Handler_IamRolePolicyAttachment_CE57C035" + } + }, + "policy_arn": "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole", + "role": "${aws_iam_role.testGET--users_Handler_IamRole_3C6B1A52.name}" + }, + "testOPTIONS--users_Handler_IamRolePolicyAttachment_41F75288": { + "//": { + "metadata": { + "path": "root/Default/Default/test:OPTIONS --users/Handler/IamRolePolicyAttachment", + "uniqueId": "testOPTIONS--users_Handler_IamRolePolicyAttachment_41F75288" + } + }, + "policy_arn": "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole", + "role": "${aws_iam_role.testOPTIONS--users_Handler_IamRole_F9362AB0.name}" } }, "aws_lambda_function": { - "cloudApi_cloudApi-OnRequest-3fc9280c_5DA20E7A": { + "cloudApi_cloudApi-OnRequest-86898773_701F5CA7": { "//": { "metadata": { - "path": "root/Default/Default/cloud.Api/cloud.Api-OnRequest-3fc9280c/Default", - "uniqueId": "cloudApi_cloudApi-OnRequest-3fc9280c_5DA20E7A" + "path": "root/Default/Default/cloud.Api/cloud.Api-OnRequest-86898773/Default", + "uniqueId": "cloudApi_cloudApi-OnRequest-86898773_701F5CA7" } }, "architectures": [ @@ -370,28 +468,31 @@ module.exports = function({ }) { ], "environment": { "variables": { - "WING_FUNCTION_NAME": "cloud-Api-OnRequest-3fc9280c-c8d3ecf9", + "DYNAMODB_TABLE_NAME_d5d44f18": "${aws_dynamodb_table.exTable.name}", + "DYNAMODB_TABLE_NAME_d5d44f18_COLUMNS": "{\"id\":0,\"name\":0,\"age\":1}", + "DYNAMODB_TABLE_NAME_d5d44f18_PRIMARY_KEY": "id", + "WING_FUNCTION_NAME": "cloud-Api-OnRequest-86898773-c8ed6547", "WING_TARGET": "tf-aws" } }, - "function_name": "cloud-Api-OnRequest-3fc9280c-c8d3ecf9", + "function_name": "cloud-Api-OnRequest-86898773-c8ed6547", "handler": "index.handler", "publish": true, - "role": "${aws_iam_role.cloudApi_cloudApi-OnRequest-3fc9280c_IamRole_EBC99528.arn}", + "role": "${aws_iam_role.cloudApi_cloudApi-OnRequest-86898773_IamRole_6300C24F.arn}", "runtime": "nodejs18.x", "s3_bucket": "${aws_s3_bucket.Code.bucket}", - "s3_key": "${aws_s3_object.cloudApi_cloudApi-OnRequest-3fc9280c_S3Object_EC722CF3.key}", + "s3_key": "${aws_s3_object.cloudApi_cloudApi-OnRequest-86898773_S3Object_12D28469.key}", "timeout": 30, "vpc_config": { "security_group_ids": [], "subnet_ids": [] } }, - "cloudApi_cloudApi-OnRequest-86898773_701F5CA7": { + "cloudApi_cloudApi-OnRequest-cdafee6e_A6C8366F": { "//": { "metadata": { - "path": "root/Default/Default/cloud.Api/cloud.Api-OnRequest-86898773/Default", - "uniqueId": "cloudApi_cloudApi-OnRequest-86898773_701F5CA7" + "path": "root/Default/Default/cloud.Api/cloud.Api-OnRequest-cdafee6e/Default", + "uniqueId": "cloudApi_cloudApi-OnRequest-cdafee6e_A6C8366F" } }, "architectures": [ @@ -402,28 +503,28 @@ module.exports = function({ }) { "DYNAMODB_TABLE_NAME_d5d44f18": "${aws_dynamodb_table.exTable.name}", "DYNAMODB_TABLE_NAME_d5d44f18_COLUMNS": "{\"id\":0,\"name\":0,\"age\":1}", "DYNAMODB_TABLE_NAME_d5d44f18_PRIMARY_KEY": "id", - "WING_FUNCTION_NAME": "cloud-Api-OnRequest-86898773-c8ed6547", + "WING_FUNCTION_NAME": "cloud-Api-OnRequest-cdafee6e-c8147384", "WING_TARGET": "tf-aws" } }, - "function_name": "cloud-Api-OnRequest-86898773-c8ed6547", + "function_name": "cloud-Api-OnRequest-cdafee6e-c8147384", "handler": "index.handler", "publish": true, - "role": "${aws_iam_role.cloudApi_cloudApi-OnRequest-86898773_IamRole_6300C24F.arn}", + "role": "${aws_iam_role.cloudApi_cloudApi-OnRequest-cdafee6e_IamRole_4382C442.arn}", "runtime": "nodejs18.x", "s3_bucket": "${aws_s3_bucket.Code.bucket}", - "s3_key": "${aws_s3_object.cloudApi_cloudApi-OnRequest-86898773_S3Object_12D28469.key}", + "s3_key": "${aws_s3_object.cloudApi_cloudApi-OnRequest-cdafee6e_S3Object_5DAAA0EF.key}", "timeout": 30, "vpc_config": { "security_group_ids": [], "subnet_ids": [] } }, - "cloudApi_cloudApi-OnRequest-cdafee6e_A6C8366F": { + "testGET--users_Handler_5E592AA6": { "//": { "metadata": { - "path": "root/Default/Default/cloud.Api/cloud.Api-OnRequest-cdafee6e/Default", - "uniqueId": "cloudApi_cloudApi-OnRequest-cdafee6e_A6C8366F" + "path": "root/Default/Default/test:GET --users/Handler/Default", + "uniqueId": "testGET--users_Handler_5E592AA6" } }, "architectures": [ @@ -431,20 +532,48 @@ module.exports = function({ }) { ], "environment": { "variables": { - "DYNAMODB_TABLE_NAME_d5d44f18": "${aws_dynamodb_table.exTable.name}", - "DYNAMODB_TABLE_NAME_d5d44f18_COLUMNS": "{\"id\":0,\"name\":0,\"age\":1}", - "DYNAMODB_TABLE_NAME_d5d44f18_PRIMARY_KEY": "id", - "WING_FUNCTION_NAME": "cloud-Api-OnRequest-cdafee6e-c8147384", - "WING_TARGET": "tf-aws" + "WING_FUNCTION_NAME": "Handler-c86bda55", + "WING_TARGET": "tf-aws", + "WING_TOKEN_TFTOKEN_TOKEN_8": "${jsonencode(aws_api_gateway_stage.cloudApi_api_stage_BBB283E4.invoke_url)}" } }, - "function_name": "cloud-Api-OnRequest-cdafee6e-c8147384", + "function_name": "Handler-c86bda55", "handler": "index.handler", "publish": true, - "role": "${aws_iam_role.cloudApi_cloudApi-OnRequest-cdafee6e_IamRole_4382C442.arn}", + "role": "${aws_iam_role.testGET--users_Handler_IamRole_3C6B1A52.arn}", "runtime": "nodejs18.x", "s3_bucket": "${aws_s3_bucket.Code.bucket}", - "s3_key": "${aws_s3_object.cloudApi_cloudApi-OnRequest-cdafee6e_S3Object_5DAAA0EF.key}", + "s3_key": "${aws_s3_object.testGET--users_Handler_S3Object_8248DE4B.key}", + "timeout": 30, + "vpc_config": { + "security_group_ids": [], + "subnet_ids": [] + } + }, + "testOPTIONS--users_Handler_01361C41": { + "//": { + "metadata": { + "path": "root/Default/Default/test:OPTIONS --users/Handler/Default", + "uniqueId": "testOPTIONS--users_Handler_01361C41" + } + }, + "architectures": [ + "arm64" + ], + "environment": { + "variables": { + "WING_FUNCTION_NAME": "Handler-c8b7642e", + "WING_TARGET": "tf-aws", + "WING_TOKEN_TFTOKEN_TOKEN_8": "${jsonencode(aws_api_gateway_stage.cloudApi_api_stage_BBB283E4.invoke_url)}" + } + }, + "function_name": "Handler-c8b7642e", + "handler": "index.handler", + "publish": true, + "role": "${aws_iam_role.testOPTIONS--users_Handler_IamRole_F9362AB0.arn}", + "runtime": "nodejs18.x", + "s3_bucket": "${aws_s3_bucket.Code.bucket}", + "s3_key": "${aws_s3_object.testOPTIONS--users_Handler_S3Object_C0C4A75C.key}", "timeout": 30, "vpc_config": { "security_group_ids": [], @@ -466,19 +595,6 @@ module.exports = function({ }) { "source_arn": "${aws_api_gateway_rest_api.cloudApi_api_2B334D75.execution_arn}/*/GET/users", "statement_id": "AllowExecutionFromAPIGateway-GET-41f0e61d" }, - "cloudApi_api_permission-OPTIONS-41f0e61d_12224EFF": { - "//": { - "metadata": { - "path": "root/Default/Default/cloud.Api/api/permission-OPTIONS-41f0e61d", - "uniqueId": "cloudApi_api_permission-OPTIONS-41f0e61d_12224EFF" - } - }, - "action": "lambda:InvokeFunction", - "function_name": "${aws_lambda_function.cloudApi_cloudApi-OnRequest-3fc9280c_5DA20E7A.function_name}", - "principal": "apigateway.amazonaws.com", - "source_arn": "${aws_api_gateway_rest_api.cloudApi_api_2B334D75.execution_arn}/*/OPTIONS/users", - "statement_id": "AllowExecutionFromAPIGateway-OPTIONS-41f0e61d" - }, "cloudApi_api_permission-POST-41f0e61d_743604B6": { "//": { "metadata": { @@ -559,17 +675,6 @@ module.exports = function({ }) { } }, "aws_s3_object": { - "cloudApi_cloudApi-OnRequest-3fc9280c_S3Object_EC722CF3": { - "//": { - "metadata": { - "path": "root/Default/Default/cloud.Api/cloud.Api-OnRequest-3fc9280c/S3Object", - "uniqueId": "cloudApi_cloudApi-OnRequest-3fc9280c_S3Object_EC722CF3" - } - }, - "bucket": "${aws_s3_bucket.Code.bucket}", - "key": "", - "source": "" - }, "cloudApi_cloudApi-OnRequest-86898773_S3Object_12D28469": { "//": { "metadata": { @@ -622,12 +727,68 @@ module.exports = function({ }) { "aws_s3_bucket.cloudWebsite_WebsiteBucket_EB03D355" ], "key": "config.json" + }, + "testGET--users_Handler_S3Object_8248DE4B": { + "//": { + "metadata": { + "path": "root/Default/Default/test:GET --users/Handler/S3Object", + "uniqueId": "testGET--users_Handler_S3Object_8248DE4B" + } + }, + "bucket": "${aws_s3_bucket.Code.bucket}", + "key": "", + "source": "" + }, + "testOPTIONS--users_Handler_S3Object_C0C4A75C": { + "//": { + "metadata": { + "path": "root/Default/Default/test:OPTIONS --users/Handler/S3Object", + "uniqueId": "testOPTIONS--users_Handler_S3Object_C0C4A75C" + } + }, + "bucket": "${aws_s3_bucket.Code.bucket}", + "key": "", + "source": "" } } } } ``` +## preflight.assertions-1.js +```js +module.exports = function({ $stdlib }) { + const std = $stdlib.std; + class Assert extends $stdlib.std.Resource { + constructor(scope, id, ) { + super(scope, id); + } + static _toInflightType(context) { + return ` + require("./inflight.Assert-1.js")({ + }) + `; + } + _toInflight() { + return ` + (await (async () => { + const AssertClient = ${Assert._toInflightType(this)}; + const client = new AssertClient({ + }); + if (client.$inflight_init) { await client.$inflight_init(); } + return client; + })()) + `; + } + _getInflightOps() { + return ["equalStr", "isNil", "equalNum", "$inflight_init"]; + } + } + return { Assert }; +}; + +``` + ## preflight.js ```js const $stdlib = require('@winglang/sdk'); @@ -637,6 +798,8 @@ const $wing_is_test = process.env.WING_IS_TEST === "true"; const std = $stdlib.std; const cloud = $stdlib.cloud; const ex = $stdlib.ex; +const http = $stdlib.http; +const t = require("./preflight.assertions-1.js")({ $stdlib }); class $Root extends $stdlib.std.Resource { constructor(scope, id) { super(scope, id); @@ -647,7 +810,7 @@ class $Root extends $stdlib.std.Resource { } static _toInflightType(context) { return ` - require("./inflight.$Closure1-1.js")({ + require("./inflight.$Closure1-2.js")({ $std_Json: ${context._lift(std.Json)}, $usersTable: ${context._lift(usersTable)}, }) @@ -681,7 +844,7 @@ class $Root extends $stdlib.std.Resource { } static _toInflightType(context) { return ` - require("./inflight.$Closure2-1.js")({ + require("./inflight.$Closure2-2.js")({ $std_Json: ${context._lift(std.Json)}, $usersTable: ${context._lift(usersTable)}, }) @@ -715,7 +878,12 @@ class $Root extends $stdlib.std.Resource { } static _toInflightType(context) { return ` - require("./inflight.$Closure3-1.js")({ + require("./inflight.$Closure3-2.js")({ + $api_url: ${context._lift(api.url)}, + $http_HttpMethod: ${context._lift(http.HttpMethod)}, + $http_Util: ${context._lift(http.Util)}, + $std_Json: ${context._lift(std.Json)}, + $t_Assert: ${context._lift(t.Assert)}, }) `; } @@ -733,17 +901,59 @@ class $Root extends $stdlib.std.Resource { _getInflightOps() { return ["handle", "$inflight_init"]; } + _registerBind(host, ops) { + if (ops.includes("handle")) { + $Closure3._registerBindObject(api.url, host, []); + } + super._registerBind(host, ops); + } + } + class $Closure4 extends $stdlib.std.Resource { + constructor(scope, id, ) { + super(scope, id); + (std.Node.of(this)).hidden = true; + } + static _toInflightType(context) { + return ` + require("./inflight.$Closure4-2.js")({ + $api_url: ${context._lift(api.url)}, + $http_HttpMethod: ${context._lift(http.HttpMethod)}, + $http_Util: ${context._lift(http.Util)}, + $t_Assert: ${context._lift(t.Assert)}, + }) + `; + } + _toInflight() { + return ` + (await (async () => { + const $Closure4Client = ${$Closure4._toInflightType(this)}; + const client = new $Closure4Client({ + }); + if (client.$inflight_init) { await client.$inflight_init(); } + return client; + })()) + `; + } + _getInflightOps() { + return ["handle", "$inflight_init"]; + } + _registerBind(host, ops) { + if (ops.includes("handle")) { + $Closure4._registerBindObject(api.url, host, []); + } + super._registerBind(host, ops); + } } - const api = this.node.root.newAbstract("@winglang/sdk.cloud.Api",this,"cloud.Api"); + const api = this.node.root.newAbstract("@winglang/sdk.cloud.Api",this,"cloud.Api",{ cors: true, corsOptions: ({"allowOrigin": ["*"],"allowMethods": [cloud.HttpMethod.GET, cloud.HttpMethod.POST, cloud.HttpMethod.OPTIONS],"allowHeaders": ["Content-Type"],"allowCredentials": false,"exposeHeaders": ["Content-Type"]}) }); const website = this.node.root.newAbstract("@winglang/sdk.cloud.Website",this,"cloud.Website",{ path: "./website_with_api" }); const usersTable = this.node.root.newAbstract("@winglang/sdk.ex.Table",this,"ex.Table",{ name: "users-table", primaryKey: "id", columns: ({"id": ex.ColumnType.STRING,"name": ex.ColumnType.STRING,"age": ex.ColumnType.NUMBER}) }); const getHandler = new $Closure1(this,"$Closure1"); const postHandler = new $Closure2(this,"$Closure2"); - const optionsHandler = new $Closure3(this,"$Closure3"); (api.get("/users",getHandler)); (api.post("/users",postHandler)); - (api.options("/users",optionsHandler)); (website.addJson("config.json",({"apiUrl": api.url}))); + this.node.root.new("@winglang/sdk.std.Test",std.Test,this,"test:GET /users",new $Closure3(this,"$Closure3")); + this.node.root.new("@winglang/sdk.std.Test",std.Test,this,"test:OPTIONS /users",new $Closure4(this,"$Closure4")); } } const $App = $stdlib.core.App.for(process.env.WING_TARGET); diff --git a/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_test_sim.md b/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_test_sim.md index 997d8455de7..c211c4afe13 100644 --- a/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_test_sim.md +++ b/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_test_sim.md @@ -3,7 +3,7 @@ ## stdout.log ```log pass ┌ website_with_api.wsim » root/env0/test:GET --users - └ {"access-control-allow-credentials":"false","access-control-allow-origin":"*","access-control-expose-headers":"Content-Type","connection":"keep-alive","content-length":"12","content-type":"text/html; charset=utf-8","date":"Thu, 24 Aug 2023 10:37:05 GMT","etag":"W/\"c-1bl/ybs8pYaLwWPVCd0mBRAdBaY\"","keep-alive":"timeout=5","x-powered-by":"Express"} + └ {"access-control-allow-credentials":"false","access-control-allow-origin":"*","access-control-expose-headers":"Content-Type","connection":"keep-alive","content-length":"12","content-type":"text/html; charset=utf-8","date":"Mon, 04 Sep 2023 13:33:12 GMT","etag":"W/\"c-1bl/ybs8pYaLwWPVCd0mBRAdBaY\"","keep-alive":"timeout=5","x-powered-by":"Express"} pass ─ website_with_api.wsim » root/env1/test:OPTIONS --users From 6975cf7269153cbd800bfacc14af20dd7866d3e3 Mon Sep 17 00:00:00 2001 From: Sebastian Korfmann Date: Mon, 4 Sep 2023 13:38:15 +0000 Subject: [PATCH 43/47] ignore .pnpm-store --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 233e1f9b1c1..c41313fee1d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,7 @@ node_modules/ +.pnpm-store/ + # Terraform state files *.tfstate *.tfstate.* From 82b582da160dbac31c69d7949ff70ce5ef16c19d Mon Sep 17 00:00:00 2001 From: Sebastian Korfmann Date: Mon, 4 Sep 2023 13:42:31 +0000 Subject: [PATCH 44/47] don't log, creates changes in snapshots --- examples/tests/valid/website_with_api.w | 2 -- 1 file changed, 2 deletions(-) diff --git a/examples/tests/valid/website_with_api.w b/examples/tests/valid/website_with_api.w index 36146c5cc7d..2735cabe09c 100644 --- a/examples/tests/valid/website_with_api.w +++ b/examples/tests/valid/website_with_api.w @@ -65,8 +65,6 @@ test "GET /users" { let headers = response.headers; t.Assert.equalNum(response.status, 200); - log(Json.stringify(headers)); - t.Assert.equalStr(headers.get("access-control-allow-origin"), "*"); t.Assert.equalStr(headers.get("access-control-expose-headers"), "Content-Type"); t.Assert.equalStr(headers.get("access-control-allow-credentials"), "false"); From 6699c5c39035051e4eb69a5adfd53a97b8e1c30c Mon Sep 17 00:00:00 2001 From: Sebastian Korfmann Date: Mon, 4 Sep 2023 13:42:40 +0000 Subject: [PATCH 45/47] typo --- examples/tests/sdk_tests/api/cors.w | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/tests/sdk_tests/api/cors.w b/examples/tests/sdk_tests/api/cors.w index 9a3fe45378c..6a99966930a 100644 --- a/examples/tests/sdk_tests/api/cors.w +++ b/examples/tests/sdk_tests/api/cors.w @@ -9,7 +9,7 @@ let body = "ok!"; api.get("/path", inflight (req) => { return { - status: 200 + status: 200, body: body }; }); From 0382e539f09aa9fbdb9b9a501c2e4cd89be01284 Mon Sep 17 00:00:00 2001 From: Sebastian Korfmann Date: Mon, 4 Sep 2023 13:48:09 +0000 Subject: [PATCH 46/47] Fix tests --- examples/tests/valid/api_cors_default.w | 2 +- .../sdk_tests/api/cors.w_compile_tf-aws.md | 394 ++++++++++++++++++ .../sdk_tests/api/cors.w_test_sim.md | 12 + .../api_cors_default.w_compile_tf-aws.md | 2 +- .../valid/api_cors_default.w_test_sim.md | 13 + .../website_with_api.w_compile_tf-aws.md | 4 +- .../valid/website_with_api.w_test_sim.md | 3 +- 7 files changed, 423 insertions(+), 7 deletions(-) create mode 100644 tools/hangar/__snapshots__/test_corpus/sdk_tests/api/cors.w_compile_tf-aws.md create mode 100644 tools/hangar/__snapshots__/test_corpus/sdk_tests/api/cors.w_test_sim.md create mode 100644 tools/hangar/__snapshots__/test_corpus/valid/api_cors_default.w_test_sim.md diff --git a/examples/tests/valid/api_cors_default.w b/examples/tests/valid/api_cors_default.w index 05f7802f1c2..f0f280f0882 100644 --- a/examples/tests/valid/api_cors_default.w +++ b/examples/tests/valid/api_cors_default.w @@ -39,7 +39,7 @@ test "OPTIONS /users has default cors headers" { t.Assert.equalNum(response.status, 204); // OPTIONS cors headers are set - t.Assert.equalStr(headers.get("access-control-allow-headers"), "Content-Type,Authorization"); + t.Assert.equalStr(headers.get("access-control-allow-headers"), "Content-Type,Authorization,X-Requested-With"); t.Assert.equalStr(headers.get("access-control-allow-methods"), "GET,POST,PUT,DELETE,HEAD,OPTIONS"); // Other headers are not set diff --git a/tools/hangar/__snapshots__/test_corpus/sdk_tests/api/cors.w_compile_tf-aws.md b/tools/hangar/__snapshots__/test_corpus/sdk_tests/api/cors.w_compile_tf-aws.md new file mode 100644 index 00000000000..0dc96f6836e --- /dev/null +++ b/tools/hangar/__snapshots__/test_corpus/sdk_tests/api/cors.w_compile_tf-aws.md @@ -0,0 +1,394 @@ +# [cors.w](../../../../../../examples/tests/sdk_tests/api/cors.w) | compile | tf-aws + +## inflight.$Closure1-1.js +```js +module.exports = function({ $body }) { + class $Closure1 { + constructor({ }) { + const $obj = (...args) => this.handle(...args); + Object.setPrototypeOf($obj, this); + return $obj; + } + async handle(req) { + return ({"status": 200,"body": $body}); + } + } + return $Closure1; +} + +``` + +## inflight.$Closure2-1.js +```js +module.exports = function({ $api_url, $http_Util }) { + class $Closure2 { + constructor({ }) { + const $obj = (...args) => this.handle(...args); + Object.setPrototypeOf($obj, this); + return $obj; + } + async handle() { + const url = ($api_url + "/path"); + const response = (await $http_Util.get(url)); + {((cond) => {if (!cond) throw new Error("assertion failed: response.headers.get(\"access-control-allow-origin\") == \"*\"")})((((a,b) => { try { return require('assert').deepStrictEqual(a,b) === undefined; } catch { return false; } })((response.headers)["access-control-allow-origin"],"*")))}; + } + } + return $Closure2; +} + +``` + +## main.tf.json +```json +{ + "//": { + "metadata": { + "backend": "local", + "stackName": "root", + "version": "0.17.0" + }, + "outputs": { + "root": { + "Default": { + "cloud.TestRunner": { + "TestFunctionArns": "WING_TEST_RUNNER_FUNCTION_ARNS" + } + } + } + } + }, + "data": { + "aws_region": { + "Region": { + "//": { + "metadata": { + "path": "root/Default/Region", + "uniqueId": "Region" + } + } + } + } + }, + "output": { + "WING_TEST_RUNNER_FUNCTION_ARNS": { + "value": "[[\"root/Default/Default/test:http.get and http.fetch can preform a call to an api\",\"${aws_lambda_function.testhttpgetandhttpfetchcanpreformacalltoanapi_Handler_9FEA2521.arn}\"]]" + } + }, + "provider": { + "aws": [ + {} + ] + }, + "resource": { + "aws_api_gateway_deployment": { + "cloudApi_api_deployment_545514BF": { + "//": { + "metadata": { + "path": "root/Default/Default/cloud.Api/api/deployment", + "uniqueId": "cloudApi_api_deployment_545514BF" + } + }, + "lifecycle": { + "create_before_destroy": true + }, + "rest_api_id": "${aws_api_gateway_rest_api.cloudApi_api_2B334D75.id}", + "triggers": { + "redeployment": "${sha256(aws_api_gateway_rest_api.cloudApi_api_2B334D75.body)}" + } + } + }, + "aws_api_gateway_rest_api": { + "cloudApi_api_2B334D75": { + "//": { + "metadata": { + "path": "root/Default/Default/cloud.Api/api/api", + "uniqueId": "cloudApi_api_2B334D75" + } + }, + "body": "{\"openapi\":\"3.0.3\",\"paths\":{\"/path\":{\"get\":{\"operationId\":\"get-path\",\"responses\":{\"200\":{\"description\":\"200 response\",\"content\":{},\"headers\":{\"Access-Control-Allow-Origin\":{\"schema\":{\"type\":\"string\"}},\"Access-Control-Allow-Methods\":{\"schema\":{\"type\":\"string\"}},\"Access-Control-Allow-Headers\":{\"schema\":{\"type\":\"string\"}}}}},\"parameters\":[],\"x-amazon-apigateway-integration\":{\"uri\":\"arn:aws:apigateway:${data.aws_region.Region.name}:lambda:path/2015-03-31/functions/${aws_lambda_function.cloudApi_cloudApi-OnRequest-cdafee6e_A6C8366F.arn}/invocations\",\"type\":\"aws_proxy\",\"httpMethod\":\"POST\",\"responses\":{\"default\":{\"statusCode\":\"200\"}},\"passthroughBehavior\":\"when_no_match\",\"contentHandling\":\"CONVERT_TO_TEXT\"}}},\"/{proxy+}\":{\"x-amazon-apigateway-any-method\":{\"produces\":[\"application/json\"],\"consumes\":[\"application/json\"],\"x-amazon-apigateway-integration\":{\"type\":\"mock\",\"requestTemplates\":{\"application/json\":\"{\\\"statusCode\\\": 404}\"},\"responses\":{\"default\":{\"statusCode\":\"404\",\"responseParameters\":{\"method.response.header.Content-Type\":\"'application/json'\"},\"responseTemplates\":{\"application/json\":\"{\\\"statusCode: 404, \\\"message\\\": \\\"Error: Resource not found\\\"}\"}}}},\"responses\":{\"404\":{\"description\":\"404 response\",\"headers\":{\"Content-Type\":{\"type\":\"string\"}}}}}}}}", + "name": "api-c895068c" + } + }, + "aws_api_gateway_stage": { + "cloudApi_api_stage_BBB283E4": { + "//": { + "metadata": { + "path": "root/Default/Default/cloud.Api/api/stage", + "uniqueId": "cloudApi_api_stage_BBB283E4" + } + }, + "deployment_id": "${aws_api_gateway_deployment.cloudApi_api_deployment_545514BF.id}", + "rest_api_id": "${aws_api_gateway_rest_api.cloudApi_api_2B334D75.id}", + "stage_name": "prod" + } + }, + "aws_iam_role": { + "cloudApi_cloudApi-OnRequest-cdafee6e_IamRole_4382C442": { + "//": { + "metadata": { + "path": "root/Default/Default/cloud.Api/cloud.Api-OnRequest-cdafee6e/IamRole", + "uniqueId": "cloudApi_cloudApi-OnRequest-cdafee6e_IamRole_4382C442" + } + }, + "assume_role_policy": "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Action\":\"sts:AssumeRole\",\"Principal\":{\"Service\":\"lambda.amazonaws.com\"},\"Effect\":\"Allow\"}]}" + }, + "testhttpgetandhttpfetchcanpreformacalltoanapi_Handler_IamRole_678DDBCB": { + "//": { + "metadata": { + "path": "root/Default/Default/test:http.get and http.fetch can preform a call to an api/Handler/IamRole", + "uniqueId": "testhttpgetandhttpfetchcanpreformacalltoanapi_Handler_IamRole_678DDBCB" + } + }, + "assume_role_policy": "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Action\":\"sts:AssumeRole\",\"Principal\":{\"Service\":\"lambda.amazonaws.com\"},\"Effect\":\"Allow\"}]}" + } + }, + "aws_iam_role_policy": { + "cloudApi_cloudApi-OnRequest-cdafee6e_IamRolePolicy_8BF9C89F": { + "//": { + "metadata": { + "path": "root/Default/Default/cloud.Api/cloud.Api-OnRequest-cdafee6e/IamRolePolicy", + "uniqueId": "cloudApi_cloudApi-OnRequest-cdafee6e_IamRolePolicy_8BF9C89F" + } + }, + "policy": "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Action\":\"none:null\",\"Resource\":\"*\"}]}", + "role": "${aws_iam_role.cloudApi_cloudApi-OnRequest-cdafee6e_IamRole_4382C442.name}" + }, + "testhttpgetandhttpfetchcanpreformacalltoanapi_Handler_IamRolePolicy_047F62EA": { + "//": { + "metadata": { + "path": "root/Default/Default/test:http.get and http.fetch can preform a call to an api/Handler/IamRolePolicy", + "uniqueId": "testhttpgetandhttpfetchcanpreformacalltoanapi_Handler_IamRolePolicy_047F62EA" + } + }, + "policy": "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Action\":\"none:null\",\"Resource\":\"*\"}]}", + "role": "${aws_iam_role.testhttpgetandhttpfetchcanpreformacalltoanapi_Handler_IamRole_678DDBCB.name}" + } + }, + "aws_iam_role_policy_attachment": { + "cloudApi_cloudApi-OnRequest-cdafee6e_IamRolePolicyAttachment_5383D6A2": { + "//": { + "metadata": { + "path": "root/Default/Default/cloud.Api/cloud.Api-OnRequest-cdafee6e/IamRolePolicyAttachment", + "uniqueId": "cloudApi_cloudApi-OnRequest-cdafee6e_IamRolePolicyAttachment_5383D6A2" + } + }, + "policy_arn": "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole", + "role": "${aws_iam_role.cloudApi_cloudApi-OnRequest-cdafee6e_IamRole_4382C442.name}" + }, + "testhttpgetandhttpfetchcanpreformacalltoanapi_Handler_IamRolePolicyAttachment_328C8EF0": { + "//": { + "metadata": { + "path": "root/Default/Default/test:http.get and http.fetch can preform a call to an api/Handler/IamRolePolicyAttachment", + "uniqueId": "testhttpgetandhttpfetchcanpreformacalltoanapi_Handler_IamRolePolicyAttachment_328C8EF0" + } + }, + "policy_arn": "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole", + "role": "${aws_iam_role.testhttpgetandhttpfetchcanpreformacalltoanapi_Handler_IamRole_678DDBCB.name}" + } + }, + "aws_lambda_function": { + "cloudApi_cloudApi-OnRequest-cdafee6e_A6C8366F": { + "//": { + "metadata": { + "path": "root/Default/Default/cloud.Api/cloud.Api-OnRequest-cdafee6e/Default", + "uniqueId": "cloudApi_cloudApi-OnRequest-cdafee6e_A6C8366F" + } + }, + "architectures": [ + "arm64" + ], + "environment": { + "variables": { + "WING_FUNCTION_NAME": "cloud-Api-OnRequest-cdafee6e-c8147384", + "WING_TARGET": "tf-aws" + } + }, + "function_name": "cloud-Api-OnRequest-cdafee6e-c8147384", + "handler": "index.handler", + "publish": true, + "role": "${aws_iam_role.cloudApi_cloudApi-OnRequest-cdafee6e_IamRole_4382C442.arn}", + "runtime": "nodejs18.x", + "s3_bucket": "${aws_s3_bucket.Code.bucket}", + "s3_key": "${aws_s3_object.cloudApi_cloudApi-OnRequest-cdafee6e_S3Object_5DAAA0EF.key}", + "timeout": 30, + "vpc_config": { + "security_group_ids": [], + "subnet_ids": [] + } + }, + "testhttpgetandhttpfetchcanpreformacalltoanapi_Handler_9FEA2521": { + "//": { + "metadata": { + "path": "root/Default/Default/test:http.get and http.fetch can preform a call to an api/Handler/Default", + "uniqueId": "testhttpgetandhttpfetchcanpreformacalltoanapi_Handler_9FEA2521" + } + }, + "architectures": [ + "arm64" + ], + "environment": { + "variables": { + "WING_FUNCTION_NAME": "Handler-c838ce37", + "WING_TARGET": "tf-aws", + "WING_TOKEN_TFTOKEN_TOKEN_8": "${jsonencode(aws_api_gateway_stage.cloudApi_api_stage_BBB283E4.invoke_url)}" + } + }, + "function_name": "Handler-c838ce37", + "handler": "index.handler", + "publish": true, + "role": "${aws_iam_role.testhttpgetandhttpfetchcanpreformacalltoanapi_Handler_IamRole_678DDBCB.arn}", + "runtime": "nodejs18.x", + "s3_bucket": "${aws_s3_bucket.Code.bucket}", + "s3_key": "${aws_s3_object.testhttpgetandhttpfetchcanpreformacalltoanapi_Handler_S3Object_88ED484E.key}", + "timeout": 30, + "vpc_config": { + "security_group_ids": [], + "subnet_ids": [] + } + } + }, + "aws_lambda_permission": { + "cloudApi_api_permission-GET-e2131352_3FDDE199": { + "//": { + "metadata": { + "path": "root/Default/Default/cloud.Api/api/permission-GET-e2131352", + "uniqueId": "cloudApi_api_permission-GET-e2131352_3FDDE199" + } + }, + "action": "lambda:InvokeFunction", + "function_name": "${aws_lambda_function.cloudApi_cloudApi-OnRequest-cdafee6e_A6C8366F.function_name}", + "principal": "apigateway.amazonaws.com", + "source_arn": "${aws_api_gateway_rest_api.cloudApi_api_2B334D75.execution_arn}/*/GET/path", + "statement_id": "AllowExecutionFromAPIGateway-GET-e2131352" + } + }, + "aws_s3_bucket": { + "Code": { + "//": { + "metadata": { + "path": "root/Default/Code", + "uniqueId": "Code" + } + }, + "bucket_prefix": "code-c84a50b1-" + } + }, + "aws_s3_object": { + "cloudApi_cloudApi-OnRequest-cdafee6e_S3Object_5DAAA0EF": { + "//": { + "metadata": { + "path": "root/Default/Default/cloud.Api/cloud.Api-OnRequest-cdafee6e/S3Object", + "uniqueId": "cloudApi_cloudApi-OnRequest-cdafee6e_S3Object_5DAAA0EF" + } + }, + "bucket": "${aws_s3_bucket.Code.bucket}", + "key": "", + "source": "" + }, + "testhttpgetandhttpfetchcanpreformacalltoanapi_Handler_S3Object_88ED484E": { + "//": { + "metadata": { + "path": "root/Default/Default/test:http.get and http.fetch can preform a call to an api/Handler/S3Object", + "uniqueId": "testhttpgetandhttpfetchcanpreformacalltoanapi_Handler_S3Object_88ED484E" + } + }, + "bucket": "${aws_s3_bucket.Code.bucket}", + "key": "", + "source": "" + } + } + } +} +``` + +## preflight.js +```js +const $stdlib = require('@winglang/sdk'); +const $plugins = ((s) => !s ? [] : s.split(';'))(process.env.WING_PLUGIN_PATHS); +const $outdir = process.env.WING_SYNTH_DIR ?? "."; +const $wing_is_test = process.env.WING_IS_TEST === "true"; +const std = $stdlib.std; +const cloud = $stdlib.cloud; +const http = $stdlib.http; +const util = $stdlib.util; +class $Root extends $stdlib.std.Resource { + constructor(scope, id) { + super(scope, id); + class $Closure1 extends $stdlib.std.Resource { + constructor(scope, id, ) { + super(scope, id); + (std.Node.of(this)).hidden = true; + } + static _toInflightType(context) { + return ` + require("./inflight.$Closure1-1.js")({ + $body: ${context._lift(body)}, + }) + `; + } + _toInflight() { + return ` + (await (async () => { + const $Closure1Client = ${$Closure1._toInflightType(this)}; + const client = new $Closure1Client({ + }); + if (client.$inflight_init) { await client.$inflight_init(); } + return client; + })()) + `; + } + _getInflightOps() { + return ["handle", "$inflight_init"]; + } + _registerBind(host, ops) { + if (ops.includes("handle")) { + $Closure1._registerBindObject(body, host, []); + } + super._registerBind(host, ops); + } + } + class $Closure2 extends $stdlib.std.Resource { + constructor(scope, id, ) { + super(scope, id); + (std.Node.of(this)).hidden = true; + } + static _toInflightType(context) { + return ` + require("./inflight.$Closure2-1.js")({ + $api_url: ${context._lift(api.url)}, + $http_Util: ${context._lift(http.Util)}, + }) + `; + } + _toInflight() { + return ` + (await (async () => { + const $Closure2Client = ${$Closure2._toInflightType(this)}; + const client = new $Closure2Client({ + }); + if (client.$inflight_init) { await client.$inflight_init(); } + return client; + })()) + `; + } + _getInflightOps() { + return ["handle", "$inflight_init"]; + } + _registerBind(host, ops) { + if (ops.includes("handle")) { + $Closure2._registerBindObject(api.url, host, []); + } + super._registerBind(host, ops); + } + } + const api = this.node.root.newAbstract("@winglang/sdk.cloud.Api",this,"cloud.Api",({"cors": true})); + const body = "ok!"; + (api.get("/path",new $Closure1(this,"$Closure1"))); + this.node.root.new("@winglang/sdk.std.Test",std.Test,this,"test:http.get and http.fetch can preform a call to an api",new $Closure2(this,"$Closure2")); + } +} +const $App = $stdlib.core.App.for(process.env.WING_TARGET); +new $App({ outdir: $outdir, name: "cors", rootConstruct: $Root, plugins: $plugins, isTestEnvironment: $wing_is_test, entrypointDir: process.env['WING_SOURCE_DIR'], rootId: process.env['WING_ROOT_ID'] }).synth(); + +``` + diff --git a/tools/hangar/__snapshots__/test_corpus/sdk_tests/api/cors.w_test_sim.md b/tools/hangar/__snapshots__/test_corpus/sdk_tests/api/cors.w_test_sim.md new file mode 100644 index 00000000000..86b9ef34bc1 --- /dev/null +++ b/tools/hangar/__snapshots__/test_corpus/sdk_tests/api/cors.w_test_sim.md @@ -0,0 +1,12 @@ +# [cors.w](../../../../../../examples/tests/sdk_tests/api/cors.w) | test | sim + +## stdout.log +```log +pass ─ cors.wsim » root/env0/test:http.get and http.fetch can preform a call to an api + + +Tests 1 passed (1) +Test Files 1 passed (1) +Duration +``` + diff --git a/tools/hangar/__snapshots__/test_corpus/valid/api_cors_default.w_compile_tf-aws.md b/tools/hangar/__snapshots__/test_corpus/valid/api_cors_default.w_compile_tf-aws.md index 08fe0e7ae47..9509893616e 100644 --- a/tools/hangar/__snapshots__/test_corpus/valid/api_cors_default.w_compile_tf-aws.md +++ b/tools/hangar/__snapshots__/test_corpus/valid/api_cors_default.w_compile_tf-aws.md @@ -56,7 +56,7 @@ module.exports = function({ $apiDefaultCors_url, $http_HttpMethod, $http_Util, $ const response = (await $http_Util.fetch(($apiDefaultCors_url + "/users"),({"method": $http_HttpMethod.OPTIONS}))); const headers = response.headers; (await $t_Assert.equalNum(response.status,204)); - (await $t_Assert.equalStr((headers)["access-control-allow-headers"],"Content-Type,Authorization")); + (await $t_Assert.equalStr((headers)["access-control-allow-headers"],"Content-Type,Authorization,X-Requested-With")); (await $t_Assert.equalStr((headers)["access-control-allow-methods"],"GET,POST,PUT,DELETE,HEAD,OPTIONS")); (await $t_Assert.isNil((headers)["access-control-allow-origin"])); (await $t_Assert.isNil((headers)["access-control-allow-credentials"])); diff --git a/tools/hangar/__snapshots__/test_corpus/valid/api_cors_default.w_test_sim.md b/tools/hangar/__snapshots__/test_corpus/valid/api_cors_default.w_test_sim.md new file mode 100644 index 00000000000..0d49eac24f9 --- /dev/null +++ b/tools/hangar/__snapshots__/test_corpus/valid/api_cors_default.w_test_sim.md @@ -0,0 +1,13 @@ +# [api_cors_default.w](../../../../../examples/tests/valid/api_cors_default.w) | test | sim + +## stdout.log +```log +pass ─ api_cors_default.wsim » root/env0/test:GET --users has default cors headers +pass ─ api_cors_default.wsim » root/env1/test:OPTIONS --users has default cors headers + + +Tests 2 passed (2) +Test Files 1 passed (1) +Duration +``` + diff --git a/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_compile_tf-aws.md b/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_compile_tf-aws.md index 09872ca6cdd..64d75a44f0a 100644 --- a/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_compile_tf-aws.md +++ b/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_compile_tf-aws.md @@ -43,7 +43,7 @@ module.exports = function({ $std_Json, $usersTable }) { ## inflight.$Closure3-2.js ```js -module.exports = function({ $api_url, $http_HttpMethod, $http_Util, $std_Json, $t_Assert }) { +module.exports = function({ $api_url, $http_HttpMethod, $http_Util, $t_Assert }) { class $Closure3 { constructor({ }) { const $obj = (...args) => this.handle(...args); @@ -54,7 +54,6 @@ module.exports = function({ $api_url, $http_HttpMethod, $http_Util, $std_Json, $ const response = (await $http_Util.fetch(($api_url + "/users"),({"method": $http_HttpMethod.GET,"headers": ({"Content-Type": "text/json"})}))); const headers = response.headers; (await $t_Assert.equalNum(response.status,200)); - {console.log(((args) => { return JSON.stringify(args[0], null, args[1]?.indent) })([headers]))}; (await $t_Assert.equalStr((headers)["access-control-allow-origin"],"*")); (await $t_Assert.equalStr((headers)["access-control-expose-headers"],"Content-Type")); (await $t_Assert.equalStr((headers)["access-control-allow-credentials"],"false")); @@ -882,7 +881,6 @@ class $Root extends $stdlib.std.Resource { $api_url: ${context._lift(api.url)}, $http_HttpMethod: ${context._lift(http.HttpMethod)}, $http_Util: ${context._lift(http.Util)}, - $std_Json: ${context._lift(std.Json)}, $t_Assert: ${context._lift(t.Assert)}, }) `; diff --git a/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_test_sim.md b/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_test_sim.md index c211c4afe13..ea41650b9b8 100644 --- a/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_test_sim.md +++ b/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_test_sim.md @@ -2,8 +2,7 @@ ## stdout.log ```log -pass ┌ website_with_api.wsim » root/env0/test:GET --users - └ {"access-control-allow-credentials":"false","access-control-allow-origin":"*","access-control-expose-headers":"Content-Type","connection":"keep-alive","content-length":"12","content-type":"text/html; charset=utf-8","date":"Mon, 04 Sep 2023 13:33:12 GMT","etag":"W/\"c-1bl/ybs8pYaLwWPVCd0mBRAdBaY\"","keep-alive":"timeout=5","x-powered-by":"Express"} +pass ─ website_with_api.wsim » root/env0/test:GET --users pass ─ website_with_api.wsim » root/env1/test:OPTIONS --users From 6041814591802bd511735ebe68fe61590d965b59 Mon Sep 17 00:00:00 2001 From: Sebastian Korfmann Date: Tue, 5 Sep 2023 20:30:58 +0200 Subject: [PATCH 47/47] CORS handling for tf-aws --- examples/tests/valid/api_cors_custom.w | 6 +- examples/tests/valid/api_cors_default.w | 2 +- libs/wingsdk/src/cloud/api.ts | 7 + libs/wingsdk/src/target-tf-aws/api.cors.ts | 146 +++++++ libs/wingsdk/src/target-tf-aws/api.ts | 49 +-- .../__snapshots__/api.test.ts.snap | 375 +++++++++++++----- .../__snapshots__/tokens.test.ts.snap | 2 +- .../sdk_tests/api/404.w_compile_tf-aws.md | 2 +- .../sdk_tests/api/cors.w_compile_tf-aws.md | 2 +- .../sdk_tests/api/delete.w_compile_tf-aws.md | 2 +- .../sdk_tests/api/get.w_compile_tf-aws.md | 2 +- .../sdk_tests/api/options.w_compile_tf-aws.md | 2 +- .../sdk_tests/api/patch.w_compile_tf-aws.md | 2 +- .../api/path_vars.w_compile_tf-aws.md | 2 +- .../sdk_tests/api/post.w_compile_tf-aws.md | 2 +- .../sdk_tests/api/put.w_compile_tf-aws.md | 2 +- .../test_corpus/valid/api.w_compile_tf-aws.md | 4 +- .../valid/api_cors_custom.w_compile_tf-aws.md | 7 +- .../api_cors_default.w_compile_tf-aws.md | 4 +- .../valid/api_valid_path.w_compile_tf-aws.md | 2 +- .../valid/capture_tokens.w_compile_tf-aws.md | 4 +- .../valid/captures.w_compile_tf-aws.md | 2 +- .../valid/inference.w_compile_tf-aws.md | 2 +- .../valid/issue_2889.w_compile_tf-aws.md | 2 +- .../website_with_api.w_compile_tf-aws.md | 2 +- 25 files changed, 471 insertions(+), 163 deletions(-) create mode 100644 libs/wingsdk/src/target-tf-aws/api.cors.ts diff --git a/examples/tests/valid/api_cors_custom.w b/examples/tests/valid/api_cors_custom.w index 31fd81d8ddc..a19c8df50fa 100644 --- a/examples/tests/valid/api_cors_custom.w +++ b/examples/tests/valid/api_cors_custom.w @@ -49,9 +49,9 @@ test "OPTIONS /users has cors headers" { // OPTIONS cors headers are set t.Assert.equalStr(headers.get("access-control-allow-methods"), "GET,POST,OPTIONS"); t.Assert.equalStr(headers.get("access-control-allow-headers"), "Content-Type,Authorization,X-Custom-Header"); + t.Assert.equalStr(headers.get("access-control-allow-origin"), "winglang.io"); // Other cors headers are not set - t.Assert.isNil(headers.get("access-control-allow-origin")); t.Assert.isNil(headers.get("access-control-expose-headers")); t.Assert.isNil(headers.get("access-control-allow-credentials")); } @@ -60,9 +60,8 @@ test "OPTIONS /users responds with proper headers for requested" { let response = http.fetch(api.url + "/users", { method: http.HttpMethod.OPTIONS, headers: { - "Content-Type": "text/json", "Access-Control-Request-Method": "PUT", - "Access-Control-Request-Headers": "Content-Type,Authorization,X-Custom-Foo" + "Access-Control-Request-Headers": "Content-Type,Authorization,X-Custom-Foo", } }); @@ -70,4 +69,5 @@ test "OPTIONS /users responds with proper headers for requested" { t.Assert.equalNum(response.status, 204); t.Assert.equalStr(headers.get("access-control-allow-methods"), "GET,POST,OPTIONS"); t.Assert.equalStr(headers.get("access-control-allow-headers"), "Content-Type,Authorization,X-Custom-Header"); + t.Assert.equalStr(headers.get("access-control-allow-origin"), "winglang.io"); } \ No newline at end of file diff --git a/examples/tests/valid/api_cors_default.w b/examples/tests/valid/api_cors_default.w index f0f280f0882..96c7a2af5f9 100644 --- a/examples/tests/valid/api_cors_default.w +++ b/examples/tests/valid/api_cors_default.w @@ -41,9 +41,9 @@ test "OPTIONS /users has default cors headers" { // OPTIONS cors headers are set t.Assert.equalStr(headers.get("access-control-allow-headers"), "Content-Type,Authorization,X-Requested-With"); t.Assert.equalStr(headers.get("access-control-allow-methods"), "GET,POST,PUT,DELETE,HEAD,OPTIONS"); + t.Assert.equalStr(headers.get("access-control-allow-origin"), "*"); // Other headers are not set - t.Assert.isNil(headers.get("access-control-allow-origin")); t.Assert.isNil(headers.get("access-control-allow-credentials")); t.Assert.isNil(headers.get("access-control-expose-headers")); } diff --git a/libs/wingsdk/src/cloud/api.ts b/libs/wingsdk/src/cloud/api.ts index 59e19804408..bdc8f12e797 100644 --- a/libs/wingsdk/src/cloud/api.ts +++ b/libs/wingsdk/src/cloud/api.ts @@ -130,6 +130,12 @@ type CorsDefaultResponseHeaders = { * Type definition for CORS option headers. */ type CorsOptionsResponseHeaders = { + /** + * Specifies the origin that is allowed to access the resource. + * https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin + */ + "Access-Control-Allow-Origin": string; + /** * Specifies the headers that are allowed in a request. * https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Headers @@ -389,6 +395,7 @@ export abstract class Api extends Resource { }; const optionsHeaders: CorsOptionsResponseHeaders = { + "Access-Control-Allow-Origin": allowOrigin.join(",") || "", "Access-Control-Allow-Headers": allowHeaders.join(",") || "", "Access-Control-Allow-Methods": allowMethods.join(",") || "", }; diff --git a/libs/wingsdk/src/target-tf-aws/api.cors.ts b/libs/wingsdk/src/target-tf-aws/api.cors.ts new file mode 100644 index 00000000000..e5a0fdad487 --- /dev/null +++ b/libs/wingsdk/src/target-tf-aws/api.cors.ts @@ -0,0 +1,146 @@ +import * as cloud from "../cloud"; + +/** + * DEFAULT_RESPONSE is a constant that defines the default response when a request occurs. + * It is used to handle all requests that do not match any defined routes in the API Gateway. + * The response is a mock integration type, which means it returns a mocked response without + * forwarding the request to any backend. The response status code is set to 200 for OPTIONS + * and 404 for any other method. The Content-Type header is set to 'application/json'. + * @internal + */ +export const API_CORS_DEFAULT_RESPONSE = ( + corsOptions?: cloud.ApiCorsOptions +) => { + if (corsOptions) { + return { + "/{proxy+}": { + "x-amazon-apigateway-any-method": { + produces: ["application/json"], + "x-amazon-apigateway-integration": { + type: "mock", + requestTemplates: { + "application/json": ` + #if ($context.httpMethod == "OPTIONS") + {"statusCode": 204} + #else + {"statusCode": 404} + #end + `, + }, + passthroughBehavior: "never", + responses: { + default: { + statusCode: "404", + responseParameters: { + "method.response.header.Content-Type": "'application/json'", + }, + responseTemplates: { + "application/json": + '{"statusCode": 404, "message": "Error: Resource not found"}', + }, + }, + "204": { + statusCode: "204", + responseParameters: { + "method.response.header.Content-Type": "'application/json'", + "method.response.header.Access-Control-Allow-Origin": `'${corsOptions.allowOrigin}'`, + "method.response.header.Access-Control-Allow-Methods": `'${corsOptions.allowMethods}'`, + "method.response.header.Access-Control-Allow-Headers": `'${corsOptions.allowHeaders}'`, + }, + responseTemplates: { + "application/json": "{}", + }, + }, + "404": { + statusCode: "404", + responseParameters: { + "method.response.header.Content-Type": "'application/json'", + }, + responseTemplates: { + "application/json": + '{"statusCode": 404, "message": "Error: Resource not found"}', + }, + }, + }, + }, + responses: { + 204: { + description: "204 response", + headers: { + "Content-Type": { + type: "string", + }, + "Access-Control-Allow-Origin": { + type: "string", + }, + "Access-Control-Allow-Methods": { + type: "string", + }, + "Access-Control-Allow-Headers": { + type: "string", + }, + }, + }, + 404: { + description: "404 response", + headers: { + "Content-Type": { + type: "string", + }, + }, + }, + }, + }, + }, + }; + } else { + return { + "/{proxy+}": { + "x-amazon-apigateway-any-method": { + produces: ["application/json"], + "x-amazon-apigateway-integration": { + type: "mock", + requestTemplates: { + "application/json": ` + {"statusCode": 404} + `, + }, + passthroughBehavior: "never", + responses: { + default: { + statusCode: "404", + responseParameters: { + "method.response.header.Content-Type": "'application/json'", + }, + responseTemplates: { + "application/json": + '{"statusCode": 404, "message": "Error: Resource not found"}', + }, + }, + "404": { + statusCode: "404", + responseParameters: { + "method.response.header.Content-Type": "'application/json'", + }, + responseTemplates: { + "application/json": + '{"statusCode": 404, "message": "Error: Resource not found"}', + }, + }, + }, + }, + responses: { + 404: { + description: "404 response", + headers: { + "Content-Type": { + type: "string", + }, + }, + }, + }, + }, + }, + }; + } +}; diff --git a/libs/wingsdk/src/target-tf-aws/api.ts b/libs/wingsdk/src/target-tf-aws/api.ts index 02680aedb1f..01d92c295ed 100644 --- a/libs/wingsdk/src/target-tf-aws/api.ts +++ b/libs/wingsdk/src/target-tf-aws/api.ts @@ -3,6 +3,7 @@ import { join } from "path"; import { Fn, Lazy } from "cdktf"; import { Construct } from "constructs"; +import { API_CORS_DEFAULT_RESPONSE } from "./api.cors"; import { App } from "./app"; import { Function } from "./function"; import { core } from ".."; @@ -368,50 +369,6 @@ export class Api extends cloud.Api { } } -/** - * DEFAULT_404_RESPONSE is a constant that defines the default response when a 404 error occurs. - * It is used to handle all requests that do not match any defined routes in the API Gateway. - * The response is a mock integration type, which means it returns a mocked response without - * forwarding the request to any backend. The response status code is set to 404 and the - * Content-Type header is set to 'application/json'. - */ -const DEFAULT_404_RESPONSE = { - "/{proxy+}": { - "x-amazon-apigateway-any-method": { - produces: ["application/json"], - consumes: ["application/json"], - "x-amazon-apigateway-integration": { - type: "mock", - requestTemplates: { - "application/json": '{"statusCode": 404}', - }, - responses: { - default: { - statusCode: "404", - responseParameters: { - "method.response.header.Content-Type": "'application/json'", - }, - responseTemplates: { - "application/json": - '{"statusCode: 404, "message": "Error: Resource not found"}', - }, - }, - }, - }, - responses: { - 404: { - description: "404 response", - headers: { - "Content-Type": { - type: "string", - }, - }, - }, - }, - }, - }, -}; - /** * Encapsulates the API Gateway REST API as a abstraction for Terraform. */ @@ -433,6 +390,8 @@ class WingRestApi extends Construct { super(scope, id); this.region = (App.of(this) as App).region; + const defaultResponse = API_CORS_DEFAULT_RESPONSE(props.cors); + this.api = new ApiGatewayRestApi(this, "api", { name: ResourceNames.generateName(this, NAME_OPTS), // Lazy generation of the api spec because routes can be added after the API is created @@ -441,7 +400,7 @@ class WingRestApi extends Construct { const injectGreedy404Handler = (openApiSpec: OpenApiSpec) => { openApiSpec.paths = { ...openApiSpec.paths, - ...DEFAULT_404_RESPONSE, + ...defaultResponse, }; return openApiSpec; }; diff --git a/libs/wingsdk/test/target-tf-aws/__snapshots__/api.test.ts.snap b/libs/wingsdk/test/target-tf-aws/__snapshots__/api.test.ts.snap index 41aedb0a3b5..d4855e37966 100644 --- a/libs/wingsdk/test/target-tf-aws/__snapshots__/api.test.ts.snap +++ b/libs/wingsdk/test/target-tf-aws/__snapshots__/api.test.ts.snap @@ -47,13 +47,27 @@ exports[`api configured for cors 1`] = ` }, "/{proxy+}": { "x-amazon-apigateway-any-method": { - "consumes": [ - "application/json", - ], "produces": [ "application/json", ], "responses": { + "204": { + "description": "204 response", + "headers": { + "Access-Control-Allow-Headers": { + "type": "string", + }, + "Access-Control-Allow-Methods": { + "type": "string", + }, + "Access-Control-Allow-Origin": { + "type": "string", + }, + "Content-Type": { + "type": "string", + }, + }, + }, "404": { "description": "404 response", "headers": { @@ -64,16 +78,44 @@ exports[`api configured for cors 1`] = ` }, }, "x-amazon-apigateway-integration": { + "passthroughBehavior": "never", "requestTemplates": { - "application/json": "{\\"statusCode\\": 404}", + "application/json": " + #if ($context.httpMethod == \\"OPTIONS\\") + {\\"statusCode\\": 204} + #else + {\\"statusCode\\": 404} + #end + ", }, "responses": { + "204": { + "responseParameters": { + "method.response.header.Access-Control-Allow-Headers": "'Content-Type,Authorization,X-Requested-With'", + "method.response.header.Access-Control-Allow-Methods": "'GET,POST,PUT,DELETE,HEAD,OPTIONS'", + "method.response.header.Access-Control-Allow-Origin": "'*'", + "method.response.header.Content-Type": "'application/json'", + }, + "responseTemplates": { + "application/json": "{}", + }, + "statusCode": "204", + }, + "404": { + "responseParameters": { + "method.response.header.Content-Type": "'application/json'", + }, + "responseTemplates": { + "application/json": "{\\"statusCode\\": 404, \\"message\\": \\"Error: Resource not found\\"}", + }, + "statusCode": "404", + }, "default": { "responseParameters": { "method.response.header.Content-Type": "'application/json'", }, "responseTemplates": { - "application/json": "{\\"statusCode: 404, \\"message\\": \\"Error: Resource not found\\"}", + "application/json": "{\\"statusCode\\": 404, \\"message\\": \\"Error: Resource not found\\"}", }, "statusCode": "404", }, @@ -133,9 +175,6 @@ exports[`api with 'name' & 'age' parameter 1`] = ` }, "/{proxy+}": { "x-amazon-apigateway-any-method": { - "consumes": [ - "application/json", - ], "produces": [ "application/json", ], @@ -150,16 +189,28 @@ exports[`api with 'name' & 'age' parameter 1`] = ` }, }, "x-amazon-apigateway-integration": { + "passthroughBehavior": "never", "requestTemplates": { - "application/json": "{\\"statusCode\\": 404}", + "application/json": " + {\\"statusCode\\": 404} + ", }, "responses": { + "404": { + "responseParameters": { + "method.response.header.Content-Type": "'application/json'", + }, + "responseTemplates": { + "application/json": "{\\"statusCode\\": 404, \\"message\\": \\"Error: Resource not found\\"}", + }, + "statusCode": "404", + }, "default": { "responseParameters": { "method.response.header.Content-Type": "'application/json'", }, "responseTemplates": { - "application/json": "{\\"statusCode: 404, \\"message\\": \\"Error: Resource not found\\"}", + "application/json": "{\\"statusCode\\": 404, \\"message\\": \\"Error: Resource not found\\"}", }, "statusCode": "404", }, @@ -211,9 +262,6 @@ exports[`api with 'name' parameter 1`] = ` }, "/{proxy+}": { "x-amazon-apigateway-any-method": { - "consumes": [ - "application/json", - ], "produces": [ "application/json", ], @@ -228,16 +276,28 @@ exports[`api with 'name' parameter 1`] = ` }, }, "x-amazon-apigateway-integration": { + "passthroughBehavior": "never", "requestTemplates": { - "application/json": "{\\"statusCode\\": 404}", + "application/json": " + {\\"statusCode\\": 404} + ", }, "responses": { + "404": { + "responseParameters": { + "method.response.header.Content-Type": "'application/json'", + }, + "responseTemplates": { + "application/json": "{\\"statusCode\\": 404, \\"message\\": \\"Error: Resource not found\\"}", + }, + "statusCode": "404", + }, "default": { "responseParameters": { "method.response.header.Content-Type": "'application/json'", }, "responseTemplates": { - "application/json": "{\\"statusCode: 404, \\"message\\": \\"Error: Resource not found\\"}", + "application/json": "{\\"statusCode\\": 404, \\"message\\": \\"Error: Resource not found\\"}", }, "statusCode": "404", }, @@ -280,9 +340,6 @@ exports[`api with CONNECT route 1`] = ` }, "/{proxy+}": { "x-amazon-apigateway-any-method": { - "consumes": [ - "application/json", - ], "produces": [ "application/json", ], @@ -297,16 +354,28 @@ exports[`api with CONNECT route 1`] = ` }, }, "x-amazon-apigateway-integration": { + "passthroughBehavior": "never", "requestTemplates": { - "application/json": "{\\"statusCode\\": 404}", + "application/json": " + {\\"statusCode\\": 404} + ", }, "responses": { + "404": { + "responseParameters": { + "method.response.header.Content-Type": "'application/json'", + }, + "responseTemplates": { + "application/json": "{\\"statusCode\\": 404, \\"message\\": \\"Error: Resource not found\\"}", + }, + "statusCode": "404", + }, "default": { "responseParameters": { "method.response.header.Content-Type": "'application/json'", }, "responseTemplates": { - "application/json": "{\\"statusCode: 404, \\"message\\": \\"Error: Resource not found\\"}", + "application/json": "{\\"statusCode\\": 404, \\"message\\": \\"Error: Resource not found\\"}", }, "statusCode": "404", }, @@ -349,9 +418,6 @@ exports[`api with DELETE route 1`] = ` }, "/{proxy+}": { "x-amazon-apigateway-any-method": { - "consumes": [ - "application/json", - ], "produces": [ "application/json", ], @@ -366,16 +432,28 @@ exports[`api with DELETE route 1`] = ` }, }, "x-amazon-apigateway-integration": { + "passthroughBehavior": "never", "requestTemplates": { - "application/json": "{\\"statusCode\\": 404}", + "application/json": " + {\\"statusCode\\": 404} + ", }, "responses": { + "404": { + "responseParameters": { + "method.response.header.Content-Type": "'application/json'", + }, + "responseTemplates": { + "application/json": "{\\"statusCode\\": 404, \\"message\\": \\"Error: Resource not found\\"}", + }, + "statusCode": "404", + }, "default": { "responseParameters": { "method.response.header.Content-Type": "'application/json'", }, "responseTemplates": { - "application/json": "{\\"statusCode: 404, \\"message\\": \\"Error: Resource not found\\"}", + "application/json": "{\\"statusCode\\": 404, \\"message\\": \\"Error: Resource not found\\"}", }, "statusCode": "404", }, @@ -418,9 +496,6 @@ exports[`api with GET route at root 1`] = ` }, "/{proxy+}": { "x-amazon-apigateway-any-method": { - "consumes": [ - "application/json", - ], "produces": [ "application/json", ], @@ -435,16 +510,28 @@ exports[`api with GET route at root 1`] = ` }, }, "x-amazon-apigateway-integration": { + "passthroughBehavior": "never", "requestTemplates": { - "application/json": "{\\"statusCode\\": 404}", + "application/json": " + {\\"statusCode\\": 404} + ", }, "responses": { + "404": { + "responseParameters": { + "method.response.header.Content-Type": "'application/json'", + }, + "responseTemplates": { + "application/json": "{\\"statusCode\\": 404, \\"message\\": \\"Error: Resource not found\\"}", + }, + "statusCode": "404", + }, "default": { "responseParameters": { "method.response.header.Content-Type": "'application/json'", }, "responseTemplates": { - "application/json": "{\\"statusCode: 404, \\"message\\": \\"Error: Resource not found\\"}", + "application/json": "{\\"statusCode\\": 404, \\"message\\": \\"Error: Resource not found\\"}", }, "statusCode": "404", }, @@ -511,9 +598,6 @@ exports[`api with GET routes with common prefix 1`] = ` }, "/{proxy+}": { "x-amazon-apigateway-any-method": { - "consumes": [ - "application/json", - ], "produces": [ "application/json", ], @@ -528,16 +612,28 @@ exports[`api with GET routes with common prefix 1`] = ` }, }, "x-amazon-apigateway-integration": { + "passthroughBehavior": "never", "requestTemplates": { - "application/json": "{\\"statusCode\\": 404}", + "application/json": " + {\\"statusCode\\": 404} + ", }, "responses": { + "404": { + "responseParameters": { + "method.response.header.Content-Type": "'application/json'", + }, + "responseTemplates": { + "application/json": "{\\"statusCode\\": 404, \\"message\\": \\"Error: Resource not found\\"}", + }, + "statusCode": "404", + }, "default": { "responseParameters": { "method.response.header.Content-Type": "'application/json'", }, "responseTemplates": { - "application/json": "{\\"statusCode: 404, \\"message\\": \\"Error: Resource not found\\"}", + "application/json": "{\\"statusCode\\": 404, \\"message\\": \\"Error: Resource not found\\"}", }, "statusCode": "404", }, @@ -604,9 +700,6 @@ exports[`api with GET routes with different prefix 1`] = ` }, "/{proxy+}": { "x-amazon-apigateway-any-method": { - "consumes": [ - "application/json", - ], "produces": [ "application/json", ], @@ -621,16 +714,28 @@ exports[`api with GET routes with different prefix 1`] = ` }, }, "x-amazon-apigateway-integration": { + "passthroughBehavior": "never", "requestTemplates": { - "application/json": "{\\"statusCode\\": 404}", + "application/json": " + {\\"statusCode\\": 404} + ", }, "responses": { + "404": { + "responseParameters": { + "method.response.header.Content-Type": "'application/json'", + }, + "responseTemplates": { + "application/json": "{\\"statusCode\\": 404, \\"message\\": \\"Error: Resource not found\\"}", + }, + "statusCode": "404", + }, "default": { "responseParameters": { "method.response.header.Content-Type": "'application/json'", }, "responseTemplates": { - "application/json": "{\\"statusCode: 404, \\"message\\": \\"Error: Resource not found\\"}", + "application/json": "{\\"statusCode\\": 404, \\"message\\": \\"Error: Resource not found\\"}", }, "statusCode": "404", }, @@ -673,9 +778,6 @@ exports[`api with HEAD route 1`] = ` }, "/{proxy+}": { "x-amazon-apigateway-any-method": { - "consumes": [ - "application/json", - ], "produces": [ "application/json", ], @@ -690,16 +792,28 @@ exports[`api with HEAD route 1`] = ` }, }, "x-amazon-apigateway-integration": { + "passthroughBehavior": "never", "requestTemplates": { - "application/json": "{\\"statusCode\\": 404}", + "application/json": " + {\\"statusCode\\": 404} + ", }, "responses": { + "404": { + "responseParameters": { + "method.response.header.Content-Type": "'application/json'", + }, + "responseTemplates": { + "application/json": "{\\"statusCode\\": 404, \\"message\\": \\"Error: Resource not found\\"}", + }, + "statusCode": "404", + }, "default": { "responseParameters": { "method.response.header.Content-Type": "'application/json'", }, "responseTemplates": { - "application/json": "{\\"statusCode: 404, \\"message\\": \\"Error: Resource not found\\"}", + "application/json": "{\\"statusCode\\": 404, \\"message\\": \\"Error: Resource not found\\"}", }, "statusCode": "404", }, @@ -742,9 +856,6 @@ exports[`api with OPTIONS route 1`] = ` }, "/{proxy+}": { "x-amazon-apigateway-any-method": { - "consumes": [ - "application/json", - ], "produces": [ "application/json", ], @@ -759,16 +870,28 @@ exports[`api with OPTIONS route 1`] = ` }, }, "x-amazon-apigateway-integration": { + "passthroughBehavior": "never", "requestTemplates": { - "application/json": "{\\"statusCode\\": 404}", + "application/json": " + {\\"statusCode\\": 404} + ", }, "responses": { + "404": { + "responseParameters": { + "method.response.header.Content-Type": "'application/json'", + }, + "responseTemplates": { + "application/json": "{\\"statusCode\\": 404, \\"message\\": \\"Error: Resource not found\\"}", + }, + "statusCode": "404", + }, "default": { "responseParameters": { "method.response.header.Content-Type": "'application/json'", }, "responseTemplates": { - "application/json": "{\\"statusCode: 404, \\"message\\": \\"Error: Resource not found\\"}", + "application/json": "{\\"statusCode\\": 404, \\"message\\": \\"Error: Resource not found\\"}", }, "statusCode": "404", }, @@ -811,9 +934,6 @@ exports[`api with PATCH route 1`] = ` }, "/{proxy+}": { "x-amazon-apigateway-any-method": { - "consumes": [ - "application/json", - ], "produces": [ "application/json", ], @@ -828,16 +948,28 @@ exports[`api with PATCH route 1`] = ` }, }, "x-amazon-apigateway-integration": { + "passthroughBehavior": "never", "requestTemplates": { - "application/json": "{\\"statusCode\\": 404}", + "application/json": " + {\\"statusCode\\": 404} + ", }, "responses": { + "404": { + "responseParameters": { + "method.response.header.Content-Type": "'application/json'", + }, + "responseTemplates": { + "application/json": "{\\"statusCode\\": 404, \\"message\\": \\"Error: Resource not found\\"}", + }, + "statusCode": "404", + }, "default": { "responseParameters": { "method.response.header.Content-Type": "'application/json'", }, "responseTemplates": { - "application/json": "{\\"statusCode: 404, \\"message\\": \\"Error: Resource not found\\"}", + "application/json": "{\\"statusCode\\": 404, \\"message\\": \\"Error: Resource not found\\"}", }, "statusCode": "404", }, @@ -880,9 +1012,6 @@ exports[`api with POST route 1`] = ` }, "/{proxy+}": { "x-amazon-apigateway-any-method": { - "consumes": [ - "application/json", - ], "produces": [ "application/json", ], @@ -897,16 +1026,28 @@ exports[`api with POST route 1`] = ` }, }, "x-amazon-apigateway-integration": { + "passthroughBehavior": "never", "requestTemplates": { - "application/json": "{\\"statusCode\\": 404}", + "application/json": " + {\\"statusCode\\": 404} + ", }, "responses": { + "404": { + "responseParameters": { + "method.response.header.Content-Type": "'application/json'", + }, + "responseTemplates": { + "application/json": "{\\"statusCode\\": 404, \\"message\\": \\"Error: Resource not found\\"}", + }, + "statusCode": "404", + }, "default": { "responseParameters": { "method.response.header.Content-Type": "'application/json'", }, "responseTemplates": { - "application/json": "{\\"statusCode: 404, \\"message\\": \\"Error: Resource not found\\"}", + "application/json": "{\\"statusCode\\": 404, \\"message\\": \\"Error: Resource not found\\"}", }, "statusCode": "404", }, @@ -949,9 +1090,6 @@ exports[`api with PUT route 1`] = ` }, "/{proxy+}": { "x-amazon-apigateway-any-method": { - "consumes": [ - "application/json", - ], "produces": [ "application/json", ], @@ -966,16 +1104,28 @@ exports[`api with PUT route 1`] = ` }, }, "x-amazon-apigateway-integration": { + "passthroughBehavior": "never", "requestTemplates": { - "application/json": "{\\"statusCode\\": 404}", + "application/json": " + {\\"statusCode\\": 404} + ", }, "responses": { + "404": { + "responseParameters": { + "method.response.header.Content-Type": "'application/json'", + }, + "responseTemplates": { + "application/json": "{\\"statusCode\\": 404, \\"message\\": \\"Error: Resource not found\\"}", + }, + "statusCode": "404", + }, "default": { "responseParameters": { "method.response.header.Content-Type": "'application/json'", }, "responseTemplates": { - "application/json": "{\\"statusCode: 404, \\"message\\": \\"Error: Resource not found\\"}", + "application/json": "{\\"statusCode\\": 404, \\"message\\": \\"Error: Resource not found\\"}", }, "statusCode": "404", }, @@ -1042,9 +1192,6 @@ exports[`api with multiple GET route and one lambda 1`] = ` }, "/{proxy+}": { "x-amazon-apigateway-any-method": { - "consumes": [ - "application/json", - ], "produces": [ "application/json", ], @@ -1059,16 +1206,28 @@ exports[`api with multiple GET route and one lambda 1`] = ` }, }, "x-amazon-apigateway-integration": { + "passthroughBehavior": "never", "requestTemplates": { - "application/json": "{\\"statusCode\\": 404}", + "application/json": " + {\\"statusCode\\": 404} + ", }, "responses": { + "404": { + "responseParameters": { + "method.response.header.Content-Type": "'application/json'", + }, + "responseTemplates": { + "application/json": "{\\"statusCode\\": 404, \\"message\\": \\"Error: Resource not found\\"}", + }, + "statusCode": "404", + }, "default": { "responseParameters": { "method.response.header.Content-Type": "'application/json'", }, "responseTemplates": { - "application/json": "{\\"statusCode: 404, \\"message\\": \\"Error: Resource not found\\"}", + "application/json": "{\\"statusCode\\": 404, \\"message\\": \\"Error: Resource not found\\"}", }, "statusCode": "404", }, @@ -1135,9 +1294,6 @@ exports[`api with multiple methods and multiple lambda 1`] = ` }, "/{proxy+}": { "x-amazon-apigateway-any-method": { - "consumes": [ - "application/json", - ], "produces": [ "application/json", ], @@ -1152,16 +1308,28 @@ exports[`api with multiple methods and multiple lambda 1`] = ` }, }, "x-amazon-apigateway-integration": { + "passthroughBehavior": "never", "requestTemplates": { - "application/json": "{\\"statusCode\\": 404}", + "application/json": " + {\\"statusCode\\": 404} + ", }, "responses": { + "404": { + "responseParameters": { + "method.response.header.Content-Type": "'application/json'", + }, + "responseTemplates": { + "application/json": "{\\"statusCode\\": 404, \\"message\\": \\"Error: Resource not found\\"}", + }, + "statusCode": "404", + }, "default": { "responseParameters": { "method.response.header.Content-Type": "'application/json'", }, "responseTemplates": { - "application/json": "{\\"statusCode: 404, \\"message\\": \\"Error: Resource not found\\"}", + "application/json": "{\\"statusCode\\": 404, \\"message\\": \\"Error: Resource not found\\"}", }, "statusCode": "404", }, @@ -1228,9 +1396,6 @@ exports[`api with multiple methods and one lambda 1`] = ` }, "/{proxy+}": { "x-amazon-apigateway-any-method": { - "consumes": [ - "application/json", - ], "produces": [ "application/json", ], @@ -1245,16 +1410,28 @@ exports[`api with multiple methods and one lambda 1`] = ` }, }, "x-amazon-apigateway-integration": { + "passthroughBehavior": "never", "requestTemplates": { - "application/json": "{\\"statusCode\\": 404}", + "application/json": " + {\\"statusCode\\": 404} + ", }, "responses": { + "404": { + "responseParameters": { + "method.response.header.Content-Type": "'application/json'", + }, + "responseTemplates": { + "application/json": "{\\"statusCode\\": 404, \\"message\\": \\"Error: Resource not found\\"}", + }, + "statusCode": "404", + }, "default": { "responseParameters": { "method.response.header.Content-Type": "'application/json'", }, "responseTemplates": { - "application/json": "{\\"statusCode: 404, \\"message\\": \\"Error: Resource not found\\"}", + "application/json": "{\\"statusCode\\": 404, \\"message\\": \\"Error: Resource not found\\"}", }, "statusCode": "404", }, @@ -1319,9 +1496,6 @@ exports[`api with multiple methods on same route 1`] = ` }, "/{proxy+}": { "x-amazon-apigateway-any-method": { - "consumes": [ - "application/json", - ], "produces": [ "application/json", ], @@ -1336,16 +1510,28 @@ exports[`api with multiple methods on same route 1`] = ` }, }, "x-amazon-apigateway-integration": { + "passthroughBehavior": "never", "requestTemplates": { - "application/json": "{\\"statusCode\\": 404}", + "application/json": " + {\\"statusCode\\": 404} + ", }, "responses": { + "404": { + "responseParameters": { + "method.response.header.Content-Type": "'application/json'", + }, + "responseTemplates": { + "application/json": "{\\"statusCode\\": 404, \\"message\\": \\"Error: Resource not found\\"}", + }, + "statusCode": "404", + }, "default": { "responseParameters": { "method.response.header.Content-Type": "'application/json'", }, "responseTemplates": { - "application/json": "{\\"statusCode: 404, \\"message\\": \\"Error: Resource not found\\"}", + "application/json": "{\\"statusCode\\": 404, \\"message\\": \\"Error: Resource not found\\"}", }, "statusCode": "404", }, @@ -1397,9 +1583,6 @@ exports[`api with path parameter 1`] = ` }, "/{proxy+}": { "x-amazon-apigateway-any-method": { - "consumes": [ - "application/json", - ], "produces": [ "application/json", ], @@ -1414,16 +1597,28 @@ exports[`api with path parameter 1`] = ` }, }, "x-amazon-apigateway-integration": { + "passthroughBehavior": "never", "requestTemplates": { - "application/json": "{\\"statusCode\\": 404}", + "application/json": " + {\\"statusCode\\": 404} + ", }, "responses": { + "404": { + "responseParameters": { + "method.response.header.Content-Type": "'application/json'", + }, + "responseTemplates": { + "application/json": "{\\"statusCode\\": 404, \\"message\\": \\"Error: Resource not found\\"}", + }, + "statusCode": "404", + }, "default": { "responseParameters": { "method.response.header.Content-Type": "'application/json'", }, "responseTemplates": { - "application/json": "{\\"statusCode: 404, \\"message\\": \\"Error: Resource not found\\"}", + "application/json": "{\\"statusCode\\": 404, \\"message\\": \\"Error: Resource not found\\"}", }, "statusCode": "404", }, diff --git a/libs/wingsdk/test/target-tf-aws/__snapshots__/tokens.test.ts.snap b/libs/wingsdk/test/target-tf-aws/__snapshots__/tokens.test.ts.snap index 9016b32324f..69e9eaa36d0 100644 --- a/libs/wingsdk/test/target-tf-aws/__snapshots__/tokens.test.ts.snap +++ b/libs/wingsdk/test/target-tf-aws/__snapshots__/tokens.test.ts.snap @@ -45,7 +45,7 @@ exports[`captures tokens 2`] = ` }, "aws_api_gateway_rest_api": { "Api_api_91C07D84": { - "body": "{\\"openapi\\":\\"3.0.3\\",\\"paths\\":{\\"/\\":{\\"get\\":{\\"operationId\\":\\"get\\",\\"responses\\":{\\"200\\":{\\"description\\":\\"200 response\\",\\"content\\":{}}},\\"parameters\\":[],\\"x-amazon-apigateway-integration\\":{\\"uri\\":\\"arn:aws:apigateway:\${data.aws_region.Region.name}:lambda:path/2015-03-31/functions/\${aws_lambda_function.Api_Api-OnRequest-c5395e41_37F21C2B.arn}/invocations\\",\\"type\\":\\"aws_proxy\\",\\"httpMethod\\":\\"POST\\",\\"responses\\":{\\"default\\":{\\"statusCode\\":\\"200\\"}},\\"passthroughBehavior\\":\\"when_no_match\\",\\"contentHandling\\":\\"CONVERT_TO_TEXT\\"}}},\\"/{proxy+}\\":{\\"x-amazon-apigateway-any-method\\":{\\"produces\\":[\\"application/json\\"],\\"consumes\\":[\\"application/json\\"],\\"x-amazon-apigateway-integration\\":{\\"type\\":\\"mock\\",\\"requestTemplates\\":{\\"application/json\\":\\"{\\\\\\"statusCode\\\\\\": 404}\\"},\\"responses\\":{\\"default\\":{\\"statusCode\\":\\"404\\",\\"responseParameters\\":{\\"method.response.header.Content-Type\\":\\"'application/json'\\"},\\"responseTemplates\\":{\\"application/json\\":\\"{\\\\\\"statusCode: 404, \\\\\\"message\\\\\\": \\\\\\"Error: Resource not found\\\\\\"}\\"}}}},\\"responses\\":{\\"404\\":{\\"description\\":\\"404 response\\",\\"headers\\":{\\"Content-Type\\":{\\"type\\":\\"string\\"}}}}}}}}", + "body": "{\\"openapi\\":\\"3.0.3\\",\\"paths\\":{\\"/\\":{\\"get\\":{\\"operationId\\":\\"get\\",\\"responses\\":{\\"200\\":{\\"description\\":\\"200 response\\",\\"content\\":{}}},\\"parameters\\":[],\\"x-amazon-apigateway-integration\\":{\\"uri\\":\\"arn:aws:apigateway:\${data.aws_region.Region.name}:lambda:path/2015-03-31/functions/\${aws_lambda_function.Api_Api-OnRequest-c5395e41_37F21C2B.arn}/invocations\\",\\"type\\":\\"aws_proxy\\",\\"httpMethod\\":\\"POST\\",\\"responses\\":{\\"default\\":{\\"statusCode\\":\\"200\\"}},\\"passthroughBehavior\\":\\"when_no_match\\",\\"contentHandling\\":\\"CONVERT_TO_TEXT\\"}}},\\"/{proxy+}\\":{\\"x-amazon-apigateway-any-method\\":{\\"produces\\":[\\"application/json\\"],\\"x-amazon-apigateway-integration\\":{\\"type\\":\\"mock\\",\\"requestTemplates\\":{\\"application/json\\":\\"\\\\n {\\\\\\"statusCode\\\\\\": 404}\\\\n \\"},\\"passthroughBehavior\\":\\"never\\",\\"responses\\":{\\"404\\":{\\"statusCode\\":\\"404\\",\\"responseParameters\\":{\\"method.response.header.Content-Type\\":\\"'application/json'\\"},\\"responseTemplates\\":{\\"application/json\\":\\"{\\\\\\"statusCode\\\\\\": 404, \\\\\\"message\\\\\\": \\\\\\"Error: Resource not found\\\\\\"}\\"}},\\"default\\":{\\"statusCode\\":\\"404\\",\\"responseParameters\\":{\\"method.response.header.Content-Type\\":\\"'application/json'\\"},\\"responseTemplates\\":{\\"application/json\\":\\"{\\\\\\"statusCode\\\\\\": 404, \\\\\\"message\\\\\\": \\\\\\"Error: Resource not found\\\\\\"}\\"}}}},\\"responses\\":{\\"404\\":{\\"description\\":\\"404 response\\",\\"headers\\":{\\"Content-Type\\":{\\"type\\":\\"string\\"}}}}}}}}", "name": "api-c8f613f0", }, }, diff --git a/tools/hangar/__snapshots__/test_corpus/sdk_tests/api/404.w_compile_tf-aws.md b/tools/hangar/__snapshots__/test_corpus/sdk_tests/api/404.w_compile_tf-aws.md index 895cd5b7a46..0287aeb265e 100644 --- a/tools/hangar/__snapshots__/test_corpus/sdk_tests/api/404.w_compile_tf-aws.md +++ b/tools/hangar/__snapshots__/test_corpus/sdk_tests/api/404.w_compile_tf-aws.md @@ -106,7 +106,7 @@ module.exports = function({ $api_url, $http_Util }) { "uniqueId": "cloudApi_api_2B334D75" } }, - "body": "{\"openapi\":\"3.0.3\",\"paths\":{\"/hello\":{\"get\":{\"operationId\":\"get-hello\",\"responses\":{\"200\":{\"description\":\"200 response\",\"content\":{}}},\"parameters\":[],\"x-amazon-apigateway-integration\":{\"uri\":\"arn:aws:apigateway:${data.aws_region.Region.name}:lambda:path/2015-03-31/functions/${aws_lambda_function.cloudApi_cloudApi-OnRequest-cdafee6e_A6C8366F.arn}/invocations\",\"type\":\"aws_proxy\",\"httpMethod\":\"POST\",\"responses\":{\"default\":{\"statusCode\":\"200\"}},\"passthroughBehavior\":\"when_no_match\",\"contentHandling\":\"CONVERT_TO_TEXT\"}}},\"/{proxy+}\":{\"x-amazon-apigateway-any-method\":{\"produces\":[\"application/json\"],\"consumes\":[\"application/json\"],\"x-amazon-apigateway-integration\":{\"type\":\"mock\",\"requestTemplates\":{\"application/json\":\"{\\\"statusCode\\\": 404}\"},\"responses\":{\"default\":{\"statusCode\":\"404\",\"responseParameters\":{\"method.response.header.Content-Type\":\"'application/json'\"},\"responseTemplates\":{\"application/json\":\"{\\\"statusCode: 404, \\\"message\\\": \\\"Error: Resource not found\\\"}\"}}}},\"responses\":{\"404\":{\"description\":\"404 response\",\"headers\":{\"Content-Type\":{\"type\":\"string\"}}}}}}}}", + "body": "{\"openapi\":\"3.0.3\",\"paths\":{\"/hello\":{\"get\":{\"operationId\":\"get-hello\",\"responses\":{\"200\":{\"description\":\"200 response\",\"content\":{}}},\"parameters\":[],\"x-amazon-apigateway-integration\":{\"uri\":\"arn:aws:apigateway:${data.aws_region.Region.name}:lambda:path/2015-03-31/functions/${aws_lambda_function.cloudApi_cloudApi-OnRequest-cdafee6e_A6C8366F.arn}/invocations\",\"type\":\"aws_proxy\",\"httpMethod\":\"POST\",\"responses\":{\"default\":{\"statusCode\":\"200\"}},\"passthroughBehavior\":\"when_no_match\",\"contentHandling\":\"CONVERT_TO_TEXT\"}}},\"/{proxy+}\":{\"x-amazon-apigateway-any-method\":{\"produces\":[\"application/json\"],\"x-amazon-apigateway-integration\":{\"type\":\"mock\",\"requestTemplates\":{\"application/json\":\"\\n {\\\"statusCode\\\": 404}\\n \"},\"passthroughBehavior\":\"never\",\"responses\":{\"404\":{\"statusCode\":\"404\",\"responseParameters\":{\"method.response.header.Content-Type\":\"'application/json'\"},\"responseTemplates\":{\"application/json\":\"{\\\"statusCode\\\": 404, \\\"message\\\": \\\"Error: Resource not found\\\"}\"}},\"default\":{\"statusCode\":\"404\",\"responseParameters\":{\"method.response.header.Content-Type\":\"'application/json'\"},\"responseTemplates\":{\"application/json\":\"{\\\"statusCode\\\": 404, \\\"message\\\": \\\"Error: Resource not found\\\"}\"}}}},\"responses\":{\"404\":{\"description\":\"404 response\",\"headers\":{\"Content-Type\":{\"type\":\"string\"}}}}}}}}", "name": "api-c895068c" } }, diff --git a/tools/hangar/__snapshots__/test_corpus/sdk_tests/api/cors.w_compile_tf-aws.md b/tools/hangar/__snapshots__/test_corpus/sdk_tests/api/cors.w_compile_tf-aws.md index 0dc96f6836e..387fe8cc53d 100644 --- a/tools/hangar/__snapshots__/test_corpus/sdk_tests/api/cors.w_compile_tf-aws.md +++ b/tools/hangar/__snapshots__/test_corpus/sdk_tests/api/cors.w_compile_tf-aws.md @@ -105,7 +105,7 @@ module.exports = function({ $api_url, $http_Util }) { "uniqueId": "cloudApi_api_2B334D75" } }, - "body": "{\"openapi\":\"3.0.3\",\"paths\":{\"/path\":{\"get\":{\"operationId\":\"get-path\",\"responses\":{\"200\":{\"description\":\"200 response\",\"content\":{},\"headers\":{\"Access-Control-Allow-Origin\":{\"schema\":{\"type\":\"string\"}},\"Access-Control-Allow-Methods\":{\"schema\":{\"type\":\"string\"}},\"Access-Control-Allow-Headers\":{\"schema\":{\"type\":\"string\"}}}}},\"parameters\":[],\"x-amazon-apigateway-integration\":{\"uri\":\"arn:aws:apigateway:${data.aws_region.Region.name}:lambda:path/2015-03-31/functions/${aws_lambda_function.cloudApi_cloudApi-OnRequest-cdafee6e_A6C8366F.arn}/invocations\",\"type\":\"aws_proxy\",\"httpMethod\":\"POST\",\"responses\":{\"default\":{\"statusCode\":\"200\"}},\"passthroughBehavior\":\"when_no_match\",\"contentHandling\":\"CONVERT_TO_TEXT\"}}},\"/{proxy+}\":{\"x-amazon-apigateway-any-method\":{\"produces\":[\"application/json\"],\"consumes\":[\"application/json\"],\"x-amazon-apigateway-integration\":{\"type\":\"mock\",\"requestTemplates\":{\"application/json\":\"{\\\"statusCode\\\": 404}\"},\"responses\":{\"default\":{\"statusCode\":\"404\",\"responseParameters\":{\"method.response.header.Content-Type\":\"'application/json'\"},\"responseTemplates\":{\"application/json\":\"{\\\"statusCode: 404, \\\"message\\\": \\\"Error: Resource not found\\\"}\"}}}},\"responses\":{\"404\":{\"description\":\"404 response\",\"headers\":{\"Content-Type\":{\"type\":\"string\"}}}}}}}}", + "body": "{\"openapi\":\"3.0.3\",\"paths\":{\"/path\":{\"get\":{\"operationId\":\"get-path\",\"responses\":{\"200\":{\"description\":\"200 response\",\"content\":{},\"headers\":{\"Access-Control-Allow-Origin\":{\"schema\":{\"type\":\"string\"}},\"Access-Control-Allow-Methods\":{\"schema\":{\"type\":\"string\"}},\"Access-Control-Allow-Headers\":{\"schema\":{\"type\":\"string\"}}}}},\"parameters\":[],\"x-amazon-apigateway-integration\":{\"uri\":\"arn:aws:apigateway:${data.aws_region.Region.name}:lambda:path/2015-03-31/functions/${aws_lambda_function.cloudApi_cloudApi-OnRequest-cdafee6e_A6C8366F.arn}/invocations\",\"type\":\"aws_proxy\",\"httpMethod\":\"POST\",\"responses\":{\"default\":{\"statusCode\":\"200\"}},\"passthroughBehavior\":\"when_no_match\",\"contentHandling\":\"CONVERT_TO_TEXT\"}}},\"/{proxy+}\":{\"x-amazon-apigateway-any-method\":{\"produces\":[\"application/json\"],\"x-amazon-apigateway-integration\":{\"type\":\"mock\",\"requestTemplates\":{\"application/json\":\"\\n #if ($context.httpMethod == \\\"OPTIONS\\\")\\n {\\\"statusCode\\\": 204}\\n #else\\n {\\\"statusCode\\\": 404}\\n #end\\n \"},\"passthroughBehavior\":\"never\",\"responses\":{\"204\":{\"statusCode\":\"204\",\"responseParameters\":{\"method.response.header.Content-Type\":\"'application/json'\",\"method.response.header.Access-Control-Allow-Origin\":\"'*'\",\"method.response.header.Access-Control-Allow-Methods\":\"'GET,POST,PUT,DELETE,HEAD,OPTIONS'\",\"method.response.header.Access-Control-Allow-Headers\":\"'Content-Type,Authorization,X-Requested-With'\"},\"responseTemplates\":{\"application/json\":\"{}\"}},\"404\":{\"statusCode\":\"404\",\"responseParameters\":{\"method.response.header.Content-Type\":\"'application/json'\"},\"responseTemplates\":{\"application/json\":\"{\\\"statusCode\\\": 404, \\\"message\\\": \\\"Error: Resource not found\\\"}\"}},\"default\":{\"statusCode\":\"404\",\"responseParameters\":{\"method.response.header.Content-Type\":\"'application/json'\"},\"responseTemplates\":{\"application/json\":\"{\\\"statusCode\\\": 404, \\\"message\\\": \\\"Error: Resource not found\\\"}\"}}}},\"responses\":{\"204\":{\"description\":\"204 response\",\"headers\":{\"Content-Type\":{\"type\":\"string\"},\"Access-Control-Allow-Origin\":{\"type\":\"string\"},\"Access-Control-Allow-Methods\":{\"type\":\"string\"},\"Access-Control-Allow-Headers\":{\"type\":\"string\"}}},\"404\":{\"description\":\"404 response\",\"headers\":{\"Content-Type\":{\"type\":\"string\"}}}}}}}}", "name": "api-c895068c" } }, diff --git a/tools/hangar/__snapshots__/test_corpus/sdk_tests/api/delete.w_compile_tf-aws.md b/tools/hangar/__snapshots__/test_corpus/sdk_tests/api/delete.w_compile_tf-aws.md index ad5a0098c83..f2fa22ee2d0 100644 --- a/tools/hangar/__snapshots__/test_corpus/sdk_tests/api/delete.w_compile_tf-aws.md +++ b/tools/hangar/__snapshots__/test_corpus/sdk_tests/api/delete.w_compile_tf-aws.md @@ -115,7 +115,7 @@ module.exports = function({ $api_url, $http_HttpMethod, $http_Util }) { "uniqueId": "cloudApi_api_2B334D75" } }, - "body": "{\"openapi\":\"3.0.3\",\"paths\":{\"/path\":{\"delete\":{\"operationId\":\"delete-path\",\"responses\":{\"200\":{\"description\":\"200 response\",\"content\":{}}},\"parameters\":[],\"x-amazon-apigateway-integration\":{\"uri\":\"arn:aws:apigateway:${data.aws_region.Region.name}:lambda:path/2015-03-31/functions/${aws_lambda_function.cloudApi_cloudApi-OnRequest-cdafee6e_A6C8366F.arn}/invocations\",\"type\":\"aws_proxy\",\"httpMethod\":\"POST\",\"responses\":{\"default\":{\"statusCode\":\"200\"}},\"passthroughBehavior\":\"when_no_match\",\"contentHandling\":\"CONVERT_TO_TEXT\"}}},\"/{proxy+}\":{\"x-amazon-apigateway-any-method\":{\"produces\":[\"application/json\"],\"consumes\":[\"application/json\"],\"x-amazon-apigateway-integration\":{\"type\":\"mock\",\"requestTemplates\":{\"application/json\":\"{\\\"statusCode\\\": 404}\"},\"responses\":{\"default\":{\"statusCode\":\"404\",\"responseParameters\":{\"method.response.header.Content-Type\":\"'application/json'\"},\"responseTemplates\":{\"application/json\":\"{\\\"statusCode: 404, \\\"message\\\": \\\"Error: Resource not found\\\"}\"}}}},\"responses\":{\"404\":{\"description\":\"404 response\",\"headers\":{\"Content-Type\":{\"type\":\"string\"}}}}}}}}", + "body": "{\"openapi\":\"3.0.3\",\"paths\":{\"/path\":{\"delete\":{\"operationId\":\"delete-path\",\"responses\":{\"200\":{\"description\":\"200 response\",\"content\":{}}},\"parameters\":[],\"x-amazon-apigateway-integration\":{\"uri\":\"arn:aws:apigateway:${data.aws_region.Region.name}:lambda:path/2015-03-31/functions/${aws_lambda_function.cloudApi_cloudApi-OnRequest-cdafee6e_A6C8366F.arn}/invocations\",\"type\":\"aws_proxy\",\"httpMethod\":\"POST\",\"responses\":{\"default\":{\"statusCode\":\"200\"}},\"passthroughBehavior\":\"when_no_match\",\"contentHandling\":\"CONVERT_TO_TEXT\"}}},\"/{proxy+}\":{\"x-amazon-apigateway-any-method\":{\"produces\":[\"application/json\"],\"x-amazon-apigateway-integration\":{\"type\":\"mock\",\"requestTemplates\":{\"application/json\":\"\\n {\\\"statusCode\\\": 404}\\n \"},\"passthroughBehavior\":\"never\",\"responses\":{\"404\":{\"statusCode\":\"404\",\"responseParameters\":{\"method.response.header.Content-Type\":\"'application/json'\"},\"responseTemplates\":{\"application/json\":\"{\\\"statusCode\\\": 404, \\\"message\\\": \\\"Error: Resource not found\\\"}\"}},\"default\":{\"statusCode\":\"404\",\"responseParameters\":{\"method.response.header.Content-Type\":\"'application/json'\"},\"responseTemplates\":{\"application/json\":\"{\\\"statusCode\\\": 404, \\\"message\\\": \\\"Error: Resource not found\\\"}\"}}}},\"responses\":{\"404\":{\"description\":\"404 response\",\"headers\":{\"Content-Type\":{\"type\":\"string\"}}}}}}}}", "name": "api-c895068c" } }, diff --git a/tools/hangar/__snapshots__/test_corpus/sdk_tests/api/get.w_compile_tf-aws.md b/tools/hangar/__snapshots__/test_corpus/sdk_tests/api/get.w_compile_tf-aws.md index bb7275b8ec8..3c8823de3ce 100644 --- a/tools/hangar/__snapshots__/test_corpus/sdk_tests/api/get.w_compile_tf-aws.md +++ b/tools/hangar/__snapshots__/test_corpus/sdk_tests/api/get.w_compile_tf-aws.md @@ -119,7 +119,7 @@ module.exports = function({ $api_url, $body, $http_HttpMethod, $http_Util }) { "uniqueId": "cloudApi_api_2B334D75" } }, - "body": "{\"openapi\":\"3.0.3\",\"paths\":{\"/path\":{\"get\":{\"operationId\":\"get-path\",\"responses\":{\"200\":{\"description\":\"200 response\",\"content\":{}}},\"parameters\":[],\"x-amazon-apigateway-integration\":{\"uri\":\"arn:aws:apigateway:${data.aws_region.Region.name}:lambda:path/2015-03-31/functions/${aws_lambda_function.cloudApi_cloudApi-OnRequest-cdafee6e_A6C8366F.arn}/invocations\",\"type\":\"aws_proxy\",\"httpMethod\":\"POST\",\"responses\":{\"default\":{\"statusCode\":\"200\"}},\"passthroughBehavior\":\"when_no_match\",\"contentHandling\":\"CONVERT_TO_TEXT\"}}},\"/{proxy+}\":{\"x-amazon-apigateway-any-method\":{\"produces\":[\"application/json\"],\"consumes\":[\"application/json\"],\"x-amazon-apigateway-integration\":{\"type\":\"mock\",\"requestTemplates\":{\"application/json\":\"{\\\"statusCode\\\": 404}\"},\"responses\":{\"default\":{\"statusCode\":\"404\",\"responseParameters\":{\"method.response.header.Content-Type\":\"'application/json'\"},\"responseTemplates\":{\"application/json\":\"{\\\"statusCode: 404, \\\"message\\\": \\\"Error: Resource not found\\\"}\"}}}},\"responses\":{\"404\":{\"description\":\"404 response\",\"headers\":{\"Content-Type\":{\"type\":\"string\"}}}}}}}}", + "body": "{\"openapi\":\"3.0.3\",\"paths\":{\"/path\":{\"get\":{\"operationId\":\"get-path\",\"responses\":{\"200\":{\"description\":\"200 response\",\"content\":{}}},\"parameters\":[],\"x-amazon-apigateway-integration\":{\"uri\":\"arn:aws:apigateway:${data.aws_region.Region.name}:lambda:path/2015-03-31/functions/${aws_lambda_function.cloudApi_cloudApi-OnRequest-cdafee6e_A6C8366F.arn}/invocations\",\"type\":\"aws_proxy\",\"httpMethod\":\"POST\",\"responses\":{\"default\":{\"statusCode\":\"200\"}},\"passthroughBehavior\":\"when_no_match\",\"contentHandling\":\"CONVERT_TO_TEXT\"}}},\"/{proxy+}\":{\"x-amazon-apigateway-any-method\":{\"produces\":[\"application/json\"],\"x-amazon-apigateway-integration\":{\"type\":\"mock\",\"requestTemplates\":{\"application/json\":\"\\n {\\\"statusCode\\\": 404}\\n \"},\"passthroughBehavior\":\"never\",\"responses\":{\"404\":{\"statusCode\":\"404\",\"responseParameters\":{\"method.response.header.Content-Type\":\"'application/json'\"},\"responseTemplates\":{\"application/json\":\"{\\\"statusCode\\\": 404, \\\"message\\\": \\\"Error: Resource not found\\\"}\"}},\"default\":{\"statusCode\":\"404\",\"responseParameters\":{\"method.response.header.Content-Type\":\"'application/json'\"},\"responseTemplates\":{\"application/json\":\"{\\\"statusCode\\\": 404, \\\"message\\\": \\\"Error: Resource not found\\\"}\"}}}},\"responses\":{\"404\":{\"description\":\"404 response\",\"headers\":{\"Content-Type\":{\"type\":\"string\"}}}}}}}}", "name": "api-c895068c" } }, diff --git a/tools/hangar/__snapshots__/test_corpus/sdk_tests/api/options.w_compile_tf-aws.md b/tools/hangar/__snapshots__/test_corpus/sdk_tests/api/options.w_compile_tf-aws.md index eb752661186..149cc13cb69 100644 --- a/tools/hangar/__snapshots__/test_corpus/sdk_tests/api/options.w_compile_tf-aws.md +++ b/tools/hangar/__snapshots__/test_corpus/sdk_tests/api/options.w_compile_tf-aws.md @@ -149,7 +149,7 @@ module.exports = function({ $api_url, $http_HttpMethod, $http_Util, $path }) { "uniqueId": "cloudApi_api_2B334D75" } }, - "body": "{\"openapi\":\"3.0.3\",\"paths\":{\"/path\":{\"options\":{\"operationId\":\"options-path\",\"responses\":{\"200\":{\"description\":\"200 response\",\"content\":{}}},\"parameters\":[],\"x-amazon-apigateway-integration\":{\"uri\":\"arn:aws:apigateway:${data.aws_region.Region.name}:lambda:path/2015-03-31/functions/${aws_lambda_function.cloudApi_cloudApi-OnRequest-cdafee6e_A6C8366F.arn}/invocations\",\"type\":\"aws_proxy\",\"httpMethod\":\"POST\",\"responses\":{\"default\":{\"statusCode\":\"200\"}},\"passthroughBehavior\":\"when_no_match\",\"contentHandling\":\"CONVERT_TO_TEXT\"}},\"head\":{\"operationId\":\"head-path\",\"responses\":{\"200\":{\"description\":\"200 response\",\"content\":{}}},\"parameters\":[],\"x-amazon-apigateway-integration\":{\"uri\":\"arn:aws:apigateway:${data.aws_region.Region.name}:lambda:path/2015-03-31/functions/${aws_lambda_function.cloudApi_cloudApi-OnRequest-86898773_701F5CA7.arn}/invocations\",\"type\":\"aws_proxy\",\"httpMethod\":\"POST\",\"responses\":{\"default\":{\"statusCode\":\"200\"}},\"passthroughBehavior\":\"when_no_match\",\"contentHandling\":\"CONVERT_TO_TEXT\"}},\"connect\":{\"operationId\":\"connect-path\",\"responses\":{\"200\":{\"description\":\"200 response\",\"content\":{}}},\"parameters\":[],\"x-amazon-apigateway-integration\":{\"uri\":\"arn:aws:apigateway:${data.aws_region.Region.name}:lambda:path/2015-03-31/functions/${aws_lambda_function.cloudApi_cloudApi-OnRequest-3fc9280c_5DA20E7A.arn}/invocations\",\"type\":\"aws_proxy\",\"httpMethod\":\"POST\",\"responses\":{\"default\":{\"statusCode\":\"200\"}},\"passthroughBehavior\":\"when_no_match\",\"contentHandling\":\"CONVERT_TO_TEXT\"}}},\"/{proxy+}\":{\"x-amazon-apigateway-any-method\":{\"produces\":[\"application/json\"],\"consumes\":[\"application/json\"],\"x-amazon-apigateway-integration\":{\"type\":\"mock\",\"requestTemplates\":{\"application/json\":\"{\\\"statusCode\\\": 404}\"},\"responses\":{\"default\":{\"statusCode\":\"404\",\"responseParameters\":{\"method.response.header.Content-Type\":\"'application/json'\"},\"responseTemplates\":{\"application/json\":\"{\\\"statusCode: 404, \\\"message\\\": \\\"Error: Resource not found\\\"}\"}}}},\"responses\":{\"404\":{\"description\":\"404 response\",\"headers\":{\"Content-Type\":{\"type\":\"string\"}}}}}}}}", + "body": "{\"openapi\":\"3.0.3\",\"paths\":{\"/path\":{\"options\":{\"operationId\":\"options-path\",\"responses\":{\"200\":{\"description\":\"200 response\",\"content\":{}}},\"parameters\":[],\"x-amazon-apigateway-integration\":{\"uri\":\"arn:aws:apigateway:${data.aws_region.Region.name}:lambda:path/2015-03-31/functions/${aws_lambda_function.cloudApi_cloudApi-OnRequest-cdafee6e_A6C8366F.arn}/invocations\",\"type\":\"aws_proxy\",\"httpMethod\":\"POST\",\"responses\":{\"default\":{\"statusCode\":\"200\"}},\"passthroughBehavior\":\"when_no_match\",\"contentHandling\":\"CONVERT_TO_TEXT\"}},\"head\":{\"operationId\":\"head-path\",\"responses\":{\"200\":{\"description\":\"200 response\",\"content\":{}}},\"parameters\":[],\"x-amazon-apigateway-integration\":{\"uri\":\"arn:aws:apigateway:${data.aws_region.Region.name}:lambda:path/2015-03-31/functions/${aws_lambda_function.cloudApi_cloudApi-OnRequest-86898773_701F5CA7.arn}/invocations\",\"type\":\"aws_proxy\",\"httpMethod\":\"POST\",\"responses\":{\"default\":{\"statusCode\":\"200\"}},\"passthroughBehavior\":\"when_no_match\",\"contentHandling\":\"CONVERT_TO_TEXT\"}},\"connect\":{\"operationId\":\"connect-path\",\"responses\":{\"200\":{\"description\":\"200 response\",\"content\":{}}},\"parameters\":[],\"x-amazon-apigateway-integration\":{\"uri\":\"arn:aws:apigateway:${data.aws_region.Region.name}:lambda:path/2015-03-31/functions/${aws_lambda_function.cloudApi_cloudApi-OnRequest-3fc9280c_5DA20E7A.arn}/invocations\",\"type\":\"aws_proxy\",\"httpMethod\":\"POST\",\"responses\":{\"default\":{\"statusCode\":\"200\"}},\"passthroughBehavior\":\"when_no_match\",\"contentHandling\":\"CONVERT_TO_TEXT\"}}},\"/{proxy+}\":{\"x-amazon-apigateway-any-method\":{\"produces\":[\"application/json\"],\"x-amazon-apigateway-integration\":{\"type\":\"mock\",\"requestTemplates\":{\"application/json\":\"\\n {\\\"statusCode\\\": 404}\\n \"},\"passthroughBehavior\":\"never\",\"responses\":{\"404\":{\"statusCode\":\"404\",\"responseParameters\":{\"method.response.header.Content-Type\":\"'application/json'\"},\"responseTemplates\":{\"application/json\":\"{\\\"statusCode\\\": 404, \\\"message\\\": \\\"Error: Resource not found\\\"}\"}},\"default\":{\"statusCode\":\"404\",\"responseParameters\":{\"method.response.header.Content-Type\":\"'application/json'\"},\"responseTemplates\":{\"application/json\":\"{\\\"statusCode\\\": 404, \\\"message\\\": \\\"Error: Resource not found\\\"}\"}}}},\"responses\":{\"404\":{\"description\":\"404 response\",\"headers\":{\"Content-Type\":{\"type\":\"string\"}}}}}}}}", "name": "api-c895068c" } }, diff --git a/tools/hangar/__snapshots__/test_corpus/sdk_tests/api/patch.w_compile_tf-aws.md b/tools/hangar/__snapshots__/test_corpus/sdk_tests/api/patch.w_compile_tf-aws.md index 471345931da..8dc60e45ab4 100644 --- a/tools/hangar/__snapshots__/test_corpus/sdk_tests/api/patch.w_compile_tf-aws.md +++ b/tools/hangar/__snapshots__/test_corpus/sdk_tests/api/patch.w_compile_tf-aws.md @@ -116,7 +116,7 @@ module.exports = function({ $_id, $api_url, $body, $http_HttpMethod, $http_Util, "uniqueId": "cloudApi_api_2B334D75" } }, - "body": "{\"openapi\":\"3.0.3\",\"paths\":{\"/path/{id}\":{\"patch\":{\"operationId\":\"patch-path/{id}\",\"responses\":{\"200\":{\"description\":\"200 response\",\"content\":{}}},\"parameters\":[{\"name\":\"id\",\"in\":\"path\",\"required\":true,\"schema\":{\"type\":\"string\"}}],\"x-amazon-apigateway-integration\":{\"uri\":\"arn:aws:apigateway:${data.aws_region.Region.name}:lambda:path/2015-03-31/functions/${aws_lambda_function.cloudApi_cloudApi-OnRequest-cdafee6e_A6C8366F.arn}/invocations\",\"type\":\"aws_proxy\",\"httpMethod\":\"POST\",\"responses\":{\"default\":{\"statusCode\":\"200\"}},\"passthroughBehavior\":\"when_no_match\",\"contentHandling\":\"CONVERT_TO_TEXT\"}}},\"/{proxy+}\":{\"x-amazon-apigateway-any-method\":{\"produces\":[\"application/json\"],\"consumes\":[\"application/json\"],\"x-amazon-apigateway-integration\":{\"type\":\"mock\",\"requestTemplates\":{\"application/json\":\"{\\\"statusCode\\\": 404}\"},\"responses\":{\"default\":{\"statusCode\":\"404\",\"responseParameters\":{\"method.response.header.Content-Type\":\"'application/json'\"},\"responseTemplates\":{\"application/json\":\"{\\\"statusCode: 404, \\\"message\\\": \\\"Error: Resource not found\\\"}\"}}}},\"responses\":{\"404\":{\"description\":\"404 response\",\"headers\":{\"Content-Type\":{\"type\":\"string\"}}}}}}}}", + "body": "{\"openapi\":\"3.0.3\",\"paths\":{\"/path/{id}\":{\"patch\":{\"operationId\":\"patch-path/{id}\",\"responses\":{\"200\":{\"description\":\"200 response\",\"content\":{}}},\"parameters\":[{\"name\":\"id\",\"in\":\"path\",\"required\":true,\"schema\":{\"type\":\"string\"}}],\"x-amazon-apigateway-integration\":{\"uri\":\"arn:aws:apigateway:${data.aws_region.Region.name}:lambda:path/2015-03-31/functions/${aws_lambda_function.cloudApi_cloudApi-OnRequest-cdafee6e_A6C8366F.arn}/invocations\",\"type\":\"aws_proxy\",\"httpMethod\":\"POST\",\"responses\":{\"default\":{\"statusCode\":\"200\"}},\"passthroughBehavior\":\"when_no_match\",\"contentHandling\":\"CONVERT_TO_TEXT\"}}},\"/{proxy+}\":{\"x-amazon-apigateway-any-method\":{\"produces\":[\"application/json\"],\"x-amazon-apigateway-integration\":{\"type\":\"mock\",\"requestTemplates\":{\"application/json\":\"\\n {\\\"statusCode\\\": 404}\\n \"},\"passthroughBehavior\":\"never\",\"responses\":{\"404\":{\"statusCode\":\"404\",\"responseParameters\":{\"method.response.header.Content-Type\":\"'application/json'\"},\"responseTemplates\":{\"application/json\":\"{\\\"statusCode\\\": 404, \\\"message\\\": \\\"Error: Resource not found\\\"}\"}},\"default\":{\"statusCode\":\"404\",\"responseParameters\":{\"method.response.header.Content-Type\":\"'application/json'\"},\"responseTemplates\":{\"application/json\":\"{\\\"statusCode\\\": 404, \\\"message\\\": \\\"Error: Resource not found\\\"}\"}}}},\"responses\":{\"404\":{\"description\":\"404 response\",\"headers\":{\"Content-Type\":{\"type\":\"string\"}}}}}}}}", "name": "api-c895068c" } }, diff --git a/tools/hangar/__snapshots__/test_corpus/sdk_tests/api/path_vars.w_compile_tf-aws.md b/tools/hangar/__snapshots__/test_corpus/sdk_tests/api/path_vars.w_compile_tf-aws.md index 9ca1e985925..dd8f9db3dfd 100644 --- a/tools/hangar/__snapshots__/test_corpus/sdk_tests/api/path_vars.w_compile_tf-aws.md +++ b/tools/hangar/__snapshots__/test_corpus/sdk_tests/api/path_vars.w_compile_tf-aws.md @@ -189,7 +189,7 @@ module.exports = function({ $api_url, $http_Util, $std_Json }) { "uniqueId": "cloudApi_api_2B334D75" } }, - "body": "{\"openapi\":\"3.0.3\",\"paths\":{\"/users/{name}\":{\"get\":{\"operationId\":\"get-users/{name}\",\"responses\":{\"200\":{\"description\":\"200 response\",\"content\":{}}},\"parameters\":[{\"name\":\"name\",\"in\":\"path\",\"required\":true,\"schema\":{\"type\":\"string\"}}],\"x-amazon-apigateway-integration\":{\"uri\":\"arn:aws:apigateway:${data.aws_region.Region.name}:lambda:path/2015-03-31/functions/${aws_lambda_function.cloudApi_cloudApi-OnRequest-cdafee6e_A6C8366F.arn}/invocations\",\"type\":\"aws_proxy\",\"httpMethod\":\"POST\",\"responses\":{\"default\":{\"statusCode\":\"200\"}},\"passthroughBehavior\":\"when_no_match\",\"contentHandling\":\"CONVERT_TO_TEXT\"}}},\"/path/{name}\":{\"get\":{\"operationId\":\"get-path/{name}\",\"responses\":{\"200\":{\"description\":\"200 response\",\"content\":{}}},\"parameters\":[{\"name\":\"name\",\"in\":\"path\",\"required\":true,\"schema\":{\"type\":\"string\"}}],\"x-amazon-apigateway-integration\":{\"uri\":\"arn:aws:apigateway:${data.aws_region.Region.name}:lambda:path/2015-03-31/functions/${aws_lambda_function.cloudApi_cloudApi-OnRequest-cdafee6e_A6C8366F.arn}/invocations\",\"type\":\"aws_proxy\",\"httpMethod\":\"POST\",\"responses\":{\"default\":{\"statusCode\":\"200\"}},\"passthroughBehavior\":\"when_no_match\",\"contentHandling\":\"CONVERT_TO_TEXT\"}}},\"/users/permission/{name}\":{\"get\":{\"operationId\":\"get-users/permission/{name}\",\"responses\":{\"200\":{\"description\":\"200 response\",\"content\":{}}},\"parameters\":[{\"name\":\"name\",\"in\":\"path\",\"required\":true,\"schema\":{\"type\":\"string\"}}],\"x-amazon-apigateway-integration\":{\"uri\":\"arn:aws:apigateway:${data.aws_region.Region.name}:lambda:path/2015-03-31/functions/${aws_lambda_function.cloudApi_cloudApi-OnRequest-cdafee6e_A6C8366F.arn}/invocations\",\"type\":\"aws_proxy\",\"httpMethod\":\"POST\",\"responses\":{\"default\":{\"statusCode\":\"200\"}},\"passthroughBehavior\":\"when_no_match\",\"contentHandling\":\"CONVERT_TO_TEXT\"}}},\"/path/{name}/{age}\":{\"get\":{\"operationId\":\"get-path/{name}/{age}\",\"responses\":{\"200\":{\"description\":\"200 response\",\"content\":{}}},\"parameters\":[{\"name\":\"name\",\"in\":\"path\",\"required\":true,\"schema\":{\"type\":\"string\"}},{\"name\":\"age\",\"in\":\"path\",\"required\":true,\"schema\":{\"type\":\"string\"}}],\"x-amazon-apigateway-integration\":{\"uri\":\"arn:aws:apigateway:${data.aws_region.Region.name}:lambda:path/2015-03-31/functions/${aws_lambda_function.cloudApi_cloudApi-OnRequest-86898773_701F5CA7.arn}/invocations\",\"type\":\"aws_proxy\",\"httpMethod\":\"POST\",\"responses\":{\"default\":{\"statusCode\":\"200\"}},\"passthroughBehavior\":\"when_no_match\",\"contentHandling\":\"CONVERT_TO_TEXT\"}}},\"/{proxy+}\":{\"x-amazon-apigateway-any-method\":{\"produces\":[\"application/json\"],\"consumes\":[\"application/json\"],\"x-amazon-apigateway-integration\":{\"type\":\"mock\",\"requestTemplates\":{\"application/json\":\"{\\\"statusCode\\\": 404}\"},\"responses\":{\"default\":{\"statusCode\":\"404\",\"responseParameters\":{\"method.response.header.Content-Type\":\"'application/json'\"},\"responseTemplates\":{\"application/json\":\"{\\\"statusCode: 404, \\\"message\\\": \\\"Error: Resource not found\\\"}\"}}}},\"responses\":{\"404\":{\"description\":\"404 response\",\"headers\":{\"Content-Type\":{\"type\":\"string\"}}}}}}}}", + "body": "{\"openapi\":\"3.0.3\",\"paths\":{\"/users/{name}\":{\"get\":{\"operationId\":\"get-users/{name}\",\"responses\":{\"200\":{\"description\":\"200 response\",\"content\":{}}},\"parameters\":[{\"name\":\"name\",\"in\":\"path\",\"required\":true,\"schema\":{\"type\":\"string\"}}],\"x-amazon-apigateway-integration\":{\"uri\":\"arn:aws:apigateway:${data.aws_region.Region.name}:lambda:path/2015-03-31/functions/${aws_lambda_function.cloudApi_cloudApi-OnRequest-cdafee6e_A6C8366F.arn}/invocations\",\"type\":\"aws_proxy\",\"httpMethod\":\"POST\",\"responses\":{\"default\":{\"statusCode\":\"200\"}},\"passthroughBehavior\":\"when_no_match\",\"contentHandling\":\"CONVERT_TO_TEXT\"}}},\"/path/{name}\":{\"get\":{\"operationId\":\"get-path/{name}\",\"responses\":{\"200\":{\"description\":\"200 response\",\"content\":{}}},\"parameters\":[{\"name\":\"name\",\"in\":\"path\",\"required\":true,\"schema\":{\"type\":\"string\"}}],\"x-amazon-apigateway-integration\":{\"uri\":\"arn:aws:apigateway:${data.aws_region.Region.name}:lambda:path/2015-03-31/functions/${aws_lambda_function.cloudApi_cloudApi-OnRequest-cdafee6e_A6C8366F.arn}/invocations\",\"type\":\"aws_proxy\",\"httpMethod\":\"POST\",\"responses\":{\"default\":{\"statusCode\":\"200\"}},\"passthroughBehavior\":\"when_no_match\",\"contentHandling\":\"CONVERT_TO_TEXT\"}}},\"/users/permission/{name}\":{\"get\":{\"operationId\":\"get-users/permission/{name}\",\"responses\":{\"200\":{\"description\":\"200 response\",\"content\":{}}},\"parameters\":[{\"name\":\"name\",\"in\":\"path\",\"required\":true,\"schema\":{\"type\":\"string\"}}],\"x-amazon-apigateway-integration\":{\"uri\":\"arn:aws:apigateway:${data.aws_region.Region.name}:lambda:path/2015-03-31/functions/${aws_lambda_function.cloudApi_cloudApi-OnRequest-cdafee6e_A6C8366F.arn}/invocations\",\"type\":\"aws_proxy\",\"httpMethod\":\"POST\",\"responses\":{\"default\":{\"statusCode\":\"200\"}},\"passthroughBehavior\":\"when_no_match\",\"contentHandling\":\"CONVERT_TO_TEXT\"}}},\"/path/{name}/{age}\":{\"get\":{\"operationId\":\"get-path/{name}/{age}\",\"responses\":{\"200\":{\"description\":\"200 response\",\"content\":{}}},\"parameters\":[{\"name\":\"name\",\"in\":\"path\",\"required\":true,\"schema\":{\"type\":\"string\"}},{\"name\":\"age\",\"in\":\"path\",\"required\":true,\"schema\":{\"type\":\"string\"}}],\"x-amazon-apigateway-integration\":{\"uri\":\"arn:aws:apigateway:${data.aws_region.Region.name}:lambda:path/2015-03-31/functions/${aws_lambda_function.cloudApi_cloudApi-OnRequest-86898773_701F5CA7.arn}/invocations\",\"type\":\"aws_proxy\",\"httpMethod\":\"POST\",\"responses\":{\"default\":{\"statusCode\":\"200\"}},\"passthroughBehavior\":\"when_no_match\",\"contentHandling\":\"CONVERT_TO_TEXT\"}}},\"/{proxy+}\":{\"x-amazon-apigateway-any-method\":{\"produces\":[\"application/json\"],\"x-amazon-apigateway-integration\":{\"type\":\"mock\",\"requestTemplates\":{\"application/json\":\"\\n {\\\"statusCode\\\": 404}\\n \"},\"passthroughBehavior\":\"never\",\"responses\":{\"404\":{\"statusCode\":\"404\",\"responseParameters\":{\"method.response.header.Content-Type\":\"'application/json'\"},\"responseTemplates\":{\"application/json\":\"{\\\"statusCode\\\": 404, \\\"message\\\": \\\"Error: Resource not found\\\"}\"}},\"default\":{\"statusCode\":\"404\",\"responseParameters\":{\"method.response.header.Content-Type\":\"'application/json'\"},\"responseTemplates\":{\"application/json\":\"{\\\"statusCode\\\": 404, \\\"message\\\": \\\"Error: Resource not found\\\"}\"}}}},\"responses\":{\"404\":{\"description\":\"404 response\",\"headers\":{\"Content-Type\":{\"type\":\"string\"}}}}}}}}", "name": "api-c895068c" } }, diff --git a/tools/hangar/__snapshots__/test_corpus/sdk_tests/api/post.w_compile_tf-aws.md b/tools/hangar/__snapshots__/test_corpus/sdk_tests/api/post.w_compile_tf-aws.md index 5c3c2344a04..d535e875a54 100644 --- a/tools/hangar/__snapshots__/test_corpus/sdk_tests/api/post.w_compile_tf-aws.md +++ b/tools/hangar/__snapshots__/test_corpus/sdk_tests/api/post.w_compile_tf-aws.md @@ -115,7 +115,7 @@ module.exports = function({ $api_url, $body, $http_HttpMethod, $http_Util, $std_ "uniqueId": "cloudApi_api_2B334D75" } }, - "body": "{\"openapi\":\"3.0.3\",\"paths\":{\"/path\":{\"post\":{\"operationId\":\"post-path\",\"responses\":{\"200\":{\"description\":\"200 response\",\"content\":{}}},\"parameters\":[],\"x-amazon-apigateway-integration\":{\"uri\":\"arn:aws:apigateway:${data.aws_region.Region.name}:lambda:path/2015-03-31/functions/${aws_lambda_function.cloudApi_cloudApi-OnRequest-cdafee6e_A6C8366F.arn}/invocations\",\"type\":\"aws_proxy\",\"httpMethod\":\"POST\",\"responses\":{\"default\":{\"statusCode\":\"200\"}},\"passthroughBehavior\":\"when_no_match\",\"contentHandling\":\"CONVERT_TO_TEXT\"}}},\"/{proxy+}\":{\"x-amazon-apigateway-any-method\":{\"produces\":[\"application/json\"],\"consumes\":[\"application/json\"],\"x-amazon-apigateway-integration\":{\"type\":\"mock\",\"requestTemplates\":{\"application/json\":\"{\\\"statusCode\\\": 404}\"},\"responses\":{\"default\":{\"statusCode\":\"404\",\"responseParameters\":{\"method.response.header.Content-Type\":\"'application/json'\"},\"responseTemplates\":{\"application/json\":\"{\\\"statusCode: 404, \\\"message\\\": \\\"Error: Resource not found\\\"}\"}}}},\"responses\":{\"404\":{\"description\":\"404 response\",\"headers\":{\"Content-Type\":{\"type\":\"string\"}}}}}}}}", + "body": "{\"openapi\":\"3.0.3\",\"paths\":{\"/path\":{\"post\":{\"operationId\":\"post-path\",\"responses\":{\"200\":{\"description\":\"200 response\",\"content\":{}}},\"parameters\":[],\"x-amazon-apigateway-integration\":{\"uri\":\"arn:aws:apigateway:${data.aws_region.Region.name}:lambda:path/2015-03-31/functions/${aws_lambda_function.cloudApi_cloudApi-OnRequest-cdafee6e_A6C8366F.arn}/invocations\",\"type\":\"aws_proxy\",\"httpMethod\":\"POST\",\"responses\":{\"default\":{\"statusCode\":\"200\"}},\"passthroughBehavior\":\"when_no_match\",\"contentHandling\":\"CONVERT_TO_TEXT\"}}},\"/{proxy+}\":{\"x-amazon-apigateway-any-method\":{\"produces\":[\"application/json\"],\"x-amazon-apigateway-integration\":{\"type\":\"mock\",\"requestTemplates\":{\"application/json\":\"\\n {\\\"statusCode\\\": 404}\\n \"},\"passthroughBehavior\":\"never\",\"responses\":{\"404\":{\"statusCode\":\"404\",\"responseParameters\":{\"method.response.header.Content-Type\":\"'application/json'\"},\"responseTemplates\":{\"application/json\":\"{\\\"statusCode\\\": 404, \\\"message\\\": \\\"Error: Resource not found\\\"}\"}},\"default\":{\"statusCode\":\"404\",\"responseParameters\":{\"method.response.header.Content-Type\":\"'application/json'\"},\"responseTemplates\":{\"application/json\":\"{\\\"statusCode\\\": 404, \\\"message\\\": \\\"Error: Resource not found\\\"}\"}}}},\"responses\":{\"404\":{\"description\":\"404 response\",\"headers\":{\"Content-Type\":{\"type\":\"string\"}}}}}}}}", "name": "api-c895068c" } }, diff --git a/tools/hangar/__snapshots__/test_corpus/sdk_tests/api/put.w_compile_tf-aws.md b/tools/hangar/__snapshots__/test_corpus/sdk_tests/api/put.w_compile_tf-aws.md index 72852cd405d..6aa19413204 100644 --- a/tools/hangar/__snapshots__/test_corpus/sdk_tests/api/put.w_compile_tf-aws.md +++ b/tools/hangar/__snapshots__/test_corpus/sdk_tests/api/put.w_compile_tf-aws.md @@ -120,7 +120,7 @@ module.exports = function({ $_id, $api_url, $body, $http_HttpMethod, $http_Util, "uniqueId": "cloudApi_api_2B334D75" } }, - "body": "{\"openapi\":\"3.0.3\",\"paths\":{\"/path/{id}/nn/{user}\":{\"put\":{\"operationId\":\"put-path/{id}/nn/{user}\",\"responses\":{\"200\":{\"description\":\"200 response\",\"content\":{}}},\"parameters\":[{\"name\":\"id\",\"in\":\"path\",\"required\":true,\"schema\":{\"type\":\"string\"}},{\"name\":\"user\",\"in\":\"path\",\"required\":true,\"schema\":{\"type\":\"string\"}}],\"x-amazon-apigateway-integration\":{\"uri\":\"arn:aws:apigateway:${data.aws_region.Region.name}:lambda:path/2015-03-31/functions/${aws_lambda_function.cloudApi_cloudApi-OnRequest-cdafee6e_A6C8366F.arn}/invocations\",\"type\":\"aws_proxy\",\"httpMethod\":\"POST\",\"responses\":{\"default\":{\"statusCode\":\"200\"}},\"passthroughBehavior\":\"when_no_match\",\"contentHandling\":\"CONVERT_TO_TEXT\"}}},\"/{proxy+}\":{\"x-amazon-apigateway-any-method\":{\"produces\":[\"application/json\"],\"consumes\":[\"application/json\"],\"x-amazon-apigateway-integration\":{\"type\":\"mock\",\"requestTemplates\":{\"application/json\":\"{\\\"statusCode\\\": 404}\"},\"responses\":{\"default\":{\"statusCode\":\"404\",\"responseParameters\":{\"method.response.header.Content-Type\":\"'application/json'\"},\"responseTemplates\":{\"application/json\":\"{\\\"statusCode: 404, \\\"message\\\": \\\"Error: Resource not found\\\"}\"}}}},\"responses\":{\"404\":{\"description\":\"404 response\",\"headers\":{\"Content-Type\":{\"type\":\"string\"}}}}}}}}", + "body": "{\"openapi\":\"3.0.3\",\"paths\":{\"/path/{id}/nn/{user}\":{\"put\":{\"operationId\":\"put-path/{id}/nn/{user}\",\"responses\":{\"200\":{\"description\":\"200 response\",\"content\":{}}},\"parameters\":[{\"name\":\"id\",\"in\":\"path\",\"required\":true,\"schema\":{\"type\":\"string\"}},{\"name\":\"user\",\"in\":\"path\",\"required\":true,\"schema\":{\"type\":\"string\"}}],\"x-amazon-apigateway-integration\":{\"uri\":\"arn:aws:apigateway:${data.aws_region.Region.name}:lambda:path/2015-03-31/functions/${aws_lambda_function.cloudApi_cloudApi-OnRequest-cdafee6e_A6C8366F.arn}/invocations\",\"type\":\"aws_proxy\",\"httpMethod\":\"POST\",\"responses\":{\"default\":{\"statusCode\":\"200\"}},\"passthroughBehavior\":\"when_no_match\",\"contentHandling\":\"CONVERT_TO_TEXT\"}}},\"/{proxy+}\":{\"x-amazon-apigateway-any-method\":{\"produces\":[\"application/json\"],\"x-amazon-apigateway-integration\":{\"type\":\"mock\",\"requestTemplates\":{\"application/json\":\"\\n {\\\"statusCode\\\": 404}\\n \"},\"passthroughBehavior\":\"never\",\"responses\":{\"404\":{\"statusCode\":\"404\",\"responseParameters\":{\"method.response.header.Content-Type\":\"'application/json'\"},\"responseTemplates\":{\"application/json\":\"{\\\"statusCode\\\": 404, \\\"message\\\": \\\"Error: Resource not found\\\"}\"}},\"default\":{\"statusCode\":\"404\",\"responseParameters\":{\"method.response.header.Content-Type\":\"'application/json'\"},\"responseTemplates\":{\"application/json\":\"{\\\"statusCode\\\": 404, \\\"message\\\": \\\"Error: Resource not found\\\"}\"}}}},\"responses\":{\"404\":{\"description\":\"404 response\",\"headers\":{\"Content-Type\":{\"type\":\"string\"}}}}}}}}", "name": "api-c895068c" } }, diff --git a/tools/hangar/__snapshots__/test_corpus/valid/api.w_compile_tf-aws.md b/tools/hangar/__snapshots__/test_corpus/valid/api.w_compile_tf-aws.md index f8ec9aef635..75d03afb368 100644 --- a/tools/hangar/__snapshots__/test_corpus/valid/api.w_compile_tf-aws.md +++ b/tools/hangar/__snapshots__/test_corpus/valid/api.w_compile_tf-aws.md @@ -153,7 +153,7 @@ module.exports = function({ }) { "uniqueId": "A_cloudApi_api_37FCEF91" } }, - "body": "{\"openapi\":\"3.0.3\",\"paths\":{\"/endpoint1\":{\"get\":{\"operationId\":\"get-endpoint1\",\"responses\":{\"200\":{\"description\":\"200 response\",\"content\":{}}},\"parameters\":[],\"x-amazon-apigateway-integration\":{\"uri\":\"arn:aws:apigateway:${data.aws_region.Region.name}:lambda:path/2015-03-31/functions/${aws_lambda_function.A_cloudApi_cloudApi-OnRequest-73c5308f_E645B0BE.arn}/invocations\",\"type\":\"aws_proxy\",\"httpMethod\":\"POST\",\"responses\":{\"default\":{\"statusCode\":\"200\"}},\"passthroughBehavior\":\"when_no_match\",\"contentHandling\":\"CONVERT_TO_TEXT\"}}},\"/{proxy+}\":{\"x-amazon-apigateway-any-method\":{\"produces\":[\"application/json\"],\"consumes\":[\"application/json\"],\"x-amazon-apigateway-integration\":{\"type\":\"mock\",\"requestTemplates\":{\"application/json\":\"{\\\"statusCode\\\": 404}\"},\"responses\":{\"default\":{\"statusCode\":\"404\",\"responseParameters\":{\"method.response.header.Content-Type\":\"'application/json'\"},\"responseTemplates\":{\"application/json\":\"{\\\"statusCode: 404, \\\"message\\\": \\\"Error: Resource not found\\\"}\"}}}},\"responses\":{\"404\":{\"description\":\"404 response\",\"headers\":{\"Content-Type\":{\"type\":\"string\"}}}}}}}}", + "body": "{\"openapi\":\"3.0.3\",\"paths\":{\"/endpoint1\":{\"get\":{\"operationId\":\"get-endpoint1\",\"responses\":{\"200\":{\"description\":\"200 response\",\"content\":{}}},\"parameters\":[],\"x-amazon-apigateway-integration\":{\"uri\":\"arn:aws:apigateway:${data.aws_region.Region.name}:lambda:path/2015-03-31/functions/${aws_lambda_function.A_cloudApi_cloudApi-OnRequest-73c5308f_E645B0BE.arn}/invocations\",\"type\":\"aws_proxy\",\"httpMethod\":\"POST\",\"responses\":{\"default\":{\"statusCode\":\"200\"}},\"passthroughBehavior\":\"when_no_match\",\"contentHandling\":\"CONVERT_TO_TEXT\"}}},\"/{proxy+}\":{\"x-amazon-apigateway-any-method\":{\"produces\":[\"application/json\"],\"x-amazon-apigateway-integration\":{\"type\":\"mock\",\"requestTemplates\":{\"application/json\":\"\\n {\\\"statusCode\\\": 404}\\n \"},\"passthroughBehavior\":\"never\",\"responses\":{\"404\":{\"statusCode\":\"404\",\"responseParameters\":{\"method.response.header.Content-Type\":\"'application/json'\"},\"responseTemplates\":{\"application/json\":\"{\\\"statusCode\\\": 404, \\\"message\\\": \\\"Error: Resource not found\\\"}\"}},\"default\":{\"statusCode\":\"404\",\"responseParameters\":{\"method.response.header.Content-Type\":\"'application/json'\"},\"responseTemplates\":{\"application/json\":\"{\\\"statusCode\\\": 404, \\\"message\\\": \\\"Error: Resource not found\\\"}\"}}}},\"responses\":{\"404\":{\"description\":\"404 response\",\"headers\":{\"Content-Type\":{\"type\":\"string\"}}}}}}}}", "name": "api-c8c7a7a3" }, "cloudApi_api_2B334D75": { @@ -163,7 +163,7 @@ module.exports = function({ }) { "uniqueId": "cloudApi_api_2B334D75" } }, - "body": "{\"openapi\":\"3.0.3\",\"paths\":{\"/hello/world\":{\"get\":{\"operationId\":\"get-hello/world\",\"responses\":{\"200\":{\"description\":\"200 response\",\"content\":{}}},\"parameters\":[],\"x-amazon-apigateway-integration\":{\"uri\":\"arn:aws:apigateway:${data.aws_region.Region.name}:lambda:path/2015-03-31/functions/${aws_lambda_function.cloudApi_cloudApi-OnRequest-cdafee6e_A6C8366F.arn}/invocations\",\"type\":\"aws_proxy\",\"httpMethod\":\"POST\",\"responses\":{\"default\":{\"statusCode\":\"200\"}},\"passthroughBehavior\":\"when_no_match\",\"contentHandling\":\"CONVERT_TO_TEXT\"}}},\"/{proxy+}\":{\"x-amazon-apigateway-any-method\":{\"produces\":[\"application/json\"],\"consumes\":[\"application/json\"],\"x-amazon-apigateway-integration\":{\"type\":\"mock\",\"requestTemplates\":{\"application/json\":\"{\\\"statusCode\\\": 404}\"},\"responses\":{\"default\":{\"statusCode\":\"404\",\"responseParameters\":{\"method.response.header.Content-Type\":\"'application/json'\"},\"responseTemplates\":{\"application/json\":\"{\\\"statusCode: 404, \\\"message\\\": \\\"Error: Resource not found\\\"}\"}}}},\"responses\":{\"404\":{\"description\":\"404 response\",\"headers\":{\"Content-Type\":{\"type\":\"string\"}}}}}}}}", + "body": "{\"openapi\":\"3.0.3\",\"paths\":{\"/hello/world\":{\"get\":{\"operationId\":\"get-hello/world\",\"responses\":{\"200\":{\"description\":\"200 response\",\"content\":{}}},\"parameters\":[],\"x-amazon-apigateway-integration\":{\"uri\":\"arn:aws:apigateway:${data.aws_region.Region.name}:lambda:path/2015-03-31/functions/${aws_lambda_function.cloudApi_cloudApi-OnRequest-cdafee6e_A6C8366F.arn}/invocations\",\"type\":\"aws_proxy\",\"httpMethod\":\"POST\",\"responses\":{\"default\":{\"statusCode\":\"200\"}},\"passthroughBehavior\":\"when_no_match\",\"contentHandling\":\"CONVERT_TO_TEXT\"}}},\"/{proxy+}\":{\"x-amazon-apigateway-any-method\":{\"produces\":[\"application/json\"],\"x-amazon-apigateway-integration\":{\"type\":\"mock\",\"requestTemplates\":{\"application/json\":\"\\n {\\\"statusCode\\\": 404}\\n \"},\"passthroughBehavior\":\"never\",\"responses\":{\"404\":{\"statusCode\":\"404\",\"responseParameters\":{\"method.response.header.Content-Type\":\"'application/json'\"},\"responseTemplates\":{\"application/json\":\"{\\\"statusCode\\\": 404, \\\"message\\\": \\\"Error: Resource not found\\\"}\"}},\"default\":{\"statusCode\":\"404\",\"responseParameters\":{\"method.response.header.Content-Type\":\"'application/json'\"},\"responseTemplates\":{\"application/json\":\"{\\\"statusCode\\\": 404, \\\"message\\\": \\\"Error: Resource not found\\\"}\"}}}},\"responses\":{\"404\":{\"description\":\"404 response\",\"headers\":{\"Content-Type\":{\"type\":\"string\"}}}}}}}}", "name": "api-c895068c" } }, diff --git a/tools/hangar/__snapshots__/test_corpus/valid/api_cors_custom.w_compile_tf-aws.md b/tools/hangar/__snapshots__/test_corpus/valid/api_cors_custom.w_compile_tf-aws.md index c56608b3d64..ddddf0a5289 100644 --- a/tools/hangar/__snapshots__/test_corpus/valid/api_cors_custom.w_compile_tf-aws.md +++ b/tools/hangar/__snapshots__/test_corpus/valid/api_cors_custom.w_compile_tf-aws.md @@ -58,7 +58,7 @@ module.exports = function({ $api_url, $http_HttpMethod, $http_Util, $t_Assert }) (await $t_Assert.equalNum(response.status,204)); (await $t_Assert.equalStr((headers)["access-control-allow-methods"],"GET,POST,OPTIONS")); (await $t_Assert.equalStr((headers)["access-control-allow-headers"],"Content-Type,Authorization,X-Custom-Header")); - (await $t_Assert.isNil((headers)["access-control-allow-origin"])); + (await $t_Assert.equalStr((headers)["access-control-allow-origin"],"winglang.io")); (await $t_Assert.isNil((headers)["access-control-expose-headers"])); (await $t_Assert.isNil((headers)["access-control-allow-credentials"])); } @@ -78,11 +78,12 @@ module.exports = function({ $api_url, $http_HttpMethod, $http_Util, $t_Assert }) return $obj; } async handle() { - const response = (await $http_Util.fetch(($api_url + "/users"),({"method": $http_HttpMethod.OPTIONS,"headers": ({"Content-Type": "text/json","Access-Control-Request-Method": "PUT","Access-Control-Request-Headers": "Content-Type,Authorization,X-Custom-Foo"})}))); + const response = (await $http_Util.fetch(($api_url + "/users"),({"method": $http_HttpMethod.OPTIONS,"headers": ({"Access-Control-Request-Method": "PUT","Access-Control-Request-Headers": "Content-Type,Authorization,X-Custom-Foo"})}))); const headers = response.headers; (await $t_Assert.equalNum(response.status,204)); (await $t_Assert.equalStr((headers)["access-control-allow-methods"],"GET,POST,OPTIONS")); (await $t_Assert.equalStr((headers)["access-control-allow-headers"],"Content-Type,Authorization,X-Custom-Header")); + (await $t_Assert.equalStr((headers)["access-control-allow-origin"],"winglang.io")); } } return $Closure4; @@ -196,7 +197,7 @@ module.exports = function({ }) { "uniqueId": "cloudApi_api_2B334D75" } }, - "body": "{\"openapi\":\"3.0.3\",\"paths\":{\"/users\":{\"get\":{\"operationId\":\"get-users\",\"responses\":{\"200\":{\"description\":\"200 response\",\"content\":{},\"headers\":{\"Access-Control-Allow-Origin\":{\"schema\":{\"type\":\"string\"}},\"Access-Control-Allow-Methods\":{\"schema\":{\"type\":\"string\"}},\"Access-Control-Allow-Headers\":{\"schema\":{\"type\":\"string\"}}}}},\"parameters\":[],\"x-amazon-apigateway-integration\":{\"uri\":\"arn:aws:apigateway:${data.aws_region.Region.name}:lambda:path/2015-03-31/functions/${aws_lambda_function.cloudApi_cloudApi-OnRequest-cdafee6e_A6C8366F.arn}/invocations\",\"type\":\"aws_proxy\",\"httpMethod\":\"POST\",\"responses\":{\"default\":{\"statusCode\":\"200\"}},\"passthroughBehavior\":\"when_no_match\",\"contentHandling\":\"CONVERT_TO_TEXT\"}}},\"/{proxy+}\":{\"x-amazon-apigateway-any-method\":{\"produces\":[\"application/json\"],\"consumes\":[\"application/json\"],\"x-amazon-apigateway-integration\":{\"type\":\"mock\",\"requestTemplates\":{\"application/json\":\"{\\\"statusCode\\\": 404}\"},\"responses\":{\"default\":{\"statusCode\":\"404\",\"responseParameters\":{\"method.response.header.Content-Type\":\"'application/json'\"},\"responseTemplates\":{\"application/json\":\"{\\\"statusCode: 404, \\\"message\\\": \\\"Error: Resource not found\\\"}\"}}}},\"responses\":{\"404\":{\"description\":\"404 response\",\"headers\":{\"Content-Type\":{\"type\":\"string\"}}}}}}}}", + "body": "{\"openapi\":\"3.0.3\",\"paths\":{\"/users\":{\"get\":{\"operationId\":\"get-users\",\"responses\":{\"200\":{\"description\":\"200 response\",\"content\":{},\"headers\":{\"Access-Control-Allow-Origin\":{\"schema\":{\"type\":\"string\"}},\"Access-Control-Allow-Methods\":{\"schema\":{\"type\":\"string\"}},\"Access-Control-Allow-Headers\":{\"schema\":{\"type\":\"string\"}}}}},\"parameters\":[],\"x-amazon-apigateway-integration\":{\"uri\":\"arn:aws:apigateway:${data.aws_region.Region.name}:lambda:path/2015-03-31/functions/${aws_lambda_function.cloudApi_cloudApi-OnRequest-cdafee6e_A6C8366F.arn}/invocations\",\"type\":\"aws_proxy\",\"httpMethod\":\"POST\",\"responses\":{\"default\":{\"statusCode\":\"200\"}},\"passthroughBehavior\":\"when_no_match\",\"contentHandling\":\"CONVERT_TO_TEXT\"}}},\"/{proxy+}\":{\"x-amazon-apigateway-any-method\":{\"produces\":[\"application/json\"],\"x-amazon-apigateway-integration\":{\"type\":\"mock\",\"requestTemplates\":{\"application/json\":\"\\n #if ($context.httpMethod == \\\"OPTIONS\\\")\\n {\\\"statusCode\\\": 204}\\n #else\\n {\\\"statusCode\\\": 404}\\n #end\\n \"},\"passthroughBehavior\":\"never\",\"responses\":{\"204\":{\"statusCode\":\"204\",\"responseParameters\":{\"method.response.header.Content-Type\":\"'application/json'\",\"method.response.header.Access-Control-Allow-Origin\":\"'winglang.io'\",\"method.response.header.Access-Control-Allow-Methods\":\"'GET,POST,OPTIONS'\",\"method.response.header.Access-Control-Allow-Headers\":\"'Content-Type,Authorization,X-Custom-Header'\"},\"responseTemplates\":{\"application/json\":\"{}\"}},\"404\":{\"statusCode\":\"404\",\"responseParameters\":{\"method.response.header.Content-Type\":\"'application/json'\"},\"responseTemplates\":{\"application/json\":\"{\\\"statusCode\\\": 404, \\\"message\\\": \\\"Error: Resource not found\\\"}\"}},\"default\":{\"statusCode\":\"404\",\"responseParameters\":{\"method.response.header.Content-Type\":\"'application/json'\"},\"responseTemplates\":{\"application/json\":\"{\\\"statusCode\\\": 404, \\\"message\\\": \\\"Error: Resource not found\\\"}\"}}}},\"responses\":{\"204\":{\"description\":\"204 response\",\"headers\":{\"Content-Type\":{\"type\":\"string\"},\"Access-Control-Allow-Origin\":{\"type\":\"string\"},\"Access-Control-Allow-Methods\":{\"type\":\"string\"},\"Access-Control-Allow-Headers\":{\"type\":\"string\"}}},\"404\":{\"description\":\"404 response\",\"headers\":{\"Content-Type\":{\"type\":\"string\"}}}}}}}}", "name": "api-c895068c" } }, diff --git a/tools/hangar/__snapshots__/test_corpus/valid/api_cors_default.w_compile_tf-aws.md b/tools/hangar/__snapshots__/test_corpus/valid/api_cors_default.w_compile_tf-aws.md index 9509893616e..2a0040d26b0 100644 --- a/tools/hangar/__snapshots__/test_corpus/valid/api_cors_default.w_compile_tf-aws.md +++ b/tools/hangar/__snapshots__/test_corpus/valid/api_cors_default.w_compile_tf-aws.md @@ -58,7 +58,7 @@ module.exports = function({ $apiDefaultCors_url, $http_HttpMethod, $http_Util, $ (await $t_Assert.equalNum(response.status,204)); (await $t_Assert.equalStr((headers)["access-control-allow-headers"],"Content-Type,Authorization,X-Requested-With")); (await $t_Assert.equalStr((headers)["access-control-allow-methods"],"GET,POST,PUT,DELETE,HEAD,OPTIONS")); - (await $t_Assert.isNil((headers)["access-control-allow-origin"])); + (await $t_Assert.equalStr((headers)["access-control-allow-origin"],"*")); (await $t_Assert.isNil((headers)["access-control-allow-credentials"])); (await $t_Assert.isNil((headers)["access-control-expose-headers"])); } @@ -174,7 +174,7 @@ module.exports = function({ }) { "uniqueId": "cloudApi_api_2B334D75" } }, - "body": "{\"openapi\":\"3.0.3\",\"paths\":{\"/users\":{\"get\":{\"operationId\":\"get-users\",\"responses\":{\"200\":{\"description\":\"200 response\",\"content\":{},\"headers\":{\"Access-Control-Allow-Origin\":{\"schema\":{\"type\":\"string\"}},\"Access-Control-Allow-Methods\":{\"schema\":{\"type\":\"string\"}},\"Access-Control-Allow-Headers\":{\"schema\":{\"type\":\"string\"}}}}},\"parameters\":[],\"x-amazon-apigateway-integration\":{\"uri\":\"arn:aws:apigateway:${data.aws_region.Region.name}:lambda:path/2015-03-31/functions/${aws_lambda_function.cloudApi_cloudApi-OnRequest-cdafee6e_A6C8366F.arn}/invocations\",\"type\":\"aws_proxy\",\"httpMethod\":\"POST\",\"responses\":{\"default\":{\"statusCode\":\"200\"}},\"passthroughBehavior\":\"when_no_match\",\"contentHandling\":\"CONVERT_TO_TEXT\"}}},\"/{proxy+}\":{\"x-amazon-apigateway-any-method\":{\"produces\":[\"application/json\"],\"consumes\":[\"application/json\"],\"x-amazon-apigateway-integration\":{\"type\":\"mock\",\"requestTemplates\":{\"application/json\":\"{\\\"statusCode\\\": 404}\"},\"responses\":{\"default\":{\"statusCode\":\"404\",\"responseParameters\":{\"method.response.header.Content-Type\":\"'application/json'\"},\"responseTemplates\":{\"application/json\":\"{\\\"statusCode: 404, \\\"message\\\": \\\"Error: Resource not found\\\"}\"}}}},\"responses\":{\"404\":{\"description\":\"404 response\",\"headers\":{\"Content-Type\":{\"type\":\"string\"}}}}}}}}", + "body": "{\"openapi\":\"3.0.3\",\"paths\":{\"/users\":{\"get\":{\"operationId\":\"get-users\",\"responses\":{\"200\":{\"description\":\"200 response\",\"content\":{},\"headers\":{\"Access-Control-Allow-Origin\":{\"schema\":{\"type\":\"string\"}},\"Access-Control-Allow-Methods\":{\"schema\":{\"type\":\"string\"}},\"Access-Control-Allow-Headers\":{\"schema\":{\"type\":\"string\"}}}}},\"parameters\":[],\"x-amazon-apigateway-integration\":{\"uri\":\"arn:aws:apigateway:${data.aws_region.Region.name}:lambda:path/2015-03-31/functions/${aws_lambda_function.cloudApi_cloudApi-OnRequest-cdafee6e_A6C8366F.arn}/invocations\",\"type\":\"aws_proxy\",\"httpMethod\":\"POST\",\"responses\":{\"default\":{\"statusCode\":\"200\"}},\"passthroughBehavior\":\"when_no_match\",\"contentHandling\":\"CONVERT_TO_TEXT\"}}},\"/{proxy+}\":{\"x-amazon-apigateway-any-method\":{\"produces\":[\"application/json\"],\"x-amazon-apigateway-integration\":{\"type\":\"mock\",\"requestTemplates\":{\"application/json\":\"\\n #if ($context.httpMethod == \\\"OPTIONS\\\")\\n {\\\"statusCode\\\": 204}\\n #else\\n {\\\"statusCode\\\": 404}\\n #end\\n \"},\"passthroughBehavior\":\"never\",\"responses\":{\"204\":{\"statusCode\":\"204\",\"responseParameters\":{\"method.response.header.Content-Type\":\"'application/json'\",\"method.response.header.Access-Control-Allow-Origin\":\"'*'\",\"method.response.header.Access-Control-Allow-Methods\":\"'GET,POST,PUT,DELETE,HEAD,OPTIONS'\",\"method.response.header.Access-Control-Allow-Headers\":\"'Content-Type,Authorization,X-Requested-With'\"},\"responseTemplates\":{\"application/json\":\"{}\"}},\"404\":{\"statusCode\":\"404\",\"responseParameters\":{\"method.response.header.Content-Type\":\"'application/json'\"},\"responseTemplates\":{\"application/json\":\"{\\\"statusCode\\\": 404, \\\"message\\\": \\\"Error: Resource not found\\\"}\"}},\"default\":{\"statusCode\":\"404\",\"responseParameters\":{\"method.response.header.Content-Type\":\"'application/json'\"},\"responseTemplates\":{\"application/json\":\"{\\\"statusCode\\\": 404, \\\"message\\\": \\\"Error: Resource not found\\\"}\"}}}},\"responses\":{\"204\":{\"description\":\"204 response\",\"headers\":{\"Content-Type\":{\"type\":\"string\"},\"Access-Control-Allow-Origin\":{\"type\":\"string\"},\"Access-Control-Allow-Methods\":{\"type\":\"string\"},\"Access-Control-Allow-Headers\":{\"type\":\"string\"}}},\"404\":{\"description\":\"404 response\",\"headers\":{\"Content-Type\":{\"type\":\"string\"}}}}}}}}", "name": "api-c895068c" } }, diff --git a/tools/hangar/__snapshots__/test_corpus/valid/api_valid_path.w_compile_tf-aws.md b/tools/hangar/__snapshots__/test_corpus/valid/api_valid_path.w_compile_tf-aws.md index c2902ae8fbd..470e0a5e6dc 100644 --- a/tools/hangar/__snapshots__/test_corpus/valid/api_valid_path.w_compile_tf-aws.md +++ b/tools/hangar/__snapshots__/test_corpus/valid/api_valid_path.w_compile_tf-aws.md @@ -85,7 +85,7 @@ module.exports = function({ }) { "uniqueId": "cloudApi_api_2B334D75" } }, - "body": "{\"openapi\":\"3.0.3\",\"paths\":{\"/test\":{\"get\":{\"operationId\":\"get-test\",\"responses\":{\"200\":{\"description\":\"200 response\",\"content\":{}}},\"parameters\":[],\"x-amazon-apigateway-integration\":{\"uri\":\"arn:aws:apigateway:${data.aws_region.Region.name}:lambda:path/2015-03-31/functions/${aws_lambda_function.cloudApi_cloudApi-OnRequest-cdafee6e_A6C8366F.arn}/invocations\",\"type\":\"aws_proxy\",\"httpMethod\":\"POST\",\"responses\":{\"default\":{\"statusCode\":\"200\"}},\"passthroughBehavior\":\"when_no_match\",\"contentHandling\":\"CONVERT_TO_TEXT\"}}},\"/test/alphanumer1cPa_th\":{\"get\":{\"operationId\":\"get-test/alphanumer1cPa_th\",\"responses\":{\"200\":{\"description\":\"200 response\",\"content\":{}}},\"parameters\":[],\"x-amazon-apigateway-integration\":{\"uri\":\"arn:aws:apigateway:${data.aws_region.Region.name}:lambda:path/2015-03-31/functions/${aws_lambda_function.cloudApi_cloudApi-OnRequest-cdafee6e_A6C8366F.arn}/invocations\",\"type\":\"aws_proxy\",\"httpMethod\":\"POST\",\"responses\":{\"default\":{\"statusCode\":\"200\"}},\"passthroughBehavior\":\"when_no_match\",\"contentHandling\":\"CONVERT_TO_TEXT\"}}},\"/test/regular/path\":{\"get\":{\"operationId\":\"get-test/regular/path\",\"responses\":{\"200\":{\"description\":\"200 response\",\"content\":{}}},\"parameters\":[],\"x-amazon-apigateway-integration\":{\"uri\":\"arn:aws:apigateway:${data.aws_region.Region.name}:lambda:path/2015-03-31/functions/${aws_lambda_function.cloudApi_cloudApi-OnRequest-cdafee6e_A6C8366F.arn}/invocations\",\"type\":\"aws_proxy\",\"httpMethod\":\"POST\",\"responses\":{\"default\":{\"statusCode\":\"200\"}},\"passthroughBehavior\":\"when_no_match\",\"contentHandling\":\"CONVERT_TO_TEXT\"}}},\"/test/pa-th/{with}/two/{variable_s}/f?bla=5&b=6\":{\"get\":{\"operationId\":\"get-test/pa-th/{with}/two/{variable_s}/f?bla=5&b=6\",\"responses\":{\"200\":{\"description\":\"200 response\",\"content\":{}}},\"parameters\":[{\"name\":\"with\",\"in\":\"path\",\"required\":true,\"schema\":{\"type\":\"string\"}},{\"name\":\"variable_s\",\"in\":\"path\",\"required\":true,\"schema\":{\"type\":\"string\"}}],\"x-amazon-apigateway-integration\":{\"uri\":\"arn:aws:apigateway:${data.aws_region.Region.name}:lambda:path/2015-03-31/functions/${aws_lambda_function.cloudApi_cloudApi-OnRequest-cdafee6e_A6C8366F.arn}/invocations\",\"type\":\"aws_proxy\",\"httpMethod\":\"POST\",\"responses\":{\"default\":{\"statusCode\":\"200\"}},\"passthroughBehavior\":\"when_no_match\",\"contentHandling\":\"CONVERT_TO_TEXT\"}}},\"/test/param/is/{last}\":{\"get\":{\"operationId\":\"get-test/param/is/{last}\",\"responses\":{\"200\":{\"description\":\"200 response\",\"content\":{}}},\"parameters\":[{\"name\":\"last\",\"in\":\"path\",\"required\":true,\"schema\":{\"type\":\"string\"}}],\"x-amazon-apigateway-integration\":{\"uri\":\"arn:aws:apigateway:${data.aws_region.Region.name}:lambda:path/2015-03-31/functions/${aws_lambda_function.cloudApi_cloudApi-OnRequest-cdafee6e_A6C8366F.arn}/invocations\",\"type\":\"aws_proxy\",\"httpMethod\":\"POST\",\"responses\":{\"default\":{\"statusCode\":\"200\"}},\"passthroughBehavior\":\"when_no_match\",\"contentHandling\":\"CONVERT_TO_TEXT\"}}},\"/test/{param}\":{\"get\":{\"operationId\":\"get-test/{param}\",\"responses\":{\"200\":{\"description\":\"200 response\",\"content\":{}}},\"parameters\":[{\"name\":\"param\",\"in\":\"path\",\"required\":true,\"schema\":{\"type\":\"string\"}}],\"x-amazon-apigateway-integration\":{\"uri\":\"arn:aws:apigateway:${data.aws_region.Region.name}:lambda:path/2015-03-31/functions/${aws_lambda_function.cloudApi_cloudApi-OnRequest-cdafee6e_A6C8366F.arn}/invocations\",\"type\":\"aws_proxy\",\"httpMethod\":\"POST\",\"responses\":{\"default\":{\"statusCode\":\"200\"}},\"passthroughBehavior\":\"when_no_match\",\"contentHandling\":\"CONVERT_TO_TEXT\"}}},\"/{param}\":{\"get\":{\"operationId\":\"get-{param}\",\"responses\":{\"200\":{\"description\":\"200 response\",\"content\":{}}},\"parameters\":[{\"name\":\"param\",\"in\":\"path\",\"required\":true,\"schema\":{\"type\":\"string\"}}],\"x-amazon-apigateway-integration\":{\"uri\":\"arn:aws:apigateway:${data.aws_region.Region.name}:lambda:path/2015-03-31/functions/${aws_lambda_function.cloudApi_cloudApi-OnRequest-cdafee6e_A6C8366F.arn}/invocations\",\"type\":\"aws_proxy\",\"httpMethod\":\"POST\",\"responses\":{\"default\":{\"statusCode\":\"200\"}},\"passthroughBehavior\":\"when_no_match\",\"contentHandling\":\"CONVERT_TO_TEXT\"}}},\"/t/{param}\":{\"get\":{\"operationId\":\"get-t/{param}\",\"responses\":{\"200\":{\"description\":\"200 response\",\"content\":{}}},\"parameters\":[{\"name\":\"param\",\"in\":\"path\",\"required\":true,\"schema\":{\"type\":\"string\"}}],\"x-amazon-apigateway-integration\":{\"uri\":\"arn:aws:apigateway:${data.aws_region.Region.name}:lambda:path/2015-03-31/functions/${aws_lambda_function.cloudApi_cloudApi-OnRequest-cdafee6e_A6C8366F.arn}/invocations\",\"type\":\"aws_proxy\",\"httpMethod\":\"POST\",\"responses\":{\"default\":{\"statusCode\":\"200\"}},\"passthroughBehavior\":\"when_no_match\",\"contentHandling\":\"CONVERT_TO_TEXT\"}}},\"/test/regular/path/{param}\":{\"get\":{\"operationId\":\"get-test/regular/path/{param}\",\"responses\":{\"200\":{\"description\":\"200 response\",\"content\":{}}},\"parameters\":[{\"name\":\"param\",\"in\":\"path\",\"required\":true,\"schema\":{\"type\":\"string\"}}],\"x-amazon-apigateway-integration\":{\"uri\":\"arn:aws:apigateway:${data.aws_region.Region.name}:lambda:path/2015-03-31/functions/${aws_lambda_function.cloudApi_cloudApi-OnRequest-cdafee6e_A6C8366F.arn}/invocations\",\"type\":\"aws_proxy\",\"httpMethod\":\"POST\",\"responses\":{\"default\":{\"statusCode\":\"200\"}},\"passthroughBehavior\":\"when_no_match\",\"contentHandling\":\"CONVERT_TO_TEXT\"}}},\"/test/segment1/{param1}/segment2?query1=value1?query2=value2\":{\"get\":{\"operationId\":\"get-test/segment1/{param1}/segment2?query1=value1?query2=value2\",\"responses\":{\"200\":{\"description\":\"200 response\",\"content\":{}}},\"parameters\":[{\"name\":\"param1\",\"in\":\"path\",\"required\":true,\"schema\":{\"type\":\"string\"}}],\"x-amazon-apigateway-integration\":{\"uri\":\"arn:aws:apigateway:${data.aws_region.Region.name}:lambda:path/2015-03-31/functions/${aws_lambda_function.cloudApi_cloudApi-OnRequest-cdafee6e_A6C8366F.arn}/invocations\",\"type\":\"aws_proxy\",\"httpMethod\":\"POST\",\"responses\":{\"default\":{\"statusCode\":\"200\"}},\"passthroughBehavior\":\"when_no_match\",\"contentHandling\":\"CONVERT_TO_TEXT\"}}},\"/test/segment1/segment2?query=value1&query2=value2\":{\"get\":{\"operationId\":\"get-test/segment1/segment2?query=value1&query2=value2\",\"responses\":{\"200\":{\"description\":\"200 response\",\"content\":{}}},\"parameters\":[],\"x-amazon-apigateway-integration\":{\"uri\":\"arn:aws:apigateway:${data.aws_region.Region.name}:lambda:path/2015-03-31/functions/${aws_lambda_function.cloudApi_cloudApi-OnRequest-cdafee6e_A6C8366F.arn}/invocations\",\"type\":\"aws_proxy\",\"httpMethod\":\"POST\",\"responses\":{\"default\":{\"statusCode\":\"200\"}},\"passthroughBehavior\":\"when_no_match\",\"contentHandling\":\"CONVERT_TO_TEXT\"}}},\"/{proxy+}\":{\"x-amazon-apigateway-any-method\":{\"produces\":[\"application/json\"],\"consumes\":[\"application/json\"],\"x-amazon-apigateway-integration\":{\"type\":\"mock\",\"requestTemplates\":{\"application/json\":\"{\\\"statusCode\\\": 404}\"},\"responses\":{\"default\":{\"statusCode\":\"404\",\"responseParameters\":{\"method.response.header.Content-Type\":\"'application/json'\"},\"responseTemplates\":{\"application/json\":\"{\\\"statusCode: 404, \\\"message\\\": \\\"Error: Resource not found\\\"}\"}}}},\"responses\":{\"404\":{\"description\":\"404 response\",\"headers\":{\"Content-Type\":{\"type\":\"string\"}}}}}}}}", + "body": "{\"openapi\":\"3.0.3\",\"paths\":{\"/test\":{\"get\":{\"operationId\":\"get-test\",\"responses\":{\"200\":{\"description\":\"200 response\",\"content\":{}}},\"parameters\":[],\"x-amazon-apigateway-integration\":{\"uri\":\"arn:aws:apigateway:${data.aws_region.Region.name}:lambda:path/2015-03-31/functions/${aws_lambda_function.cloudApi_cloudApi-OnRequest-cdafee6e_A6C8366F.arn}/invocations\",\"type\":\"aws_proxy\",\"httpMethod\":\"POST\",\"responses\":{\"default\":{\"statusCode\":\"200\"}},\"passthroughBehavior\":\"when_no_match\",\"contentHandling\":\"CONVERT_TO_TEXT\"}}},\"/test/alphanumer1cPa_th\":{\"get\":{\"operationId\":\"get-test/alphanumer1cPa_th\",\"responses\":{\"200\":{\"description\":\"200 response\",\"content\":{}}},\"parameters\":[],\"x-amazon-apigateway-integration\":{\"uri\":\"arn:aws:apigateway:${data.aws_region.Region.name}:lambda:path/2015-03-31/functions/${aws_lambda_function.cloudApi_cloudApi-OnRequest-cdafee6e_A6C8366F.arn}/invocations\",\"type\":\"aws_proxy\",\"httpMethod\":\"POST\",\"responses\":{\"default\":{\"statusCode\":\"200\"}},\"passthroughBehavior\":\"when_no_match\",\"contentHandling\":\"CONVERT_TO_TEXT\"}}},\"/test/regular/path\":{\"get\":{\"operationId\":\"get-test/regular/path\",\"responses\":{\"200\":{\"description\":\"200 response\",\"content\":{}}},\"parameters\":[],\"x-amazon-apigateway-integration\":{\"uri\":\"arn:aws:apigateway:${data.aws_region.Region.name}:lambda:path/2015-03-31/functions/${aws_lambda_function.cloudApi_cloudApi-OnRequest-cdafee6e_A6C8366F.arn}/invocations\",\"type\":\"aws_proxy\",\"httpMethod\":\"POST\",\"responses\":{\"default\":{\"statusCode\":\"200\"}},\"passthroughBehavior\":\"when_no_match\",\"contentHandling\":\"CONVERT_TO_TEXT\"}}},\"/test/pa-th/{with}/two/{variable_s}/f?bla=5&b=6\":{\"get\":{\"operationId\":\"get-test/pa-th/{with}/two/{variable_s}/f?bla=5&b=6\",\"responses\":{\"200\":{\"description\":\"200 response\",\"content\":{}}},\"parameters\":[{\"name\":\"with\",\"in\":\"path\",\"required\":true,\"schema\":{\"type\":\"string\"}},{\"name\":\"variable_s\",\"in\":\"path\",\"required\":true,\"schema\":{\"type\":\"string\"}}],\"x-amazon-apigateway-integration\":{\"uri\":\"arn:aws:apigateway:${data.aws_region.Region.name}:lambda:path/2015-03-31/functions/${aws_lambda_function.cloudApi_cloudApi-OnRequest-cdafee6e_A6C8366F.arn}/invocations\",\"type\":\"aws_proxy\",\"httpMethod\":\"POST\",\"responses\":{\"default\":{\"statusCode\":\"200\"}},\"passthroughBehavior\":\"when_no_match\",\"contentHandling\":\"CONVERT_TO_TEXT\"}}},\"/test/param/is/{last}\":{\"get\":{\"operationId\":\"get-test/param/is/{last}\",\"responses\":{\"200\":{\"description\":\"200 response\",\"content\":{}}},\"parameters\":[{\"name\":\"last\",\"in\":\"path\",\"required\":true,\"schema\":{\"type\":\"string\"}}],\"x-amazon-apigateway-integration\":{\"uri\":\"arn:aws:apigateway:${data.aws_region.Region.name}:lambda:path/2015-03-31/functions/${aws_lambda_function.cloudApi_cloudApi-OnRequest-cdafee6e_A6C8366F.arn}/invocations\",\"type\":\"aws_proxy\",\"httpMethod\":\"POST\",\"responses\":{\"default\":{\"statusCode\":\"200\"}},\"passthroughBehavior\":\"when_no_match\",\"contentHandling\":\"CONVERT_TO_TEXT\"}}},\"/test/{param}\":{\"get\":{\"operationId\":\"get-test/{param}\",\"responses\":{\"200\":{\"description\":\"200 response\",\"content\":{}}},\"parameters\":[{\"name\":\"param\",\"in\":\"path\",\"required\":true,\"schema\":{\"type\":\"string\"}}],\"x-amazon-apigateway-integration\":{\"uri\":\"arn:aws:apigateway:${data.aws_region.Region.name}:lambda:path/2015-03-31/functions/${aws_lambda_function.cloudApi_cloudApi-OnRequest-cdafee6e_A6C8366F.arn}/invocations\",\"type\":\"aws_proxy\",\"httpMethod\":\"POST\",\"responses\":{\"default\":{\"statusCode\":\"200\"}},\"passthroughBehavior\":\"when_no_match\",\"contentHandling\":\"CONVERT_TO_TEXT\"}}},\"/{param}\":{\"get\":{\"operationId\":\"get-{param}\",\"responses\":{\"200\":{\"description\":\"200 response\",\"content\":{}}},\"parameters\":[{\"name\":\"param\",\"in\":\"path\",\"required\":true,\"schema\":{\"type\":\"string\"}}],\"x-amazon-apigateway-integration\":{\"uri\":\"arn:aws:apigateway:${data.aws_region.Region.name}:lambda:path/2015-03-31/functions/${aws_lambda_function.cloudApi_cloudApi-OnRequest-cdafee6e_A6C8366F.arn}/invocations\",\"type\":\"aws_proxy\",\"httpMethod\":\"POST\",\"responses\":{\"default\":{\"statusCode\":\"200\"}},\"passthroughBehavior\":\"when_no_match\",\"contentHandling\":\"CONVERT_TO_TEXT\"}}},\"/t/{param}\":{\"get\":{\"operationId\":\"get-t/{param}\",\"responses\":{\"200\":{\"description\":\"200 response\",\"content\":{}}},\"parameters\":[{\"name\":\"param\",\"in\":\"path\",\"required\":true,\"schema\":{\"type\":\"string\"}}],\"x-amazon-apigateway-integration\":{\"uri\":\"arn:aws:apigateway:${data.aws_region.Region.name}:lambda:path/2015-03-31/functions/${aws_lambda_function.cloudApi_cloudApi-OnRequest-cdafee6e_A6C8366F.arn}/invocations\",\"type\":\"aws_proxy\",\"httpMethod\":\"POST\",\"responses\":{\"default\":{\"statusCode\":\"200\"}},\"passthroughBehavior\":\"when_no_match\",\"contentHandling\":\"CONVERT_TO_TEXT\"}}},\"/test/regular/path/{param}\":{\"get\":{\"operationId\":\"get-test/regular/path/{param}\",\"responses\":{\"200\":{\"description\":\"200 response\",\"content\":{}}},\"parameters\":[{\"name\":\"param\",\"in\":\"path\",\"required\":true,\"schema\":{\"type\":\"string\"}}],\"x-amazon-apigateway-integration\":{\"uri\":\"arn:aws:apigateway:${data.aws_region.Region.name}:lambda:path/2015-03-31/functions/${aws_lambda_function.cloudApi_cloudApi-OnRequest-cdafee6e_A6C8366F.arn}/invocations\",\"type\":\"aws_proxy\",\"httpMethod\":\"POST\",\"responses\":{\"default\":{\"statusCode\":\"200\"}},\"passthroughBehavior\":\"when_no_match\",\"contentHandling\":\"CONVERT_TO_TEXT\"}}},\"/test/segment1/{param1}/segment2?query1=value1?query2=value2\":{\"get\":{\"operationId\":\"get-test/segment1/{param1}/segment2?query1=value1?query2=value2\",\"responses\":{\"200\":{\"description\":\"200 response\",\"content\":{}}},\"parameters\":[{\"name\":\"param1\",\"in\":\"path\",\"required\":true,\"schema\":{\"type\":\"string\"}}],\"x-amazon-apigateway-integration\":{\"uri\":\"arn:aws:apigateway:${data.aws_region.Region.name}:lambda:path/2015-03-31/functions/${aws_lambda_function.cloudApi_cloudApi-OnRequest-cdafee6e_A6C8366F.arn}/invocations\",\"type\":\"aws_proxy\",\"httpMethod\":\"POST\",\"responses\":{\"default\":{\"statusCode\":\"200\"}},\"passthroughBehavior\":\"when_no_match\",\"contentHandling\":\"CONVERT_TO_TEXT\"}}},\"/test/segment1/segment2?query=value1&query2=value2\":{\"get\":{\"operationId\":\"get-test/segment1/segment2?query=value1&query2=value2\",\"responses\":{\"200\":{\"description\":\"200 response\",\"content\":{}}},\"parameters\":[],\"x-amazon-apigateway-integration\":{\"uri\":\"arn:aws:apigateway:${data.aws_region.Region.name}:lambda:path/2015-03-31/functions/${aws_lambda_function.cloudApi_cloudApi-OnRequest-cdafee6e_A6C8366F.arn}/invocations\",\"type\":\"aws_proxy\",\"httpMethod\":\"POST\",\"responses\":{\"default\":{\"statusCode\":\"200\"}},\"passthroughBehavior\":\"when_no_match\",\"contentHandling\":\"CONVERT_TO_TEXT\"}}},\"/{proxy+}\":{\"x-amazon-apigateway-any-method\":{\"produces\":[\"application/json\"],\"x-amazon-apigateway-integration\":{\"type\":\"mock\",\"requestTemplates\":{\"application/json\":\"\\n {\\\"statusCode\\\": 404}\\n \"},\"passthroughBehavior\":\"never\",\"responses\":{\"404\":{\"statusCode\":\"404\",\"responseParameters\":{\"method.response.header.Content-Type\":\"'application/json'\"},\"responseTemplates\":{\"application/json\":\"{\\\"statusCode\\\": 404, \\\"message\\\": \\\"Error: Resource not found\\\"}\"}},\"default\":{\"statusCode\":\"404\",\"responseParameters\":{\"method.response.header.Content-Type\":\"'application/json'\"},\"responseTemplates\":{\"application/json\":\"{\\\"statusCode\\\": 404, \\\"message\\\": \\\"Error: Resource not found\\\"}\"}}}},\"responses\":{\"404\":{\"description\":\"404 response\",\"headers\":{\"Content-Type\":{\"type\":\"string\"}}}}}}}}", "name": "api-c895068c" } }, diff --git a/tools/hangar/__snapshots__/test_corpus/valid/capture_tokens.w_compile_tf-aws.md b/tools/hangar/__snapshots__/test_corpus/valid/capture_tokens.w_compile_tf-aws.md index ad5d79e6fd6..67f5e5dc666 100644 --- a/tools/hangar/__snapshots__/test_corpus/valid/capture_tokens.w_compile_tf-aws.md +++ b/tools/hangar/__snapshots__/test_corpus/valid/capture_tokens.w_compile_tf-aws.md @@ -140,7 +140,7 @@ module.exports = function({ }) { "uniqueId": "MyResource_cloudApi_api_4CB9B8E3" } }, - "body": "{\"openapi\":\"3.0.3\",\"paths\":{\"/{proxy+}\":{\"x-amazon-apigateway-any-method\":{\"produces\":[\"application/json\"],\"consumes\":[\"application/json\"],\"x-amazon-apigateway-integration\":{\"type\":\"mock\",\"requestTemplates\":{\"application/json\":\"{\\\"statusCode\\\": 404}\"},\"responses\":{\"default\":{\"statusCode\":\"404\",\"responseParameters\":{\"method.response.header.Content-Type\":\"'application/json'\"},\"responseTemplates\":{\"application/json\":\"{\\\"statusCode: 404, \\\"message\\\": \\\"Error: Resource not found\\\"}\"}}}},\"responses\":{\"404\":{\"description\":\"404 response\",\"headers\":{\"Content-Type\":{\"type\":\"string\"}}}}}}}}", + "body": "{\"openapi\":\"3.0.3\",\"paths\":{\"/{proxy+}\":{\"x-amazon-apigateway-any-method\":{\"produces\":[\"application/json\"],\"x-amazon-apigateway-integration\":{\"type\":\"mock\",\"requestTemplates\":{\"application/json\":\"\\n {\\\"statusCode\\\": 404}\\n \"},\"passthroughBehavior\":\"never\",\"responses\":{\"404\":{\"statusCode\":\"404\",\"responseParameters\":{\"method.response.header.Content-Type\":\"'application/json'\"},\"responseTemplates\":{\"application/json\":\"{\\\"statusCode\\\": 404, \\\"message\\\": \\\"Error: Resource not found\\\"}\"}},\"default\":{\"statusCode\":\"404\",\"responseParameters\":{\"method.response.header.Content-Type\":\"'application/json'\"},\"responseTemplates\":{\"application/json\":\"{\\\"statusCode\\\": 404, \\\"message\\\": \\\"Error: Resource not found\\\"}\"}}}},\"responses\":{\"404\":{\"description\":\"404 response\",\"headers\":{\"Content-Type\":{\"type\":\"string\"}}}}}}}}", "name": "api-c8ef4b64" }, "cloudApi_api_2B334D75": { @@ -150,7 +150,7 @@ module.exports = function({ }) { "uniqueId": "cloudApi_api_2B334D75" } }, - "body": "{\"openapi\":\"3.0.3\",\"paths\":{\"/{proxy+}\":{\"x-amazon-apigateway-any-method\":{\"produces\":[\"application/json\"],\"consumes\":[\"application/json\"],\"x-amazon-apigateway-integration\":{\"type\":\"mock\",\"requestTemplates\":{\"application/json\":\"{\\\"statusCode\\\": 404}\"},\"responses\":{\"default\":{\"statusCode\":\"404\",\"responseParameters\":{\"method.response.header.Content-Type\":\"'application/json'\"},\"responseTemplates\":{\"application/json\":\"{\\\"statusCode: 404, \\\"message\\\": \\\"Error: Resource not found\\\"}\"}}}},\"responses\":{\"404\":{\"description\":\"404 response\",\"headers\":{\"Content-Type\":{\"type\":\"string\"}}}}}}}}", + "body": "{\"openapi\":\"3.0.3\",\"paths\":{\"/{proxy+}\":{\"x-amazon-apigateway-any-method\":{\"produces\":[\"application/json\"],\"x-amazon-apigateway-integration\":{\"type\":\"mock\",\"requestTemplates\":{\"application/json\":\"\\n {\\\"statusCode\\\": 404}\\n \"},\"passthroughBehavior\":\"never\",\"responses\":{\"404\":{\"statusCode\":\"404\",\"responseParameters\":{\"method.response.header.Content-Type\":\"'application/json'\"},\"responseTemplates\":{\"application/json\":\"{\\\"statusCode\\\": 404, \\\"message\\\": \\\"Error: Resource not found\\\"}\"}},\"default\":{\"statusCode\":\"404\",\"responseParameters\":{\"method.response.header.Content-Type\":\"'application/json'\"},\"responseTemplates\":{\"application/json\":\"{\\\"statusCode\\\": 404, \\\"message\\\": \\\"Error: Resource not found\\\"}\"}}}},\"responses\":{\"404\":{\"description\":\"404 response\",\"headers\":{\"Content-Type\":{\"type\":\"string\"}}}}}}}}", "name": "api-c895068c" } }, diff --git a/tools/hangar/__snapshots__/test_corpus/valid/captures.w_compile_tf-aws.md b/tools/hangar/__snapshots__/test_corpus/valid/captures.w_compile_tf-aws.md index de168c05e50..c58edcda9a2 100644 --- a/tools/hangar/__snapshots__/test_corpus/valid/captures.w_compile_tf-aws.md +++ b/tools/hangar/__snapshots__/test_corpus/valid/captures.w_compile_tf-aws.md @@ -117,7 +117,7 @@ module.exports = function({ $headers }) { "uniqueId": "cloudApi_api_2B334D75" } }, - "body": "{\"openapi\":\"3.0.3\",\"paths\":{\"/hello\":{\"get\":{\"operationId\":\"get-hello\",\"responses\":{\"200\":{\"description\":\"200 response\",\"content\":{}}},\"parameters\":[],\"x-amazon-apigateway-integration\":{\"uri\":\"arn:aws:apigateway:${data.aws_region.Region.name}:lambda:path/2015-03-31/functions/${aws_lambda_function.cloudApi_cloudApi-OnRequest-86898773_701F5CA7.arn}/invocations\",\"type\":\"aws_proxy\",\"httpMethod\":\"POST\",\"responses\":{\"default\":{\"statusCode\":\"200\"}},\"passthroughBehavior\":\"when_no_match\",\"contentHandling\":\"CONVERT_TO_TEXT\"}}},\"/{proxy+}\":{\"x-amazon-apigateway-any-method\":{\"produces\":[\"application/json\"],\"consumes\":[\"application/json\"],\"x-amazon-apigateway-integration\":{\"type\":\"mock\",\"requestTemplates\":{\"application/json\":\"{\\\"statusCode\\\": 404}\"},\"responses\":{\"default\":{\"statusCode\":\"404\",\"responseParameters\":{\"method.response.header.Content-Type\":\"'application/json'\"},\"responseTemplates\":{\"application/json\":\"{\\\"statusCode: 404, \\\"message\\\": \\\"Error: Resource not found\\\"}\"}}}},\"responses\":{\"404\":{\"description\":\"404 response\",\"headers\":{\"Content-Type\":{\"type\":\"string\"}}}}}}}}", + "body": "{\"openapi\":\"3.0.3\",\"paths\":{\"/hello\":{\"get\":{\"operationId\":\"get-hello\",\"responses\":{\"200\":{\"description\":\"200 response\",\"content\":{}}},\"parameters\":[],\"x-amazon-apigateway-integration\":{\"uri\":\"arn:aws:apigateway:${data.aws_region.Region.name}:lambda:path/2015-03-31/functions/${aws_lambda_function.cloudApi_cloudApi-OnRequest-86898773_701F5CA7.arn}/invocations\",\"type\":\"aws_proxy\",\"httpMethod\":\"POST\",\"responses\":{\"default\":{\"statusCode\":\"200\"}},\"passthroughBehavior\":\"when_no_match\",\"contentHandling\":\"CONVERT_TO_TEXT\"}}},\"/{proxy+}\":{\"x-amazon-apigateway-any-method\":{\"produces\":[\"application/json\"],\"x-amazon-apigateway-integration\":{\"type\":\"mock\",\"requestTemplates\":{\"application/json\":\"\\n {\\\"statusCode\\\": 404}\\n \"},\"passthroughBehavior\":\"never\",\"responses\":{\"404\":{\"statusCode\":\"404\",\"responseParameters\":{\"method.response.header.Content-Type\":\"'application/json'\"},\"responseTemplates\":{\"application/json\":\"{\\\"statusCode\\\": 404, \\\"message\\\": \\\"Error: Resource not found\\\"}\"}},\"default\":{\"statusCode\":\"404\",\"responseParameters\":{\"method.response.header.Content-Type\":\"'application/json'\"},\"responseTemplates\":{\"application/json\":\"{\\\"statusCode\\\": 404, \\\"message\\\": \\\"Error: Resource not found\\\"}\"}}}},\"responses\":{\"404\":{\"description\":\"404 response\",\"headers\":{\"Content-Type\":{\"type\":\"string\"}}}}}}}}", "name": "api-c895068c" } }, diff --git a/tools/hangar/__snapshots__/test_corpus/valid/inference.w_compile_tf-aws.md b/tools/hangar/__snapshots__/test_corpus/valid/inference.w_compile_tf-aws.md index f86901b1d16..877bd5838f5 100644 --- a/tools/hangar/__snapshots__/test_corpus/valid/inference.w_compile_tf-aws.md +++ b/tools/hangar/__snapshots__/test_corpus/valid/inference.w_compile_tf-aws.md @@ -85,7 +85,7 @@ module.exports = function({ }) { "uniqueId": "cloudApi_api_2B334D75" } }, - "body": "{\"openapi\":\"3.0.3\",\"paths\":{\"/hello/world\":{\"get\":{\"operationId\":\"get-hello/world\",\"responses\":{\"200\":{\"description\":\"200 response\",\"content\":{}}},\"parameters\":[],\"x-amazon-apigateway-integration\":{\"uri\":\"arn:aws:apigateway:${data.aws_region.Region.name}:lambda:path/2015-03-31/functions/${aws_lambda_function.cloudApi_cloudApi-OnRequest-cdafee6e_A6C8366F.arn}/invocations\",\"type\":\"aws_proxy\",\"httpMethod\":\"POST\",\"responses\":{\"default\":{\"statusCode\":\"200\"}},\"passthroughBehavior\":\"when_no_match\",\"contentHandling\":\"CONVERT_TO_TEXT\"}}},\"/{proxy+}\":{\"x-amazon-apigateway-any-method\":{\"produces\":[\"application/json\"],\"consumes\":[\"application/json\"],\"x-amazon-apigateway-integration\":{\"type\":\"mock\",\"requestTemplates\":{\"application/json\":\"{\\\"statusCode\\\": 404}\"},\"responses\":{\"default\":{\"statusCode\":\"404\",\"responseParameters\":{\"method.response.header.Content-Type\":\"'application/json'\"},\"responseTemplates\":{\"application/json\":\"{\\\"statusCode: 404, \\\"message\\\": \\\"Error: Resource not found\\\"}\"}}}},\"responses\":{\"404\":{\"description\":\"404 response\",\"headers\":{\"Content-Type\":{\"type\":\"string\"}}}}}}}}", + "body": "{\"openapi\":\"3.0.3\",\"paths\":{\"/hello/world\":{\"get\":{\"operationId\":\"get-hello/world\",\"responses\":{\"200\":{\"description\":\"200 response\",\"content\":{}}},\"parameters\":[],\"x-amazon-apigateway-integration\":{\"uri\":\"arn:aws:apigateway:${data.aws_region.Region.name}:lambda:path/2015-03-31/functions/${aws_lambda_function.cloudApi_cloudApi-OnRequest-cdafee6e_A6C8366F.arn}/invocations\",\"type\":\"aws_proxy\",\"httpMethod\":\"POST\",\"responses\":{\"default\":{\"statusCode\":\"200\"}},\"passthroughBehavior\":\"when_no_match\",\"contentHandling\":\"CONVERT_TO_TEXT\"}}},\"/{proxy+}\":{\"x-amazon-apigateway-any-method\":{\"produces\":[\"application/json\"],\"x-amazon-apigateway-integration\":{\"type\":\"mock\",\"requestTemplates\":{\"application/json\":\"\\n {\\\"statusCode\\\": 404}\\n \"},\"passthroughBehavior\":\"never\",\"responses\":{\"404\":{\"statusCode\":\"404\",\"responseParameters\":{\"method.response.header.Content-Type\":\"'application/json'\"},\"responseTemplates\":{\"application/json\":\"{\\\"statusCode\\\": 404, \\\"message\\\": \\\"Error: Resource not found\\\"}\"}},\"default\":{\"statusCode\":\"404\",\"responseParameters\":{\"method.response.header.Content-Type\":\"'application/json'\"},\"responseTemplates\":{\"application/json\":\"{\\\"statusCode\\\": 404, \\\"message\\\": \\\"Error: Resource not found\\\"}\"}}}},\"responses\":{\"404\":{\"description\":\"404 response\",\"headers\":{\"Content-Type\":{\"type\":\"string\"}}}}}}}}", "name": "api-c895068c" } }, diff --git a/tools/hangar/__snapshots__/test_corpus/valid/issue_2889.w_compile_tf-aws.md b/tools/hangar/__snapshots__/test_corpus/valid/issue_2889.w_compile_tf-aws.md index 1941063f61e..19a370efafd 100644 --- a/tools/hangar/__snapshots__/test_corpus/valid/issue_2889.w_compile_tf-aws.md +++ b/tools/hangar/__snapshots__/test_corpus/valid/issue_2889.w_compile_tf-aws.md @@ -107,7 +107,7 @@ module.exports = function({ $api_url, $http_Util, $std_Json }) { "uniqueId": "cloudApi_api_2B334D75" } }, - "body": "{\"openapi\":\"3.0.3\",\"paths\":{\"/foo\":{\"get\":{\"operationId\":\"get-foo\",\"responses\":{\"200\":{\"description\":\"200 response\",\"content\":{}}},\"parameters\":[],\"x-amazon-apigateway-integration\":{\"uri\":\"arn:aws:apigateway:${data.aws_region.Region.name}:lambda:path/2015-03-31/functions/${aws_lambda_function.cloudApi_cloudApi-OnRequest-cdafee6e_A6C8366F.arn}/invocations\",\"type\":\"aws_proxy\",\"httpMethod\":\"POST\",\"responses\":{\"default\":{\"statusCode\":\"200\"}},\"passthroughBehavior\":\"when_no_match\",\"contentHandling\":\"CONVERT_TO_TEXT\"}}},\"/{proxy+}\":{\"x-amazon-apigateway-any-method\":{\"produces\":[\"application/json\"],\"consumes\":[\"application/json\"],\"x-amazon-apigateway-integration\":{\"type\":\"mock\",\"requestTemplates\":{\"application/json\":\"{\\\"statusCode\\\": 404}\"},\"responses\":{\"default\":{\"statusCode\":\"404\",\"responseParameters\":{\"method.response.header.Content-Type\":\"'application/json'\"},\"responseTemplates\":{\"application/json\":\"{\\\"statusCode: 404, \\\"message\\\": \\\"Error: Resource not found\\\"}\"}}}},\"responses\":{\"404\":{\"description\":\"404 response\",\"headers\":{\"Content-Type\":{\"type\":\"string\"}}}}}}}}", + "body": "{\"openapi\":\"3.0.3\",\"paths\":{\"/foo\":{\"get\":{\"operationId\":\"get-foo\",\"responses\":{\"200\":{\"description\":\"200 response\",\"content\":{}}},\"parameters\":[],\"x-amazon-apigateway-integration\":{\"uri\":\"arn:aws:apigateway:${data.aws_region.Region.name}:lambda:path/2015-03-31/functions/${aws_lambda_function.cloudApi_cloudApi-OnRequest-cdafee6e_A6C8366F.arn}/invocations\",\"type\":\"aws_proxy\",\"httpMethod\":\"POST\",\"responses\":{\"default\":{\"statusCode\":\"200\"}},\"passthroughBehavior\":\"when_no_match\",\"contentHandling\":\"CONVERT_TO_TEXT\"}}},\"/{proxy+}\":{\"x-amazon-apigateway-any-method\":{\"produces\":[\"application/json\"],\"x-amazon-apigateway-integration\":{\"type\":\"mock\",\"requestTemplates\":{\"application/json\":\"\\n {\\\"statusCode\\\": 404}\\n \"},\"passthroughBehavior\":\"never\",\"responses\":{\"404\":{\"statusCode\":\"404\",\"responseParameters\":{\"method.response.header.Content-Type\":\"'application/json'\"},\"responseTemplates\":{\"application/json\":\"{\\\"statusCode\\\": 404, \\\"message\\\": \\\"Error: Resource not found\\\"}\"}},\"default\":{\"statusCode\":\"404\",\"responseParameters\":{\"method.response.header.Content-Type\":\"'application/json'\"},\"responseTemplates\":{\"application/json\":\"{\\\"statusCode\\\": 404, \\\"message\\\": \\\"Error: Resource not found\\\"}\"}}}},\"responses\":{\"404\":{\"description\":\"404 response\",\"headers\":{\"Content-Type\":{\"type\":\"string\"}}}}}}}}", "name": "api-c895068c" } }, diff --git a/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_compile_tf-aws.md b/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_compile_tf-aws.md index 64d75a44f0a..c95419f312e 100644 --- a/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_compile_tf-aws.md +++ b/tools/hangar/__snapshots__/test_corpus/valid/website_with_api.w_compile_tf-aws.md @@ -231,7 +231,7 @@ module.exports = function({ }) { "uniqueId": "cloudApi_api_2B334D75" } }, - "body": "{\"openapi\":\"3.0.3\",\"paths\":{\"/users\":{\"get\":{\"operationId\":\"get-users\",\"responses\":{\"200\":{\"description\":\"200 response\",\"content\":{},\"headers\":{\"Access-Control-Allow-Origin\":{\"schema\":{\"type\":\"string\"}},\"Access-Control-Allow-Methods\":{\"schema\":{\"type\":\"string\"}},\"Access-Control-Allow-Headers\":{\"schema\":{\"type\":\"string\"}}}}},\"parameters\":[],\"x-amazon-apigateway-integration\":{\"uri\":\"arn:aws:apigateway:${data.aws_region.Region.name}:lambda:path/2015-03-31/functions/${aws_lambda_function.cloudApi_cloudApi-OnRequest-cdafee6e_A6C8366F.arn}/invocations\",\"type\":\"aws_proxy\",\"httpMethod\":\"POST\",\"responses\":{\"default\":{\"statusCode\":\"200\"}},\"passthroughBehavior\":\"when_no_match\",\"contentHandling\":\"CONVERT_TO_TEXT\"}},\"post\":{\"operationId\":\"post-users\",\"responses\":{\"200\":{\"description\":\"200 response\",\"content\":{},\"headers\":{\"Access-Control-Allow-Origin\":{\"schema\":{\"type\":\"string\"}},\"Access-Control-Allow-Methods\":{\"schema\":{\"type\":\"string\"}},\"Access-Control-Allow-Headers\":{\"schema\":{\"type\":\"string\"}}}}},\"parameters\":[],\"x-amazon-apigateway-integration\":{\"uri\":\"arn:aws:apigateway:${data.aws_region.Region.name}:lambda:path/2015-03-31/functions/${aws_lambda_function.cloudApi_cloudApi-OnRequest-86898773_701F5CA7.arn}/invocations\",\"type\":\"aws_proxy\",\"httpMethod\":\"POST\",\"responses\":{\"default\":{\"statusCode\":\"200\"}},\"passthroughBehavior\":\"when_no_match\",\"contentHandling\":\"CONVERT_TO_TEXT\"}}},\"/{proxy+}\":{\"x-amazon-apigateway-any-method\":{\"produces\":[\"application/json\"],\"consumes\":[\"application/json\"],\"x-amazon-apigateway-integration\":{\"type\":\"mock\",\"requestTemplates\":{\"application/json\":\"{\\\"statusCode\\\": 404}\"},\"responses\":{\"default\":{\"statusCode\":\"404\",\"responseParameters\":{\"method.response.header.Content-Type\":\"'application/json'\"},\"responseTemplates\":{\"application/json\":\"{\\\"statusCode: 404, \\\"message\\\": \\\"Error: Resource not found\\\"}\"}}}},\"responses\":{\"404\":{\"description\":\"404 response\",\"headers\":{\"Content-Type\":{\"type\":\"string\"}}}}}}}}", + "body": "{\"openapi\":\"3.0.3\",\"paths\":{\"/users\":{\"get\":{\"operationId\":\"get-users\",\"responses\":{\"200\":{\"description\":\"200 response\",\"content\":{},\"headers\":{\"Access-Control-Allow-Origin\":{\"schema\":{\"type\":\"string\"}},\"Access-Control-Allow-Methods\":{\"schema\":{\"type\":\"string\"}},\"Access-Control-Allow-Headers\":{\"schema\":{\"type\":\"string\"}}}}},\"parameters\":[],\"x-amazon-apigateway-integration\":{\"uri\":\"arn:aws:apigateway:${data.aws_region.Region.name}:lambda:path/2015-03-31/functions/${aws_lambda_function.cloudApi_cloudApi-OnRequest-cdafee6e_A6C8366F.arn}/invocations\",\"type\":\"aws_proxy\",\"httpMethod\":\"POST\",\"responses\":{\"default\":{\"statusCode\":\"200\"}},\"passthroughBehavior\":\"when_no_match\",\"contentHandling\":\"CONVERT_TO_TEXT\"}},\"post\":{\"operationId\":\"post-users\",\"responses\":{\"200\":{\"description\":\"200 response\",\"content\":{},\"headers\":{\"Access-Control-Allow-Origin\":{\"schema\":{\"type\":\"string\"}},\"Access-Control-Allow-Methods\":{\"schema\":{\"type\":\"string\"}},\"Access-Control-Allow-Headers\":{\"schema\":{\"type\":\"string\"}}}}},\"parameters\":[],\"x-amazon-apigateway-integration\":{\"uri\":\"arn:aws:apigateway:${data.aws_region.Region.name}:lambda:path/2015-03-31/functions/${aws_lambda_function.cloudApi_cloudApi-OnRequest-86898773_701F5CA7.arn}/invocations\",\"type\":\"aws_proxy\",\"httpMethod\":\"POST\",\"responses\":{\"default\":{\"statusCode\":\"200\"}},\"passthroughBehavior\":\"when_no_match\",\"contentHandling\":\"CONVERT_TO_TEXT\"}}},\"/{proxy+}\":{\"x-amazon-apigateway-any-method\":{\"produces\":[\"application/json\"],\"x-amazon-apigateway-integration\":{\"type\":\"mock\",\"requestTemplates\":{\"application/json\":\"\\n #if ($context.httpMethod == \\\"OPTIONS\\\")\\n {\\\"statusCode\\\": 204}\\n #else\\n {\\\"statusCode\\\": 404}\\n #end\\n \"},\"passthroughBehavior\":\"never\",\"responses\":{\"204\":{\"statusCode\":\"204\",\"responseParameters\":{\"method.response.header.Content-Type\":\"'application/json'\",\"method.response.header.Access-Control-Allow-Origin\":\"'*'\",\"method.response.header.Access-Control-Allow-Methods\":\"'GET,POST,OPTIONS'\",\"method.response.header.Access-Control-Allow-Headers\":\"'Content-Type'\"},\"responseTemplates\":{\"application/json\":\"{}\"}},\"404\":{\"statusCode\":\"404\",\"responseParameters\":{\"method.response.header.Content-Type\":\"'application/json'\"},\"responseTemplates\":{\"application/json\":\"{\\\"statusCode\\\": 404, \\\"message\\\": \\\"Error: Resource not found\\\"}\"}},\"default\":{\"statusCode\":\"404\",\"responseParameters\":{\"method.response.header.Content-Type\":\"'application/json'\"},\"responseTemplates\":{\"application/json\":\"{\\\"statusCode\\\": 404, \\\"message\\\": \\\"Error: Resource not found\\\"}\"}}}},\"responses\":{\"204\":{\"description\":\"204 response\",\"headers\":{\"Content-Type\":{\"type\":\"string\"},\"Access-Control-Allow-Origin\":{\"type\":\"string\"},\"Access-Control-Allow-Methods\":{\"type\":\"string\"},\"Access-Control-Allow-Headers\":{\"type\":\"string\"}}},\"404\":{\"description\":\"404 response\",\"headers\":{\"Content-Type\":{\"type\":\"string\"}}}}}}}}", "name": "api-c895068c" } },