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.*
diff --git a/docs/docs/04-standard-library/01-cloud/api.md b/docs/docs/04-standard-library/01-cloud/api.md
index 1df2c8d9fa0..1f3a0d77309 100644
--- a/docs/docs/04-standard-library/01-cloud/api.md
+++ b/docs/docs/04-standard-library/01-cloud/api.md
@@ -467,6 +467,123 @@ let ApiConnectProps = cloud.ApiConnectProps{ ... };
```
+### ApiCorsOptions
+
+Cors Options for `Api`.
+
+#### Initializer
+
+```wing
+bring cloud;
+
+let ApiCorsOptions = cloud.ApiCorsOptions{ ... };
+```
+
+#### Properties
+
+| **Name** | **Type** | **Description** |
+| --- | --- | --- |
+| 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
+
+```wing
+allowCredentials: bool;
+```
+
+- *Type:* bool
+- *Default:* false
+
+Whether to allow credentials.
+
+---
+
+##### `allowHeaders`Optional
+
+```wing
+allowHeaders: MutArray;
+```
+
+- *Type:* MutArray<str>
+- *Default:* ["Content-Type", "Authorization"]
+
+The list of allowed headers.
+
+---
+
+*Example*
+
+```wing
+["Content-Type"]
+```
+
+
+##### `allowMethods`Optional
+
+```wing
+allowMethods: 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]
+```
+
+
+##### `allowOrigin`Optional
+
+```wing
+allowOrigin: MutArray;
+```
+
+- *Type:* MutArray<str>
+- *Default:* ["*"]
+
+The list of allowed allowOrigin.
+
+---
+
+*Example*
+
+```wing
+["https://example.com"]
+```
+
+
+##### `exposeHeaders`Optional
+
+```wing
+exposeHeaders: MutArray;
+```
+
+- *Type:* MutArray<str>
+- *Default:* []
+
+The list of exposed headers.
+
+---
+
+*Example*
+
+```wing
+["Content-Type"]
+```
+
+
### ApiDeleteProps
Options for Api put endpoint.
@@ -557,6 +674,60 @@ bring cloud;
let ApiProps = cloud.ApiProps{ ... };
```
+#### Properties
+
+| **Name** | **Type** | **Description** |
+| --- | --- | --- |
+| 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: 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:* 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)
+
+---
+
+*Example*
+
+```wing
+{ allowOrigin: ["https://example.com"] }
+```
+
### ApiPutProps
diff --git a/examples/tests/sdk_tests/api/cors.w b/examples/tests/sdk_tests/api/cors.w
new file mode 100644
index 00000000000..6a99966930a
--- /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..a19c8df50fa
--- /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(api.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");
+ t.Assert.equalStr(headers.get("access-control-allow-origin"), "winglang.io");
+
+ // Other cors headers are not set
+ 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(api.url + "/users", {
+ method: http.HttpMethod.OPTIONS,
+ headers: {
+ "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");
+ 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
new file mode 100644
index 00000000000..96c7a2af5f9
--- /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,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-credentials"));
+ t.Assert.isNil(headers.get("access-control-expose-headers"));
+}
+
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/website_with_api.w b/examples/tests/valid/website_with_api.w
index 1339840c09a..2735cabe09c 100644
--- a/examples/tests/valid/website_with_api.w
+++ b/examples/tests/valid/website_with_api.w
@@ -1,8 +1,19 @@
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();
+let api = new cloud.Api(
+ cors: true,
+ corsOptions: cloud.ApiCorsOptions {
+ allowOrigin: ["*"],
+ allowMethods: [cloud.HttpMethod.GET, cloud.HttpMethod.POST, cloud.HttpMethod.OPTIONS],
+ allowHeaders: ["Content-Type"],
+ allowCredentials: false,
+ exposeHeaders: ["Content-Type"]
+ }
+);
let website = new cloud.Website(path: "./website_with_api");
@@ -16,7 +27,6 @@ let usersTable = new ex.Table(
}
);
-
let getHandler = inflight (req: cloud.ApiRequest): cloud.ApiResponse => {
return cloud.ApiResponse {
body: Json.stringify({ users: usersTable.list() }),
@@ -39,20 +49,40 @@ 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);
-api.options("/users", optionsHandler);
website.addJson("config.json", { apiUrl: api.url });
+
+test "GET /users" {
+ let response = http.fetch(api.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-expose-headers"), "Content-Type");
+ 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-allow-methods"));
+}
+
+test "OPTIONS /users" {
+ let response = http.fetch(api.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-methods"), "GET,POST,OPTIONS");
+ t.Assert.equalStr(headers.get("access-control-allow-headers"), "Content-Type");
+}
\ 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 e2e85c6bb64..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,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: ApiCorsOptions
+ kind: 22
+ documentation:
+ kind: markdown
+ 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:
@@ -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.\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 e2e85c6bb64..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,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: ApiCorsOptions
+ kind: 22
+ documentation:
+ kind: markdown
+ 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:
@@ -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.\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 e2e85c6bb64..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,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: ApiCorsOptions
+ kind: 22
+ documentation:
+ kind: markdown
+ 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:
@@ -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.\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 f839f51f7a7..bdc8f12e797 100644
--- a/libs/wingsdk/src/cloud/api.ts
+++ b/libs/wingsdk/src/cloud/api.ts
@@ -9,11 +9,82 @@ import { IResource, Node, Resource } from "../std";
export const API_FQN = fqnForType("cloud.Api");
+/**
+ * Cors Options for `Api`.
+ */
+export interface ApiCorsOptions {
+ /**
+ * The list of allowed allowOrigin.
+ * @example ["https://example.com"]
+ * @default - ["*"]
+ */
+ 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 allowMethods?: Array;
+
+ /**
+ * The list of allowed headers.
+ * @example ["Content-Type"]
+ * @default - ["Content-Type", "Authorization"]
+ */
+ readonly allowHeaders?: Array;
+
+ /**
+ * The list of exposed headers.
+ * @example ["Content-Type"]
+ * @default - []
+ */
+ readonly exposeHeaders?: Array;
+
+ /**
+ * Whether to allow credentials.
+ * @default - false
+ */
+ readonly allowCredentials?: boolean;
+}
+
/**
* Options 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)
+ * 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 { allowOrigin: ["https://example.com"] }
+ * @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,
+ *
+ */
+ readonly corsOptions?: ApiCorsOptions;
+}
/**
* The OpenAPI spec.
*/
@@ -26,6 +97,65 @@ 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 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
+ */
+ "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`
@@ -55,11 +185,32 @@ export abstract class Api extends Resource {
paths: {},
};
+ private corsDefaultValues: ApiCorsOptions = {
+ allowOrigin: ["*"],
+ allowMethods: [
+ HttpMethod.GET,
+ HttpMethod.POST,
+ HttpMethod.PUT,
+ HttpMethod.DELETE,
+ HttpMethod.HEAD,
+ HttpMethod.OPTIONS,
+ ],
+ allowHeaders: ["Content-Type", "Authorization", "X-Requested-With"],
+ exposeHeaders: [],
+ allowCredentials: false,
+ };
+
+ /**
+ * CORS options for api
+ */
+ protected corsOptions?: ApiCorsOptions;
+
constructor(scope: Construct, id: string, props: ApiProps = {}) {
super(scope, id);
props;
+ this.corsOptions = props.cors ? this._cors(props.corsOptions) : undefined;
Node.of(this).title = "Api";
Node.of(this).description = "A REST API endpoint";
}
@@ -183,6 +334,78 @@ 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,
+ };
+ }
+
+ /**
+ * 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-Origin": allowOrigin.join(",") || "",
+ "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.
@@ -194,7 +417,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(
@@ -219,6 +443,7 @@ export abstract class Api extends Resource {
});
});
}
+ const corsOpenApiSchema = this._corsOpenApiSchema(corsOptions);
const methodSpec = {
[method.toLowerCase()]: {
operationId: operationId,
@@ -226,6 +451,9 @@ export abstract class Api extends Resource {
"200": {
description: "200 response",
content: {},
+ ...(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..3e42f414d23 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 7f805c44bb8..3c2b9050f44 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 { corsHeaders } = props;
// Set up an express server that handles the routes.
this.app = express();
@@ -46,6 +47,30 @@ 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 (corsHeaders) {
+ this.app.use((req, res, next) => {
+ const method =
+ req.method && req.method.toUpperCase && req.method.toUpperCase();
+
+ if (method === "OPTIONS") {
+ for (const [key, value] of Object.entries(
+ corsHeaders.optionsResponse
+ )) {
+ res.setHeader(key, value);
+ }
+ res.status(204).send();
+ } else {
+ for (const [key, value] of Object.entries(
+ corsHeaders.defaultResponse
+ )) {
+ res.setHeader(key, value);
+ }
+ next();
+ }
+ });
+ }
}
public async addEventSubscription(
diff --git a/libs/wingsdk/src/target-sim/api.ts b/libs/wingsdk/src/target-sim/api.ts
index 63c5a42d937..96bbb91287e 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";
@@ -16,6 +17,10 @@ import { BaseResourceSchema } from "../testing/simulator";
export class Api extends cloud.Api implements ISimulatorResource {
private eventMappings: { [key: string]: EventMapping } = {};
+ constructor(scope: Construct, id: string, props: cloud.ApiProps = {}) {
+ super(scope, id, props);
+ }
+
public get url(): string {
return simulatorAttrToken(this, "url");
}
@@ -74,7 +79,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);
Node.of(this).addConnection({
@@ -202,6 +207,7 @@ export class Api extends cloud.Api implements ISimulatorResource {
path: this.node.path,
props: {
openApiSpec: this._getApiSpec(),
+ 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 c660c860a9e..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 { HttpMethod, OpenApiSpec } from "../cloud";
+import { CorsHeaders, HttpMethod, OpenApiSpec } from "../cloud";
import { ColumnType } from "../ex";
import { Json } from "../std";
import {
@@ -31,6 +31,7 @@ export interface ApiSchema extends BaseResourceSchema {
readonly type: typeof API_TYPE;
readonly props: {
openApiSpec: OpenApiSpec;
+ corsHeaders?: CorsHeaders;
};
readonly attrs: ApiAttributes & BaseResourceAttributes;
}
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 8207ef9f9ab..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 "..";
@@ -38,10 +39,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,
});
}
@@ -67,7 +70,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);
Node.of(this).addConnection({
source: this,
@@ -94,7 +97,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);
Node.of(this).addConnection({
source: this,
@@ -121,7 +124,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);
Node.of(this).addConnection({
source: this,
@@ -148,7 +151,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);
Node.of(this).addConnection({
source: this,
@@ -175,7 +178,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);
Node.of(this).addConnection({
source: this,
@@ -202,7 +205,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);
Node.of(this).addConnection({
source: this,
@@ -229,7 +232,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);
Node.of(this).addConnection({
source: this,
@@ -323,7 +326,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,
@@ -362,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.
*/
@@ -415,17 +378,20 @@ class WingRestApi extends Construct {
public readonly stage: ApiGatewayStage;
private readonly deployment: ApiGatewayDeployment;
private readonly region: string;
+
constructor(
scope: Construct,
id: string,
props: {
apiSpec: OpenApiSpec;
+ cors?: cloud.ApiCorsOptions;
}
) {
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
@@ -434,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/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 daccf9da22f..32595907d48 100644
--- a/libs/wingsdk/test/target-sim/api.test.ts
+++ b/libs/wingsdk/test/target-sim/api.test.ts
@@ -597,3 +597,93 @@ test("404 handler", async () => {
expect(response.status).toEqual(404);
expect(body).toContain("Error");
});
+
+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: {
+ allowOrigin: ["https://example.com"],
+ allowCredentials: true,
+ exposeHeaders: ["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"
+ );
+});
+
+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 3dd2d4d766d..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
@@ -1,5 +1,133 @@
// 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": {
+ "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": {
+ "Content-Type": {
+ "type": "string",
+ },
+ },
+ },
+ },
+ "x-amazon-apigateway-integration": {
+ "passthroughBehavior": "never",
+ "requestTemplates": {
+ "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\\"}",
+ },
+ "statusCode": "404",
+ },
+ },
+ "type": "mock",
+ },
+ },
+ },
+ },
+}
+`;
+
exports[`api with 'name' & 'age' parameter 1`] = `
{
"openapi": "3.0.3",
@@ -47,9 +175,6 @@ exports[`api with 'name' & 'age' parameter 1`] = `
},
"/{proxy+}": {
"x-amazon-apigateway-any-method": {
- "consumes": [
- "application/json",
- ],
"produces": [
"application/json",
],
@@ -64,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",
},
@@ -125,9 +262,6 @@ exports[`api with 'name' parameter 1`] = `
},
"/{proxy+}": {
"x-amazon-apigateway-any-method": {
- "consumes": [
- "application/json",
- ],
"produces": [
"application/json",
],
@@ -142,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",
},
@@ -194,9 +340,6 @@ exports[`api with CONNECT route 1`] = `
},
"/{proxy+}": {
"x-amazon-apigateway-any-method": {
- "consumes": [
- "application/json",
- ],
"produces": [
"application/json",
],
@@ -211,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",
},
@@ -263,9 +418,6 @@ exports[`api with DELETE route 1`] = `
},
"/{proxy+}": {
"x-amazon-apigateway-any-method": {
- "consumes": [
- "application/json",
- ],
"produces": [
"application/json",
],
@@ -280,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",
},
@@ -332,9 +496,6 @@ exports[`api with GET route at root 1`] = `
},
"/{proxy+}": {
"x-amazon-apigateway-any-method": {
- "consumes": [
- "application/json",
- ],
"produces": [
"application/json",
],
@@ -349,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",
},
@@ -425,9 +598,6 @@ exports[`api with GET routes with common prefix 1`] = `
},
"/{proxy+}": {
"x-amazon-apigateway-any-method": {
- "consumes": [
- "application/json",
- ],
"produces": [
"application/json",
],
@@ -442,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",
},
@@ -518,9 +700,6 @@ exports[`api with GET routes with different prefix 1`] = `
},
"/{proxy+}": {
"x-amazon-apigateway-any-method": {
- "consumes": [
- "application/json",
- ],
"produces": [
"application/json",
],
@@ -535,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",
},
@@ -587,9 +778,6 @@ exports[`api with HEAD route 1`] = `
},
"/{proxy+}": {
"x-amazon-apigateway-any-method": {
- "consumes": [
- "application/json",
- ],
"produces": [
"application/json",
],
@@ -604,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",
},
@@ -656,9 +856,6 @@ exports[`api with OPTIONS route 1`] = `
},
"/{proxy+}": {
"x-amazon-apigateway-any-method": {
- "consumes": [
- "application/json",
- ],
"produces": [
"application/json",
],
@@ -673,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",
},
@@ -725,9 +934,6 @@ exports[`api with PATCH route 1`] = `
},
"/{proxy+}": {
"x-amazon-apigateway-any-method": {
- "consumes": [
- "application/json",
- ],
"produces": [
"application/json",
],
@@ -742,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",
},
@@ -794,9 +1012,6 @@ exports[`api with POST route 1`] = `
},
"/{proxy+}": {
"x-amazon-apigateway-any-method": {
- "consumes": [
- "application/json",
- ],
"produces": [
"application/json",
],
@@ -811,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",
},
@@ -863,9 +1090,6 @@ exports[`api with PUT route 1`] = `
},
"/{proxy+}": {
"x-amazon-apigateway-any-method": {
- "consumes": [
- "application/json",
- ],
"produces": [
"application/json",
],
@@ -880,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",
},
@@ -956,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",
],
@@ -973,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",
},
@@ -1049,9 +1294,6 @@ exports[`api with multiple methods and multiple lambda 1`] = `
},
"/{proxy+}": {
"x-amazon-apigateway-any-method": {
- "consumes": [
- "application/json",
- ],
"produces": [
"application/json",
],
@@ -1066,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",
},
@@ -1142,9 +1396,6 @@ exports[`api with multiple methods and one lambda 1`] = `
},
"/{proxy+}": {
"x-amazon-apigateway-any-method": {
- "consumes": [
- "application/json",
- ],
"produces": [
"application/json",
],
@@ -1159,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",
},
@@ -1233,9 +1496,6 @@ exports[`api with multiple methods on same route 1`] = `
},
"/{proxy+}": {
"x-amazon-apigateway-any-method": {
- "consumes": [
- "application/json",
- ],
"produces": [
"application/json",
],
@@ -1250,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",
},
@@ -1311,9 +1583,6 @@ exports[`api with path parameter 1`] = `
},
"/{proxy+}": {
"x-amazon-apigateway-any-method": {
- "consumes": [
- "application/json",
- ],
"produces": [
"application/json",
],
@@ -1328,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/libs/wingsdk/test/target-tf-aws/api.test.ts b/libs/wingsdk/test/target-tf-aws/api.test.ts
index 97556cfe2d3..40efb48b41f 100644
--- a/libs/wingsdk/test/target-tf-aws/api.test.ts
+++ b/libs/wingsdk/test/target-tf-aws/api.test.ts
@@ -350,3 +350,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();
+});
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"],
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
new file mode 100644
index 00000000000..387fe8cc53d
--- /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\"],\"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"
+ }
+ },
+ "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/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
new file mode 100644
index 00000000000..ddddf0a5289
--- /dev/null
+++ b/tools/hangar/__snapshots__/test_corpus/valid/api_cors_custom.w_compile_tf-aws.md
@@ -0,0 +1,728 @@
+# [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.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"]));
+ }
+ }
+ 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": ({"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;
+}
+
+```
+
+## 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\"],\"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"
+ }
+ },
+ "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..2a0040d26b0
--- /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,X-Requested-With"));
+ (await $t_Assert.equalStr((headers)["access-control-allow-methods"],"GET,POST,PUT,DELETE,HEAD,OPTIONS"));
+ (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"]));
+ }
+ }
+ 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\"],\"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"
+ }
+ },
+ "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/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/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/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..20388291ca6
--- /dev/null
+++ b/tools/hangar/__snapshots__/test_corpus/valid/assertions.w_compile_tf-aws.md
@@ -0,0 +1,115 @@
+# [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;
+ 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"
+ }
+ }
+ }
+ }
+ },
+ "output": {
+ "WING_TEST_RUNNER_FUNCTION_ARNS": {
+ "value": "[]"
+ }
+ },
+ "provider": {
+ "aws": [
+ {}
+ ]
+ }
+}
+```
+
+## 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;
+class $Root extends $stdlib.std.Resource {
+ constructor(scope, id) {
+ super(scope, id);
+ 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"];
+ }
+ }
+ }
+}
+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/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/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/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 0db07378045..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
@@ -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,24 @@ 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, $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));
+ (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 +66,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 +197,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 +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\":{}}},\"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\"],\"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"
}
},
@@ -265,15 +333,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 +350,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 +390,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 +432,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 +467,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 +502,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 +531,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 +594,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 +674,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 +726,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 +797,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 +809,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 +843,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 +877,11 @@ 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)},
+ $t_Assert: ${context._lift(t.Assert)},
})
`;
}
@@ -733,17 +899,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 d722e2edca8..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,10 +2,11 @@
## stdout.log
```log
-pass ─ website_with_api.wsim (no tests)
+pass ─ website_with_api.wsim » root/env0/test:GET --users
+pass ─ website_with_api.wsim » root/env1/test:OPTIONS --users
-Tests 1 passed (1)
+Tests 2 passed (2)
Test Files 1 passed (1)
Duration
```