Skip to content

Commit

Permalink
wip: Workflows in Wrangler
Browse files Browse the repository at this point in the history
  • Loading branch information
sidharthachatterjee committed Jul 30, 2024
1 parent 6a51aa3 commit 4041e42
Show file tree
Hide file tree
Showing 20 changed files with 259 additions and 1 deletion.
7 changes: 7 additions & 0 deletions fixtures/workflow/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# worker-ts

## 0.0.1

### Patch Changes

- [#3588](https://github.com/cloudflare/workers-sdk/pull/3588) [`64631d8b`](https://github.com/cloudflare/workers-sdk/commit/64631d8b59572f49d65325d8f6fec098c5e912b9) Thanks [@penalosa](https://github.com/penalosa)! - fix: Preserve email handlers when applying middleware to user workers.
15 changes: 15 additions & 0 deletions fixtures/workflow/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"name": "worker-ts",
"private": true,
"scripts": {
"deploy": "wrangler deploy",
"start": "wrangler dev --x-dev-env"
},
"devDependencies": {
"@cloudflare/workers-types": "^4.20240725.0",
"wrangler": "workspace:*"
},
"volta": {
"extends": "../../package.json"
}
}
28 changes: 28 additions & 0 deletions fixtures/workflow/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { WorkerEntrypoint } from "cloudflare:workers";

export default class extends WorkerEntrypoint {
async init(event: any, step: any): Promise<void> {
await step.run("first step", async function () {
await fetch(
`https://webhook.site/16ffb499-32d1-4a0b-b2fd-ad8b15114f30?step=first`
);
return {
result: "ok",
};
});

await step.sleep("10 seconds");

await step.run("second step", async function () {
await fetch(
`https://webhook.site/16ffb499-32d1-4a0b-b2fd-ad8b15114f30?step=second`
);
return {
result: "ok",
};
});
}
async fetch(): Promise<Response> {
return new Response();
}
}
13 changes: 13 additions & 0 deletions fixtures/workflow/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"compilerOptions": {
"target": "es2021",
"lib": ["es2021"],
"module": "es2022",
"types": ["@cloudflare/workers-types/experimental"],
"noEmit": true,
"isolatedModules": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"skipLibCheck": true
}
}
10 changes: 10 additions & 0 deletions fixtures/workflow/wrangler.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#:schema node_modules/wrangler/config-schema.json
name = "my-workflow"
main = "src/index.ts"
compatibility_date = "2024-07-25"

