0z2i6v3u5t 1.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/.devcontainer/devcontainer.json +4 -0
- package/.devcontainer/setup.sh +11 -0
- package/.dockerignore +2 -0
- package/.github/CONTRIBUTING.md +52 -0
- package/.github/FUNDING.yml +3 -0
- package/.github/ISSUE_TEMPLATE/bug_report.yml +59 -0
- package/.github/ISSUE_TEMPLATE/config.yml +5 -0
- package/.github/ISSUE_TEMPLATE/feature_request.yml +43 -0
- package/.github/dependabot.yml +17 -0
- package/.github/workflows/codeql.yml +76 -0
- package/.github/workflows/publish_docs.yml +25 -0
- package/.github/workflows/test.yml +78 -0
- package/.nvmrc +1 -0
- package/.prettierignore +1 -0
- package/.prettierrc +1 -0
- package/.vscode/launch.json +42 -0
- package/CODE_OF_CONDUCT.md +76 -0
- package/Dockerfile +17 -0
- package/LICENSE +21 -0
- package/README.md +3 -0
- package/SECURITY.md +5 -0
- package/__tests__/actions/cacheTest.ts +58 -0
- package/__tests__/actions/randomNumber.ts +26 -0
- package/__tests__/actions/recursiveAction.ts +16 -0
- package/__tests__/actions/sleepTest.ts +24 -0
- package/__tests__/actions/status.ts +17 -0
- package/__tests__/actions/swagger.ts +76 -0
- package/__tests__/actions/validationTest.ts +63 -0
- package/__tests__/cli/cli.ts +126 -0
- package/__tests__/core/api.ts +632 -0
- package/__tests__/core/cache.ts +400 -0
- package/__tests__/core/chatRoom.ts +589 -0
- package/__tests__/core/cli.ts +349 -0
- package/__tests__/core/cluster.ts +132 -0
- package/__tests__/core/config.ts +78 -0
- package/__tests__/core/errors.ts +112 -0
- package/__tests__/core/log.ts +23 -0
- package/__tests__/core/middleware.ts +427 -0
- package/__tests__/core/plugins/partialPlugin.ts +94 -0
- package/__tests__/core/plugins/withPlugin.ts +88 -0
- package/__tests__/core/plugins/withoutPlugin.ts +81 -0
- package/__tests__/core/process.ts +42 -0
- package/__tests__/core/specHelper.ts +330 -0
- package/__tests__/core/staticFile/compression.ts +99 -0
- package/__tests__/core/staticFile/staticFile.ts +180 -0
- package/__tests__/core/tasks/customQueueFunction.ts +67 -0
- package/__tests__/core/tasks/fullWorkerFlow.ts +199 -0
- package/__tests__/core/tasks/tasks.ts +605 -0
- package/__tests__/integration/browser.ts +133 -0
- package/__tests__/integration/ioredis-mock.ts +194 -0
- package/__tests__/integration/sendBuffer.ts +97 -0
- package/__tests__/integration/sendFile.ts +24 -0
- package/__tests__/integration/sharedFingerprint.ts +82 -0
- package/__tests__/integration/taskFlow.ts +110 -0
- package/__tests__/jest.ts +5 -0
- package/__tests__/modules/action.ts +103 -0
- package/__tests__/modules/config.ts +19 -0
- package/__tests__/modules/utils/ensureNoTsHeaderOrSpecFiles.ts +24 -0
- package/__tests__/servers/web/allowedRequestHosts.ts +88 -0
- package/__tests__/servers/web/enableMultiples.ts +83 -0
- package/__tests__/servers/web/fileUpload.ts +79 -0
- package/__tests__/servers/web/jsonp.ts +57 -0
- package/__tests__/servers/web/nonMultiples.ts +83 -0
- package/__tests__/servers/web/rawBody.ts +208 -0
- package/__tests__/servers/web/returnErrorCodes.ts +55 -0
- package/__tests__/servers/web/routes/deepRoutes.ts +96 -0
- package/__tests__/servers/web/routes/routes.ts +579 -0
- package/__tests__/servers/web/routes/veryDeepRoutes.ts +92 -0
- package/__tests__/servers/web/web.ts +1031 -0
- package/__tests__/servers/websocket.ts +795 -0
- package/__tests__/tasks/runAction.ts +37 -0
- package/__tests__/template.ts.example +20 -0
- package/__tests__/testCliCommands/hello.ts +44 -0
- package/__tests__/testPlugin/public/plugin.html +1 -0
- package/__tests__/testPlugin/src/actions/pluginAction.ts +14 -0
- package/__tests__/testPlugin/src/bin/hello.ts +22 -0
- package/__tests__/testPlugin/src/initializers/pluginInitializer.ts +17 -0
- package/__tests__/testPlugin/src/tasks/pluginTask.ts +15 -0
- package/__tests__/testPlugin/tsconfig.json +10 -0
- package/__tests__/utils/utils.ts +492 -0
- package/app.json +23 -0
- package/bin/deploy-docs +39 -0
- package/client/ActionheroWebsocketClient.js +277 -0
- package/docker-compose.yml +73 -0
- package/package.json +24 -0
- package/public/chat.html +194 -0
- package/public/css/cosmo.css +12 -0
- package/public/favicon.ico +0 -0
- package/public/index.html +115 -0
- package/public/javascript/.gitkeep +0 -0
- package/public/linkedSession.html +80 -0
- package/public/logo/actionhero-small.png +0 -0
- package/public/logo/actionhero.png +0 -0
- package/public/pixel.gif +0 -0
- package/public/simple.html +2 -0
- package/public/swagger.html +32 -0
- package/public/websocketLoadTest.html +322 -0
- package/src/actions/cacheTest.ts +58 -0
- package/src/actions/createChatRoom.ts +20 -0
- package/src/actions/randomNumber.ts +17 -0
- package/src/actions/recursiveAction.ts +13 -0
- package/src/actions/sendFile.ts +12 -0
- package/src/actions/sleepTest.ts +40 -0
- package/src/actions/status.ts +73 -0
- package/src/actions/swagger.ts +155 -0
- package/src/actions/validationTest.ts +36 -0
- package/src/bin/actionhero.ts +225 -0
- package/src/bin/methods/actions/list.ts +30 -0
- package/src/bin/methods/console.ts +26 -0
- package/src/bin/methods/generate/action.ts +58 -0
- package/src/bin/methods/generate/cli.ts +51 -0
- package/src/bin/methods/generate/initializer.ts +54 -0
- package/src/bin/methods/generate/plugin.ts +57 -0
- package/src/bin/methods/generate/server.ts +38 -0
- package/src/bin/methods/generate/task.ts +68 -0
- package/src/bin/methods/generate.ts +176 -0
- package/src/bin/methods/task/enqueue.ts +35 -0
- package/src/classes/action.ts +98 -0
- package/src/classes/actionProcessor.ts +463 -0
- package/src/classes/api.ts +51 -0
- package/src/classes/cli.ts +67 -0
- package/src/classes/config.ts +15 -0
- package/src/classes/connection.ts +321 -0
- package/src/classes/exceptionReporter.ts +9 -0
- package/src/classes/initializer.ts +59 -0
- package/src/classes/initializers.ts +5 -0
- package/src/classes/input.ts +9 -0
- package/src/classes/inputs.ts +34 -0
- package/src/classes/process/actionheroVersion.ts +15 -0
- package/src/classes/process/env.ts +16 -0
- package/src/classes/process/id.ts +34 -0
- package/src/classes/process/pid.ts +32 -0
- package/src/classes/process/projectRoot.ts +16 -0
- package/src/classes/process/typescript.ts +47 -0
- package/src/classes/process.ts +479 -0
- package/src/classes/server.ts +251 -0
- package/src/classes/task.ts +87 -0
- package/src/config/api.ts +107 -0
- package/src/config/errors.ts +162 -0
- package/src/config/logger.ts +113 -0
- package/src/config/plugins.ts +37 -0
- package/src/config/redis.ts +78 -0
- package/src/config/routes.ts +44 -0
- package/src/config/tasks.ts +84 -0
- package/src/config/web.ts +136 -0
- package/src/config/websocket.ts +62 -0
- package/src/index.ts +46 -0
- package/src/initializers/actions.ts +125 -0
- package/src/initializers/chatRoom.ts +214 -0
- package/src/initializers/connections.ts +124 -0
- package/src/initializers/exceptions.ts +155 -0
- package/src/initializers/params.ts +52 -0
- package/src/initializers/redis.ts +191 -0
- package/src/initializers/resque.ts +248 -0
- package/src/initializers/routes.ts +229 -0
- package/src/initializers/servers.ts +134 -0
- package/src/initializers/specHelper.ts +195 -0
- package/src/initializers/staticFile.ts +253 -0
- package/src/initializers/tasks.ts +188 -0
- package/src/modules/action.ts +89 -0
- package/src/modules/cache.ts +326 -0
- package/src/modules/chatRoom.ts +321 -0
- package/src/modules/config.ts +246 -0
- package/src/modules/log.ts +62 -0
- package/src/modules/redis.ts +93 -0
- package/src/modules/route.ts +59 -0
- package/src/modules/specHelper.ts +182 -0
- package/src/modules/task.ts +527 -0
- package/src/modules/utils/argv.ts +3 -0
- package/src/modules/utils/arrayStartingMatch.ts +21 -0
- package/src/modules/utils/arrayUnique.ts +15 -0
- package/src/modules/utils/collapseObjectToArray.ts +33 -0
- package/src/modules/utils/deepCopy.ts +3 -0
- package/src/modules/utils/ensureNoTsHeaderOrSpecFiles.ts +19 -0
- package/src/modules/utils/eventLoopDelay.ts +34 -0
- package/src/modules/utils/fileUtils.ts +119 -0
- package/src/modules/utils/filterObjectForLogging.ts +51 -0
- package/src/modules/utils/filterResponseForLogging.ts +53 -0
- package/src/modules/utils/getExternalIPAddress.ts +17 -0
- package/src/modules/utils/hashMerge.ts +63 -0
- package/src/modules/utils/isPlainObject.ts +45 -0
- package/src/modules/utils/isRunning.ts +7 -0
- package/src/modules/utils/parseCookies.ts +20 -0
- package/src/modules/utils/parseHeadersForClientAddress.ts +53 -0
- package/src/modules/utils/parseIPv6URI.ts +24 -0
- package/src/modules/utils/replaceDistWithSrc.ts +9 -0
- package/src/modules/utils/safeGlob.ts +6 -0
- package/src/modules/utils/sleep.ts +8 -0
- package/src/modules/utils/sortGlobalMiddleware.ts +17 -0
- package/src/modules/utils/sourceRelativeLinkPath.ts +29 -0
- package/src/modules/utils.ts +66 -0
- package/src/server.ts +20 -0
- package/src/servers/web.ts +894 -0
- package/src/servers/websocket.ts +304 -0
- package/src/tasks/runAction.ts +29 -0
- package/tea.yaml +9 -0
- package/templates/README.md.template +17 -0
- package/templates/action.ts.template +15 -0
- package/templates/boot.js.template +9 -0
- package/templates/cli.ts.template +15 -0
- package/templates/gitignore.template +23 -0
- package/templates/initializer.ts.template +17 -0
- package/templates/package-plugin.json.template +12 -0
- package/templates/package.json.template +45 -0
- package/templates/projectMap.txt +39 -0
- package/templates/projectServer.ts.template +20 -0
- package/templates/server.ts.template +37 -0
- package/templates/task.ts.template +16 -0
- package/templates/test/action.ts.template +13 -0
- package/templates/test/task.ts.template +20 -0
- package/tsconfig.json +11 -0
@@ -0,0 +1,632 @@
|
|
1
|
+
import { AsyncReturnType } from "type-fest";
|
2
|
+
import { config, api, Process, Action, specHelper } from "./../../src";
|
3
|
+
|
4
|
+
const actionhero = new Process();
|
5
|
+
|
6
|
+
describe("Core", () => {
|
7
|
+
describe("api", () => {
|
8
|
+
beforeAll(async () => {
|
9
|
+
await actionhero.start();
|
10
|
+
});
|
11
|
+
afterAll(async () => {
|
12
|
+
await actionhero.stop();
|
13
|
+
});
|
14
|
+
|
15
|
+
test("should have an api object with proper parts", () => {
|
16
|
+
[
|
17
|
+
api.actions.actions,
|
18
|
+
api.actions.versions,
|
19
|
+
api.actions.actions.cacheTest["1"],
|
20
|
+
api.actions.actions.randomNumber["1"],
|
21
|
+
api.actions.actions.status["1"],
|
22
|
+
].forEach((item) => {
|
23
|
+
expect(item).toBeInstanceOf(Object);
|
24
|
+
});
|
25
|
+
|
26
|
+
[
|
27
|
+
api.actions.actions.cacheTest["1"].run,
|
28
|
+
api.actions.actions.randomNumber["1"].run,
|
29
|
+
api.actions.actions.status["1"].run,
|
30
|
+
].forEach((item) => {
|
31
|
+
expect(item).toBeInstanceOf(Function);
|
32
|
+
});
|
33
|
+
|
34
|
+
[
|
35
|
+
api.actions.actions.randomNumber["1"].name,
|
36
|
+
api.actions.actions.randomNumber["1"].description,
|
37
|
+
].forEach((item) => {
|
38
|
+
expect(typeof item).toEqual("string");
|
39
|
+
});
|
40
|
+
|
41
|
+
expect(config).toBeInstanceOf(Object);
|
42
|
+
});
|
43
|
+
|
44
|
+
test("should have loaded postVariables properly", () => {
|
45
|
+
[
|
46
|
+
"file",
|
47
|
+
"callback",
|
48
|
+
"action",
|
49
|
+
"apiVersion",
|
50
|
+
"key", // from cacheTest action
|
51
|
+
"value", // from cacheTest action
|
52
|
+
].forEach((item) => {
|
53
|
+
expect(api.params.postVariables.indexOf(item) >= 0).toEqual(true);
|
54
|
+
});
|
55
|
+
});
|
56
|
+
|
57
|
+
describe("api versions", () => {
|
58
|
+
beforeAll(() => {
|
59
|
+
api.actions.versions.versionedAction = [1, 2, 3];
|
60
|
+
api.actions.actions.versionedAction = {
|
61
|
+
//@ts-ignore
|
62
|
+
1: {
|
63
|
+
name: "versionedAction",
|
64
|
+
description: "I am a test",
|
65
|
+
version: 1,
|
66
|
+
outputExample: {},
|
67
|
+
run: async () => {
|
68
|
+
return { version: 1 };
|
69
|
+
},
|
70
|
+
},
|
71
|
+
//@ts-ignore
|
72
|
+
2: {
|
73
|
+
name: "versionedAction",
|
74
|
+
description: "I am a test",
|
75
|
+
version: 2,
|
76
|
+
outputExample: {},
|
77
|
+
run: async () => {
|
78
|
+
return { version: 2 };
|
79
|
+
},
|
80
|
+
},
|
81
|
+
//@ts-ignore
|
82
|
+
3: {
|
83
|
+
name: "versionedAction",
|
84
|
+
description: "I am a test",
|
85
|
+
version: 3,
|
86
|
+
outputExample: {},
|
87
|
+
run: async (data) => {
|
88
|
+
data.response!.version = 3;
|
89
|
+
data.response!.error = {
|
90
|
+
a: { complex: "error" },
|
91
|
+
};
|
92
|
+
},
|
93
|
+
},
|
94
|
+
};
|
95
|
+
});
|
96
|
+
|
97
|
+
afterAll(() => {
|
98
|
+
delete api.actions.actions.versionedAction;
|
99
|
+
delete api.actions.versions.versionedAction;
|
100
|
+
});
|
101
|
+
|
102
|
+
test("will default actions to version 1 when no version is provided by the definition", async () => {
|
103
|
+
const response = await specHelper.runAction("randomNumber");
|
104
|
+
expect(
|
105
|
+
response.requesterInformation!.receivedParams.apiVersion,
|
106
|
+
).toEqual(1);
|
107
|
+
});
|
108
|
+
|
109
|
+
test("can specify an apiVersion", async () => {
|
110
|
+
let response;
|
111
|
+
response = await specHelper.runAction("versionedAction", {
|
112
|
+
apiVersion: 1,
|
113
|
+
});
|
114
|
+
expect(response.requesterInformation.receivedParams.apiVersion).toEqual(
|
115
|
+
1,
|
116
|
+
);
|
117
|
+
response = await specHelper.runAction("versionedAction", {
|
118
|
+
apiVersion: 2,
|
119
|
+
});
|
120
|
+
expect(response.requesterInformation.receivedParams.apiVersion).toEqual(
|
121
|
+
2,
|
122
|
+
);
|
123
|
+
});
|
124
|
+
|
125
|
+
test("will default clients to the latest version of the action", async () => {
|
126
|
+
const response = await specHelper.runAction("versionedAction");
|
127
|
+
expect(
|
128
|
+
response.requesterInformation!.receivedParams.apiVersion,
|
129
|
+
).toEqual(3);
|
130
|
+
});
|
131
|
+
|
132
|
+
test("will fail on a missing action + version", async () => {
|
133
|
+
const response = await specHelper.runAction("versionedAction", {
|
134
|
+
apiVersion: 10,
|
135
|
+
});
|
136
|
+
expect(response.error).toEqual(
|
137
|
+
"Error: unknown action or invalid apiVersion",
|
138
|
+
);
|
139
|
+
});
|
140
|
+
|
141
|
+
test("can return complex error responses", async () => {
|
142
|
+
const response = await specHelper.runAction("versionedAction", {
|
143
|
+
apiVersion: 3,
|
144
|
+
});
|
145
|
+
expect(response.error.a.complex).toEqual("error");
|
146
|
+
});
|
147
|
+
});
|
148
|
+
|
149
|
+
describe("action constructor", () => {
|
150
|
+
test("validates actions", () => {
|
151
|
+
class GoodAction extends Action {
|
152
|
+
constructor() {
|
153
|
+
super();
|
154
|
+
this.name = "good";
|
155
|
+
this.description = "good";
|
156
|
+
this.outputExample = {};
|
157
|
+
}
|
158
|
+
|
159
|
+
async run() {}
|
160
|
+
}
|
161
|
+
|
162
|
+
class BadAction extends Action {
|
163
|
+
constructor() {
|
164
|
+
super();
|
165
|
+
// this.name = 'bad'
|
166
|
+
this.description = "bad";
|
167
|
+
this.outputExample = {};
|
168
|
+
}
|
169
|
+
|
170
|
+
async run() {}
|
171
|
+
}
|
172
|
+
|
173
|
+
const goodAction = new GoodAction();
|
174
|
+
const badAction = new BadAction();
|
175
|
+
|
176
|
+
goodAction.validate();
|
177
|
+
|
178
|
+
try {
|
179
|
+
badAction.validate();
|
180
|
+
throw new Error("should not get here");
|
181
|
+
} catch (error) {
|
182
|
+
expect(error.toString()).toMatch(/name is required for this action/);
|
183
|
+
}
|
184
|
+
});
|
185
|
+
|
186
|
+
test("actions cannot use reserved params as inputs", () => {
|
187
|
+
class BadAction extends Action {
|
188
|
+
constructor() {
|
189
|
+
super();
|
190
|
+
this.name = "bad";
|
191
|
+
this.description = "bad";
|
192
|
+
this.outputExample = {};
|
193
|
+
this.inputs = {
|
194
|
+
apiVersion: { required: true },
|
195
|
+
};
|
196
|
+
}
|
197
|
+
|
198
|
+
async run() {}
|
199
|
+
}
|
200
|
+
|
201
|
+
const badAction = new BadAction();
|
202
|
+
|
203
|
+
expect(() => badAction.validate()).toThrow(
|
204
|
+
"input `apiVersion` in action `bad` is a reserved param",
|
205
|
+
);
|
206
|
+
});
|
207
|
+
|
208
|
+
test("the return types of actions can be imported", async () => {
|
209
|
+
const { RandomNumber } = await import("../../src/actions/randomNumber");
|
210
|
+
type ResponseType = AsyncReturnType<typeof RandomNumber.prototype.run>;
|
211
|
+
|
212
|
+
// now that we know the types, we can enforce that new objects match the type
|
213
|
+
const responsePayload: ResponseType = {
|
214
|
+
randomNumber: 1,
|
215
|
+
stringRandomNumber: "some string",
|
216
|
+
};
|
217
|
+
|
218
|
+
const responsePartial: ResponseType["randomNumber"] = 2;
|
219
|
+
|
220
|
+
expect(responsePartial).toBe(2);
|
221
|
+
});
|
222
|
+
});
|
223
|
+
|
224
|
+
describe("Action Params", () => {
|
225
|
+
beforeAll(() => {
|
226
|
+
api.actions.versions.testAction = [1];
|
227
|
+
api.actions.actions.testAction = {
|
228
|
+
//@ts-ignore
|
229
|
+
1: {
|
230
|
+
name: "testAction",
|
231
|
+
description: "this action has some required params",
|
232
|
+
version: 1,
|
233
|
+
inputs: {
|
234
|
+
requiredParam: { required: true },
|
235
|
+
optionalParam: { required: false },
|
236
|
+
fancyParam: {
|
237
|
+
required: false,
|
238
|
+
default: () => {
|
239
|
+
return "abc123";
|
240
|
+
},
|
241
|
+
validator: function (s: unknown) {
|
242
|
+
if (s !== "abc123") {
|
243
|
+
return 'fancyParam should be "abc123". so says ' + this.id;
|
244
|
+
}
|
245
|
+
},
|
246
|
+
formatter: function (s: unknown) {
|
247
|
+
return String(s);
|
248
|
+
},
|
249
|
+
},
|
250
|
+
},
|
251
|
+
run: async (data) => {
|
252
|
+
data.response!.params = data.params;
|
253
|
+
},
|
254
|
+
},
|
255
|
+
};
|
256
|
+
});
|
257
|
+
|
258
|
+
afterAll(() => {
|
259
|
+
delete api.actions.actions.testAction;
|
260
|
+
delete api.actions.versions.testAction;
|
261
|
+
config.general!.missingParamChecks = [null, "", undefined];
|
262
|
+
});
|
263
|
+
|
264
|
+
test("correct params that are falsey (false, []) should be allowed", async () => {
|
265
|
+
let response;
|
266
|
+
response = await specHelper.runAction("testAction", {
|
267
|
+
requiredParam: false,
|
268
|
+
});
|
269
|
+
expect(response.params.requiredParam).toEqual(false);
|
270
|
+
response = await specHelper.runAction("testAction", {
|
271
|
+
requiredParam: [],
|
272
|
+
});
|
273
|
+
expect(response.params.requiredParam).toHaveLength(0);
|
274
|
+
});
|
275
|
+
|
276
|
+
test("will fail for missing or empty string params", async () => {
|
277
|
+
let response = await specHelper.runAction("testAction", {
|
278
|
+
requiredParam: "",
|
279
|
+
});
|
280
|
+
expect(response.error).toContain("required parameter for this action");
|
281
|
+
response = await specHelper.runAction("testAction", {});
|
282
|
+
expect(response.error).toMatch(
|
283
|
+
/requiredParam is a required parameter for this action/,
|
284
|
+
);
|
285
|
+
});
|
286
|
+
|
287
|
+
test("correct params respect config options", async () => {
|
288
|
+
let response;
|
289
|
+
config.general!.missingParamChecks = [undefined];
|
290
|
+
response = await specHelper.runAction("testAction", {
|
291
|
+
requiredParam: "",
|
292
|
+
});
|
293
|
+
expect(response.params.requiredParam).toEqual("");
|
294
|
+
response = await specHelper.runAction("testAction", {
|
295
|
+
requiredParam: null,
|
296
|
+
});
|
297
|
+
expect(response.params.requiredParam).toBeNull();
|
298
|
+
});
|
299
|
+
|
300
|
+
test("will set a default when params are not provided", async () => {
|
301
|
+
const response = await specHelper.runAction<any>("testAction", {
|
302
|
+
requiredParam: true,
|
303
|
+
});
|
304
|
+
expect(response.params.fancyParam).toEqual("abc123");
|
305
|
+
});
|
306
|
+
|
307
|
+
test("will use validator if provided", async () => {
|
308
|
+
const response = await specHelper.runAction("testAction", {
|
309
|
+
requiredParam: true,
|
310
|
+
fancyParam: 123,
|
311
|
+
});
|
312
|
+
expect(response.error).toMatch(/Error: fancyParam should be "abc123"/);
|
313
|
+
});
|
314
|
+
|
315
|
+
test("validator will have the API object in scope as this", async () => {
|
316
|
+
const response = await specHelper.runAction("testAction", {
|
317
|
+
requiredParam: true,
|
318
|
+
fancyParam: 123,
|
319
|
+
});
|
320
|
+
expect(response.error).toMatch(new RegExp(api.id));
|
321
|
+
});
|
322
|
+
|
323
|
+
test("will use formatter if provided (and still use validator)", async () => {
|
324
|
+
const response = await specHelper.runAction("testAction", {
|
325
|
+
requiredParam: true,
|
326
|
+
fancyParam: 123,
|
327
|
+
});
|
328
|
+
expect(
|
329
|
+
response.requesterInformation!.receivedParams.fancyParam,
|
330
|
+
).toEqual("123");
|
331
|
+
});
|
332
|
+
|
333
|
+
test("succeeds a validator which returns no response", async () => {
|
334
|
+
const response = await specHelper.runAction("testAction", {
|
335
|
+
requiredParam: true,
|
336
|
+
fancyParam: "abc123",
|
337
|
+
});
|
338
|
+
expect(response.error).toBeUndefined();
|
339
|
+
});
|
340
|
+
|
341
|
+
test("will filter params not set in the target action or global safelist", async () => {
|
342
|
+
const response = await specHelper.runAction("testAction", {
|
343
|
+
requiredParam: true,
|
344
|
+
sleepDuration: true,
|
345
|
+
});
|
346
|
+
expect(
|
347
|
+
response.requesterInformation!.receivedParams.requiredParam,
|
348
|
+
).toBeTruthy();
|
349
|
+
expect(
|
350
|
+
response.requesterInformation!.receivedParams.sleepDuration,
|
351
|
+
).toBeUndefined();
|
352
|
+
});
|
353
|
+
});
|
354
|
+
|
355
|
+
describe("Action Params schema type", () => {
|
356
|
+
beforeAll(() => {
|
357
|
+
api.actions.versions.testAction = [1];
|
358
|
+
api.actions.actions.testAction = {
|
359
|
+
//@ts-ignore
|
360
|
+
1: {
|
361
|
+
name: "testAction",
|
362
|
+
description: "this action has some required params",
|
363
|
+
version: 1,
|
364
|
+
inputs: {
|
365
|
+
schemaParam: {
|
366
|
+
schema: {
|
367
|
+
requiredParam: { required: true },
|
368
|
+
optionalParam: { required: false },
|
369
|
+
fancyParam: {
|
370
|
+
required: false,
|
371
|
+
default: () => {
|
372
|
+
return "abc123";
|
373
|
+
},
|
374
|
+
validator: function (s: unknown) {
|
375
|
+
if (s === "abc123") {
|
376
|
+
return true;
|
377
|
+
} else {
|
378
|
+
return (
|
379
|
+
'fancyParam should be "abc123". so says ' + this.id
|
380
|
+
);
|
381
|
+
}
|
382
|
+
},
|
383
|
+
formatter: (s: unknown) => {
|
384
|
+
return String(s);
|
385
|
+
},
|
386
|
+
},
|
387
|
+
},
|
388
|
+
},
|
389
|
+
},
|
390
|
+
run: async (data) => {
|
391
|
+
data.response!.params = data.params;
|
392
|
+
},
|
393
|
+
},
|
394
|
+
};
|
395
|
+
});
|
396
|
+
|
397
|
+
afterAll(() => {
|
398
|
+
delete api.actions.actions.testAction;
|
399
|
+
delete api.actions.versions.testAction;
|
400
|
+
config.general!.missingParamChecks = [null, "", undefined];
|
401
|
+
});
|
402
|
+
|
403
|
+
test("correct params that are falsey (false, []) should be allowed", async () => {
|
404
|
+
let response;
|
405
|
+
response = await specHelper.runAction("testAction", {
|
406
|
+
schemaParam: { requiredParam: false },
|
407
|
+
});
|
408
|
+
expect(response.params.schemaParam.requiredParam).toEqual(false);
|
409
|
+
response = await specHelper.runAction("testAction", {
|
410
|
+
schemaParam: { requiredParam: [] },
|
411
|
+
});
|
412
|
+
expect(response.params.schemaParam.requiredParam).toHaveLength(0);
|
413
|
+
});
|
414
|
+
|
415
|
+
test("will fail for missing or empty string params", async () => {
|
416
|
+
let response;
|
417
|
+
response = await specHelper.runAction("testAction", {
|
418
|
+
schemaParam: { requiredParam: "" },
|
419
|
+
});
|
420
|
+
expect(response.error).toContain(
|
421
|
+
"schemaParam.requiredParam is a required parameter for this action",
|
422
|
+
);
|
423
|
+
response = await specHelper.runAction("testAction", {
|
424
|
+
schemaParam: {},
|
425
|
+
});
|
426
|
+
expect(response.error).toContain(
|
427
|
+
"schemaParam.requiredParam is a required parameter for this action",
|
428
|
+
);
|
429
|
+
});
|
430
|
+
|
431
|
+
test("correct params respect config options", async () => {
|
432
|
+
let response: Record<string, any>;
|
433
|
+
config.general!.missingParamChecks = [undefined];
|
434
|
+
response = await specHelper.runAction("testAction", {
|
435
|
+
schemaParam: { requiredParam: "" },
|
436
|
+
});
|
437
|
+
expect(response.params.schemaParam.requiredParam).toEqual("");
|
438
|
+
response = await specHelper.runAction("testAction", {
|
439
|
+
schemaParam: { requiredParam: null },
|
440
|
+
});
|
441
|
+
expect(response.params.schemaParam.requiredParam).toBeNull();
|
442
|
+
});
|
443
|
+
|
444
|
+
test("will set a default when params are not provided", async () => {
|
445
|
+
const response = await specHelper.runAction<any>("testAction", {
|
446
|
+
schemaParam: { requiredParam: true },
|
447
|
+
});
|
448
|
+
expect(response.params.schemaParam.fancyParam).toEqual("abc123");
|
449
|
+
});
|
450
|
+
|
451
|
+
test("will use validator if provided", async () => {
|
452
|
+
const response = await specHelper.runAction("testAction", {
|
453
|
+
schemaParam: { requiredParam: true, fancyParam: 123 },
|
454
|
+
});
|
455
|
+
expect(response.error).toMatch(/Error: fancyParam should be "abc123"/);
|
456
|
+
});
|
457
|
+
|
458
|
+
test("validator will have the API object in scope as this", async () => {
|
459
|
+
const response = await specHelper.runAction("testAction", {
|
460
|
+
schemaParam: { requiredParam: true, fancyParam: 123 },
|
461
|
+
});
|
462
|
+
expect(response.error).toMatch(new RegExp(api.id));
|
463
|
+
});
|
464
|
+
|
465
|
+
test("will use formatter if provided (and still use validator)", async () => {
|
466
|
+
const response = await specHelper.runAction("testAction", {
|
467
|
+
schemaParam: { requiredParam: true, fancyParam: 123 },
|
468
|
+
});
|
469
|
+
expect(
|
470
|
+
response.requesterInformation!.receivedParams.schemaParam.fancyParam,
|
471
|
+
).toEqual("123");
|
472
|
+
});
|
473
|
+
|
474
|
+
test("will filter params not set in the target action or global safelist", async () => {
|
475
|
+
const response = await specHelper.runAction("testAction", {
|
476
|
+
schemaParam: { requiredParam: true, sleepDuration: true },
|
477
|
+
});
|
478
|
+
expect(
|
479
|
+
response.requesterInformation!.receivedParams.schemaParam
|
480
|
+
.requiredParam,
|
481
|
+
).toBeTruthy();
|
482
|
+
expect(
|
483
|
+
response.requesterInformation!.receivedParams.schemaParam
|
484
|
+
.sleepDuration,
|
485
|
+
).toBeUndefined();
|
486
|
+
});
|
487
|
+
});
|
488
|
+
|
489
|
+
describe("named action validations", () => {
|
490
|
+
beforeAll(() => {
|
491
|
+
api.validators = {
|
492
|
+
validator1: (param: unknown) => {
|
493
|
+
if (typeof param !== "string") {
|
494
|
+
throw new Error("only strings");
|
495
|
+
}
|
496
|
+
return true;
|
497
|
+
},
|
498
|
+
validator2: (param: unknown) => {
|
499
|
+
if (param !== "correct") {
|
500
|
+
throw new Error("that is not correct");
|
501
|
+
}
|
502
|
+
return true;
|
503
|
+
},
|
504
|
+
};
|
505
|
+
|
506
|
+
api.actions.versions.testAction = [1];
|
507
|
+
api.actions.actions.testAction = {
|
508
|
+
//@ts-ignore
|
509
|
+
1: {
|
510
|
+
name: "testAction",
|
511
|
+
description: "I am a test",
|
512
|
+
inputs: {
|
513
|
+
a: {
|
514
|
+
validator: [
|
515
|
+
"api.validators.validator1",
|
516
|
+
"api.validators.validator2",
|
517
|
+
],
|
518
|
+
},
|
519
|
+
},
|
520
|
+
run: async () => {},
|
521
|
+
},
|
522
|
+
};
|
523
|
+
});
|
524
|
+
|
525
|
+
afterAll(() => {
|
526
|
+
delete api.actions.versions.testAction;
|
527
|
+
delete api.actions.actions.testAction;
|
528
|
+
delete api.validators;
|
529
|
+
});
|
530
|
+
|
531
|
+
test("runs validator arrays in the proper order", async () => {
|
532
|
+
const response = await specHelper.runAction("testAction", { a: 6 });
|
533
|
+
expect(response.error).toEqual("Error: only strings");
|
534
|
+
});
|
535
|
+
|
536
|
+
test("runs more than 1 validator", async () => {
|
537
|
+
const response = await specHelper.runAction("testAction", {
|
538
|
+
a: "hello",
|
539
|
+
});
|
540
|
+
expect(response.error).toEqual("Error: that is not correct");
|
541
|
+
});
|
542
|
+
|
543
|
+
test("succeeds multiple validators", async () => {
|
544
|
+
const response = await specHelper.runAction("testAction", {
|
545
|
+
a: "correct",
|
546
|
+
});
|
547
|
+
expect(response.error).toBeUndefined();
|
548
|
+
});
|
549
|
+
});
|
550
|
+
|
551
|
+
describe("named action formatters", () => {
|
552
|
+
beforeAll(() => {
|
553
|
+
api._formatters = {
|
554
|
+
formatter1: (param: unknown) => {
|
555
|
+
return "*" + param + "*";
|
556
|
+
},
|
557
|
+
formatter2: (param: unknown) => {
|
558
|
+
return "~" + param + "~";
|
559
|
+
},
|
560
|
+
};
|
561
|
+
|
562
|
+
api.actions.versions.testAction = [1];
|
563
|
+
api.actions.actions.testAction = {
|
564
|
+
// @ts-ignore
|
565
|
+
1: {
|
566
|
+
name: "testAction",
|
567
|
+
description: "I am a test",
|
568
|
+
inputs: {
|
569
|
+
a: {
|
570
|
+
formatter: [
|
571
|
+
"api._formatters.formatter1",
|
572
|
+
"api._formatters.formatter2",
|
573
|
+
],
|
574
|
+
},
|
575
|
+
},
|
576
|
+
run: async (data) => {
|
577
|
+
data.response!.a = data.params!.a;
|
578
|
+
},
|
579
|
+
},
|
580
|
+
};
|
581
|
+
});
|
582
|
+
|
583
|
+
afterAll(() => {
|
584
|
+
delete api.actions.versions.testAction;
|
585
|
+
delete api.actions.actions.testAction;
|
586
|
+
delete api._formatters;
|
587
|
+
});
|
588
|
+
|
589
|
+
test("runs formatter arrays in the proper order", async () => {
|
590
|
+
const response = await specHelper.runAction<any>("testAction", {
|
591
|
+
a: 6,
|
592
|
+
});
|
593
|
+
expect(response.a).toEqual("~*6*~");
|
594
|
+
});
|
595
|
+
});
|
596
|
+
|
597
|
+
describe("immutability of data.params", () => {
|
598
|
+
beforeAll(() => {
|
599
|
+
api.actions.versions.testAction = [1];
|
600
|
+
api.actions.actions.testAction = {
|
601
|
+
// @ts-ignore
|
602
|
+
1: {
|
603
|
+
name: "testAction",
|
604
|
+
description: "I am a test",
|
605
|
+
inputs: {
|
606
|
+
a: { required: true },
|
607
|
+
},
|
608
|
+
run: async ({ params, response }) => {
|
609
|
+
params!.a = "changed!";
|
610
|
+
response!.a = params!.a;
|
611
|
+
},
|
612
|
+
},
|
613
|
+
};
|
614
|
+
});
|
615
|
+
|
616
|
+
afterAll(() => {
|
617
|
+
delete api.actions.actions.testAction;
|
618
|
+
delete api.actions.versions.testAction;
|
619
|
+
});
|
620
|
+
|
621
|
+
test("prevents data.params from being modified", async () => {
|
622
|
+
const response = await specHelper.runAction<any>("testAction", {
|
623
|
+
a: "original",
|
624
|
+
});
|
625
|
+
expect(response.a).toBeUndefined();
|
626
|
+
expect(response.error).toMatch(
|
627
|
+
/Cannot assign to read only property 'a' of object/,
|
628
|
+
);
|
629
|
+
});
|
630
|
+
});
|
631
|
+
});
|
632
|
+
});
|