@knocklabs/cli 0.1.0-rc.0 → 0.1.0-rc.1
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/README.md +73 -12
- package/dist/commands/commit/index.js +58 -0
- package/dist/commands/commit/promote.js +49 -0
- package/dist/commands/workflow/activate.js +67 -0
- package/dist/commands/workflow/new.js +135 -0
- package/dist/commands/workflow/pull.js +2 -2
- package/dist/commands/workflow/push.js +15 -4
- package/dist/commands/workflow/validate.js +125 -0
- package/dist/lib/api-v1.js +42 -2
- package/dist/lib/base-command.js +1 -1
- package/dist/lib/helpers/{env.js → const.js} +6 -0
- package/dist/lib/helpers/dir-context.js +1 -7
- package/dist/lib/helpers/error.js +22 -7
- package/dist/lib/helpers/flag.js +16 -0
- package/dist/lib/helpers/json.js +2 -1
- package/dist/lib/helpers/liquid.js +39 -0
- package/dist/lib/helpers/request.js +43 -4
- package/dist/lib/helpers/spinner.js +20 -0
- package/dist/lib/helpers/string.js +16 -8
- package/dist/lib/marshal/workflow/generator.js +230 -0
- package/dist/lib/marshal/workflow/helpers.js +10 -0
- package/dist/lib/marshal/workflow/index.js +1 -0
- package/dist/lib/marshal/workflow/reader.js +34 -28
- package/dist/lib/marshal/workflow/writer.js +22 -18
- package/dist/lib/user-config.js +3 -3
- package/oclif.manifest.json +242 -6
- package/package.json +11 -10
package/dist/lib/api-v1.js
CHANGED
|
@@ -17,7 +17,7 @@ function _interopRequireDefault(obj) {
|
|
|
17
17
|
const DEFAULT_ORIGIN = "https://control.knock.app";
|
|
18
18
|
const API_VERSION = "v1";
|
|
19
19
|
class ApiV1 {
|
|
20
|
-
// By resources:
|
|
20
|
+
// By resources: Workflows
|
|
21
21
|
async ping() {
|
|
22
22
|
return this.get("/ping");
|
|
23
23
|
}
|
|
@@ -45,7 +45,9 @@ class ApiV1 {
|
|
|
45
45
|
async upsertWorkflow({ args , flags }, workflow) {
|
|
46
46
|
const params = (0, _object.prune)({
|
|
47
47
|
environment: flags.environment,
|
|
48
|
-
annotate: flags.annotate
|
|
48
|
+
annotate: flags.annotate,
|
|
49
|
+
commit: flags.commit,
|
|
50
|
+
commit_message: flags["commit-message"]
|
|
49
51
|
});
|
|
50
52
|
const data = {
|
|
51
53
|
workflow
|
|
@@ -54,6 +56,44 @@ class ApiV1 {
|
|
|
54
56
|
params
|
|
55
57
|
});
|
|
56
58
|
}
|
|
59
|
+
async validateWorkflow({ args , flags }, workflow) {
|
|
60
|
+
const params = (0, _object.prune)({
|
|
61
|
+
environment: flags.environment
|
|
62
|
+
});
|
|
63
|
+
const data = {
|
|
64
|
+
workflow
|
|
65
|
+
};
|
|
66
|
+
return this.put(`/workflows/${args.workflowKey}/validate`, data, {
|
|
67
|
+
params
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
async activateWorkflow({ args , flags }) {
|
|
71
|
+
const params = (0, _object.prune)({
|
|
72
|
+
environment: flags.environment,
|
|
73
|
+
status: flags.status
|
|
74
|
+
});
|
|
75
|
+
return this.put(`/workflows/${args.workflowKey}/activate`, {}, {
|
|
76
|
+
params
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
// By resources: Commits
|
|
80
|
+
async commitAllChanges({ flags }) {
|
|
81
|
+
const params = (0, _object.prune)({
|
|
82
|
+
environment: flags.environment,
|
|
83
|
+
commit_message: flags.commit_message
|
|
84
|
+
});
|
|
85
|
+
return this.put(`/commits`, {}, {
|
|
86
|
+
params
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
async promoteAllChanges({ flags }) {
|
|
90
|
+
const params = (0, _object.prune)({
|
|
91
|
+
to_environment: flags.to
|
|
92
|
+
});
|
|
93
|
+
return this.put(`/commits/promote`, {}, {
|
|
94
|
+
params
|
|
95
|
+
});
|
|
96
|
+
}
|
|
57
97
|
// By methods:
|
|
58
98
|
async get(subpath, config) {
|
|
59
99
|
return this.client.get(`/${API_VERSION}` + subpath, config);
|
package/dist/lib/base-command.js
CHANGED
|
@@ -74,7 +74,7 @@ BaseCommand.globalFlags = {
|
|
|
74
74
|
// - if not provided, fall back to env variable
|
|
75
75
|
// - if not available, fall back to user config
|
|
76
76
|
"service-token": _core.Flags.string({
|
|
77
|
-
summary: "service token to authenticate with",
|
|
77
|
+
summary: "The service token to authenticate with",
|
|
78
78
|
required: true,
|
|
79
79
|
multiple: false,
|
|
80
80
|
env: "KNOCK_SERVICE_TOKEN",
|
|
@@ -9,6 +9,7 @@ function _export(target, all) {
|
|
|
9
9
|
});
|
|
10
10
|
}
|
|
11
11
|
_export(exports, {
|
|
12
|
+
KnockEnv: ()=>KnockEnv,
|
|
12
13
|
isTestEnv: ()=>isTestEnv,
|
|
13
14
|
sandboxDir: ()=>sandboxDir
|
|
14
15
|
});
|
|
@@ -59,5 +60,10 @@ function _interopRequireWildcard(obj, nodeInterop) {
|
|
|
59
60
|
}
|
|
60
61
|
return newObj;
|
|
61
62
|
}
|
|
63
|
+
var KnockEnv;
|
|
64
|
+
(function(KnockEnv) {
|
|
65
|
+
KnockEnv["Development"] = "development";
|
|
66
|
+
KnockEnv["Production"] = "production";
|
|
67
|
+
})(KnockEnv || (KnockEnv = {}));
|
|
62
68
|
const isTestEnv = process.env.NODE_ENV === "test";
|
|
63
69
|
const sandboxDir = _nodePath.resolve(_fsExtra.realpathSync(_nodeOs.default.tmpdir()), ".knock");
|
|
@@ -8,18 +8,12 @@ Object.defineProperty(exports, "ensureResourceDirForTarget", {
|
|
|
8
8
|
});
|
|
9
9
|
const _core = require("@oclif/core");
|
|
10
10
|
const ensureResourceDirForTarget = (resourceDirCtx, target)=>{
|
|
11
|
-
switch(target.commandId){
|
|
12
|
-
case "workflow:pull":
|
|
13
|
-
break;
|
|
14
|
-
default:
|
|
15
|
-
throw new Error(`Unhandled commandId: ${target.commandId}`);
|
|
16
|
-
}
|
|
17
11
|
// If the target resource is a different type than the current resource dir
|
|
18
12
|
// type, error out.
|
|
19
13
|
if (resourceDirCtx.type !== target.type) {
|
|
20
14
|
return _core.CliUx.ux.error(`Cannot run ${target.commandId} inside a ${resourceDirCtx.type} directory`);
|
|
21
15
|
}
|
|
22
|
-
// If the resource key was not
|
|
16
|
+
// If the resource key was not provided with the command, then infer from the
|
|
23
17
|
// current resource directory context.
|
|
24
18
|
if (!target.key) {
|
|
25
19
|
return resourceDirCtx;
|
|
@@ -10,7 +10,9 @@ function _export(target, all) {
|
|
|
10
10
|
}
|
|
11
11
|
_export(exports, {
|
|
12
12
|
ApiError: ()=>ApiError,
|
|
13
|
-
|
|
13
|
+
JsonSyntaxError: ()=>JsonSyntaxError,
|
|
14
|
+
JsonDataError: ()=>JsonDataError,
|
|
15
|
+
LiquidParseError: ()=>LiquidParseError,
|
|
14
16
|
formatErrors: ()=>formatErrors
|
|
15
17
|
});
|
|
16
18
|
const _string = require("./string");
|
|
@@ -28,10 +30,18 @@ class CustomError extends Error {
|
|
|
28
30
|
}
|
|
29
31
|
class ApiError extends CustomError {
|
|
30
32
|
}
|
|
31
|
-
class
|
|
32
|
-
|
|
33
|
+
class JsonSyntaxError extends CustomError {
|
|
34
|
+
}
|
|
35
|
+
class JsonDataError extends CustomError {
|
|
36
|
+
constructor(message, objPath){
|
|
37
|
+
super(message);
|
|
38
|
+
this.objPath = objPath;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
class LiquidParseError extends CustomError {
|
|
42
|
+
constructor(message, context){
|
|
33
43
|
super(message);
|
|
34
|
-
this.
|
|
44
|
+
this.context = context;
|
|
35
45
|
}
|
|
36
46
|
}
|
|
37
47
|
/*
|
|
@@ -42,12 +52,17 @@ class JsonError extends CustomError {
|
|
|
42
52
|
*/ const formatError = (error)=>{
|
|
43
53
|
switch(true){
|
|
44
54
|
case error instanceof ApiError:
|
|
45
|
-
case error instanceof
|
|
55
|
+
case error instanceof JsonSyntaxError:
|
|
46
56
|
return `${error.name}: ${error.message}`;
|
|
47
|
-
case error instanceof
|
|
57
|
+
case error instanceof JsonDataError:
|
|
58
|
+
{
|
|
59
|
+
const e = error;
|
|
60
|
+
return e.objPath ? `${e.name}: data at "${e.objPath}" ${e.message}` : `${e.name}: ${e.message}`;
|
|
61
|
+
}
|
|
62
|
+
case error instanceof LiquidParseError:
|
|
48
63
|
{
|
|
49
64
|
const e = error;
|
|
50
|
-
return
|
|
65
|
+
return `${e.name}: ${e.message + "\n" + e.context}`;
|
|
51
66
|
}
|
|
52
67
|
default:
|
|
53
68
|
throw new Error(`Unhandled error type: ${error}`);
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3
|
+
value: true
|
|
4
|
+
});
|
|
5
|
+
Object.defineProperty(exports, "booleanStr", {
|
|
6
|
+
enumerable: true,
|
|
7
|
+
get: ()=>booleanStr
|
|
8
|
+
});
|
|
9
|
+
const _core = require("@oclif/core");
|
|
10
|
+
const booleanStr = _core.Flags.custom({
|
|
11
|
+
options: [
|
|
12
|
+
"true",
|
|
13
|
+
"false"
|
|
14
|
+
],
|
|
15
|
+
parse: async (input)=>input === "true"
|
|
16
|
+
});
|
package/dist/lib/helpers/json.js
CHANGED
|
@@ -14,6 +14,7 @@ _export(exports, {
|
|
|
14
14
|
});
|
|
15
15
|
const _jsonlint = /*#__PURE__*/ _interopRequireWildcard(require("@prantlf/jsonlint"));
|
|
16
16
|
const _fsExtra = /*#__PURE__*/ _interopRequireWildcard(require("fs-extra"));
|
|
17
|
+
const _error = require("./error");
|
|
17
18
|
function _getRequireWildcardCache(nodeInterop) {
|
|
18
19
|
if (typeof WeakMap !== "function") return null;
|
|
19
20
|
var cacheBabelInterop = new WeakMap();
|
|
@@ -63,7 +64,7 @@ const readJson = async (filePath)=>{
|
|
|
63
64
|
} catch (error) {
|
|
64
65
|
// https://github.com/prantlf/jsonlint#error-handling
|
|
65
66
|
if (!(error instanceof SyntaxError)) throw error;
|
|
66
|
-
errors.push(error);
|
|
67
|
+
errors.push(new _error.JsonSyntaxError(error.message));
|
|
67
68
|
}
|
|
68
69
|
return [
|
|
69
70
|
payload,
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3
|
+
value: true
|
|
4
|
+
});
|
|
5
|
+
Object.defineProperty(exports, "validateLiquidSyntax", {
|
|
6
|
+
enumerable: true,
|
|
7
|
+
get: ()=>validateLiquidSyntax
|
|
8
|
+
});
|
|
9
|
+
const _liquidjs = require("liquidjs");
|
|
10
|
+
const _error = require("./error");
|
|
11
|
+
const engine = new _liquidjs.Liquid({
|
|
12
|
+
timezoneOffset: 0
|
|
13
|
+
});
|
|
14
|
+
// Turn off a couple tags we do not support.
|
|
15
|
+
const disabled = {
|
|
16
|
+
parse (token) {
|
|
17
|
+
throw new Error(`"${token.name}" tag is not supported in Knock`);
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
engine.registerTag("render", disabled);
|
|
21
|
+
engine.registerTag("include", disabled);
|
|
22
|
+
function validateLiquidSyntax(input) {
|
|
23
|
+
try {
|
|
24
|
+
engine.parse(input);
|
|
25
|
+
return undefined;
|
|
26
|
+
} catch (error) {
|
|
27
|
+
// All liquidjs errors should be extended from the LiquidError class.
|
|
28
|
+
// Reference: https://liquidjs.com/api/classes/util_error_.liquiderror.html
|
|
29
|
+
if (!(error instanceof _liquidjs.LiquidError)) throw error;
|
|
30
|
+
// Here we only care about syntactical errors specifically.
|
|
31
|
+
switch(true){
|
|
32
|
+
case error instanceof _liquidjs.TokenizationError:
|
|
33
|
+
case error instanceof _liquidjs.ParseError:
|
|
34
|
+
return new _error.LiquidParseError(error.message, error.context);
|
|
35
|
+
default:
|
|
36
|
+
return undefined;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -7,8 +7,47 @@ Object.defineProperty(exports, "withSpinner", {
|
|
|
7
7
|
get: ()=>withSpinner
|
|
8
8
|
});
|
|
9
9
|
const _core = require("@oclif/core");
|
|
10
|
-
const _env = require("./env");
|
|
11
10
|
const _error = require("./error");
|
|
11
|
+
const _spinner = /*#__PURE__*/ _interopRequireWildcard(require("./spinner"));
|
|
12
|
+
function _getRequireWildcardCache(nodeInterop) {
|
|
13
|
+
if (typeof WeakMap !== "function") return null;
|
|
14
|
+
var cacheBabelInterop = new WeakMap();
|
|
15
|
+
var cacheNodeInterop = new WeakMap();
|
|
16
|
+
return (_getRequireWildcardCache = function(nodeInterop) {
|
|
17
|
+
return nodeInterop ? cacheNodeInterop : cacheBabelInterop;
|
|
18
|
+
})(nodeInterop);
|
|
19
|
+
}
|
|
20
|
+
function _interopRequireWildcard(obj, nodeInterop) {
|
|
21
|
+
if (!nodeInterop && obj && obj.__esModule) {
|
|
22
|
+
return obj;
|
|
23
|
+
}
|
|
24
|
+
if (obj === null || typeof obj !== "object" && typeof obj !== "function") {
|
|
25
|
+
return {
|
|
26
|
+
default: obj
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
var cache = _getRequireWildcardCache(nodeInterop);
|
|
30
|
+
if (cache && cache.has(obj)) {
|
|
31
|
+
return cache.get(obj);
|
|
32
|
+
}
|
|
33
|
+
var newObj = {};
|
|
34
|
+
var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor;
|
|
35
|
+
for(var key in obj){
|
|
36
|
+
if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) {
|
|
37
|
+
var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null;
|
|
38
|
+
if (desc && (desc.get || desc.set)) {
|
|
39
|
+
Object.defineProperty(newObj, key, desc);
|
|
40
|
+
} else {
|
|
41
|
+
newObj[key] = obj[key];
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
newObj.default = obj;
|
|
46
|
+
if (cache) {
|
|
47
|
+
cache.set(obj, newObj);
|
|
48
|
+
}
|
|
49
|
+
return newObj;
|
|
50
|
+
}
|
|
12
51
|
const isSuccessResp = (resp)=>resp.status >= 200 && resp.status < 300;
|
|
13
52
|
/*
|
|
14
53
|
* Returns a formatted error message from an error response based on status code.
|
|
@@ -18,7 +57,7 @@ const isSuccessResp = (resp)=>resp.status >= 200 && resp.status < 300;
|
|
|
18
57
|
}
|
|
19
58
|
const { message , errors =[] } = data;
|
|
20
59
|
if (status >= 400) {
|
|
21
|
-
const errs = errors.map((e)=>new _error.
|
|
60
|
+
const errs = errors.map((e)=>new _error.JsonDataError(e.message, e.field));
|
|
22
61
|
return errs.length === 0 ? message : message + "\n\n" + (0, _error.formatErrors)(errs);
|
|
23
62
|
}
|
|
24
63
|
return message;
|
|
@@ -26,13 +65,13 @@ const isSuccessResp = (resp)=>resp.status >= 200 && resp.status < 300;
|
|
|
26
65
|
const withSpinner = async (requestFn, opts = {})=>{
|
|
27
66
|
const { action ="‣ Loading" , ensureSuccess =true } = opts;
|
|
28
67
|
// Suppress printing the spinner in tests, oclif doesn't for some reasons.
|
|
29
|
-
|
|
68
|
+
_spinner.start(action);
|
|
30
69
|
const resp = await requestFn();
|
|
31
70
|
// Error out before the action stop so the spinner can update accordingly.
|
|
32
71
|
if (ensureSuccess && !isSuccessResp(resp)) {
|
|
33
72
|
const message = formatErrorRespMessage(resp);
|
|
34
73
|
_core.CliUx.ux.error(new _error.ApiError(message));
|
|
35
74
|
}
|
|
36
|
-
|
|
75
|
+
_spinner.stop();
|
|
37
76
|
return resp;
|
|
38
77
|
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3
|
+
value: true
|
|
4
|
+
});
|
|
5
|
+
function _export(target, all) {
|
|
6
|
+
for(var name in all)Object.defineProperty(target, name, {
|
|
7
|
+
enumerable: true,
|
|
8
|
+
get: all[name]
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
_export(exports, {
|
|
12
|
+
start: ()=>start,
|
|
13
|
+
stop: ()=>stop
|
|
14
|
+
});
|
|
15
|
+
const _core = require("@oclif/core");
|
|
16
|
+
const _const = require("./const");
|
|
17
|
+
const start = (action)=>{
|
|
18
|
+
if (!_const.isTestEnv) _core.CliUx.ux.action.start(action);
|
|
19
|
+
};
|
|
20
|
+
const stop = ()=>_core.CliUx.ux.action.stop();
|
|
@@ -1,17 +1,25 @@
|
|
|
1
1
|
/*
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
* NOTE: Copied over from https://github.com/sindresorhus/indent-string for now,
|
|
5
|
-
* because getting an "[ERR_REQUIRE_ESM]: Must use import to load ES Module"
|
|
6
|
-
* error when pulling in the package.
|
|
2
|
+
* Checks if a given string is in a slugified format.
|
|
7
3
|
*/ "use strict";
|
|
8
4
|
Object.defineProperty(exports, "__esModule", {
|
|
9
5
|
value: true
|
|
10
6
|
});
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
7
|
+
function _export(target, all) {
|
|
8
|
+
for(var name in all)Object.defineProperty(target, name, {
|
|
9
|
+
enumerable: true,
|
|
10
|
+
get: all[name]
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
_export(exports, {
|
|
14
|
+
checkSlugifiedFormat: ()=>checkSlugifiedFormat,
|
|
15
|
+
indentString: ()=>indentString
|
|
14
16
|
});
|
|
17
|
+
const SLUG_FORMAT_RE = /^[\w-]+$/;
|
|
18
|
+
const SLUG_LOWERCASE_FORMAT_RE = /^[\d_a-z-]+$/;
|
|
19
|
+
const checkSlugifiedFormat = (input, opts = {})=>{
|
|
20
|
+
const { onlyLowerCase =true } = opts;
|
|
21
|
+
return onlyLowerCase ? SLUG_LOWERCASE_FORMAT_RE.test(input) : SLUG_FORMAT_RE.test(input);
|
|
22
|
+
};
|
|
15
23
|
const indentString = (string, count = 0, options = {})=>{
|
|
16
24
|
const { indent =" " , includeEmptyLines =false } = options;
|
|
17
25
|
if (typeof string !== "string") {
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3
|
+
value: true
|
|
4
|
+
});
|
|
5
|
+
function _export(target, all) {
|
|
6
|
+
for(var name in all)Object.defineProperty(target, name, {
|
|
7
|
+
enumerable: true,
|
|
8
|
+
get: all[name]
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
_export(exports, {
|
|
12
|
+
parseStepsInput: ()=>parseStepsInput,
|
|
13
|
+
generateWorkflowDir: ()=>generateWorkflowDir,
|
|
14
|
+
scaffoldWorkflowDirBundle: ()=>scaffoldWorkflowDirBundle
|
|
15
|
+
});
|
|
16
|
+
const _lodash = require("lodash");
|
|
17
|
+
const _helpers = require("./helpers");
|
|
18
|
+
const _types = require("./types");
|
|
19
|
+
const _writer = require("./writer");
|
|
20
|
+
const scaffoldDelayStep = (refSuffix)=>{
|
|
21
|
+
const scaffoldedStep = {
|
|
22
|
+
ref: `${_types.StepType.Delay}_${refSuffix}`,
|
|
23
|
+
type: _types.StepType.Delay,
|
|
24
|
+
settings: {
|
|
25
|
+
delay_for: {
|
|
26
|
+
unit: "seconds",
|
|
27
|
+
value: 30
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
return [
|
|
32
|
+
scaffoldedStep,
|
|
33
|
+
{}
|
|
34
|
+
];
|
|
35
|
+
};
|
|
36
|
+
const scaffoldBatchStep = (refSuffix)=>{
|
|
37
|
+
const scaffoldedStep = {
|
|
38
|
+
ref: `${_types.StepType.Batch}_${refSuffix}`,
|
|
39
|
+
type: _types.StepType.Batch,
|
|
40
|
+
settings: {
|
|
41
|
+
batch_order: "asc",
|
|
42
|
+
batch_window: {
|
|
43
|
+
unit: "seconds",
|
|
44
|
+
value: 30
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
return [
|
|
49
|
+
scaffoldedStep,
|
|
50
|
+
{}
|
|
51
|
+
];
|
|
52
|
+
};
|
|
53
|
+
const scaffoldHttpFetchStep = (refSuffix)=>{
|
|
54
|
+
const scaffoldedStep = {
|
|
55
|
+
ref: `${_types.StepType.HttpFetch}_${refSuffix}`,
|
|
56
|
+
type: _types.StepType.HttpFetch,
|
|
57
|
+
settings: {
|
|
58
|
+
method: "get",
|
|
59
|
+
url: "https://example.com"
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
return [
|
|
63
|
+
scaffoldedStep,
|
|
64
|
+
{}
|
|
65
|
+
];
|
|
66
|
+
};
|
|
67
|
+
const scaffoldEmailChannelStep = (refSuffix)=>{
|
|
68
|
+
const stepRef = `email_${refSuffix}`;
|
|
69
|
+
const templateFilePath = (0, _writer.newTemplateFilePath)(stepRef, "html_body", "html");
|
|
70
|
+
const scaffoldedStep = {
|
|
71
|
+
ref: stepRef,
|
|
72
|
+
type: _types.StepType.Channel,
|
|
73
|
+
channel_key: "<EMAIL CHANNEL KEY>",
|
|
74
|
+
template: {
|
|
75
|
+
settings: {
|
|
76
|
+
layout_key: "default"
|
|
77
|
+
},
|
|
78
|
+
subject: "You've got mail!",
|
|
79
|
+
["html_body" + _helpers.FILEPATH_MARKER]: templateFilePath
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
const bundleFragment = {
|
|
83
|
+
[templateFilePath]: "<p>Hello, <strong>{{ recipient.name }}</strong>!</p>"
|
|
84
|
+
};
|
|
85
|
+
return [
|
|
86
|
+
scaffoldedStep,
|
|
87
|
+
bundleFragment
|
|
88
|
+
];
|
|
89
|
+
};
|
|
90
|
+
const scaffoldInAppFeedChannelStep = (refSuffix)=>{
|
|
91
|
+
const stepRef = `in_app_feed_${refSuffix}`;
|
|
92
|
+
const templateFilePath = (0, _writer.newTemplateFilePath)(stepRef, "markdown_body", "md");
|
|
93
|
+
const scaffoldedStep = {
|
|
94
|
+
ref: stepRef,
|
|
95
|
+
type: _types.StepType.Channel,
|
|
96
|
+
channel_key: "<IN-APP-FEED CHANNEL KEY>",
|
|
97
|
+
template: {
|
|
98
|
+
action_url: "{{ vars.app_url }}",
|
|
99
|
+
["markdown_body" + _helpers.FILEPATH_MARKER]: templateFilePath
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
const bundleFragment = {
|
|
103
|
+
[templateFilePath]: "Hello, **{{ recipient.name }}**!"
|
|
104
|
+
};
|
|
105
|
+
return [
|
|
106
|
+
scaffoldedStep,
|
|
107
|
+
bundleFragment
|
|
108
|
+
];
|
|
109
|
+
};
|
|
110
|
+
const scaffoldSmsChannelStep = (refSuffix)=>{
|
|
111
|
+
const stepRef = `sms_${refSuffix}`;
|
|
112
|
+
const templateFilePath = (0, _writer.newTemplateFilePath)(stepRef, "text_body", "txt");
|
|
113
|
+
const scaffoldedStep = {
|
|
114
|
+
ref: stepRef,
|
|
115
|
+
type: _types.StepType.Channel,
|
|
116
|
+
channel_key: "<SMS CHANNEL KEY>",
|
|
117
|
+
template: {
|
|
118
|
+
["text_body" + _helpers.FILEPATH_MARKER]: templateFilePath
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
const bundleFragment = {
|
|
122
|
+
[templateFilePath]: "Hello, {{ recipient.name }}!"
|
|
123
|
+
};
|
|
124
|
+
return [
|
|
125
|
+
scaffoldedStep,
|
|
126
|
+
bundleFragment
|
|
127
|
+
];
|
|
128
|
+
};
|
|
129
|
+
const scaffoldPushChannelStep = (refSuffix)=>{
|
|
130
|
+
const stepRef = `push_${refSuffix}`;
|
|
131
|
+
const templateFilePath = (0, _writer.newTemplateFilePath)(stepRef, "text_body", "txt");
|
|
132
|
+
const scaffoldedStep = {
|
|
133
|
+
ref: stepRef,
|
|
134
|
+
type: _types.StepType.Channel,
|
|
135
|
+
channel_key: "<PUSH CHANNEL KEY>",
|
|
136
|
+
template: {
|
|
137
|
+
settings: {
|
|
138
|
+
delivery_type: "content"
|
|
139
|
+
},
|
|
140
|
+
["text_body" + _helpers.FILEPATH_MARKER]: templateFilePath
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
const bundleFragment = {
|
|
144
|
+
[templateFilePath]: "Hello, {{ recipient.name }}!"
|
|
145
|
+
};
|
|
146
|
+
return [
|
|
147
|
+
scaffoldedStep,
|
|
148
|
+
bundleFragment
|
|
149
|
+
];
|
|
150
|
+
};
|
|
151
|
+
const scaffoldChatChannelStep = (refSuffix)=>{
|
|
152
|
+
const stepRef = `chat_${refSuffix}`;
|
|
153
|
+
const templateFilePath = (0, _writer.newTemplateFilePath)(stepRef, "markdown_body", "md");
|
|
154
|
+
const scaffoldedStep = {
|
|
155
|
+
ref: stepRef,
|
|
156
|
+
type: _types.StepType.Channel,
|
|
157
|
+
channel_key: "<CHAT CHANNEL KEY>",
|
|
158
|
+
template: {
|
|
159
|
+
["markdown_body" + _helpers.FILEPATH_MARKER]: templateFilePath
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
const bundleFragment = {
|
|
163
|
+
[templateFilePath]: "Hello, **{{ recipient.name }}**!"
|
|
164
|
+
};
|
|
165
|
+
return [
|
|
166
|
+
scaffoldedStep,
|
|
167
|
+
bundleFragment
|
|
168
|
+
];
|
|
169
|
+
};
|
|
170
|
+
const STEP_TAGS = [
|
|
171
|
+
"delay",
|
|
172
|
+
"batch",
|
|
173
|
+
"fetch",
|
|
174
|
+
"email",
|
|
175
|
+
"in-app",
|
|
176
|
+
"sms",
|
|
177
|
+
"push",
|
|
178
|
+
"chat"
|
|
179
|
+
];
|
|
180
|
+
const stepScaffoldFuncs = {
|
|
181
|
+
// Function steps
|
|
182
|
+
delay: scaffoldDelayStep,
|
|
183
|
+
batch: scaffoldBatchStep,
|
|
184
|
+
fetch: scaffoldHttpFetchStep,
|
|
185
|
+
// Channel steps
|
|
186
|
+
email: scaffoldEmailChannelStep,
|
|
187
|
+
"in-app": scaffoldInAppFeedChannelStep,
|
|
188
|
+
sms: scaffoldSmsChannelStep,
|
|
189
|
+
push: scaffoldPushChannelStep,
|
|
190
|
+
chat: scaffoldChatChannelStep
|
|
191
|
+
};
|
|
192
|
+
const parseStepsInput = (input)=>{
|
|
193
|
+
const tags = input.split(",").filter((x)=>x);
|
|
194
|
+
const invalidTags = tags.filter((tag)=>!(0, _lodash.get)(stepScaffoldFuncs, tag));
|
|
195
|
+
if (invalidTags.length > 0) {
|
|
196
|
+
return [
|
|
197
|
+
undefined,
|
|
198
|
+
"must be a comma-separated string of the following values: " + STEP_TAGS.join(", ")
|
|
199
|
+
];
|
|
200
|
+
}
|
|
201
|
+
return [
|
|
202
|
+
tags,
|
|
203
|
+
undefined
|
|
204
|
+
];
|
|
205
|
+
};
|
|
206
|
+
const scaffoldWorkflowDirBundle = (attrs)=>{
|
|
207
|
+
// A map of 1-based counters to track the number of each step tag seen, used
|
|
208
|
+
// for formatting step refs.
|
|
209
|
+
const stepCountByTag = (0, _lodash.assign)({}, ...STEP_TAGS.map((tag)=>({
|
|
210
|
+
[tag]: 0
|
|
211
|
+
})));
|
|
212
|
+
// Scaffold workflow steps data based on given step tags.
|
|
213
|
+
const [scaffoldedSteps = [], bundleFragments = []] = (0, _lodash.zip)(...(attrs.steps || []).map((tag)=>{
|
|
214
|
+
const stepCount = ++stepCountByTag[tag];
|
|
215
|
+
return stepScaffoldFuncs[tag](stepCount);
|
|
216
|
+
}));
|
|
217
|
+
// Build a workflow directory bundle with scaffolded steps data, plus other
|
|
218
|
+
// workflow attributes.
|
|
219
|
+
const workflowJson = {
|
|
220
|
+
...attrs,
|
|
221
|
+
steps: scaffoldedSteps
|
|
222
|
+
};
|
|
223
|
+
return (0, _lodash.assign)({
|
|
224
|
+
[_helpers.WORKFLOW_JSON]: workflowJson
|
|
225
|
+
}, ...bundleFragments);
|
|
226
|
+
};
|
|
227
|
+
const generateWorkflowDir = async (workflowDirCtx, attrs)=>{
|
|
228
|
+
const bundle = scaffoldWorkflowDirBundle(attrs);
|
|
229
|
+
return (0, _writer.writeWorkflowDirFromBundle)(workflowDirCtx, bundle);
|
|
230
|
+
};
|
|
@@ -12,6 +12,7 @@ _export(exports, {
|
|
|
12
12
|
WORKFLOW_JSON: ()=>WORKFLOW_JSON,
|
|
13
13
|
FILEPATH_MARKER: ()=>FILEPATH_MARKER,
|
|
14
14
|
FILEPATH_MARKED_RE: ()=>FILEPATH_MARKED_RE,
|
|
15
|
+
validateWorkflowKey: ()=>validateWorkflowKey,
|
|
15
16
|
lsWorkflowJson: ()=>lsWorkflowJson,
|
|
16
17
|
isWorkflowDir: ()=>isWorkflowDir,
|
|
17
18
|
formatCategories: ()=>formatCategories,
|
|
@@ -21,6 +22,7 @@ _export(exports, {
|
|
|
21
22
|
const _nodePath = /*#__PURE__*/ _interopRequireWildcard(require("node:path"));
|
|
22
23
|
const _fsExtra = /*#__PURE__*/ _interopRequireWildcard(require("fs-extra"));
|
|
23
24
|
const _lodash = require("lodash");
|
|
25
|
+
const _string = require("../../helpers/string");
|
|
24
26
|
const _types = require("./types");
|
|
25
27
|
function _getRequireWildcardCache(nodeInterop) {
|
|
26
28
|
if (typeof WeakMap !== "function") return null;
|
|
@@ -64,6 +66,14 @@ function _interopRequireWildcard(obj, nodeInterop) {
|
|
|
64
66
|
const WORKFLOW_JSON = "workflow.json";
|
|
65
67
|
const FILEPATH_MARKER = "@";
|
|
66
68
|
const FILEPATH_MARKED_RE = new RegExp(`${FILEPATH_MARKER}$`);
|
|
69
|
+
const validateWorkflowKey = (input)=>{
|
|
70
|
+
if (!(0, _string.checkSlugifiedFormat)(input, {
|
|
71
|
+
onlyLowerCase: true
|
|
72
|
+
})) {
|
|
73
|
+
return "must include only lowercase alphanumeric, dash, or underscore characters";
|
|
74
|
+
}
|
|
75
|
+
return undefined;
|
|
76
|
+
};
|
|
67
77
|
const lsWorkflowJson = async (dirPath)=>{
|
|
68
78
|
const workflowJsonPath = _nodePath.join(dirPath, WORKFLOW_JSON);
|
|
69
79
|
const exists = await _fsExtra.pathExists(workflowJsonPath);
|