Skip to content

Commit

Permalink
Merge branch 'main' into rybickic/inline-policy
Browse files Browse the repository at this point in the history
  • Loading branch information
Chriscbr authored Apr 8, 2024
2 parents d5cb366 + 47fd645 commit 73d7a7a
Show file tree
Hide file tree
Showing 69 changed files with 1,476 additions and 776 deletions.
11 changes: 9 additions & 2 deletions apps/wing-console/console/server/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,10 @@ export type RouteNames = keyof inferRouterInputs<Router> | undefined;

export { isTermsAccepted } from "./utils/terms-and-conditions.js";

const enableSimUpdates =
process.env.WING_ENABLE_INPLACE_UPDATES === "true" ||
process.env.WING_ENABLE_INPLACE_UPDATES === "1";

export interface CreateConsoleServerOptions {
wingfile: string;
log: LogInterface;
Expand Down Expand Up @@ -134,7 +138,10 @@ export const createConsoleServer = async ({
let isStarting = false;
let isStopping = false;

const simulator = createSimulator({ stateDir });
const simulator = createSimulator({
stateDir,
enableSimUpdates,
});
if (onTrace) {
simulator.on("trace", onTrace);
}
Expand All @@ -150,7 +157,7 @@ export const createConsoleServer = async ({
platform,
testing: true,
});
const testSimulator = createSimulator();
const testSimulator = createSimulator({ enableSimUpdates });
testCompiler.on("compiled", ({ simfile }) => {
testSimulator.start(simfile);
});
Expand Down
24 changes: 18 additions & 6 deletions apps/wing-console/console/server/src/utils/simulator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export interface Simulator {

export interface CreateSimulatorProps {
stateDir?: string;
enableSimUpdates?: boolean;
}

const stopSilently = async (simulator: simulator.Simulator) => {
Expand All @@ -45,13 +46,25 @@ const stopSilently = async (simulator: simulator.Simulator) => {
export const createSimulator = (props?: CreateSimulatorProps): Simulator => {
const events = new Emittery<SimulatorEvents>();
let instance: simulator.Simulator | undefined;
const handleExistingInstance = async (simfile: string): Promise<boolean> => {
if (!instance) {
return true;
}
if (props?.enableSimUpdates) {
await events.emit("starting", { instance });
await instance.update(simfile);
await events.emit("started");
return false;
} else {
await events.emit("stopping");
await stopSilently(instance);
return true;
}
};
const start = async (simfile: string) => {
try {
if (instance) {
await events.emit("starting", { instance });
await instance.update(simfile);
await events.emit("started");
} else {
const shouldStartSim = await handleExistingInstance(simfile);
if (shouldStartSim) {
instance = new simulator.Simulator({
simfile,
stateDir: props?.stateDir,
Expand All @@ -61,7 +74,6 @@ export const createSimulator = (props?: CreateSimulatorProps): Simulator => {
events.emit("trace", trace);
},
});

await events.emit("starting", { instance });
await instance.start();
await events.emit("started");
Expand Down
5 changes: 3 additions & 2 deletions apps/wing/src/commands/test/test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { basename, join, relative, resolve, sep } from "path";
import { promisify } from "util";
import { BuiltinPlatform, determineTargetFromPlatforms } from "@winglang/compiler";
import { std, simulator } from "@winglang/sdk";
import { TraceType } from "@winglang/sdk/lib/std";
import { Util } from "@winglang/sdk/lib/util";
import { prettyPrintError } from "@winglang/sdk/lib/util/enhanced-error";
import chalk from "chalk";
Expand Down Expand Up @@ -199,10 +200,10 @@ export async function renderTestReport(
// add any log messages that were emitted during the test
for (const trace of result.traces) {
// only show detailed traces if we are in debug mode
if (trace.type === "resource" && process.env.DEBUG) {
if (trace.type === TraceType.RESOURCE && process.env.DEBUG) {
details.push(chalk.gray("[trace] " + trace.data.message));
}
if (trace.type === "log") {
if (trace.type === TraceType.LOG) {
details.push(chalk.gray(trace.data.message));
}
}
Expand Down
2 changes: 1 addition & 1 deletion docs/contributing/01-start-here/01-contributing-to-wing.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ There are many ways to contribute to Wing:

* Contributing to the [Wing SDK (standard library)](/contributing/start-here/wingsdk)
* Reporting bugs through a [GitHub issue](https://github.com/winglang/wing/issues)
* Writing [documentation and guides](https://github.com/winglang/wing/issues?q=is%3Aissue+is%3Aopen+label%3A%22%F0%9F%93%9A+documentation%22) or adding [examples](/contributing/start-here/docs#%EF%B8%8F-how-do-i-add-an-example)
* Writing [documentation and guides](https://github.com/winglang/wing/issues?q=is%3Aissue+is%3Aopen+label%3A%22%F0%9F%93%9A+documentation%22) or adding [examples](https://github.com/winglang/examples)
* Setting up your [development environment](/contributing/start-here/development) and working on the code
* Submitting [pull requests](/contributing/start-here/pull_requests) for new features or helping with [reviews](https://github.com/winglang/wing/pulls)
* Picking up a [good first issue](https://github.com/winglang/wing/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22+no%3Aassignee+sort%3Aupdated-desc+) to work on
Expand Down
18 changes: 9 additions & 9 deletions docs/contributing/01-start-here/07-wingsdk.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,16 +67,16 @@ A resource in the SDK has several parts:

* A set of base APIs that must be implemented by all cloud targets. Typically the resource's preflight APIs correspond to a base class in TypeScript, and the resource's inflight APIs correspond to an interface in TypeScript. These are defined in `src/cloud` or `src/ex`. For example, [`src/cloud/bucket.ts`](https://github.com/winglang/wing/tree/main/libs/wingsdk/src/cloud/bucket.ts).
* An interface representing the inflight API common across all cloud targets. By convention, if the resource is named like `Gizmo`, the inflight interface should be named `IGizmoClient`. This is usually in the same file as the preflight API.
* A simulator implementation in `src/sim`. This includes:
* A schema with information to simulate the resource and display the resource in the Wing console. Currently these are in [`src/sim/schema-resources.ts`](https://github.com/winglang/wing/tree/main/libs/wingsdk/src/sim/schema-resources.ts).
* A class that implements the polycon API and can produce the resource's simulation schema. For example, [`src/sim/bucket.ts`](https://github.com/winglang/wing/tree/main/libs/wingsdk/src/sim/bucket.ts).
* An class that implements the inflight API and can simulate the resource. For example, [`src/sim/bucket.sim.ts`](https://github.com/winglang/wing/tree/main/libs/wingsdk/src/sim/bucket.sim.ts).
* Unit tests for the simulator implementation. For example, [`test/sim/bucket.test.ts`](https://github.com/winglang/wing/tree/main/libs/wingsdk/test/sim/bucket.test.ts).
* A simulator implementation in `src/target-sim`. This includes:
* A schema with information to simulate the resource and display the resource in the Wing console. Currently these are in [`src/target-sim/schema-resources.ts`](https://github.com/winglang/wing/tree/main/libs/wingsdk/src/target-sim/schema-resources.ts).
* A class that implements the polycon API and can produce the resource's simulation schema. For example, [`src/target-sim/bucket.ts`](https://github.com/winglang/wing/tree/main/libs/wingsdk/src/target-sim/bucket.ts).
* An class that implements the inflight API and can simulate the resource. For example, [`src/target-sim/bucket.inflight.ts`](https://github.com/winglang/wing/tree/main/libs/wingsdk/src/target-sim/bucket.inflight.ts).
* Unit tests for the simulator implementation. For example, [`test/target-sim/bucket.test.ts`](https://github.com/winglang/wing/tree/main/libs/wingsdk/test/target-sim/bucket.test.ts).
* An implementation for each target cloud (currently just AWS). This includes:
* A class that implements the polycon API and creates all of the required terraform resources. For example, [`src/tf-aws/bucket.ts`](https://github.com/winglang/wing/tree/main/libs/wingsdk/src/tf-aws/bucket.ts).
* A class that implements the inflight API that interacts with the cloud resource. For example, [`src/tf-aws/bucket.inflight.ts`](https://github.com/winglang/wing/tree/main/libs/wingsdk/src/tf-aws/bucket.inflight.ts).
* Unit tests for the cloud infrastructure. For example, [`test/tf-aws/bucket.test.ts`](https://github.com/winglang/wing/tree/main/libs/wingsdk/test/tf-aws/bucket.test.ts).
* End-to-end tests. These are added to the "examples" directory at the root of the repository. For example, [`examples/tests/sdk_tests/bucket/bucket_list.w`](https://github.com/winglang/wing/blob/main/examples/tests/sdk_tests/bucket/bucket_list.w).
* A class that implements the polycon API and creates all of the required terraform resources. For example, [`src/shared-aws/bucket.ts`](https://github.com/winglang/wing/tree/main/libs/wingsdk/src/shared-aws/bucket.ts).
* A class that implements the inflight API that interacts with the cloud resource. For example, [`src/shared-aws/bucket.inflight.ts`](https://github.com/winglang/wing/tree/main/libs/wingsdk/src/shared-aws/bucket.inflight.ts).
* Unit tests for the cloud infrastructure. For example, [`test/target-tf-aws/bucket.test.ts`](https://github.com/winglang/wing/tree/main/libs/wingsdk/test/target-tf-aws/bucket.test.ts).
* End-to-end tests. These are added to the "examples" directory at the root of the repository. For example, [`examples/tests/sdk_tests/bucket/bucket_list.test.w`](https://github.com/winglang/wing/blob/main/examples/tests/sdk_tests/bucket/bucket_list.test.w).

If you are implementing a new resource, or implementing an existing resource for a new cloud provider, try to take a look at code for existing resources (`Bucket`, `Function`, `Queue`) to see how to structure your code.

Expand Down
12 changes: 0 additions & 12 deletions docs/contributing/01-start-here/08-docs.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,3 @@ pnpm docs
This magical script will clone the [winglang/docsite](https://github.com/winglang/docsite)
repository into `~/.winglang-docsite`, symlink your local copy into it and start a browser with the
site.

## 🖼️ How do I add an example?

Adding a code example is a great way to contribute to Wing. Here's how to do it:

* Fork this repository on GitHub.
* Create a new branch for your example.
* Add your Wing code to the `examples` directory.
* If your example involves multiple files, create a dedicated directory for it.
* Add a link to your example to the `examples/README.md` file.
* Commit your changes and push them to your fork.
* Open a pull request. A Wing maintainer will review it as soon as possible!
75 changes: 75 additions & 0 deletions docs/contributing/999-rfcs/2024-03-24-snapshot-tests.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
---
title: "#5958 Cloud Snapshot Tests"
description: Spec and design for adding cloud snapshots to the Wing test framework
---

# Cloud Snapshot Tests

- **Author(s)**: @eladb
- **Submission Date**: 2024-03-24
- **Stage**: Approved
- **Stage Date**: 2024-04-03

## Requirements

We would like to be able to capture a snapshot of the synthesized output when compiling to cloud
targets, and use it to protect against regressions in CI, without having to deploy to the cloud.

For example, let's say I am creating a Wing library with a resource that supports both `sim` and
`tf-aws`. I am writing a bunch of tests for it.

I can run these tests in the simulator (`wing test`) and can also run them on AWS through `wing test
-t tf-aws`.

As my code **or its dependencies** are updated, I'd like to continuously run all these tests to make
sure my library is not broken. Running the tests against `sim` is easy (I can just run `wing test`
in my CI build environment), but it's not realistic to run all these tests against the real cloud
target, given it could take a very long time and could also be very expensive.

In some scenarios (such as the Wing SDK tests), users will prefer to setup a continuous integration
workflow and run tests in the cloud, but for the most part running all my tests on the cloud for
every commit is too expensive and requires a complicated setup (i.e. an AWS account), so I'd like to
use a more lightweight approach...

> NOTE: This is not https://github.com/winglang/wing/issues/1214 (which is about using snapshots for
> assertions).
## Design

We propose to add a feature to `wing test` which will capture a snapshot of the synthesized output
of every `.test.w` file and store them in the repository.

> This is a similar approach used successfully and scalably in the AWS CDK. See
> [docs](https://github.com/aws/aws-cdk/blob/main/INTEGRATION_TESTS.md) for details. The main
> difference is that `cdk-integ` there was no way to add assertions to tests, so this is so much
> cooler.
When I run `wing test -t tf-aws foo.test.w` in my dev environment, if the test succeeds (e.g.
deploys and all test cases pass), the test framework will create `foo.test.w.tfaws.snap` which will
include a nicely formatted (markdown?) snapshot of all the synthesis output from this test.

> In a future version we can add support for placing all snapshots in an adjacent subdirectory.
Now, when `wing test -t tf-aws foo.test.w` is executed in CI (`CI=1`), by default it won't actually
go and deploy to the cloud. Instead, it will just synthesize the output and compare it to the
committed `.snap` file. If the output is not the same, the test will fail with a nice diff
indicating that there was an unexpected regression.

The `--snapshots` or `-s` switch can be used to control behavior:

* `--snapshots=auto` - auto-detect based on CI flag (described above)
* `--snapshots=never` - disables snapshots altogether
* `--snapshots=update` - skips deployment and only update the snapshots.
* `--snapshots=deploy` - forces a deployment even if `CI=1`.
* `--snapshots=assert` - only asserts that the snapshots have not changed

## Implementation Notes

* We need to decide if we want the inflight JavaScript code to also be included or just trust the
fact that if inflight code has changed, it should be reflected somehow in the asset names
referenced from the IAC model.
* Ideally this shouldn't be Terraform-specific. `app.synth()` should return something that can be
used to capture the snapshots.
* The output will need to be sanitized somehow so that it will be deterministic (e.g. paths should
not be included).

4 changes: 2 additions & 2 deletions docs/docs/03-language-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -1869,8 +1869,8 @@ Mapping JSII types to Wing types:
| **JSII Type** | **Wing Type** |
|---------------|--------------------|
| [class](https://aws.github.io/jsii/user-guides/language-support/assembly/#classes) | A [Wing class](#32-classes).<br> The [phase](#13-phase-modifiers) of the class will be `preflight` if the imported class is a [construct](https://github.com/aws/constructs) (derived from `constructs.Construct`). Otherwise the class will be phase independent.<br> By convention construct constructors have a `scope` and `id` as their first parameters. These will be used by Wing to define the default scope and id for new instances of this [preflight class](#33-preflight-classes) or explicit scope and id using the `in` and `as` keywords. |
| [interface](https://aws.github.io/jsii/user-guides/language-support/assembly/#interfaces) | A [Wing interface](#34-interfaces). All imported interfaces are `preflight` interfaces.<br> JSII library authors may annotate their interface with a docstring tag like `@inflight IMyClient` to indicate a second interface that'll be used to import inflight methods **into** this Wing interface. |
| [class](https://aws.github.io/jsii/user-guides/language-support/assembly/#classes) | A [Wing class](#32-classes).<br/> The [phase](#13-phase-modifiers) of the class will be `preflight` if the imported class is a [construct](https://github.com/aws/constructs) (derived from `constructs.Construct`). Otherwise the class will be phase independent.<br/> By convention construct constructors have a `scope` and `id` as their first parameters. These will be used by Wing to define the default scope and id for new instances of this [preflight class](#33-preflight-classes) or explicit scope and id using the `in` and `as` keywords. |
| [interface](https://aws.github.io/jsii/user-guides/language-support/assembly/#interfaces) | A [Wing interface](#34-interfaces). All imported interfaces are `preflight` interfaces.<br/> JSII library authors may annotate their interface with a docstring tag like `@inflight IMyClient` to indicate a second interface that'll be used to import inflight methods **into** this Wing interface. |
| [struct (a.k.a. data-type)](https://aws.github.io/jsii/user-guides/language-support/assembly/#structs-aka-data-types) | A [Wing struct](#31-structs). Always phase independent. |
| [enum](https://aws.github.io/jsii/user-guides/language-support/assembly/#enums) | A [Wing enum](#38-enumeration). |
Expand Down
82 changes: 82 additions & 0 deletions docs/docs/04-standard-library/cloud/topic.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,20 @@ topic.onMessage(inflight (message: str) => {
});
```

### Subscribing a Queue to a Topic

```js
bring cloud;

let queue = new cloud.Queue();
queue.setConsumer(inflight (message str) => {
log("Topic published message: {message}");
});

let topic = new cloud.Topic();
topic.subscribeQueue(queue);
```

### Publishing to a topic

The inflight method `publish` sends a message to all of the topic's subscribers.
Expand Down Expand Up @@ -135,6 +149,7 @@ new cloud.Topic(props?: TopicProps);
| **Name** | **Description** |
| --- | --- |
| <code><a href="#@winglang/sdk.cloud.Topic.onMessage">onMessage</a></code> | Run an inflight whenever an message is published to the topic. |
| <code><a href="#@winglang/sdk.cloud.Topic.subscribeQueue">subscribeQueue</a></code> | Subscribing queue to the topic. |

##### Inflight Methods

Expand Down Expand Up @@ -164,6 +179,26 @@ Run an inflight whenever an message is published to the topic.

---

##### `subscribeQueue` <a name="subscribeQueue" id="@winglang/sdk.cloud.Topic.subscribeQueue"></a>

```wing
subscribeQueue(queue: Queue, props?: TopicSubscribeQueueOptions): void
```

Subscribing queue to the topic.

###### `queue`<sup>Required</sup> <a name="queue" id="@winglang/sdk.cloud.Topic.subscribeQueue.parameter.queue"></a>

- *Type:* <a href="#@winglang/sdk.cloud.Queue">Queue</a>

---

###### `props`<sup>Optional</sup> <a name="props" id="@winglang/sdk.cloud.Topic.subscribeQueue.parameter.props"></a>

- *Type:* <a href="#@winglang/sdk.cloud.TopicSubscribeQueueOptions">TopicSubscribeQueueOptions</a>

---

##### `publish` <a name="publish" id="@winglang/sdk.cloud.ITopicClient.publish"></a>

```wing
Expand Down Expand Up @@ -344,6 +379,53 @@ let TopicProps = cloud.TopicProps{ ... };
```


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

Options for `Topic.subscribeQueue`.

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

```wing
bring cloud;
let TopicSubscribeQueueOptions = cloud.TopicSubscribeQueueOptions{ ... };
```

#### Properties <a name="Properties" id="Properties"></a>

| **Name** | **Type** | **Description** |
| --- | --- | --- |
| <code><a href="#@winglang/sdk.cloud.TopicSubscribeQueueOptions.property.retentionPeriod">retentionPeriod</a></code> | <code><a href="#@winglang/sdk.std.Duration">duration</a></code> | How long a queue retains a message. |
| <code><a href="#@winglang/sdk.cloud.TopicSubscribeQueueOptions.property.timeout">timeout</a></code> | <code><a href="#@winglang/sdk.std.Duration">duration</a></code> | How long a queue's consumers have to process a message. |

---

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

```wing
retentionPeriod: duration;
```

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

How long a queue retains a message.

---

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

```wing
timeout: duration;
```

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

How long a queue's consumers have to process a message.

---

## Protocols <a name="Protocols" id="Protocols"></a>

### ITopicOnMessageHandler <a name="ITopicOnMessageHandler" id="@winglang/sdk.cloud.ITopicOnMessageHandler"></a>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -869,6 +869,14 @@
"tf-aws": {
"implemented": true
}
},
"subscribeQueue": {
"sim": {
"implemented": true
},
"tf-aws": {
"implemented": true
}
}
},
"Schedule": {
Expand Down
Loading

0 comments on commit 73d7a7a

Please sign in to comment.