[[workflows]]
binding = "WORKFLOW"
name = "my-workflow" # name of the workflow
class_name = "default"
script_name = "my-workflow" # we could skip this if we can infer from validator
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ function createWorkerBundleFormData(
ai: config?.ai,
version_metadata: config?.version_metadata,
durable_objects: config?.durable_objects,
workflows: config?.workflows,
queues: config?.queues.producers?.map((producer) => {
return { binding: producer.binding, queue_name: producer.queue };
}),
Expand Down
2 changes: 2 additions & 0 deletions packages/wrangler/src/api/startDevWorker/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import type {
CfService,
CfUnsafe,
CfVectorize,
CfWorkflow,
} from "../../deployment-bundle/worker";
import type { WorkerRegistry } from "../../dev-registry";
import type { CfAccount } from "../../dev/create-worker-preview";
Expand Down Expand Up @@ -247,6 +248,7 @@ export type Binding =
| { type: "version_metadata" }
| { type: "data_blob"; source: BinaryFile }
| ({ type: "durable_object_namespace" } & BindingOmit<CfDurableObject>)
| ({ type: "workflow" } & BindingOmit<CfWorkflow>)
| ({ type: "queue" } & BindingOmit<CfQueue>)
| ({ type: "r2_bucket" } & BindingOmit<CfR2Bucket>)
| ({ type: "d1" } & Omit<CfD1Database, "binding">)
Expand Down
7 changes: 7 additions & 0 deletions packages/wrangler/src/api/startDevWorker/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,12 @@ export function convertCfWorkerInitBindingstoBindings(
}
break;
}
case "workflows": {
for (const { binding, ...x } of info) {
output[binding] = { type: "workflow", ...x };
}
break;
}
case "queues": {
for (const { binding, ...x } of info) {
output[binding] = { type: "queue", ...x };
Expand Down Expand Up @@ -268,6 +274,7 @@ export async function convertBindingsToCfWorkerInitBindings(
durable_objects: undefined,
queues: undefined,
r2_buckets: undefined,
workflows: undefined,
d1_databases: undefined,
vectorize: undefined,
hyperdrive: undefined,
Expand Down
1 change: 1 addition & 0 deletions packages/wrangler/src/config/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,7 @@ export const defaultWranglerConfig: Config = {
d1_databases: [],
vectorize: [],
hyperdrive: [],
workflows: [],
services: [],
analytics_engine_datasets: [],
ai: undefined,
Expand Down
24 changes: 24 additions & 0 deletions packages/wrangler/src/config/environment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,19 @@ export type DurableObjectBindings = {
environment?: string;
}[];

export type WorkflowBinding = {
/** The name of the binding used to refer to the Workflow */
binding: string;
/** The name of the Workflow */
name: string;
/** The exported class name of the Workflow */
class_name: string;
/** The script where the Workflow is defined (if it's external to this Worker) */
script_name?: string;
/** The service environment of the script_name to bind to */
environment?: string;
};

/**
* The `EnvironmentNonInheritable` interface declares all the configuration fields for an environment
* that cannot be inherited from the top-level environment, and must be defined specifically.
Expand Down Expand Up @@ -397,6 +410,17 @@ export interface EnvironmentNonInheritable {
bindings: DurableObjectBindings;
};

/**
* A list of workflows that your Worker should be bound to.
*
* NOTE: This field is not automatically inherited from the top level environment,
* and so must be specified in every named environment.
*
* @default `[]`
* @nonInheritable
*/
workflows: WorkflowBinding[];

/**
* Cloudchamber configuration
*
Expand Down
23 changes: 23 additions & 0 deletions packages/wrangler/src/config/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@ export function printBindings(bindings: CfWorkerInit["bindings"]) {
const {
data_blobs,
durable_objects,
workflows,
kv_namespaces,
send_email,
queues,
Expand Down Expand Up @@ -254,6 +255,28 @@ export function printBindings(bindings: CfWorkerInit["bindings"]) {
});
}

if (workflows !== undefined && workflows.length > 0) {
output.push({
type: "Workflows",
entries: workflows.map(
({ class_name, script_name, environment, binding }) => {
let value = class_name;
if (script_name) {
value += ` (defined in ${script_name})`;
}
if (environment) {
value += ` - ${environment}`;
}

return {
key: binding,
value,
};
}
),
});
}

if (kv_namespaces !== undefined && kv_namespaces.length > 0) {
output.push({
type: "KV Namespaces",
Expand Down
67 changes: 67 additions & 0 deletions packages/wrangler/src/config/validation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1276,6 +1276,16 @@ function normalizeAndValidateEnvironment(
bindings: [],
}
),
workflows: notInheritable(
diagnostics,
topLevelEnv,
rawConfig,
rawEnv,
envName,
"workflows",
validateBindingArray(envName, validateWorkflowBinding),
[]
),
migrations: inheritable(
diagnostics,
topLevelEnv,
Expand Down Expand Up @@ -1980,6 +1990,63 @@ const validateDurableObjectBinding: ValidatorFn = (
return isValid;
};

/**
* Check that the given field is a valid "workflow" binding object.
*/
const validateWorkflowBinding: ValidatorFn = (diagnostics, field, value) => {
// if (typeof value !== "object" || value === null) {
// diagnostics.errors.push(
// `Expected "${field}" to be an object but got ${JSON.stringify(value)}`
// );
// return false;
// }

// Workflow bindings must have a name, class_name and binding, and optionally a script_name and an environment.
let isValid = true;
// if (!isRequiredProperty(value, "name", "string")) {
// diagnostics.errors.push(`binding should have a string "name" field.`);
// isValid = false;
// }
// if (!isRequiredProperty(value, "binding", "string")) {
// diagnostics.errors.push(`binding should have a string "binding" field.`);
// isValid = false;
// }
// if (!isRequiredProperty(value, "class_name", "string")) {
// diagnostics.errors.push(`binding should have a string "class_name" field.`);
// isValid = false;
// }
// if (!isOptionalProperty(value, "script_name", "string")) {
// diagnostics.errors.push(
// `the field "script_name", when present, should be a string.`
// );
// isValid = false;
// }
// // environment requires a script_name
// if (!isOptionalProperty(value, "environment", "string")) {
// diagnostics.errors.push(
// `the field "environment", when present, should be a string.`
// );
// isValid = false;
// }

// if ("environment" in value && !("script_name" in value)) {
// diagnostics.errors.push(
// `binding should have a "script_name" field if "environment" is present.`
// );
// isValid = false;
// }

// validateAdditionalProperties(diagnostics, field, Object.keys(value), [
// "class_name",
// "environment",
// "name",
// "script_name",
// "binding",
// ]);

return isValid;
};

const validateCflogfwdrObject: (env: string) => ValidatorFn =
(envName) => (diagnostics, field, value, topLevelEnv) => {
//validate the bindings property first, as this also validates that it's an object, etc.
Expand Down
1 change: 1 addition & 0 deletions packages/wrangler/src/deploy/deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -622,6 +622,7 @@ See https://developers.cloudflare.com/workers/platform/compatibility-dates for m
},
data_blobs: config.data_blobs,
durable_objects: config.durable_objects,
workflows: config.workflows,
queues: config.queues.producers?.map((producer) => {
return { binding: producer.binding, queue_name: producer.queue };
}),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,14 @@ export type WorkerMetadataBinding =
script_name?: string;
environment?: string;
}
| {
type: "workflow";
binding: string;
name: string;
class_name: string;
script_name?: string;
environment?: string;
}
| { type: "queue"; name: string; queue_name: string; delivery_delay?: number }
| {
type: "r2_bucket";
Expand Down Expand Up @@ -214,6 +222,19 @@ export function createWorkerUploadForm(worker: CfWorkerInit): FormData {
}
);

// bindings.workflows?.forEach(
// ({ name, class_name, script_name, environment, binding }) => {
// metadataBindings.push({
// name,
// type: "workflow",
// class_name: class_name,
// binding,
// ...(script_name && { script_name }),
// ...(environment && { environment }),
// });
// }
// );

bindings.queues?.forEach(({ binding, queue_name, delivery_delay }) => {
metadataBindings.push({
type: "queue",
Expand Down
9 changes: 9 additions & 0 deletions packages/wrangler/src/deployment-bundle/worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,14 @@ export interface CfDurableObject {
environment?: string;
}

export interface CfWorkflow {
name: string;
class_name: string; // not supported at the moment
binding: string;
script_name?: string;
environment?: string; // not supported at the moment
}

export interface CfQueue {
binding: string;
queue_name: string;
Expand Down Expand Up @@ -306,6 +314,7 @@ export interface CfWorkerInit {
version_metadata: CfVersionMetadataBinding | undefined;
data_blobs: CfDataBlobBindings | undefined;
durable_objects: { bindings: CfDurableObject[] } | undefined;
workflows: CfWorkflow[] | undefined;
queues: CfQueue[] | undefined;
r2_buckets: CfR2Bucket[] | undefined;
d1_databases: CfD1Database[] | undefined;
Expand Down
4 changes: 3 additions & 1 deletion packages/wrangler/src/dev.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -696,6 +696,7 @@ export async function startDev(args: StartDevOptions) {
version_metadata: args.version_metadata,
data_blobs: undefined,
durable_objects: { bindings: args.durableObjects ?? [] },
workflows: undefined,
queues: undefined,
r2_buckets: args.r2,
d1_databases: args.d1Databases,
Expand Down Expand Up @@ -1482,7 +1483,7 @@ export function getBindings(
}),
];

const bindings = {
const bindings: CfWorkerInit["bindings"] = {
// top-level fields
wasm_modules: configParam.wasm_modules,
text_blobs: configParam.text_blobs,
Expand All @@ -1501,6 +1502,7 @@ export function getBindings(
durable_objects: {
bindings: mergedDOBindings,
},
workflows: configParam.workflows,
kv_namespaces: mergedKVBindings,
queues: queuesBindings,
r2_buckets: mergedR2Bindings,
Expand Down
1 change: 1 addition & 0 deletions packages/wrangler/src/secret/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ async function createDraftWorker({
send_email: [],
vars: {},
durable_objects: { bindings: [] },
workflows: [],
queues: [],
r2_buckets: [],
d1_databases: [],
Expand Down
Loading

0 comments on commit 4041e42

Please sign in to comment.