Skip to content

Commit

Permalink
Added generic fluent builder proxy
Browse files Browse the repository at this point in the history
Signed-off-by: Jean-Baptiste Bianchi <[email protected]>
  • Loading branch information
JBBianchi committed Jul 24, 2024
1 parent 2ea82dd commit c29c6ac
Show file tree
Hide file tree
Showing 7 changed files with 142 additions and 25 deletions.
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ npm install && npm run build && npm run test
##### Version >= 4.0.0
Note: Version 4.0.0 has not been released yet.
```sh
npm i @serverless-workflow/sdk
npm i @serverlessworkflow/sdk
```


Expand All @@ -55,7 +55,7 @@ npm i @severlessworkflow/sdk-typescript
#### Create Workflow using builder API

```typescript
import { workflowBuilder, injectstateBuilder, Specification } from '@serverless-workflow/sdk';
import { workflowBuilder, injectstateBuilder, Specification } from '@serverlessworkflow/sdk';

const workflow: Specification.Workflow = workflowBuilder()
.id("helloworld")
Expand All @@ -78,7 +78,7 @@ const workflow: Specification.Workflow = workflowBuilder()
#### Create Workflow from JSON/YAML source

```typescript
import { Specification, Workflow } from '@serverless-workflow/sdk';
import { Specification, Workflow } from '@serverlessworkflow/sdk';

const source = `id: helloworld
version: '1.0'
Expand All @@ -102,7 +102,7 @@ Where `source` can be in both JSON or YAML format.
Having the following workflow instance:

```typescript
import { workflowBuilder, injectstateBuilder, Specification } from '@serverless-workflow/sdk';
import { workflowBuilder, injectstateBuilder, Specification } from '@serverlessworkflow/sdk';

const workflow: Specification.Workflow = workflowBuilder()
.id("helloworld")
Expand Down Expand Up @@ -156,7 +156,7 @@ The sdk provides a way to validate if a workflow object is compliant with the se
- `validate(): boolean`

```typescript
import {WorkflowValidator, Specification} from '@serverless-workflow/sdk';
import {WorkflowValidator, Specification} from '@serverlessworkflow/sdk';
import {Workflow} from "./workflow";

const workflow = {
Expand Down Expand Up @@ -188,7 +188,7 @@ You can also validate parts of a workflow using `validators`:

```typescript
import { ValidateFunction } from 'ajv';
import { validators, Specification } from '@serverless-workflow/sdk';
import { validators, Specification } from '@serverlessworkflow/sdk';

const injectionState: Specification.Injectstate = workflow.states[0];
const injectionStateValidator: ValidateFunction<Specification.Injectstate> = validators.get('Injectstate');
Expand Down
11 changes: 9 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"name": "@serverless-workflow/sdk",
"name": "@serverlessworkflow/sdk",
"version": "1.0.0-alpha2.0",
"schemaVersion": "1.0.0-alpha2",
"description": "Typescript SDK for Serverless Workflow Specification",
Expand Down Expand Up @@ -64,6 +64,7 @@
"rollup-plugin-terser": "^7.0.2",
"rollup-plugin-typescript2": "^0.36.0",
"shx": "^0.3.4",
"ts-inference-check": "^0.3.0",
"ts-jest": "^29.2.2",
"ts-morph": "^23.0.0",
"ts-node": "^10.9.2",
Expand Down
15 changes: 0 additions & 15 deletions src/index.ts

This file was deleted.

58 changes: 58 additions & 0 deletions src/lib/builder.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
* Copyright 2021-Present The Serverless Workflow Specification Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* oUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/

/**
* Represents a fluent builder proxy
*/
export type Builder<T> = {
build: () => T;
} & {
[K in keyof T]-?: (arg: T[K]) => Builder<T>;
};

/**
* The default function used to build an object, basically just return the provided object
* @param data The object to "build"
* @returns
*/
function defaultBuildingFn<T>(data: Partial<T>): () => T {
return () => data as T;
}

/**
* A factory for fluent builders that proxy properties assignations and can validate against schema on build()
* @param {Function} buildingFn The function used to validate and produce the object on build()
* @returns {Builder} A fluent builder
*/
export function builder<T>(buildingFn?: (data: Partial<T>) => () => T): Builder<T> {
const data: Partial<T> = {};
const proxy = new Proxy({} as Builder<T>, {
get: (_, prop) => {
if (prop === 'build') {
return (buildingFn || defaultBuildingFn)(data);
}
return (value: unknown): Builder<T> => {
(data as any)[prop.toString()] = value;
return proxy;
};
},
set: () => {
return false;
},
});
return proxy;
}
66 changes: 66 additions & 0 deletions tests/builders/builder.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/*
* Copyright 2021-Present The Serverless Workflow Specification Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* oUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/

import { Builder, builder } from '../../src/lib/builder';
import { type } from 'ts-inference-check';

type Person = {
name: string;
age: number;
friends?: Array<Person>;
[k: string]: unknown;
};

const darknessMyOldFriend = { name: 'Darkness', age: 999 };
const isPerson = (data: Partial<Person>): data is Person => !!data.name && !!data.age;
function personBuildingFn(data: Partial<Person>): () => Person {
return () => {
if (!isPerson(data)) {
throw new Error('The provided object is not a person');
}
return {
...data,
friends: [...(data.friends || []), darknessMyOldFriend],
};
};
}
const personBuilder = (): Builder<Person> => builder<Person>(personBuildingFn);

describe('builder proxy', () => {
it('should infer property types', () => {
const builder = personBuilder();
expect(type(builder.name).is<(arg: string) => Builder<Person>>(true)).toBe(true);
expect(type(builder.age).is<(arg: number) => Builder<Person>>(true)).toBe(true);
expect(type(builder.friends).is<(arg: Array<Person> | undefined) => Builder<Person>>(true)).toBe(true);
expect(type(builder.lover).is<(arg: unknown) => Builder<Person>>(true)).toBe(true);
});

it('should build', () => {
const name = 'John Doe';
const age = 42;
const friend = { name: 'Cookie Doe', age: 42 };
const lover = 'Jane Doe';
const person = personBuilder().name(name).age(age).friends([friend]).lover(lover).build();
expect(person).toBeDefined();
expect(person.name).toBe(name);
expect(person.age).toBe(age);
expect(person.friends?.length).toBe(2);
expect(person.friends?.includes(friend)).toBe(true);
expect(person.friends?.includes(darknessMyOldFriend)).toBe(true);
expect(person.lover).toBe(lover);
});
});
2 changes: 1 addition & 1 deletion tsconfig.base.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"compilerOptions": {
"target": "ES2015", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */
"target": "ES2017", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */
"module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */
"lib": [ "ES2019", "DOM" ], /* Specify library files to be included in the compilation. */
"declaration": true, /* Generates corresponding '.d.ts' file. */
Expand Down

0 comments on commit c29c6ac

Please sign in to comment.