@xeplr/workflow-engine 0.0.0-stage → 2.0.0
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 +182 -2
- package/db/xcfgSetup.js +8 -0
- package/index.js +290 -0
- package/lib/actionCatalog.js +197 -0
- package/lib/actions/jobChainSteps.js +79 -0
- package/lib/actions/jobRun.js +171 -0
- package/lib/actions/screenShow.js +71 -0
- package/lib/auth.js +19 -0
- package/lib/db.js +82 -0
- package/lib/envExposed.js +87 -0
- package/lib/flows.js +916 -0
- package/lib/jobChains.js +426 -0
- package/lib/workflowRunner.js +652 -0
- package/migrations/0003_workflows.sql +35 -0
- package/migrations/0004_workflow_steps.sql +79 -0
- package/migrations/0005_workflow_runs.sql +49 -0
- package/migrations/0006_workflow_step_runs.sql +42 -0
- package/migrations/0007_workflow_resume_keys.sql +42 -0
- package/migrations/0008_workflow_run_edges.sql +43 -0
- package/migrations/0009_workflow_steps_layout.sql +11 -0
- package/migrations/0010_workflow_steps_sample_output.sql +17 -0
- package/migrations/0011_workflow_steps_params.sql +27 -0
- package/migrations/0012_workflows_kind.sql +27 -0
- package/migrations/0013_workflows_key.sql +48 -0
- package/migrations/0014_workflows_trigger.sql +30 -0
- package/migrations-tenants/0001_companies.sql +23 -0
- package/migrations-tenants/0002_workspaces.sql +22 -0
- package/models/Company.js +82 -0
- package/models/Workflow.js +67 -0
- package/models/WorkflowResumeKey.js +32 -0
- package/models/WorkflowRun.js +64 -0
- package/models/WorkflowRunEdge.js +49 -0
- package/models/WorkflowStep.js +66 -0
- package/models/WorkflowStepRun.js +49 -0
- package/models/Workspace.js +75 -0
- package/models/index.js +25 -0
- package/package.json +47 -4
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
var { BaseModel } = require('@xeplr/db');
|
|
2
|
+
|
|
3
|
+
// The document. Mounted through genericRoute as the parent of `steps` — one
|
|
4
|
+
// POST /workflows/save writes the workflow and its whole step list in a
|
|
5
|
+
// single transaction (see routes/index.js). Everything about a RUN of this
|
|
6
|
+
// document — WorkflowRun, WorkflowStepRun, WorkflowResumeKey,
|
|
7
|
+
// WorkflowRunEdge — is deliberately a separate model family, not part of
|
|
8
|
+
// this hierarchy: a run is not something you edit by saving the workflow.
|
|
9
|
+
|
|
10
|
+
class Workflow extends BaseModel {
|
|
11
|
+
static get tableName() { return 'workflows'; }
|
|
12
|
+
static get idColumn() { return 'id'; }
|
|
13
|
+
|
|
14
|
+
static get jsonSchema() {
|
|
15
|
+
return {
|
|
16
|
+
type: 'object',
|
|
17
|
+
required: ['id', 'name'],
|
|
18
|
+
properties: {
|
|
19
|
+
id: { type: 'string', maxLength: 25 },
|
|
20
|
+
name: { type: 'string', maxLength: 255 },
|
|
21
|
+
// A STABLE, AUTHOR-CHOSEN HANDLE, and only a FLOW has one. The flows
|
|
22
|
+
// facade addresses a flow by key in its URLs (GET /flows/:key), so the
|
|
23
|
+
// app that designs the screens refers to it by a name it chose rather
|
|
24
|
+
// than by an id this database minted. Null for every ordinary
|
|
25
|
+
// workflow — the builder finds those by id. See migrations/0013.
|
|
26
|
+
key: { type: ['string', 'null'], maxLength: 64 },
|
|
27
|
+
description: { type: ['string', 'null'], maxLength: 1000 },
|
|
28
|
+
// [{ name, type, required, default, description, order }]
|
|
29
|
+
params: { type: ['array', 'null'] },
|
|
30
|
+
// WHAT STARTS A RUN, as the designer recorded it — { kind, … }, one
|
|
31
|
+
// shape per kind (see migrations/0014). The engine does not arm
|
|
32
|
+
// anything from it yet; it is the intent the arming work will read.
|
|
33
|
+
trigger: { type: ['object', 'null'] },
|
|
34
|
+
status: { type: 'string', maxLength: 20 },
|
|
35
|
+
// WHAT THE BUILDER OFFERS, not what the engine does — 'workflow' (the
|
|
36
|
+
// action catalogue), 'jobs' (the job list), or 'screens' (a flow,
|
|
37
|
+
// designed in another app entirely through the /flows facade and
|
|
38
|
+
// read-only in this product's own builder). workflowRunner never reads
|
|
39
|
+
// it. See migrations/0012 and 0013.
|
|
40
|
+
kind: { type: 'string', enum: ['workflow', 'jobs', 'screens', 'job-chain'] },
|
|
41
|
+
isActive: { type: 'boolean' },
|
|
42
|
+
recordCreatedDate: { type: ['string', 'null'] },
|
|
43
|
+
recordModifiedDate: { type: ['string', 'null'] },
|
|
44
|
+
recordCreatedBy: { type: ['string', 'null'], maxLength: 25 },
|
|
45
|
+
recordModifiedBy: { type: ['string', 'null'], maxLength: 25 }
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
$beforeInsert() {
|
|
51
|
+
super.$beforeInsert();
|
|
52
|
+
if (!this.status) this.status = 'draft';
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
static get relationMappings() {
|
|
56
|
+
var WorkflowStep = require('./WorkflowStep');
|
|
57
|
+
return {
|
|
58
|
+
steps: {
|
|
59
|
+
relation: BaseModel.HasManyRelation,
|
|
60
|
+
modelClass: WorkflowStep,
|
|
61
|
+
join: { from: 'workflows.id', to: 'workflow_steps.workflowId' }
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
module.exports = Workflow;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
var { BaseModel } = require('@xeplr/db');
|
|
2
|
+
|
|
3
|
+
// The entire wait/resume mechanism lives behind this one table — see
|
|
4
|
+
// 0007_workflow_resume_keys.sql for the reasoning. Nothing outside
|
|
5
|
+
// lib/workflowRunner.js should query this model directly; the public surface
|
|
6
|
+
// is workflowRunner.resumeByKey(key, output).
|
|
7
|
+
|
|
8
|
+
class WorkflowResumeKey extends BaseModel {
|
|
9
|
+
static get tableName() { return 'workflow_resume_keys'; }
|
|
10
|
+
static get idColumn() { return 'id'; }
|
|
11
|
+
|
|
12
|
+
static get jsonSchema() {
|
|
13
|
+
return {
|
|
14
|
+
type: 'object',
|
|
15
|
+
required: ['id', 'runId', 'stepId', 'key'],
|
|
16
|
+
properties: {
|
|
17
|
+
id: { type: 'string', maxLength: 25 },
|
|
18
|
+
runId: { type: 'string', maxLength: 25 },
|
|
19
|
+
stepId: { type: 'string', maxLength: 25 },
|
|
20
|
+
key: { type: 'string', maxLength: 64 },
|
|
21
|
+
consumedDate: { type: ['string', 'null'] },
|
|
22
|
+
isActive: { type: 'boolean' },
|
|
23
|
+
recordCreatedDate: { type: ['string', 'null'] },
|
|
24
|
+
recordModifiedDate: { type: ['string', 'null'] },
|
|
25
|
+
recordCreatedBy: { type: ['string', 'null'], maxLength: 25 },
|
|
26
|
+
recordModifiedBy: { type: ['string', 'null'], maxLength: 25 }
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
module.exports = WorkflowResumeKey;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
var { BaseModel } = require('@xeplr/db');
|
|
2
|
+
|
|
3
|
+
// One occurrence of a Workflow. Not part of the workflow document's
|
|
4
|
+
// genericRoute hierarchy — a run is created and driven by lib/workflowRunner,
|
|
5
|
+
// never hand-edited via a document save. Deliberately independent of
|
|
6
|
+
// @xeplr/jobs' Job/JobOccurrence: a workflow ties multiple steps (and, later,
|
|
7
|
+
// possibly jobs) together, but owns its own execution bookkeeping rather than
|
|
8
|
+
// borrowing theirs.
|
|
9
|
+
|
|
10
|
+
class WorkflowRun extends BaseModel {
|
|
11
|
+
static get tableName() { return 'workflow_runs'; }
|
|
12
|
+
static get idColumn() { return 'id'; }
|
|
13
|
+
|
|
14
|
+
static get jsonSchema() {
|
|
15
|
+
return {
|
|
16
|
+
type: 'object',
|
|
17
|
+
required: ['id', 'workflowId', 'status'],
|
|
18
|
+
properties: {
|
|
19
|
+
id: { type: 'string', maxLength: 25 },
|
|
20
|
+
workflowId: { type: 'string', maxLength: 25 },
|
|
21
|
+
status: { type: 'string', enum: ['queued', 'running', 'waiting', 'success', 'failed'] },
|
|
22
|
+
params: { type: ['object', 'null'] },
|
|
23
|
+
// Set only on a child run spawned by an 'each' transition — the one
|
|
24
|
+
// element this occurrence exists to process. See WorkflowRunEdge.
|
|
25
|
+
item: { type: ['object', 'null'] },
|
|
26
|
+
trigger: { type: ['string', 'null'], maxLength: 64 },
|
|
27
|
+
startedAt: { type: ['string', 'null'] },
|
|
28
|
+
finishedAt: { type: ['string', 'null'] },
|
|
29
|
+
durationMs: { type: ['integer', 'null'] },
|
|
30
|
+
error: { type: ['object', 'null'] },
|
|
31
|
+
isActive: { type: 'boolean' },
|
|
32
|
+
recordCreatedDate: { type: ['string', 'null'] },
|
|
33
|
+
recordModifiedDate: { type: ['string', 'null'] },
|
|
34
|
+
recordCreatedBy: { type: ['string', 'null'], maxLength: 25 },
|
|
35
|
+
recordModifiedBy: { type: ['string', 'null'], maxLength: 25 }
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
$beforeInsert() {
|
|
41
|
+
super.$beforeInsert();
|
|
42
|
+
if (!this.status) this.status = 'queued';
|
|
43
|
+
if (!this.trigger) this.trigger = 'manual';
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
static get relationMappings() {
|
|
47
|
+
var Workflow = require('./Workflow');
|
|
48
|
+
var WorkflowStepRun = require('./WorkflowStepRun');
|
|
49
|
+
return {
|
|
50
|
+
workflow: {
|
|
51
|
+
relation: BaseModel.BelongsToOneRelation,
|
|
52
|
+
modelClass: Workflow,
|
|
53
|
+
join: { from: 'workflow_runs.workflowId', to: 'workflows.id' }
|
|
54
|
+
},
|
|
55
|
+
stepRuns: {
|
|
56
|
+
relation: BaseModel.HasManyRelation,
|
|
57
|
+
modelClass: WorkflowStepRun,
|
|
58
|
+
join: { from: 'workflow_runs.id', to: 'workflow_step_runs.runId' }
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
module.exports = WorkflowRun;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
var { BaseModel } = require('@xeplr/db');
|
|
2
|
+
|
|
3
|
+
// The only place a run's family tree lives — see 0008_workflow_run_edges.sql.
|
|
4
|
+
// workflow_steps stays flat; fan-out ancestry is entirely a run-time fact
|
|
5
|
+
// recorded here, never something an authored step definition carries.
|
|
6
|
+
|
|
7
|
+
class WorkflowRunEdge extends BaseModel {
|
|
8
|
+
static get tableName() { return 'workflow_run_edges'; }
|
|
9
|
+
static get idColumn() { return 'id'; }
|
|
10
|
+
|
|
11
|
+
static get jsonSchema() {
|
|
12
|
+
return {
|
|
13
|
+
type: 'object',
|
|
14
|
+
required: ['id', 'parentRunId', 'childRunId'],
|
|
15
|
+
properties: {
|
|
16
|
+
id: { type: 'string', maxLength: 25 },
|
|
17
|
+
parentRunId: { type: 'string', maxLength: 25 },
|
|
18
|
+
childRunId: { type: 'string', maxLength: 25 },
|
|
19
|
+
sourceStepKey: { type: ['string', 'null'], maxLength: 64 },
|
|
20
|
+
itemIndex: { type: ['integer', 'null'] },
|
|
21
|
+
itemKey: { type: ['string', 'null'], maxLength: 255 },
|
|
22
|
+
item: { type: ['object', 'null'] },
|
|
23
|
+
isActive: { type: 'boolean' },
|
|
24
|
+
recordCreatedDate: { type: ['string', 'null'] },
|
|
25
|
+
recordModifiedDate: { type: ['string', 'null'] },
|
|
26
|
+
recordCreatedBy: { type: ['string', 'null'], maxLength: 25 },
|
|
27
|
+
recordModifiedBy: { type: ['string', 'null'], maxLength: 25 }
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
static get relationMappings() {
|
|
33
|
+
var WorkflowRun = require('./WorkflowRun');
|
|
34
|
+
return {
|
|
35
|
+
parentRun: {
|
|
36
|
+
relation: BaseModel.BelongsToOneRelation,
|
|
37
|
+
modelClass: WorkflowRun,
|
|
38
|
+
join: { from: 'workflow_run_edges.parentRunId', to: 'workflow_runs.id' }
|
|
39
|
+
},
|
|
40
|
+
childRun: {
|
|
41
|
+
relation: BaseModel.BelongsToOneRelation,
|
|
42
|
+
modelClass: WorkflowRun,
|
|
43
|
+
join: { from: 'workflow_run_edges.childRunId', to: 'workflow_runs.id' }
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
module.exports = WorkflowRunEdge;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
var { BaseModel } = require('@xeplr/db');
|
|
2
|
+
|
|
3
|
+
class WorkflowStep extends BaseModel {
|
|
4
|
+
static get tableName() { return 'workflow_steps'; }
|
|
5
|
+
static get idColumn() { return 'id'; }
|
|
6
|
+
|
|
7
|
+
static get jsonSchema() {
|
|
8
|
+
return {
|
|
9
|
+
type: 'object',
|
|
10
|
+
required: ['id', 'workflowId', 'stepKey', 'actionName'],
|
|
11
|
+
properties: {
|
|
12
|
+
id: { type: 'string', maxLength: 25 },
|
|
13
|
+
workflowId: { type: 'string', maxLength: 25 },
|
|
14
|
+
stepKey: { type: 'string', maxLength: 64 },
|
|
15
|
+
name: { type: ['string', 'null'], maxLength: 255 },
|
|
16
|
+
actionName: { type: 'string', maxLength: 128 },
|
|
17
|
+
values: { type: ['object', 'null'] },
|
|
18
|
+
kind: { type: 'string', enum: ['auto', 'wait'] },
|
|
19
|
+
timeoutMs: { type: ['integer', 'null'] },
|
|
20
|
+
onError: { type: 'string', enum: ['stop', 'continue'] },
|
|
21
|
+
// [{ condition: expressionHandlerExpr|null, mode: 'single'|'each', target: stepKey|'end_success'|'end_failed' }]
|
|
22
|
+
transitions: { type: ['array', 'null'] },
|
|
23
|
+
joinStep: { type: ['string', 'null'], maxLength: 64 },
|
|
24
|
+
position: { type: 'integer' },
|
|
25
|
+
// { x, y } canvas pixels — where the builder draws this step. Purely
|
|
26
|
+
// visual; never read by workflowRunner. See migrations/0009.
|
|
27
|
+
layout: { type: ['object', 'null'] },
|
|
28
|
+
// An EXAMPLE of what this step returns, for the builder's field
|
|
29
|
+
// picker — never what it did return (that is on workflow_step_runs),
|
|
30
|
+
// and never read by workflowRunner. See migrations/0010.
|
|
31
|
+
sampleOutput: { type: ['object', 'null'] },
|
|
32
|
+
// What this step needs the CALLER to supply at run start:
|
|
33
|
+
// [{ name, type, required, default, description, order, sample? }] —
|
|
34
|
+
// the same field shape as an action's inputSchema. UNLIKE layout and
|
|
35
|
+
// sampleOutput, workflowRunner DOES read this: every step's
|
|
36
|
+
// declaration unions into the schema a run is validated against
|
|
37
|
+
// before any step executes. See migrations/0011.
|
|
38
|
+
params: { type: ['array', 'null'] },
|
|
39
|
+
isActive: { type: 'boolean' },
|
|
40
|
+
recordCreatedDate: { type: ['string', 'null'] },
|
|
41
|
+
recordModifiedDate: { type: ['string', 'null'] },
|
|
42
|
+
recordCreatedBy: { type: ['string', 'null'], maxLength: 25 },
|
|
43
|
+
recordModifiedBy: { type: ['string', 'null'], maxLength: 25 }
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
$beforeInsert() {
|
|
49
|
+
super.$beforeInsert();
|
|
50
|
+
if (!this.kind) this.kind = 'auto';
|
|
51
|
+
if (!this.onError) this.onError = 'stop';
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
static get relationMappings() {
|
|
55
|
+
var Workflow = require('./Workflow');
|
|
56
|
+
return {
|
|
57
|
+
workflow: {
|
|
58
|
+
relation: BaseModel.BelongsToOneRelation,
|
|
59
|
+
modelClass: Workflow,
|
|
60
|
+
join: { from: 'workflow_steps.workflowId', to: 'workflows.id' }
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
module.exports = WorkflowStep;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
var { BaseModel } = require('@xeplr/db');
|
|
2
|
+
|
|
3
|
+
class WorkflowStepRun extends BaseModel {
|
|
4
|
+
static get tableName() { return 'workflow_step_runs'; }
|
|
5
|
+
static get idColumn() { return 'id'; }
|
|
6
|
+
|
|
7
|
+
static get jsonSchema() {
|
|
8
|
+
return {
|
|
9
|
+
type: 'object',
|
|
10
|
+
required: ['id', 'runId', 'status'],
|
|
11
|
+
properties: {
|
|
12
|
+
id: { type: 'string', maxLength: 25 },
|
|
13
|
+
runId: { type: 'string', maxLength: 25 },
|
|
14
|
+
stepId: { type: ['string', 'null'], maxLength: 25 },
|
|
15
|
+
stepKey: { type: ['string', 'null'], maxLength: 64 },
|
|
16
|
+
actionName: { type: ['string', 'null'], maxLength: 128 },
|
|
17
|
+
status: { type: 'string', enum: ['pending', 'running', 'waiting', 'success', 'failed', 'skipped'] },
|
|
18
|
+
input: { type: ['object', 'null'] },
|
|
19
|
+
output: { type: ['object', 'null'] },
|
|
20
|
+
error: { type: ['object', 'null'] },
|
|
21
|
+
durationMs: { type: ['integer', 'null'] },
|
|
22
|
+
position: { type: 'integer' },
|
|
23
|
+
isActive: { type: 'boolean' },
|
|
24
|
+
recordCreatedDate: { type: ['string', 'null'] },
|
|
25
|
+
recordModifiedDate: { type: ['string', 'null'] },
|
|
26
|
+
recordCreatedBy: { type: ['string', 'null'], maxLength: 25 },
|
|
27
|
+
recordModifiedBy: { type: ['string', 'null'], maxLength: 25 }
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
$beforeInsert() {
|
|
33
|
+
super.$beforeInsert();
|
|
34
|
+
if (!this.status) this.status = 'pending';
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
static get relationMappings() {
|
|
38
|
+
var WorkflowRun = require('./WorkflowRun');
|
|
39
|
+
return {
|
|
40
|
+
run: {
|
|
41
|
+
relation: BaseModel.BelongsToOneRelation,
|
|
42
|
+
modelClass: WorkflowRun,
|
|
43
|
+
join: { from: 'workflow_step_runs.runId', to: 'workflow_runs.id' }
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
module.exports = WorkflowStepRun;
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
// Workspace — the second tenancy level, under a company. Plain CRUD record;
|
|
2
|
+
// siloed access is granted per workspace in the auth DB. companyId is required
|
|
3
|
+
// (a workspace always belongs to a company).
|
|
4
|
+
//
|
|
5
|
+
// Single MT level (mtLevels: 1) — a workspace row is scoped by its company
|
|
6
|
+
// (mtId1) only. It doesn't have a meaningful mtId2 of its own (a workspace
|
|
7
|
+
// isn't scoped "by workspace"); resources that live INSIDE a workspace
|
|
8
|
+
// (workflows, runs) stay at the app's full registered level count (2: company
|
|
9
|
+
// + workspace) via the default.
|
|
10
|
+
var { BaseModel } = require('@xeplr/db');
|
|
11
|
+
var { generateId } = require('@xeplr/utils/lib/helpers');
|
|
12
|
+
|
|
13
|
+
// Cross-DB — see Company.js for the identical pattern this mirrors: a
|
|
14
|
+
// CompanyAdmin's l1 grant does NOT imply l2 access (mtMembershipMiddleware
|
|
15
|
+
// checks each configured level independently), so without this, entering a
|
|
16
|
+
// just-created workspace 403s with "Not authorized for workspaceId ..." exactly
|
|
17
|
+
// like an ungranted company did before Company.$afterInsert existed.
|
|
18
|
+
function auth() { return require('../lib/auth'); }
|
|
19
|
+
|
|
20
|
+
async function grantWorkspaceCreator(userId, workspaceId) {
|
|
21
|
+
var a = auth();
|
|
22
|
+
await a.ready();
|
|
23
|
+
var Role = a.model('Role');
|
|
24
|
+
var UserTenantsMapping = a.model('UserTenantsMapping');
|
|
25
|
+
|
|
26
|
+
var role = await Role.query().where({ name: 'Creator' }).first();
|
|
27
|
+
if (!role) return; // not seeded (yet) — skip rather than throw
|
|
28
|
+
|
|
29
|
+
var existing = await UserTenantsMapping.query()
|
|
30
|
+
.where({ userId: userId, level: 'l2', value: workspaceId }).first();
|
|
31
|
+
if (existing) return;
|
|
32
|
+
|
|
33
|
+
await UserTenantsMapping.query().insert({
|
|
34
|
+
id: generateId(),
|
|
35
|
+
userId: userId,
|
|
36
|
+
level: 'l2',
|
|
37
|
+
value: workspaceId,
|
|
38
|
+
roleId: role.id,
|
|
39
|
+
isActive: true
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
class Workspace extends BaseModel {
|
|
44
|
+
static get tableName() { return 'workspaces'; }
|
|
45
|
+
static get idColumn() { return 'id'; }
|
|
46
|
+
static get mtLevels() { return 1; }
|
|
47
|
+
|
|
48
|
+
static get jsonAttributes() { return ['tags']; }
|
|
49
|
+
|
|
50
|
+
static get jsonSchema() {
|
|
51
|
+
return BaseModel.schema({
|
|
52
|
+
required: ['companyId', 'name'],
|
|
53
|
+
properties: { tags: { type: ['array', 'null'] } }
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Deferred via queryContext.afterCommit — see Company.js's $afterInsert for
|
|
58
|
+
// why this can't grant inline.
|
|
59
|
+
async $afterInsert(queryContext) {
|
|
60
|
+
await super.$afterInsert(queryContext);
|
|
61
|
+
if (queryContext && queryContext.user && queryContext.user.id && Array.isArray(queryContext.afterCommit)) {
|
|
62
|
+
var userId = queryContext.user.id;
|
|
63
|
+
var workspaceId = this.id;
|
|
64
|
+
queryContext.afterCommit.push(async function() {
|
|
65
|
+
try {
|
|
66
|
+
await grantWorkspaceCreator(userId, workspaceId);
|
|
67
|
+
} catch (err) {
|
|
68
|
+
console.error('[Workspace] failed to grant Creator access to creator:', err.message);
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
module.exports = Workspace;
|
package/models/index.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// The domain models. Company and Workspace are the tenancy skeleton every
|
|
2
|
+
// xeplr app starts from. Workflow/WorkflowStep are the document, mounted as a
|
|
3
|
+
// genericRoute hierarchy in routes/index.js. WorkflowRun/WorkflowStepRun/
|
|
4
|
+
// WorkflowResumeKey/WorkflowRunEdge are a separate family — an occurrence of
|
|
5
|
+
// a workflow, driven by lib/workflowRunner, never hand-edited via a document
|
|
6
|
+
// save.
|
|
7
|
+
var Company = require('./Company');
|
|
8
|
+
var Workspace = require('./Workspace');
|
|
9
|
+
var Workflow = require('./Workflow');
|
|
10
|
+
var WorkflowStep = require('./WorkflowStep');
|
|
11
|
+
var WorkflowRun = require('./WorkflowRun');
|
|
12
|
+
var WorkflowStepRun = require('./WorkflowStepRun');
|
|
13
|
+
var WorkflowResumeKey = require('./WorkflowResumeKey');
|
|
14
|
+
var WorkflowRunEdge = require('./WorkflowRunEdge');
|
|
15
|
+
|
|
16
|
+
module.exports = {
|
|
17
|
+
Company: Company,
|
|
18
|
+
Workspace: Workspace,
|
|
19
|
+
Workflow: Workflow,
|
|
20
|
+
WorkflowStep: WorkflowStep,
|
|
21
|
+
WorkflowRun: WorkflowRun,
|
|
22
|
+
WorkflowStepRun: WorkflowStepRun,
|
|
23
|
+
WorkflowResumeKey: WorkflowResumeKey,
|
|
24
|
+
WorkflowRunEdge: WorkflowRunEdge
|
|
25
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,49 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xeplr/workflow-engine",
|
|
3
|
-
"version": "
|
|
4
|
-
"
|
|
5
|
-
"description": "
|
|
6
|
-
|
|
3
|
+
"version": "2.0.0",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"description": "The workflow engine — workflow's own database and tables, the action catalog, starting and resuming runs, and the flows facade. No HTTP: @xeplr/workflow-api puts the routes on top.",
|
|
6
|
+
"main": "index.js",
|
|
7
|
+
"files": [
|
|
8
|
+
"index.js",
|
|
9
|
+
"lib/",
|
|
10
|
+
"models/",
|
|
11
|
+
"migrations/",
|
|
12
|
+
"migrations-tenants/",
|
|
13
|
+
"db/",
|
|
14
|
+
"LICENSE"
|
|
15
|
+
],
|
|
16
|
+
"scripts": {
|
|
17
|
+
"test": "node test/run.mjs"
|
|
18
|
+
},
|
|
19
|
+
"author": "xeplr",
|
|
20
|
+
"repository": {
|
|
21
|
+
"type": "git",
|
|
22
|
+
"url": "https://github.com/Xeplr/xeplr-workflow-engine.git"
|
|
23
|
+
},
|
|
24
|
+
"peerDependencies": {
|
|
25
|
+
"@xeplr/actions": "^1.1.0",
|
|
26
|
+
"@xeplr/auth": "^1.0.0",
|
|
27
|
+
"@xeplr/db": "^1.0.0",
|
|
28
|
+
"@xeplr/expression-handler": "^2.0.0",
|
|
29
|
+
"@xeplr/schema-handler": "^1.0.0",
|
|
30
|
+
"@xeplr/utils": "^1.0.0"
|
|
31
|
+
},
|
|
32
|
+
"xeplr": {
|
|
33
|
+
"readsFromDisk": [
|
|
34
|
+
"migrations",
|
|
35
|
+
"migrations-tenants"
|
|
36
|
+
]
|
|
37
|
+
},
|
|
38
|
+
"homepage": "https://github.com/Xeplr/xeplr-workflow-engine#readme",
|
|
39
|
+
"bugs": {
|
|
40
|
+
"url": "https://github.com/Xeplr/xeplr-workflow-engine/issues"
|
|
41
|
+
},
|
|
42
|
+
"publishConfig": {
|
|
43
|
+
"access": "public"
|
|
44
|
+
},
|
|
45
|
+
"devDependencies": {
|
|
46
|
+
"@xeplr/jobs": "^2.3.0",
|
|
47
|
+
"knex": "^3.3.0"
|
|
48
|
+
}
|
|
49
|
+
}
|