Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore(sdk): add Max-Age CORS header for cloud.Api #4194

Merged
merged 15 commits into from
Sep 20, 2023
Merged
14 changes: 14 additions & 0 deletions docs/docs/04-standard-library/01-cloud/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -488,6 +488,7 @@ let ApiCorsOptions = cloud.ApiCorsOptions{ ... };
| <code><a href="#@winglang/sdk.cloud.ApiCorsOptions.property.allowMethods">allowMethods</a></code> | <code>MutArray&lt;<a href="#@winglang/sdk.cloud.HttpMethod">HttpMethod</a>&gt;</code> | The list of allowed methods. |
| <code><a href="#@winglang/sdk.cloud.ApiCorsOptions.property.allowOrigin">allowOrigin</a></code> | <code>MutArray&lt;str&gt;</code> | The list of allowed allowOrigin. |
| <code><a href="#@winglang/sdk.cloud.ApiCorsOptions.property.exposeHeaders">exposeHeaders</a></code> | <code>MutArray&lt;str&gt;</code> | The list of exposed headers. |
| <code><a href="#@winglang/sdk.cloud.ApiCorsOptions.property.maxAge">maxAge</a></code> | <code><a href="#@winglang/sdk.std.Duration">duration</a></code> | How long the browser should cache preflight request results. |

---

