@saasquatch/program-boilerplate 3.7.4 → 3.10.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/dist/buh.d.ts ADDED
@@ -0,0 +1 @@
1
+ export {};
package/dist/buh.js ADDED
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const jsonata_1 = require("./jsonata");
4
+ function main() {
5
+ console.log(jsonata_1.safeJsonata("", {}));
6
+ }
7
+ main();
package/dist/index.d.ts CHANGED
@@ -8,9 +8,9 @@ import * as types from "./types";
8
8
  import { Program, ProgramRequirement, RequirementValidationResult, ValidationProgramField, ProgramTriggerBody } from "./types/rpc";
9
9
  import { timeboxExpression, timeboxedJsonata, safeJsonata } from "./jsonata";
10
10
  import { ProgramType } from "./types/saasquatch";
11
- import { inferType, getGoalAnalyticTimestamp, setRewardSchedule, numToEquality, getTriggerSchema, getUserCustomFieldsFromJsonata, getRewardUnitsFromJsonata } from "./utils";
11
+ import { inferType, getGoalAnalyticTimestamp, setRewardSchedule, numToEquality, getTriggerSchema, getUserCustomFieldsFromJsonata, getRewardUnitsFromJsonata, loadStandardWebtaskConfig, WebtaskConfig } from "./utils";
12
12
  export { types };
13
- export { Transaction, ProgramTriggerBody, Program, ProgramType, RequirementValidationResult, ProgramRequirement, ValidationProgramField, meetEventTriggerRules, meetCustomFieldRules, meetEdgeTriggerConditions, rewardEmailQuery, setRewardSchedule, getGoalAnalyticTimestamp, triggerProgram, inferType, numToEquality, getTriggerSchema, getUserCustomFieldsFromJsonata, getRewardUnitsFromJsonata, timeboxExpression, timeboxedJsonata, safeJsonata, getLogger, setLogLevel, };
13
+ export { Transaction, ProgramTriggerBody, Program, ProgramType, RequirementValidationResult, ProgramRequirement, ValidationProgramField, meetEventTriggerRules, meetCustomFieldRules, meetEdgeTriggerConditions, rewardEmailQuery, setRewardSchedule, getGoalAnalyticTimestamp, triggerProgram, inferType, numToEquality, getTriggerSchema, getUserCustomFieldsFromJsonata, getRewardUnitsFromJsonata, timeboxExpression, timeboxedJsonata, safeJsonata, getLogger, setLogLevel, loadStandardWebtaskConfig, };
14
14
  /**
15
15
  * Returns an express server that serves the provided handlers
16
16
  * as a program
@@ -20,3 +20,4 @@ export { Transaction, ProgramTriggerBody, Program, ProgramType, RequirementValid
20
20
  * @return {Object} The express server
21
21
  */
22
22
  export declare function webtask(program?: Program): express.Application;
23
+ export declare function runWebtask(webtask: express.Application, config: WebtaskConfig): void;
package/dist/index.js CHANGED
@@ -22,7 +22,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
22
22
  return (mod && mod.__esModule) ? mod : { "default": mod };
23
23
  };
24
24
  Object.defineProperty(exports, "__esModule", { value: true });
25
- exports.webtask = exports.setLogLevel = exports.getLogger = exports.safeJsonata = exports.timeboxedJsonata = exports.timeboxExpression = exports.getRewardUnitsFromJsonata = exports.getUserCustomFieldsFromJsonata = exports.getTriggerSchema = exports.numToEquality = exports.inferType = exports.triggerProgram = exports.getGoalAnalyticTimestamp = exports.setRewardSchedule = exports.rewardEmailQuery = exports.meetEdgeTriggerConditions = exports.meetCustomFieldRules = exports.meetEventTriggerRules = exports.Transaction = exports.types = void 0;
25
+ exports.runWebtask = exports.webtask = exports.loadStandardWebtaskConfig = exports.setLogLevel = exports.getLogger = exports.safeJsonata = exports.timeboxedJsonata = exports.timeboxExpression = exports.getRewardUnitsFromJsonata = exports.getUserCustomFieldsFromJsonata = exports.getTriggerSchema = exports.numToEquality = exports.inferType = exports.triggerProgram = exports.getGoalAnalyticTimestamp = exports.setRewardSchedule = exports.rewardEmailQuery = exports.meetEdgeTriggerConditions = exports.meetCustomFieldRules = exports.meetEventTriggerRules = exports.Transaction = exports.types = void 0;
26
26
  const express_1 = __importDefault(require("express"));
