@tsed/temporal 8.0.0 → 8.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +7 -6
- package/src/TemporalModule.ts +40 -0
- package/src/constants.ts +1 -0
- package/src/decorators/activity.spec.ts +41 -0
- package/src/decorators/activity.ts +15 -0
- package/src/decorators/temporal.spec.ts +12 -0
- package/src/decorators/temporal.ts +12 -0
- package/src/index.ts +11 -0
- package/src/interfaces/TemporalStore.ts +9 -0
- package/src/interfaces/interfaces.ts +13 -0
- package/src/services/TemporalFactory.ts +56 -0
- package/src/utils/worker.ts +45 -0
package/package.json
CHANGED
|
@@ -2,13 +2,14 @@
|
|
|
2
2
|
"name": "@tsed/temporal",
|
|
3
3
|
"description": "Temporal.io package for Ts.ED framework",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"version": "8.0.
|
|
5
|
+
"version": "8.0.2",
|
|
6
6
|
"source": "./src/index.ts",
|
|
7
7
|
"main": "./lib/esm/index.js",
|
|
8
8
|
"module": "./lib/esm/index.js",
|
|
9
9
|
"typings": "./lib/types/index.d.ts",
|
|
10
10
|
"exports": {
|
|
11
11
|
".": {
|
|
12
|
+
"@tsed/source": "./src/index.ts",
|
|
12
13
|
"types": "./lib/types/index.d.ts",
|
|
13
14
|
"import": "./lib/esm/index.js",
|
|
14
15
|
"default": "./lib/esm/index.js"
|
|
@@ -28,11 +29,11 @@
|
|
|
28
29
|
"@temporalio/client": "1.8.4",
|
|
29
30
|
"@temporalio/testing": "1.8.4",
|
|
30
31
|
"@temporalio/worker": "1.8.4",
|
|
31
|
-
"@tsed/barrels": "8.0.
|
|
32
|
-
"@tsed/core": "8.0.
|
|
33
|
-
"@tsed/di": "8.0.
|
|
34
|
-
"@tsed/platform-http": "8.0.
|
|
35
|
-
"@tsed/typescript": "8.0.
|
|
32
|
+
"@tsed/barrels": "8.0.2",
|
|
33
|
+
"@tsed/core": "8.0.2",
|
|
34
|
+
"@tsed/di": "8.0.2",
|
|
35
|
+
"@tsed/platform-http": "8.0.2",
|
|
36
|
+
"@tsed/typescript": "8.0.2",
|
|
36
37
|
"cross-env": "7.0.3",
|
|
37
38
|
"eslint": "9.12.0",
|
|
38
39
|
"typescript": "5.4.5",
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import {classOf} from "@tsed/core";
|
|
2
|
+
import {Inject, InjectorService, Module, Provider} from "@tsed/di";
|
|
3
|
+
import {Logger} from "@tsed/logger";
|
|
4
|
+
|
|
5
|
+
import {PROVIDER_TYPE_TEMPORAL} from "./constants.js";
|
|
6
|
+
import {TEMPORAL_STORE_KEY, TemporalStore} from "./interfaces/TemporalStore.js";
|
|
7
|
+
import {TemporalClient} from "./services/TemporalFactory.js";
|
|
8
|
+
|
|
9
|
+
@Module()
|
|
10
|
+
export class TemporalModule {
|
|
11
|
+
@Inject()
|
|
12
|
+
protected logger!: Logger;
|
|
13
|
+
|
|
14
|
+
@Inject()
|
|
15
|
+
protected injector!: InjectorService;
|
|
16
|
+
|
|
17
|
+
@Inject()
|
|
18
|
+
protected client!: TemporalClient;
|
|
19
|
+
|
|
20
|
+
constructor(@Inject(PROVIDER_TYPE_TEMPORAL) private temporalServices: any[]) {}
|
|
21
|
+
|
|
22
|
+
public getActivities(): object {
|
|
23
|
+
return this.temporalServices.reduce((activities, instance) => Object.assign(activities, this.getActivitiesFromInstance(instance)), {});
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
protected getActivitiesFromInstance(instance: any) {
|
|
27
|
+
const provider = this.injector.getProvider(classOf(instance))!;
|
|
28
|
+
const store = provider.store.get<TemporalStore>(TEMPORAL_STORE_KEY, {});
|
|
29
|
+
|
|
30
|
+
return Object.entries(store.activities || {}).reduce((activities, [propertyKey, {name}]) => {
|
|
31
|
+
const jobProcessor = instance[propertyKey].bind(instance);
|
|
32
|
+
const jobName = name || propertyKey;
|
|
33
|
+
|
|
34
|
+
return {
|
|
35
|
+
...activities,
|
|
36
|
+
[jobName]: jobProcessor
|
|
37
|
+
};
|
|
38
|
+
}, {});
|
|
39
|
+
}
|
|
40
|
+
}
|
package/src/constants.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const PROVIDER_TYPE_TEMPORAL = "PROVIDER_TYPE_TEMPORAL";
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import {Store} from "@tsed/core";
|
|
2
|
+
|
|
3
|
+
import {Activity} from "./activity.js";
|
|
4
|
+
import {Temporal} from "./temporal.js";
|
|
5
|
+
|
|
6
|
+
describe("@Activity()", () => {
|
|
7
|
+
it("should set metadata", () => {
|
|
8
|
+
@Temporal()
|
|
9
|
+
class Test {
|
|
10
|
+
@Activity()
|
|
11
|
+
testActivityDecorator() {
|
|
12
|
+
// test
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const store = Store.from(Test);
|
|
17
|
+
|
|
18
|
+
expect(store.get("temporal").activities).toEqual({
|
|
19
|
+
testActivityDecorator: {}
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it("should set options metadata", () => {
|
|
24
|
+
@Temporal()
|
|
25
|
+
class Test {
|
|
26
|
+
@Activity({
|
|
27
|
+
name: "testActivityDecoratorCustomName"
|
|
28
|
+
})
|
|
29
|
+
test() {
|
|
30
|
+
// test
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const store = Store.from(Test);
|
|
35
|
+
expect(store.get("temporal").activities).toEqual({
|
|
36
|
+
test: {
|
|
37
|
+
name: "testActivityDecoratorCustomName"
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
});
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import {Store} from "@tsed/core";
|
|
2
|
+
|
|
3
|
+
import {ActivityOptions, TEMPORAL_STORE_KEY, TemporalStore} from "../interfaces/TemporalStore.js";
|
|
4
|
+
|
|
5
|
+
export function Activity(options: ActivityOptions = {}): MethodDecorator {
|
|
6
|
+
return (target: any, propertyKey: string | symbol) => {
|
|
7
|
+
const store: TemporalStore = {
|
|
8
|
+
activities: {
|
|
9
|
+
[propertyKey]: options
|
|
10
|
+
}
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
Store.from(target).merge(TEMPORAL_STORE_KEY, store);
|
|
14
|
+
};
|
|
15
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import {GlobalProviders} from "@tsed/di";
|
|
2
|
+
|
|
3
|
+
import {Temporal} from "./temporal.js";
|
|
4
|
+
|
|
5
|
+
describe("@Activity()", () => {
|
|
6
|
+
it("should set metadata", () => {
|
|
7
|
+
@Temporal()
|
|
8
|
+
class Test {}
|
|
9
|
+
|
|
10
|
+
expect(GlobalProviders.get(Test)?.useClass).toEqual(Test);
|
|
11
|
+
});
|
|
12
|
+
});
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import {useDecorators} from "@tsed/core";
|
|
2
|
+
import {Injectable} from "@tsed/di";
|
|
3
|
+
|
|
4
|
+
import {PROVIDER_TYPE_TEMPORAL} from "../constants.js";
|
|
5
|
+
|
|
6
|
+
export function Temporal(): ClassDecorator {
|
|
7
|
+
return useDecorators(
|
|
8
|
+
Injectable({
|
|
9
|
+
type: PROVIDER_TYPE_TEMPORAL
|
|
10
|
+
})
|
|
11
|
+
);
|
|
12
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Automatically generated by @tsed/barrels.
|
|
3
|
+
*/
|
|
4
|
+
export * from "./constants.js";
|
|
5
|
+
export * from "./decorators/activity.js";
|
|
6
|
+
export * from "./decorators/temporal.js";
|
|
7
|
+
export * from "./interfaces/interfaces.js";
|
|
8
|
+
export * from "./interfaces/TemporalStore.js";
|
|
9
|
+
export * from "./services/TemporalFactory.js";
|
|
10
|
+
export * from "./TemporalModule.js";
|
|
11
|
+
export * from "./utils/worker.js";
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import {ClientOptions, ConnectionOptions} from "@temporalio/client";
|
|
2
|
+
|
|
3
|
+
declare global {
|
|
4
|
+
namespace TsED {
|
|
5
|
+
interface Configuration {
|
|
6
|
+
temporal?: {
|
|
7
|
+
enabled?: boolean;
|
|
8
|
+
connection?: ConnectionOptions;
|
|
9
|
+
client?: Omit<ClientOptions, "connection">;
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import {Client, Connection} from "@temporalio/client";
|
|
2
|
+
import {Configuration, registerProvider} from "@tsed/di";
|
|
3
|
+
import {Logger} from "@tsed/logger";
|
|
4
|
+
|
|
5
|
+
export const TemporalConnection = Connection;
|
|
6
|
+
export type TemporalConnection = Connection;
|
|
7
|
+
|
|
8
|
+
export const TemporalClient = Client;
|
|
9
|
+
export type TemporalClient = Client;
|
|
10
|
+
|
|
11
|
+
registerProvider({
|
|
12
|
+
token: TemporalConnection,
|
|
13
|
+
deps: [Configuration, Logger],
|
|
14
|
+
async useAsyncFactory(settings: Configuration, logger: Logger) {
|
|
15
|
+
const temporal = settings.get("temporal");
|
|
16
|
+
|
|
17
|
+
if (!temporal?.enabled) {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
try {
|
|
22
|
+
logger.info("Connecting to Temporal Server: ", temporal.connection?.address || "default");
|
|
23
|
+
const connection = await Connection.connect(temporal.connection);
|
|
24
|
+
logger.info("... connected to Temporal Server: ", temporal.connection?.address || "default");
|
|
25
|
+
return connection;
|
|
26
|
+
} catch (error) {
|
|
27
|
+
logger.error("Failed to connect to Temporal Server:", error);
|
|
28
|
+
throw error;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
registerProvider({
|
|
34
|
+
token: TemporalClient,
|
|
35
|
+
deps: [Configuration, TemporalConnection],
|
|
36
|
+
useFactory(settings: Configuration, connection: TemporalConnection) {
|
|
37
|
+
const temporal = settings.get("temporal");
|
|
38
|
+
if (!temporal?.enabled) {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const client = new TemporalClient({
|
|
43
|
+
connection,
|
|
44
|
+
...temporal.client
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
return client;
|
|
48
|
+
},
|
|
49
|
+
hooks: {
|
|
50
|
+
$onDestroy: async (client: Client | null) => {
|
|
51
|
+
if (client) {
|
|
52
|
+
await client.connection.close();
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
});
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import {NativeConnection, NativeConnectionOptions, Worker, WorkerOptions} from "@temporalio/worker";
|
|
2
|
+
import {$log} from "@tsed/logger";
|
|
3
|
+
import {PlatformBuilderSettings} from "@tsed/platform-http";
|
|
4
|
+
import {PlatformTest} from "@tsed/platform-http/testing";
|
|
5
|
+
|
|
6
|
+
import {TemporalModule} from "../TemporalModule.js";
|
|
7
|
+
|
|
8
|
+
type BootstrapWorkerOptions = {
|
|
9
|
+
platform?: PlatformBuilderSettings;
|
|
10
|
+
worker: Omit<WorkerOptions, "activities">;
|
|
11
|
+
connection?: NativeConnection | NativeConnectionOptions;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export async function bootstrapWorker(mod: any, settings: BootstrapWorkerOptions) {
|
|
15
|
+
// TODO remove this code and use production
|
|
16
|
+
await PlatformTest.bootstrap(mod, settings.platform)();
|
|
17
|
+
const temporalioModule = PlatformTest.get<TemporalModule>(TemporalModule);
|
|
18
|
+
const activities = temporalioModule.getActivities();
|
|
19
|
+
$log.info('Starting temporal worker with queue "%s" and activities:', settings.worker.taskQueue);
|
|
20
|
+
|
|
21
|
+
if (Object.keys(activities).length > 0) {
|
|
22
|
+
$log.printTable(
|
|
23
|
+
Object.entries(activities).map((x) => ({
|
|
24
|
+
name: x[0],
|
|
25
|
+
activity: x[1].toString()
|
|
26
|
+
}))
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
let connection: NativeConnection;
|
|
31
|
+
if (settings.connection instanceof NativeConnection) {
|
|
32
|
+
connection = settings.connection;
|
|
33
|
+
} else {
|
|
34
|
+
$log.info("Connecting to Temporal Server: ", settings.connection?.address || "default");
|
|
35
|
+
connection = await NativeConnection.connect(settings.connection);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const worker = await Worker.create({
|
|
39
|
+
connection,
|
|
40
|
+
...settings.worker,
|
|
41
|
+
activities
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
return worker;
|
|
45
|
+
}
|