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 ```