27
27
  const conversion_1 = require("./conversion");
28
28
  Object.defineProperty(exports, "meetCustomFieldRules", { enumerable: true, get: function () { return conversion_1.meetCustomFieldRules; } });
@@ -52,6 +52,7 @@ Object.defineProperty(exports, "numToEquality", { enumerable: true, get: functio
52
52
  Object.defineProperty(exports, "getTriggerSchema", { enumerable: true, get: function () { return utils_1.getTriggerSchema; } });
53
53
  Object.defineProperty(exports, "getUserCustomFieldsFromJsonata", { enumerable: true, get: function () { return utils_1.getUserCustomFieldsFromJsonata; } });
54
54
  Object.defineProperty(exports, "getRewardUnitsFromJsonata", { enumerable: true, get: function () { return utils_1.getRewardUnitsFromJsonata; } });
55
+ Object.defineProperty(exports, "loadStandardWebtaskConfig", { enumerable: true, get: function () { return utils_1.loadStandardWebtaskConfig; } });
55
56
  const logger_3 = require("@saasquatch/logger");
56
57
  /**
57
58
  * Returns an express server that serves the provided handlers
@@ -67,21 +68,59 @@ function webtask(program = {}) {
67
68
  const logger = logger_2.getLogger("program-boilerplate");
68
69
  app.use(express_1.default.json({ limit: process.env.MAX_PAYLOAD_SIZE || "1mb" }));
69
70
  app.use(compression());
70
- app.use(logger_3.httpLogMiddleware(logger));
71
+ app.use(logger_3.httpLogMiddleware(logger, { logNonErrorResponses: false }));
71
72
  // Enforce HTTPS. The server does not redirect http -> https
72
73
  // because OWASP advises not to
73
74
  app.use((req, res, next) => {
74
75
  if (process.env.NODE_ENV === "production" &&
75
- req.header("X-Forwarded-Proto") !== "https") {
76
+ req.header("X-Forwarded-Proto") !== "https" &&
77
+ !["/healthz", "/livez", "/readyz"].includes(req.path)) {
76
78
  return res.status(403).send({ message: "SSL required" });
77
79
  }
78
80
  // allow the request to continue if https is used
79
81
  next();
80
82
  });
81
- app.post("/*", (context, res) => {
82
- const { json, code } = trigger_1.triggerProgram(context.body, program);
83
- res.status(code).json(json);
83
+ const healthCheck = (_req, res) => {
84
+ const terminating = app.locals["terminating"];
85
+ if (typeof terminating === "boolean" && terminating) {
86
+ logger.info("App is in TERMINATING state, sending health check failure");
87
+ return res.status(503).json({ status: "TERMINATING" });
88
+ }
89
+ return res.status(200).json({ status: "OK" });
90
+ };
91
+ app.get("/healthz", healthCheck);
92
+ app.get("/livez", healthCheck);
93
+ app.get("/readyz", healthCheck);
94
+ app.post("/*", (req, res) => {
95
+ const { json, code } = trigger_1.triggerProgram(req.body, program);
96
+ return res.status(code).json(json);
84
97
  });
85
98
  return app;
86
99
  }
87
100
  exports.webtask = webtask;
101
+ function runWebtask(webtask, config) {
102
+ const logger = logger_2.getLogger("program-boilerplate");
103
+ const server = webtask.listen(config.port, () => logger.notice(`${config.webtaskName} running on port ${config.port}`));
104
+ if (config.keepAliveTimeoutSeconds !== undefined) {
105
+ // https://cloud.google.com/load-balancing/docs/https/https-logging-monitoring#failure-messages
106
+ // (see the section on backend_connection_closed_before_data_sent_to_client)
107
+ server.keepAliveTimeout = config.keepAliveTimeoutSeconds * 1000;
108
+ server.headersTimeout = (config.keepAliveTimeoutSeconds + 1) * 1000;
109
+ }
110
+ const gracefulShutdown = (signal) => () => {
111
+ var _a;
112
+ const isTerminating = webtask.locals["terminating"];
113
+ if (typeof isTerminating === "boolean" && isTerminating) {
114
+ logger.warn("Server is already in TERMINATING state, not starting shutdown procedure again");
115
+ return;
116
+ }
117
+ webtask.locals["terminating"] = true;
118
+ logger.notice(`Received ${signal} signal, starting shutdown procedure`);
119
+ setTimeout(() => {
120
+ server.close(() => logger.notice("Server closed"));
121
+ }, ((_a = config.terminationDelaySeconds) !== null && _a !== void 0 ? _a : 1) * 1000);
122
+ };
123
+ process.on("SIGTERM", gracefulShutdown("SIGTERM"));
124
+ process.on("SIGINT", gracefulShutdown("SIGINT"));
125
+ }
126
+ exports.runWebtask = runWebtask;
package/dist/trigger.js CHANGED
@@ -103,8 +103,7 @@ function handleProgramIntrospection(body, program) {
103
103
  const handleIntrospection = program["PROGRAM_INTROSPECTION"];
104
104
  try {
105
105
  const newTemplate = (handleIntrospection &&
106
- (handleIntrospection(template, rules, bodyProgram, tenant) ||
107
- handleIntrospection(template, rules))) ||
106
+ handleIntrospection(template, rules, bodyProgram, tenant)) ||
108
107
  template;
109
108
  return {
110
109
  json: newTemplate,
@@ -1,3 +1,4 @@
1
+ import { ProgramTemplateBuilder } from "@saasquatch/schema/types/ProgramTemplate";
1
2
  import Transaction from "../transaction";
2
3
  /********************************************************/
3
4
  /********************************************************/
@@ -29,12 +30,18 @@ export declare type ProgramTriggerBody = {
29
30
  activeTrigger: any;
30
31
  program: any;
31
32
  tenant: {
33
+ impactBrandId: number | undefined | null;
32
34
  settings: {
33
35
  suspectedFraudModerationState: string;
34
36
  };
35
37
  };
36
38
  ids: string[];
37
39
  };
40
+ declare type TenantInfo = {
41
+ tenantAlias: string;
42
+ featureFlags?: string[] | null;
43
+ isLiveMode: boolean;
44
+ };
38
45
  /**
39
46
  * A JSON request body for the PROGRAM_INTROSPECTION case
40
47
  */
@@ -43,10 +50,7 @@ export declare type ProgramIntrospectionBody = {
43
50
  template: any;
44
51
  rules: any;
45
52
  program: any;
46
- tenant: {
47
- tenantAlias: string;
48
- isLiveMode: boolean;
49
- };
53
+ tenant: TenantInfo;
50
54
  };
51
55
  /**
52
56
  * The program validation request contains the requirement key
@@ -90,13 +94,21 @@ export declare type ProgramVariableSchemaRequestBody = {
90
94
  * Handler for the default program trigger cases
91
95
  */
92
96
  export declare type ProgramTriggerHandler = (transaction: Transaction) => void;
97
+ /**
98
+ * Output of program introspection
99
+ */
100
+ export declare type ProgramIntrospectionResponse = ProgramTemplateBuilder | {
101
+ template: ProgramTemplateBuilder;
102
+ errors?: {
103
+ key: string;
104
+ jsonPointer: string;
105
+ description: string;
106
+ }[];
107
+ };
93
108
  /**
94
109
  * Introspection handler
95
110
  */
96
- export declare type ProgramIntrospectionHandler = (template: any, rules: any, program?: any, tenant?: {
97
- tenantAlias: string;
98
- isLiveMode: boolean;
99
- }) => any;
111
+ export declare type ProgramIntrospectionHandler = (template: ProgramTemplateBuilder, rules: any, program: any, tenant: TenantInfo) => ProgramIntrospectionResponse;
100
112
  /**
101
113
  * Handler for an individual program requirement validation.
102
114
  * Handlers will be exported as key-value pairs in the
package/dist/utils.d.ts CHANGED
@@ -1,5 +1,12 @@
1
1
  import { ProgramTriggerBody } from "./types/rpc";
2
2
  import jsonata from "jsonata";
3
+ export declare type WebtaskConfig = {
4
+ port: number;
5
+ webtaskName: string;
6
+ keepAliveTimeoutSeconds: number;
7
+ terminationDelaySeconds: number;
8
+ };
9
+ export declare function loadStandardWebtaskConfig(): WebtaskConfig;
3
10
  /**
4
11
  * Append a reward schedule to the template and return the new template
5
12
  *
package/dist/utils.js CHANGED
@@ -3,9 +3,33 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.getRewardUnitsFromJsonata = exports.getUserCustomFieldsFromJsonata = exports.getTriggerSchema = exports.numToEquality = exports.inferType = exports.getGoalAnalyticTimestamp = exports.setRewardSchedule = void 0;
6
+ exports.getRewardUnitsFromJsonata = exports.getUserCustomFieldsFromJsonata = exports.getTriggerSchema = exports.numToEquality = exports.inferType = exports.getGoalAnalyticTimestamp = exports.setRewardSchedule = exports.loadStandardWebtaskConfig = void 0;
7
7
  const queries_1 = require("./queries");
8
8
  const jsonata_paths_extractor_1 = __importDefault(require("@saasquatch/jsonata-paths-extractor"));
9
+ function loadStandardWebtaskConfig() {
10
+ const optionalInt = (key, defaultVal) => {
11
+ const env = process.env[key];
12
+ if (!env) {
13
+ return defaultVal;
14
+ }
15
+ const parsedEnv = parseInt(env, 10);
16
+ if (Number.isNaN(parsedEnv)) {
17
+ throw new Error(`Environment variable "${key}" is not an integer`);
18
+ }
19
+ return parsedEnv;
20
+ };
21
+ const port = optionalInt("PORT", 3000);
22
+ const keepAliveTimeoutSeconds = optionalInt("HTTP_KEEP_ALIVE_SECONDS", 60);
23
+ const terminationDelaySeconds = optionalInt("TERMINATION_DELAY_SECONDS", 0);
24
+ const webtaskName = process.env["PROGRAM_NAME"] || "Unknown Program";
25
+ return {
26
+ port,
27
+ webtaskName,
28
+ keepAliveTimeoutSeconds,
29
+ terminationDelaySeconds,
30
+ };
31
+ }
32
+ exports.loadStandardWebtaskConfig = loadStandardWebtaskConfig;
9
33
  /**
10
34
  * Append a reward schedule to the template and return the new template
11
35
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@saasquatch/program-boilerplate",
3
- "version": "3.7.4",
3
+ "version": "3.10.0-0",
4
4
  "description": "Boilerplate for writing programs",
5
5
  "main": "dist/index.js",
6
6
  "files": [
@@ -17,14 +17,17 @@
17
17
  },
18
18
  "author": "ReferralSaaSquatch.com, Inc.",
19
19
  "license": "MIT",
20
+ "resolutions": {
21
+ "source-map": "^0.7.4"
22
+ },
20
23
  "devDependencies": {
21
24
  "@types/express": "^4.17.11",
22
25
  "@types/node": "^13.13.50",
23
26
  "@types/supertest": "^2.0.11",
24
27
  "jest": "^26.6.3",
25
28
  "jest-cucumber": "^3.0.1",
26
- "np": "^7.7.0",
27
29
  "prettier": "^2.2.1",
30
+ "source-map": "^0.7.4",
28
31
  "supertest": "^6.1.3",
29
32
  "ts-jest": "^26.5.5",
30
33
  "ts-node": "^9.1.1",
@@ -33,7 +36,8 @@
33
36
  },
34
37
  "dependencies": {
35
38
  "@saasquatch/jsonata-paths-extractor": "^1.0.1",
36
- "@saasquatch/logger": "^1.0.0",
39
+ "@saasquatch/logger": "^1.5.1",
40
+ "@saasquatch/schema": "^1.28.2",
37
41
  "bson-objectid": "^1.3.1",
38
42
  "compression": "^1.7.4",
39
43
  "express": "^4.17.1",