Expand Down Expand Up @@ -584,6 +585,19 @@ The list of exposed headers.
```


##### `maxAge`<sup>Optional</sup> <a name="maxAge" id="@winglang/sdk.cloud.ApiCorsOptions.property.maxAge"></a>

```wing
maxAge: duration;
```

- *Type:* <a href="#@winglang/sdk.std.Duration">duration</a>
- *Default:* 300 seconds

How long the browser should cache preflight request results.

---

### ApiDeleteProps <a name="ApiDeleteProps" id="@winglang/sdk.cloud.ApiDeleteProps"></a>

Options for Api put endpoint.
Expand Down
3 changes: 2 additions & 1 deletion examples/tests/valid/website_with_api.main.w
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ let api = new cloud.Api(
allowMethods: [cloud.HttpMethod.GET, cloud.HttpMethod.POST, cloud.HttpMethod.OPTIONS],
allowHeaders: ["Content-Type"],
allowCredentials: false,
exposeHeaders: ["Content-Type"]
exposeHeaders: ["Content-Type"],
maxAge: 600s
}
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ source: libs/wingc/src/lsp/completions.rs
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."
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.\n- `maxAge?` — How long the browser should cache preflight request results."
sortText: hh|ApiCorsOptions
- label: ApiDeleteProps
kind: 22
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ source: libs/wingc/src/lsp/completions.rs
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."
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.\n- `maxAge?` — How long the browser should cache preflight request results."
sortText: hh|ApiCorsOptions
- label: ApiDeleteProps
kind: 22
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ source: libs/wingc/src/lsp/completions.rs
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."
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.\n- `maxAge?` — How long the browser should cache preflight request results."
sortText: hh|ApiCorsOptions
- label: ApiDeleteProps
kind: 22
Expand Down
32 changes: 24 additions & 8 deletions libs/wingsdk/src/cloud/api.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Construct } from "constructs";
import { fqnForType } from "../constants";
import { App } from "../core";
import { IResource, Node, Resource } from "../std";
import { IResource, Node, Resource, Duration } from "../std";

/**
* Global identifier for `Api`.
Expand Down Expand Up @@ -46,6 +46,12 @@ export interface ApiCorsOptions {
* @default - false
*/
readonly allowCredentials?: boolean;

/**
* How long the browser should cache preflight request results.
* @default - 300 seconds
*/
readonly maxAge?: Duration;
garysassano marked this conversation as resolved.
Show resolved Hide resolved
}

/**
Expand Down Expand Up @@ -93,7 +99,7 @@ export type OpenApiSpec = any;

/**
* The OpenAPI spec extension for a route.
* see https://spec.openapis.org/oas/v3.0.3
* @see https://spec.openapis.org/oas/v3.0.3
* */
export type OpenApiSpecExtension = any;

Expand All @@ -108,20 +114,20 @@ export type OpenApiCorsHeaders = Record<string, { schema: { type: string } }>;
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
* @see 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
* @see 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
* @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Credentials
*/
"Access-Control-Allow-Credentials": string;
};
Expand All @@ -132,21 +138,27 @@ type CorsDefaultResponseHeaders = {
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
* @see 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
* @see 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
* @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Methods
*/
"Access-Control-Allow-Methods": string;

/**
* Indicates how long the results of a preflight request can be cached.
* @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Max-Age
*/
"Access-Control-Max-Age": string;
};

/**
Expand Down Expand Up @@ -198,6 +210,7 @@ export abstract class Api extends Resource {
allowHeaders: ["Content-Type", "Authorization", "X-Requested-With"],
exposeHeaders: [],
allowCredentials: false,
maxAge: Duration.fromMinutes(5),
};

/**
Expand Down Expand Up @@ -363,6 +376,7 @@ export abstract class Api extends Resource {
corsHeaders["Access-Control-Allow-Origin"] = corsHeaderSchema;
corsHeaders["Access-Control-Allow-Methods"] = corsHeaderSchema;
corsHeaders["Access-Control-Allow-Headers"] = corsHeaderSchema;
corsHeaders["Access-Control-Max-Age"] = corsHeaderSchema;
}
return corsHeaders;
}
Expand All @@ -386,6 +400,7 @@ export abstract class Api extends Resource {
allowMethods = [],
exposeHeaders = [],
allowCredentials = false,
maxAge = Duration.fromMinutes(5),
} = corsOptions;

const defaultHeaders: CorsDefaultResponseHeaders = {
Expand All @@ -398,6 +413,7 @@ export abstract class Api extends Resource {
"Access-Control-Allow-Origin": allowOrigin.join(",") || "",
"Access-Control-Allow-Headers": allowHeaders.join(",") || "",
"Access-Control-Allow-Methods": allowMethods.join(",") || "",
"Access-Control-Max-Age": maxAge.seconds.toString(),
};

return {
Expand Down
1 change: 1 addition & 0 deletions libs/wingsdk/test/target-sim/api.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -686,4 +686,5 @@ test("api with CORS settings responds to OPTIONS request", async () => {
expect(response.headers.get("access-control-allow-methods")).toEqual(
"GET,POST,PUT,DELETE,HEAD,OPTIONS"
);
expect(response.headers.get("access-control-max-age")).toEqual("300");
});
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@ exports[`api configured for cors 1`] = `
"type": "string",
},
},
"Access-Control-Max-Age": {
"schema": {
"type": "string",
},
},
},
},
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ module.exports = function({ $api_url, $http_Util }) {
"uniqueId": "cloudApi_api_2B334D75"
}
},
"body": "{\"openapi\":\"3.0.3\",\"paths\":{\"/path\":{\"get\":{\"operationId\":\"get-path\",\"responses\":{\"200\":{\"description\":\"200 response\",\"content\":{},\"headers\":{\"Access-Control-Allow-Origin\":{\"schema\":{\"type\":\"string\"}},\"Access-Control-Allow-Methods\":{\"schema\":{\"type\":\"string\"}},\"Access-Control-Allow-Headers\":{\"schema\":{\"type\":\"string\"}}}}},\"parameters\":[],\"x-amazon-apigateway-integration\":{\"uri\":\"arn:aws:apigateway:${data.aws_region.Region.name}:lambda:path/2015-03-31/functions/${aws_lambda_function.cloudApi_cloudApi-OnRequest-cdafee6e_A6C8366F.arn}/invocations\",\"type\":\"aws_proxy\",\"httpMethod\":\"POST\",\"responses\":{\"default\":{\"statusCode\":\"200\"}},\"passthroughBehavior\":\"when_no_match\",\"contentHandling\":\"CONVERT_TO_TEXT\"}}},\"/{proxy+}\":{\"x-amazon-apigateway-any-method\":{\"produces\":[\"application/json\"],\"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\"}}}}}}}}",
"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\"}},\"Access-Control-Max-Age\":{\"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"
}
},
Expand Down
Loading
Loading