@webiny/api-scheduler-aws 0.0.0-unstable.9f53ea597d
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/LICENSE +21 -0
- package/README.md +11 -0
- package/context.d.ts +7 -0
- package/context.js +25 -0
- package/context.js.map +1 -0
- package/createEventHandler.d.ts +15 -0
- package/createEventHandler.js +47 -0
- package/createEventHandler.js.map +1 -0
- package/features/SchedulerService/EventBridgeSchedulerService.d.ts +19 -0
- package/features/SchedulerService/EventBridgeSchedulerService.js +108 -0
- package/features/SchedulerService/EventBridgeSchedulerService.js.map +1 -0
- package/index.d.ts +2 -0
- package/index.js +1 -0
- package/manifest.d.ts +13 -0
- package/manifest.js +34 -0
- package/manifest.js.map +1 -0
- package/package.json +50 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) Webiny
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# @webiny/api-scheduler-aws
|
|
2
|
+
|
|
3
|
+
> [!NOTE]
|
|
4
|
+
> This package is part of the [Webiny](https://www.webiny.com) monorepo.
|
|
5
|
+
> It’s **included in every Webiny project by default** and is not meant to be used as a standalone package.
|
|
6
|
+
|
|
7
|
+
📘 **Documentation:** [https://www.webiny.com/docs](https://www.webiny.com/docs)
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
_This README file is automatically generated during the publish process._
|
package/context.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { SchedulerClient, SchedulerClientConfig } from "@webiny/aws-sdk/client-scheduler/index.js";
|
|
2
|
+
import type { CmsContext } from "@webiny/api-headless-cms/types/index.js";
|
|
3
|
+
import { ContextPlugin } from "@webiny/api";
|
|
4
|
+
export interface IRegisterSchedulerAwsExtensionParams {
|
|
5
|
+
getClient(config?: SchedulerClientConfig): Pick<SchedulerClient, "send">;
|
|
6
|
+
}
|
|
7
|
+
export declare const registerSchedulerAwsExtension: (params: IRegisterSchedulerAwsExtensionParams) => ContextPlugin<CmsContext>;
|
package/context.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { getManifest } from "./manifest.js";
|
|
2
|
+
import { SchedulerService } from "@webiny/api-scheduler/shared/abstractions.js";
|
|
3
|
+
import { EventBridgeSchedulerService } from "./features/SchedulerService/EventBridgeSchedulerService.js";
|
|
4
|
+
import { VoidSchedulerService } from "@webiny/api-scheduler/features/SchedulerService/VoidSchedulerService.js";
|
|
5
|
+
import { TenantContext } from "@webiny/api-core/features/tenancy/TenantContext/index.js";
|
|
6
|
+
import { ContextPlugin } from "@webiny/api";
|
|
7
|
+
import { createScheduledActionEventHandler } from "./createEventHandler.js";
|
|
8
|
+
const registerSchedulerAwsExtension = (params)=>{
|
|
9
|
+
const plugin = new ContextPlugin(async (context)=>{
|
|
10
|
+
context.plugins.register(createScheduledActionEventHandler());
|
|
11
|
+
const tenantContext = context.container.resolve(TenantContext);
|
|
12
|
+
if (!tenantContext.getTenant()) return;
|
|
13
|
+
const manifest = await getManifest();
|
|
14
|
+
if (manifest.error) context.container.registerInstance(SchedulerService, new VoidSchedulerService());
|
|
15
|
+
else context.container.registerInstance(SchedulerService, new EventBridgeSchedulerService(params.getClient, {
|
|
16
|
+
lambdaArn: manifest.data.lambdaArn,
|
|
17
|
+
roleArn: manifest.data.roleArn
|
|
18
|
+
}));
|
|
19
|
+
});
|
|
20
|
+
plugin.name = "scheduler.aws.extension";
|
|
21
|
+
return plugin;
|
|
22
|
+
};
|
|
23
|
+
export { registerSchedulerAwsExtension };
|
|
24
|
+
|
|
25
|
+
//# sourceMappingURL=context.js.map
|
package/context.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.js","sources":["../src/context.ts"],"sourcesContent":["import type {\n SchedulerClient,\n SchedulerClientConfig\n} from \"@webiny/aws-sdk/client-scheduler/index.js\";\nimport { getManifest } from \"~/manifest.js\";\nimport type { CmsContext } from \"@webiny/api-headless-cms/types/index.js\";\nimport { SchedulerService } from \"@webiny/api-scheduler/shared/abstractions.js\";\nimport { EventBridgeSchedulerService } from \"~/features/SchedulerService/EventBridgeSchedulerService.js\";\nimport { VoidSchedulerService } from \"@webiny/api-scheduler/features/SchedulerService/VoidSchedulerService.js\";\nimport { TenantContext } from \"@webiny/api-core/features/tenancy/TenantContext/index.js\";\nimport { ContextPlugin } from \"@webiny/api\";\nimport { createScheduledActionEventHandler } from \"~/createEventHandler.js\";\n\nexport interface IRegisterSchedulerAwsExtensionParams {\n getClient(config?: SchedulerClientConfig): Pick<SchedulerClient, \"send\">;\n}\n\nexport const registerSchedulerAwsExtension = (params: IRegisterSchedulerAwsExtensionParams) => {\n const plugin = new ContextPlugin<CmsContext>(async context => {\n context.plugins.register(createScheduledActionEventHandler());\n const tenantContext = context.container.resolve(TenantContext);\n\n if (!tenantContext.getTenant()) {\n return;\n }\n\n const manifest = await getManifest();\n\n if (manifest.error) {\n context.container.registerInstance(SchedulerService, new VoidSchedulerService());\n } else {\n context.container.registerInstance(\n SchedulerService,\n new EventBridgeSchedulerService(params.getClient, {\n lambdaArn: manifest.data.lambdaArn,\n roleArn: manifest.data.roleArn\n })\n );\n }\n });\n\n plugin.name = \"scheduler.aws.extension\";\n\n return plugin;\n};\n"],"names":["registerSchedulerAwsExtension","params","plugin","ContextPlugin","context","createScheduledActionEventHandler","tenantContext","TenantContext","manifest","getManifest","SchedulerService","VoidSchedulerService","EventBridgeSchedulerService"],"mappings":";;;;;;;AAiBO,MAAMA,gCAAgC,CAACC;IAC1C,MAAMC,SAAS,IAAIC,cAA0B,OAAMC;QAC/CA,QAAQ,OAAO,CAAC,QAAQ,CAACC;QACzB,MAAMC,gBAAgBF,QAAQ,SAAS,CAAC,OAAO,CAACG;QAEhD,IAAI,CAACD,cAAc,SAAS,IACxB;QAGJ,MAAME,WAAW,MAAMC;QAEvB,IAAID,SAAS,KAAK,EACdJ,QAAQ,SAAS,CAAC,gBAAgB,CAACM,kBAAkB,IAAIC;aAEzDP,QAAQ,SAAS,CAAC,gBAAgB,CAC9BM,kBACA,IAAIE,4BAA4BX,OAAO,SAAS,EAAE;YAC9C,WAAWO,SAAS,IAAI,CAAC,SAAS;YAClC,SAASA,SAAS,IAAI,CAAC,OAAO;QAClC;IAGZ;IAEAN,OAAO,IAAI,GAAG;IAEd,OAAOA;AACX"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { HandlerFactoryParams } from "@webiny/handler-aws/types.js";
|
|
2
|
+
import { SCHEDULED_ACTION_EVENT_IDENTIFIER } from "@webiny/api-scheduler/constants.js";
|
|
3
|
+
export interface IScheduledActionEventPayload {
|
|
4
|
+
namespace: string;
|
|
5
|
+
id: string;
|
|
6
|
+
scheduleFor: string;
|
|
7
|
+
tenant: string;
|
|
8
|
+
}
|
|
9
|
+
export interface IScheduledActionEvent {
|
|
10
|
+
[SCHEDULED_ACTION_EVENT_IDENTIFIER]: IScheduledActionEventPayload;
|
|
11
|
+
}
|
|
12
|
+
export interface HandlerParams extends HandlerFactoryParams {
|
|
13
|
+
debug?: boolean;
|
|
14
|
+
}
|
|
15
|
+
export declare const createScheduledActionEventHandler: () => import("@webiny/handler-aws/raw/index.js").RawEventHandler<IScheduledActionEvent, import("@webiny/handler/types").Context, any>;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { registry } from "@webiny/handler-aws/registry.js";
|
|
2
|
+
import { createSourceHandler } from "@webiny/handler-aws/sourceHandler.js";
|
|
3
|
+
import { createEventHandler, createHandler } from "@webiny/handler-aws/raw/index.js";
|
|
4
|
+
import { SCHEDULED_ACTION_EVENT_IDENTIFIER } from "@webiny/api-scheduler/constants.js";
|
|
5
|
+
import { ExecuteScheduledActionUseCase } from "@webiny/api-scheduler/features/ExecuteScheduledAction/index.js";
|
|
6
|
+
import { TenantContext } from "@webiny/api-core/features/tenancy/TenantContext/index.js";
|
|
7
|
+
import { GetTenantByIdUseCase } from "@webiny/api-core/exports/api/tenancy.js";
|
|
8
|
+
const canHandle = (event)=>{
|
|
9
|
+
if ("function" != typeof event?.hasOwnProperty) return false;
|
|
10
|
+
if (!event.hasOwnProperty(SCHEDULED_ACTION_EVENT_IDENTIFIER)) return false;
|
|
11
|
+
const value = event[SCHEDULED_ACTION_EVENT_IDENTIFIER];
|
|
12
|
+
return !!(value?.id && value?.scheduleFor);
|
|
13
|
+
};
|
|
14
|
+
const handler = createSourceHandler({
|
|
15
|
+
name: "handler-aws-event-bridge-scheduled-cms-action-event",
|
|
16
|
+
canUse: canHandle,
|
|
17
|
+
handle: async ({ params, event, context })=>createHandler(params)(event, context)
|
|
18
|
+
});
|
|
19
|
+
registry.register(handler);
|
|
20
|
+
const createScheduledActionEventHandler = ()=>createEventHandler({
|
|
21
|
+
canHandle: canHandle,
|
|
22
|
+
handle: async (params)=>{
|
|
23
|
+
const { payload, context } = params;
|
|
24
|
+
const input = payload[SCHEDULED_ACTION_EVENT_IDENTIFIER];
|
|
25
|
+
const tenantContext = context.container.resolve(TenantContext);
|
|
26
|
+
if (!input.tenant) input.tenant = tenantContext.getTenant().id;
|
|
27
|
+
const getTenantByIdUseCase = context.container.resolve(GetTenantByIdUseCase);
|
|
28
|
+
const tenantResult = await getTenantByIdUseCase.execute(input.tenant);
|
|
29
|
+
if (tenantResult.isFail()) throw tenantResult.error;
|
|
30
|
+
const tenant = tenantResult.value;
|
|
31
|
+
return tenantContext.withTenant(tenant, async ()=>{
|
|
32
|
+
const executeScheduledAction = context.container.resolve(ExecuteScheduledActionUseCase);
|
|
33
|
+
const result = await executeScheduledAction.execute(input);
|
|
34
|
+
if (result.isFail()) {
|
|
35
|
+
const error = result.error;
|
|
36
|
+
console.error(error.code, error.message);
|
|
37
|
+
throw error;
|
|
38
|
+
}
|
|
39
|
+
return {
|
|
40
|
+
success: true
|
|
41
|
+
};
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
export { createScheduledActionEventHandler };
|
|
46
|
+
|
|
47
|
+
//# sourceMappingURL=createEventHandler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"createEventHandler.js","sources":["../src/createEventHandler.ts"],"sourcesContent":["import { registry } from \"@webiny/handler-aws/registry.js\";\nimport type { HandlerFactoryParams } from \"@webiny/handler-aws/types.js\";\nimport { createSourceHandler } from \"@webiny/handler-aws/sourceHandler.js\";\nimport { createEventHandler, createHandler } from \"@webiny/handler-aws/raw/index.js\";\nimport { SCHEDULED_ACTION_EVENT_IDENTIFIER } from \"@webiny/api-scheduler/constants.js\";\nimport { ExecuteScheduledActionUseCase } from \"@webiny/api-scheduler/features/ExecuteScheduledAction/index.js\";\nimport { TenantContext } from \"@webiny/api-core/features/tenancy/TenantContext/index.js\";\nimport { GetTenantByIdUseCase } from \"@webiny/api-core/exports/api/tenancy.js\";\n\nexport interface IScheduledActionEventPayload {\n namespace: string;\n id: string;\n scheduleFor: string;\n tenant: string;\n}\n\nexport interface IScheduledActionEvent {\n [SCHEDULED_ACTION_EVENT_IDENTIFIER]: IScheduledActionEventPayload;\n}\n\nexport interface HandlerParams extends HandlerFactoryParams {\n debug?: boolean;\n}\n\nconst canHandle = (event: Partial<IScheduledActionEvent>): boolean => {\n if (typeof event?.hasOwnProperty !== \"function\") {\n return false;\n } else if (!event.hasOwnProperty(SCHEDULED_ACTION_EVENT_IDENTIFIER)) {\n return false;\n }\n\n const value = event[SCHEDULED_ACTION_EVENT_IDENTIFIER];\n return !!(value?.id && value?.scheduleFor);\n};\n\nconst handler = createSourceHandler<IScheduledActionEvent, HandlerParams>({\n name: \"handler-aws-event-bridge-scheduled-cms-action-event\",\n canUse: canHandle,\n handle: async ({ params, event, context }) => {\n return createHandler(params)(event, context);\n }\n});\n\nregistry.register(handler);\n\nexport const createScheduledActionEventHandler = () => {\n return createEventHandler<IScheduledActionEvent>({\n canHandle,\n handle: async params => {\n const { payload, context } = params;\n const input = payload[SCHEDULED_ACTION_EVENT_IDENTIFIER];\n\n const tenantContext = context.container.resolve(TenantContext);\n /**\n * Use default tenant if tenant is not provided in the payload.\n * This allows backward compatibility with existing scheduled actions that do not have tenant information in their payload.\n */\n if (!input.tenant) {\n input.tenant = tenantContext.getTenant().id;\n }\n const getTenantByIdUseCase = context.container.resolve(GetTenantByIdUseCase);\n const tenantResult = await getTenantByIdUseCase.execute(input.tenant);\n if (tenantResult.isFail()) {\n throw tenantResult.error;\n }\n const tenant = tenantResult.value;\n\n return tenantContext.withTenant(tenant, async () => {\n const executeScheduledAction = context.container.resolve(\n ExecuteScheduledActionUseCase\n );\n const result = await executeScheduledAction.execute(input);\n\n if (result.isFail()) {\n const error = result.error;\n console.error(error.code, error.message);\n throw error;\n }\n return {\n success: true\n };\n });\n }\n });\n};\n"],"names":["canHandle","event","SCHEDULED_ACTION_EVENT_IDENTIFIER","value","handler","createSourceHandler","params","context","createHandler","registry","createScheduledActionEventHandler","createEventHandler","payload","input","tenantContext","TenantContext","getTenantByIdUseCase","GetTenantByIdUseCase","tenantResult","tenant","executeScheduledAction","ExecuteScheduledActionUseCase","result","error","console"],"mappings":";;;;;;;AAwBA,MAAMA,YAAY,CAACC;IACf,IAAI,AAAiC,cAAjC,OAAOA,OAAO,gBACd,OAAO;IACJ,IAAI,CAACA,MAAM,cAAc,CAACC,oCAC7B,OAAO;IAGX,MAAMC,QAAQF,KAAK,CAACC,kCAAkC;IACtD,OAAO,CAAC,CAAEC,CAAAA,OAAO,MAAMA,OAAO,WAAU;AAC5C;AAEA,MAAMC,UAAUC,oBAA0D;IACtE,MAAM;IACN,QAAQL;IACR,QAAQ,OAAO,EAAEM,MAAM,EAAEL,KAAK,EAAEM,OAAO,EAAE,GAC9BC,cAAcF,QAAQL,OAAOM;AAE5C;AAEAE,SAAS,QAAQ,CAACL;AAEX,MAAMM,oCAAoC,IACtCC,mBAA0C;QAC7CX,WAAAA;QACA,QAAQ,OAAMM;YACV,MAAM,EAAEM,OAAO,EAAEL,OAAO,EAAE,GAAGD;YAC7B,MAAMO,QAAQD,OAAO,CAACV,kCAAkC;YAExD,MAAMY,gBAAgBP,QAAQ,SAAS,CAAC,OAAO,CAACQ;YAKhD,IAAI,CAACF,MAAM,MAAM,EACbA,MAAM,MAAM,GAAGC,cAAc,SAAS,GAAG,EAAE;YAE/C,MAAME,uBAAuBT,QAAQ,SAAS,CAAC,OAAO,CAACU;YACvD,MAAMC,eAAe,MAAMF,qBAAqB,OAAO,CAACH,MAAM,MAAM;YACpE,IAAIK,aAAa,MAAM,IACnB,MAAMA,aAAa,KAAK;YAE5B,MAAMC,SAASD,aAAa,KAAK;YAEjC,OAAOJ,cAAc,UAAU,CAACK,QAAQ;gBACpC,MAAMC,yBAAyBb,QAAQ,SAAS,CAAC,OAAO,CACpDc;gBAEJ,MAAMC,SAAS,MAAMF,uBAAuB,OAAO,CAACP;gBAEpD,IAAIS,OAAO,MAAM,IAAI;oBACjB,MAAMC,QAAQD,OAAO,KAAK;oBAC1BE,QAAQ,KAAK,CAACD,MAAM,IAAI,EAAEA,MAAM,OAAO;oBACvC,MAAMA;gBACV;gBACA,OAAO;oBACH,SAAS;gBACb;YACJ;QACJ;IACJ"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { SchedulerService } from "@webiny/api-scheduler/shared/abstractions.js";
|
|
2
|
+
import { type SchedulerClient } from "@webiny/aws-sdk/client-scheduler/index.js";
|
|
3
|
+
export interface ISchedulerConfig {
|
|
4
|
+
lambdaArn: string;
|
|
5
|
+
roleArn: string;
|
|
6
|
+
}
|
|
7
|
+
export declare class EventBridgeSchedulerService implements SchedulerService.Interface {
|
|
8
|
+
private getClient;
|
|
9
|
+
private config;
|
|
10
|
+
constructor(getClient: (config?: any) => Pick<SchedulerClient, "send">, config: ISchedulerConfig);
|
|
11
|
+
create(params: SchedulerService.CreateParams): Promise<void>;
|
|
12
|
+
update(params: SchedulerService.UpdateParams): Promise<void>;
|
|
13
|
+
delete(params: SchedulerService.DeleteParams): Promise<void>;
|
|
14
|
+
exists(params: SchedulerService.ExistsParams): Promise<boolean>;
|
|
15
|
+
private createScheduleExpression;
|
|
16
|
+
private createScheduledActionEventInput;
|
|
17
|
+
private createScheduledActionEventPayload;
|
|
18
|
+
private createScheduleName;
|
|
19
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { WebinyError } from "@webiny/error";
|
|
2
|
+
import { CreateScheduleCommand, DeleteScheduleCommand, GetScheduleCommand, UpdateScheduleCommand } from "@webiny/aws-sdk/client-scheduler/index.js";
|
|
3
|
+
import { SCHEDULED_ACTION_EVENT_IDENTIFIER } from "@webiny/api-scheduler/constants.js";
|
|
4
|
+
class EventBridgeSchedulerService {
|
|
5
|
+
constructor(getClient, config){
|
|
6
|
+
this.getClient = getClient;
|
|
7
|
+
this.config = config;
|
|
8
|
+
}
|
|
9
|
+
async create(params) {
|
|
10
|
+
const { id, scheduleFor, tenant, namespace } = params;
|
|
11
|
+
if (scheduleFor <= new Date()) throw new WebinyError(`Cannot create a schedule for "${id}" with date in the past`, "INVALID_SCHEDULE_DATE", {
|
|
12
|
+
scheduleFor,
|
|
13
|
+
tenant,
|
|
14
|
+
id
|
|
15
|
+
});
|
|
16
|
+
const client = this.getClient();
|
|
17
|
+
const exists = await this.exists({
|
|
18
|
+
id,
|
|
19
|
+
tenant,
|
|
20
|
+
namespace
|
|
21
|
+
});
|
|
22
|
+
if (exists) return this.update(params);
|
|
23
|
+
await client.send(new CreateScheduleCommand({
|
|
24
|
+
Name: this.createScheduleName(params),
|
|
25
|
+
ScheduleExpression: this.createScheduleExpression(scheduleFor),
|
|
26
|
+
FlexibleTimeWindow: {
|
|
27
|
+
Mode: "OFF"
|
|
28
|
+
},
|
|
29
|
+
Target: {
|
|
30
|
+
Arn: this.config.lambdaArn,
|
|
31
|
+
RoleArn: this.config.roleArn,
|
|
32
|
+
Input: this.createScheduledActionEventInput(params)
|
|
33
|
+
},
|
|
34
|
+
ActionAfterCompletion: "DELETE"
|
|
35
|
+
}));
|
|
36
|
+
}
|
|
37
|
+
async update(params) {
|
|
38
|
+
const { id, scheduleFor } = params;
|
|
39
|
+
if (scheduleFor <= new Date()) throw new WebinyError(`Cannot update an existing schedule for "${id}" with date in the past`, "INVALID_SCHEDULE_DATE", {
|
|
40
|
+
scheduleFor,
|
|
41
|
+
id
|
|
42
|
+
});
|
|
43
|
+
const client = this.getClient();
|
|
44
|
+
await client.send(new UpdateScheduleCommand({
|
|
45
|
+
Name: this.createScheduleName(params),
|
|
46
|
+
ScheduleExpression: this.createScheduleExpression(scheduleFor),
|
|
47
|
+
FlexibleTimeWindow: {
|
|
48
|
+
Mode: "OFF"
|
|
49
|
+
},
|
|
50
|
+
Target: {
|
|
51
|
+
Arn: this.config.lambdaArn,
|
|
52
|
+
RoleArn: this.config.roleArn,
|
|
53
|
+
Input: this.createScheduledActionEventInput(params)
|
|
54
|
+
},
|
|
55
|
+
ActionAfterCompletion: "DELETE"
|
|
56
|
+
}));
|
|
57
|
+
}
|
|
58
|
+
async delete(params) {
|
|
59
|
+
const client = this.getClient();
|
|
60
|
+
const exists = await this.exists(params);
|
|
61
|
+
if (!exists) throw new WebinyError(`Cannot delete schedule "${params.id}", tenant "${params.tenant}", because it does not exist.`);
|
|
62
|
+
const name = this.createScheduleName(params);
|
|
63
|
+
try {
|
|
64
|
+
await client.send(new DeleteScheduleCommand({
|
|
65
|
+
Name: name
|
|
66
|
+
}));
|
|
67
|
+
} catch (ex) {
|
|
68
|
+
if ("ResourceNotFoundException" === ex.name) return;
|
|
69
|
+
throw WebinyError.from(ex);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
async exists(params) {
|
|
73
|
+
const client = this.getClient();
|
|
74
|
+
const name = this.createScheduleName(params);
|
|
75
|
+
try {
|
|
76
|
+
await client.send(new GetScheduleCommand({
|
|
77
|
+
Name: name
|
|
78
|
+
}));
|
|
79
|
+
return true;
|
|
80
|
+
} catch (ex) {
|
|
81
|
+
if ("ResourceNotFoundException" === ex.name) return false;
|
|
82
|
+
throw ex;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
createScheduleExpression(scheduleFor) {
|
|
86
|
+
return `at(${scheduleFor.toISOString().replace(/\.\d{3}Z$/, "")})`;
|
|
87
|
+
}
|
|
88
|
+
createScheduledActionEventInput(params) {
|
|
89
|
+
return JSON.stringify({
|
|
90
|
+
[SCHEDULED_ACTION_EVENT_IDENTIFIER]: this.createScheduledActionEventPayload(params)
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
createScheduledActionEventPayload(params) {
|
|
94
|
+
const { id, scheduleFor, tenant, namespace } = params;
|
|
95
|
+
return {
|
|
96
|
+
id,
|
|
97
|
+
tenant,
|
|
98
|
+
namespace,
|
|
99
|
+
scheduleFor: scheduleFor.toISOString()
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
createScheduleName(params) {
|
|
103
|
+
return params.id;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
export { EventBridgeSchedulerService };
|
|
107
|
+
|
|
108
|
+
//# sourceMappingURL=EventBridgeSchedulerService.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"features/SchedulerService/EventBridgeSchedulerService.js","sources":["../../../src/features/SchedulerService/EventBridgeSchedulerService.ts"],"sourcesContent":["import { WebinyError } from \"@webiny/error\";\nimport {\n type ISchedulerServiceCreateParams,\n type ISchedulerServiceUpdateParams,\n SchedulerService\n} from \"@webiny/api-scheduler/shared/abstractions.js\";\nimport {\n CreateScheduleCommand,\n DeleteScheduleCommand,\n GetScheduleCommand,\n type SchedulerClient,\n UpdateScheduleCommand\n} from \"@webiny/aws-sdk/client-scheduler/index.js\";\nimport { SCHEDULED_ACTION_EVENT_IDENTIFIER } from \"@webiny/api-scheduler/constants.js\";\nimport type { IScheduledActionEventPayload } from \"~/createEventHandler.js\";\n\nexport interface ISchedulerConfig {\n lambdaArn: string;\n roleArn: string;\n}\n\nexport class EventBridgeSchedulerService implements SchedulerService.Interface {\n public constructor(\n private getClient: (config?: any) => Pick<SchedulerClient, \"send\">,\n private config: ISchedulerConfig\n ) {}\n\n public async create(params: SchedulerService.CreateParams): Promise<void> {\n const { id, scheduleFor, tenant, namespace } = params;\n\n if (scheduleFor <= new Date()) {\n throw new WebinyError(\n `Cannot create a schedule for \"${id}\" with date in the past`,\n \"INVALID_SCHEDULE_DATE\",\n {\n scheduleFor,\n tenant,\n id\n }\n );\n }\n\n const client = this.getClient();\n\n const exists = await this.exists({\n id,\n tenant,\n namespace\n });\n if (exists) {\n return this.update(params);\n }\n\n await client.send(\n new CreateScheduleCommand({\n Name: this.createScheduleName(params),\n ScheduleExpression: this.createScheduleExpression(scheduleFor),\n FlexibleTimeWindow: {\n Mode: \"OFF\"\n },\n Target: {\n Arn: this.config.lambdaArn,\n RoleArn: this.config.roleArn,\n Input: this.createScheduledActionEventInput(params)\n },\n ActionAfterCompletion: \"DELETE\"\n })\n );\n }\n\n public async update(params: SchedulerService.UpdateParams): Promise<void> {\n const { id, scheduleFor } = params;\n\n if (scheduleFor <= new Date()) {\n throw new WebinyError(\n `Cannot update an existing schedule for \"${id}\" with date in the past`,\n \"INVALID_SCHEDULE_DATE\",\n {\n scheduleFor,\n id\n }\n );\n }\n\n const client = this.getClient();\n\n await client.send(\n new UpdateScheduleCommand({\n Name: this.createScheduleName(params),\n ScheduleExpression: this.createScheduleExpression(scheduleFor),\n FlexibleTimeWindow: {\n Mode: \"OFF\"\n },\n Target: {\n Arn: this.config.lambdaArn,\n RoleArn: this.config.roleArn,\n Input: this.createScheduledActionEventInput(params)\n },\n ActionAfterCompletion: \"DELETE\"\n })\n );\n }\n\n public async delete(params: SchedulerService.DeleteParams): Promise<void> {\n const client = this.getClient();\n\n const exists = await this.exists(params);\n if (!exists) {\n throw new WebinyError(\n `Cannot delete schedule \"${params.id}\", tenant \"${params.tenant}\", because it does not exist.`\n );\n }\n\n const name = this.createScheduleName(params);\n\n try {\n await client.send(new DeleteScheduleCommand({ Name: name }));\n } catch (ex) {\n if (ex.name === \"ResourceNotFoundException\") {\n return;\n }\n throw WebinyError.from(ex);\n }\n }\n\n public async exists(params: SchedulerService.ExistsParams): Promise<boolean> {\n const client = this.getClient();\n\n const name = this.createScheduleName(params);\n\n try {\n await client.send(new GetScheduleCommand({ Name: name }));\n return true;\n } catch (ex) {\n if (ex.name === \"ResourceNotFoundException\") {\n return false;\n }\n throw ex;\n }\n }\n\n private createScheduleExpression(scheduleFor: Date): string {\n return `at(${scheduleFor.toISOString().replace(/\\.\\d{3}Z$/, \"\")})`;\n }\n\n private createScheduledActionEventInput(\n params: ISchedulerServiceCreateParams | ISchedulerServiceUpdateParams\n ): string {\n return JSON.stringify({\n [SCHEDULED_ACTION_EVENT_IDENTIFIER]: this.createScheduledActionEventPayload(params)\n });\n }\n\n private createScheduledActionEventPayload(\n params: SchedulerService.CreateParams | SchedulerService.UpdateParams\n ): IScheduledActionEventPayload {\n const { id, scheduleFor, tenant, namespace } = params;\n\n return {\n id,\n tenant,\n namespace,\n scheduleFor: scheduleFor.toISOString()\n };\n }\n\n private createScheduleName(params: SchedulerService.ExistsParams): string {\n // AWS EventBridge Scheduler names must match [0-9a-zA-Z-_.]+ and be <= 64 chars. `id`\n // (ScheduledActionId: \"wby-schedule-<hash>\") already satisfies both and is unique per\n // namespace + action type + target, so use it verbatim. Do NOT interpolate `namespace`\n // (contains \"/\", e.g. \"Cms/Entry/<modelId>\") or `tenant` — that produced names with illegal\n // characters that exceeded the length limit (ValidationException).\n return params.id;\n }\n}\n"],"names":["EventBridgeSchedulerService","getClient","config","params","id","scheduleFor","tenant","namespace","Date","WebinyError","client","exists","CreateScheduleCommand","UpdateScheduleCommand","name","DeleteScheduleCommand","ex","GetScheduleCommand","JSON","SCHEDULED_ACTION_EVENT_IDENTIFIER"],"mappings":"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"}
|
package/index.d.ts
ADDED
package/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { registerSchedulerAwsExtension } from "./context.js";
|
package/manifest.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export interface IGetManifestErrorResult {
|
|
2
|
+
error: Error;
|
|
3
|
+
data?: never;
|
|
4
|
+
}
|
|
5
|
+
export interface IGetManifestSuccessResult {
|
|
6
|
+
data: {
|
|
7
|
+
lambdaArn: string;
|
|
8
|
+
roleArn: string;
|
|
9
|
+
};
|
|
10
|
+
error?: never;
|
|
11
|
+
}
|
|
12
|
+
export type IGetManifestResult = IGetManifestSuccessResult | IGetManifestErrorResult;
|
|
13
|
+
export declare const getManifest: () => Promise<IGetManifestResult>;
|
package/manifest.js
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { ServiceDiscovery } from "@webiny/api-core/features/serviceDiscovery/index.js";
|
|
2
|
+
import { createZodError } from "@webiny/utils";
|
|
3
|
+
import zod from "zod";
|
|
4
|
+
const schema = zod.object({
|
|
5
|
+
scheduler: zod.object({
|
|
6
|
+
lambdaArn: zod.string(),
|
|
7
|
+
roleArn: zod.string()
|
|
8
|
+
})
|
|
9
|
+
});
|
|
10
|
+
const getManifest = async ()=>{
|
|
11
|
+
try {
|
|
12
|
+
const manifest = await ServiceDiscovery.load();
|
|
13
|
+
if (!manifest) return {
|
|
14
|
+
error: new Error("Manifest could not be loaded.")
|
|
15
|
+
};
|
|
16
|
+
if (!manifest.scheduler) return {
|
|
17
|
+
error: new Error("Scheduler not found in the Manifest.")
|
|
18
|
+
};
|
|
19
|
+
const result = await schema.safeParseAsync(manifest);
|
|
20
|
+
if (!result.success) return {
|
|
21
|
+
error: createZodError(result.error)
|
|
22
|
+
};
|
|
23
|
+
return {
|
|
24
|
+
data: result.data.scheduler
|
|
25
|
+
};
|
|
26
|
+
} catch (ex) {
|
|
27
|
+
return {
|
|
28
|
+
error: ex
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
export { getManifest };
|
|
33
|
+
|
|
34
|
+
//# sourceMappingURL=manifest.js.map
|
package/manifest.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manifest.js","sources":["../src/manifest.ts"],"sourcesContent":["import { ServiceDiscovery } from \"@webiny/api-core/features/serviceDiscovery/index.js\";\nimport { createZodError } from \"@webiny/utils\";\nimport zod from \"zod\";\n\nconst schema = zod.object({\n scheduler: zod.object({\n lambdaArn: zod.string(),\n roleArn: zod.string()\n })\n});\n\nexport interface IGetManifestErrorResult {\n error: Error;\n data?: never;\n}\n\nexport interface IGetManifestSuccessResult {\n data: {\n lambdaArn: string;\n roleArn: string;\n };\n error?: never;\n}\n\nexport type IGetManifestResult = IGetManifestSuccessResult | IGetManifestErrorResult;\n\nexport const getManifest = async (): Promise<IGetManifestResult> => {\n try {\n const manifest = await ServiceDiscovery.load();\n if (!manifest) {\n return {\n error: new Error(\"Manifest could not be loaded.\")\n };\n } else if (!manifest.scheduler) {\n return {\n error: new Error(\"Scheduler not found in the Manifest.\")\n };\n }\n\n const result = await schema.safeParseAsync(manifest);\n if (!result.success) {\n return {\n error: createZodError(result.error)\n };\n }\n\n return {\n data: result.data.scheduler\n };\n } catch (ex) {\n return {\n error: ex\n };\n }\n};\n"],"names":["schema","zod","getManifest","manifest","ServiceDiscovery","Error","result","createZodError","ex"],"mappings":";;;AAIA,MAAMA,SAASC,IAAI,MAAM,CAAC;IACtB,WAAWA,IAAI,MAAM,CAAC;QAClB,WAAWA,IAAI,MAAM;QACrB,SAASA,IAAI,MAAM;IACvB;AACJ;AAiBO,MAAMC,cAAc;IACvB,IAAI;QACA,MAAMC,WAAW,MAAMC,iBAAiB,IAAI;QAC5C,IAAI,CAACD,UACD,OAAO;YACH,OAAO,IAAIE,MAAM;QACrB;QACG,IAAI,CAACF,SAAS,SAAS,EAC1B,OAAO;YACH,OAAO,IAAIE,MAAM;QACrB;QAGJ,MAAMC,SAAS,MAAMN,OAAO,cAAc,CAACG;QAC3C,IAAI,CAACG,OAAO,OAAO,EACf,OAAO;YACH,OAAOC,eAAeD,OAAO,KAAK;QACtC;QAGJ,OAAO;YACH,MAAMA,OAAO,IAAI,CAAC,SAAS;QAC/B;IACJ,EAAE,OAAOE,IAAI;QACT,OAAO;YACH,OAAOA;QACX;IACJ;AACJ"}
|
package/package.json
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@webiny/api-scheduler-aws",
|
|
3
|
+
"version": "0.0.0-unstable.9f53ea597d",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"exports": {
|
|
6
|
+
".": "./index.js",
|
|
7
|
+
"./*": "./*"
|
|
8
|
+
},
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "https://github.com/webiny/webiny-js.git",
|
|
12
|
+
"directory": "packages/api-scheduler-aws"
|
|
13
|
+
},
|
|
14
|
+
"keywords": [
|
|
15
|
+
"scheduler:aws"
|
|
16
|
+
],
|
|
17
|
+
"author": "Webiny Ltd",
|
|
18
|
+
"description": "AWS EventBridge implementation for the Webiny action scheduler.",
|
|
19
|
+
"license": "MIT",
|
|
20
|
+
"dependencies": {
|
|
21
|
+
"@webiny/api": "0.0.0-unstable.9f53ea597d",
|
|
22
|
+
"@webiny/api-core": "0.0.0-unstable.9f53ea597d",
|
|
23
|
+
"@webiny/api-headless-cms": "0.0.0-unstable.9f53ea597d",
|
|
24
|
+
"@webiny/api-scheduler": "0.0.0-unstable.9f53ea597d",
|
|
25
|
+
"@webiny/aws-sdk": "0.0.0-unstable.9f53ea597d",
|
|
26
|
+
"@webiny/error": "0.0.0-unstable.9f53ea597d",
|
|
27
|
+
"@webiny/handler": "0.0.0-unstable.9f53ea597d",
|
|
28
|
+
"@webiny/handler-aws": "0.0.0-unstable.9f53ea597d",
|
|
29
|
+
"@webiny/plugins": "0.0.0-unstable.9f53ea597d",
|
|
30
|
+
"@webiny/utils": "0.0.0-unstable.9f53ea597d",
|
|
31
|
+
"zod": "4.4.3"
|
|
32
|
+
},
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"@webiny/build-tools": "0.0.0-unstable.9f53ea597d",
|
|
35
|
+
"@webiny/feature": "0.0.0-unstable.9f53ea597d",
|
|
36
|
+
"@webiny/handler-graphql": "0.0.0-unstable.9f53ea597d",
|
|
37
|
+
"@webiny/project-utils": "0.0.0-unstable.9f53ea597d",
|
|
38
|
+
"@webiny/testing": "0.0.0-unstable.9f53ea597d",
|
|
39
|
+
"@webiny/wcp": "0.0.0-unstable.9f53ea597d",
|
|
40
|
+
"aws-sdk-client-mock": "4.1.0",
|
|
41
|
+
"typescript": "7.0.2",
|
|
42
|
+
"vitest": "4.1.10"
|
|
43
|
+
},
|
|
44
|
+
"publishConfig": {
|
|
45
|
+
"access": "public"
|
|
46
|
+
},
|
|
47
|
+
"webiny": {
|
|
48
|
+
"publishFrom": "dist"
|
|
49
|
+
}
|
|
50
|
+
}
|