@pipedream/platform
Advanced tools
Comparing version 0.10.0 to 1.0.0
// purposefully test COMPILED platform.js! (just as lambda_maker would) | ||
const { $sendConfigRuntimeTypeChecker, HTTP_METHODS } = require("../dist") | ||
const { | ||
$sendConfigRuntimeTypeChecker, HTTP_METHODS, | ||
} = require("../dist"); | ||
function randString() { | ||
return ""+Math.random() | ||
return "" + Math.random(); | ||
} | ||
function randHttpMethod() { | ||
const idx = Math.floor(Math.random() * HTTP_METHODS.length) | ||
return HTTP_METHODS[idx] | ||
const idx = Math.floor(Math.random() * HTTP_METHODS.length); | ||
return HTTP_METHODS[idx]; | ||
} | ||
const emptyConfig = {} | ||
const emptyConfig = {}; | ||
@@ -19,4 +21,4 @@ // XXX would be nice to generate a lot of these tests automatically... | ||
describe("$send.email", () => { | ||
const checker = $sendConfigRuntimeTypeChecker.email | ||
let config | ||
const checker = $sendConfigRuntimeTypeChecker.email; | ||
let config; | ||
beforeEach(() => { | ||
@@ -27,22 +29,22 @@ config = { | ||
text: randString(), | ||
} | ||
}) | ||
}; | ||
}); | ||
it("should pass with empty config", () => { | ||
expect(() => checker(emptyConfig)).not.toThrow() | ||
}) | ||
expect(() => checker(emptyConfig)).not.toThrow(); | ||
}); | ||
it("should pass with full config", () => { | ||
expect(() => checker(config)).not.toThrow() | ||
}) | ||
expect(() => checker(config)).not.toThrow(); | ||
}); | ||
xit("should fail with .__extra", () => { | ||
config.__extra = randString() | ||
expect(() => checker(config)).toThrow() | ||
}) | ||
}) | ||
it.skip("should fail with .__extra", () => { | ||
config.__extra = randString(); | ||
expect(() => checker(config)).toThrow(); | ||
}); | ||
}); | ||
describe("$send.emit", () => { | ||
const checker = $sendConfigRuntimeTypeChecker.emit | ||
let config | ||
const checker = $sendConfigRuntimeTypeChecker.emit; | ||
let config; | ||
beforeEach(() => { | ||
@@ -53,22 +55,22 @@ config = { | ||
}, | ||
} | ||
}) | ||
}; | ||
}); | ||
it("should fail with empty config", () => { | ||
expect(() => checker({})).toThrow() | ||
}) | ||
expect(() => checker({})).toThrow(); | ||
}); | ||
it("should pass with full config", () => { | ||
expect(() => checker(config)).not.toThrow() | ||
}) | ||
expect(() => checker(config)).not.toThrow(); | ||
}); | ||
xit("should fail with .__extra", () => { | ||
config.__extra = randString() | ||
expect(() => checker(config)).toThrow() | ||
}) | ||
}) | ||
it.skip("should fail with .__extra", () => { | ||
config.__extra = randString(); | ||
expect(() => checker(config)).toThrow(); | ||
}); | ||
}); | ||
describe("$send.http", () => { | ||
const checker = $sendConfigRuntimeTypeChecker.http | ||
let config | ||
const checker = $sendConfigRuntimeTypeChecker.http; | ||
let config; | ||
beforeEach(() => { | ||
@@ -91,52 +93,52 @@ config = { | ||
}, | ||
} | ||
}) | ||
}; | ||
}); | ||
it("should fail with empty config", () => { | ||
expect(() => checker({})).toThrow() | ||
}) | ||
expect(() => checker({})).toThrow(); | ||
}); | ||
it("should pass with full config", () => { | ||
expect(() => checker(config)).not.toThrow() | ||
}) | ||
expect(() => checker(config)).not.toThrow(); | ||
}); | ||
xit("should fail with .__extra", () => { | ||
config.__extra = randString() | ||
expect(() => checker(config)).toThrow() | ||
}) | ||
it.skip("should fail with .__extra", () => { | ||
config.__extra = randString(); | ||
expect(() => checker(config)).toThrow(); | ||
}); | ||
it("should fail without .method", () => { | ||
delete config.method | ||
expect(() => checker(config)).toThrow() | ||
}) | ||
delete config.method; | ||
expect(() => checker(config)).toThrow(); | ||
}); | ||
it("should fail without .url", () => { | ||
delete config.url | ||
expect(() => checker(config)).toThrow() | ||
}) | ||
delete config.url; | ||
expect(() => checker(config)).toThrow(); | ||
}); | ||
it("should pass without .auth", () => { | ||
delete config.auth | ||
expect(() => checker(config)).not.toThrow() | ||
}) | ||
delete config.auth; | ||
expect(() => checker(config)).not.toThrow(); | ||
}); | ||
it("should fail without .auth.username", () => { | ||
delete config.auth.username | ||
expect(() => checker(config)).toThrow() | ||
}) | ||
delete config.auth.username; | ||
expect(() => checker(config)).toThrow(); | ||
}); | ||
it("should fail without .auth.password", () => { | ||
delete config.auth.password | ||
expect(() => checker(config)).toThrow() | ||
}) | ||
delete config.auth.password; | ||
expect(() => checker(config)).toThrow(); | ||
}); | ||
xit("should fail with .auth.__extra", () => { | ||
config.auth.__extra = randString() | ||
expect(() => checker(config)).toThrow() | ||
}) | ||
it.skip("should fail with .auth.__extra", () => { | ||
config.auth.__extra = randString(); | ||
expect(() => checker(config)).toThrow(); | ||
}); | ||
it("should pass without .data", () => { | ||
delete config.data | ||
expect(() => checker(config)).not.toThrow() | ||
}) | ||
delete config.data; | ||
expect(() => checker(config)).not.toThrow(); | ||
}); | ||
@@ -147,20 +149,20 @@ it("should pass with object .data", () => { | ||
[randString()]: randString(), | ||
} | ||
expect(() => checker(config)).not.toThrow() | ||
}) | ||
}; | ||
expect(() => checker(config)).not.toThrow(); | ||
}); | ||
it("should pass without .headers", () => { | ||
delete config.headers | ||
expect(() => checker(config)).not.toThrow() | ||
}) | ||
delete config.headers; | ||
expect(() => checker(config)).not.toThrow(); | ||
}); | ||
it("should pass without .params", () => { | ||
delete config.params | ||
expect(() => checker(config)).not.toThrow() | ||
}) | ||
}) | ||
delete config.params; | ||
expect(() => checker(config)).not.toThrow(); | ||
}); | ||
}); | ||
describe("$send.s3", () => { | ||
const checker = $sendConfigRuntimeTypeChecker.s3 | ||
let config | ||
const checker = $sendConfigRuntimeTypeChecker.s3; | ||
let config; | ||
beforeEach(() => { | ||
@@ -171,37 +173,37 @@ config = { | ||
prefix: randString(), | ||
} | ||
}) | ||
}; | ||
}); | ||
it("should fail with empty config", () => { | ||
expect(() => checker({})).toThrow() | ||
}) | ||
expect(() => checker({})).toThrow(); | ||
}); | ||
it("should pass with full config", () => { | ||
expect(() => checker(config)).not.toThrow() | ||
}) | ||
expect(() => checker(config)).not.toThrow(); | ||
}); | ||
xit("should fail with .__extra", () => { | ||
config.__extra = randString() | ||
expect(() => checker(config)).toThrow() | ||
}) | ||
it.skip("should fail with .__extra", () => { | ||
config.__extra = randString(); | ||
expect(() => checker(config)).toThrow(); | ||
}); | ||
it("should fail without .bucket", () => { | ||
delete config.bucket | ||
expect(() => checker(config)).toThrow() | ||
}) | ||
delete config.bucket; | ||
expect(() => checker(config)).toThrow(); | ||
}); | ||
it("should fail without .payload", () => { | ||
delete config.payload | ||
expect(() => checker(config)).toThrow() | ||
}) | ||
delete config.payload; | ||
expect(() => checker(config)).toThrow(); | ||
}); | ||
it("should fail without .prefix", () => { | ||
delete config.prefix | ||
expect(() => checker(config)).toThrow() | ||
}) | ||
}) | ||
delete config.prefix; | ||
expect(() => checker(config)).toThrow(); | ||
}); | ||
}); | ||
describe("$send.sql", () => { | ||
const checker = $sendConfigRuntimeTypeChecker.sql | ||
let config | ||
const checker = $sendConfigRuntimeTypeChecker.sql; | ||
let config; | ||
beforeEach(() => { | ||
@@ -211,32 +213,32 @@ config = { | ||
table: randString(), | ||
} | ||
}) | ||
}; | ||
}); | ||
it("should fail with empty config", () => { | ||
expect(() => checker({})).toThrow() | ||
}) | ||
expect(() => checker({})).toThrow(); | ||
}); | ||
it("should pass with full config", () => { | ||
expect(() => checker(config)).not.toThrow() | ||
}) | ||
expect(() => checker(config)).not.toThrow(); | ||
}); | ||
xit("should fail with .__extra", () => { | ||
config.__extra = randString() | ||
expect(() => checker(config)).toThrow() | ||
}) | ||
it.skip("should fail with .__extra", () => { | ||
config.__extra = randString(); | ||
expect(() => checker(config)).toThrow(); | ||
}); | ||
it("should fail without .payload", () => { | ||
delete config.payload | ||
expect(() => checker(config)).toThrow() | ||
}) | ||
delete config.payload; | ||
expect(() => checker(config)).toThrow(); | ||
}); | ||
it("should fail without .table", () => { | ||
delete config.table | ||
expect(() => checker(config)).toThrow() | ||
}) | ||
}) | ||
delete config.table; | ||
expect(() => checker(config)).toThrow(); | ||
}); | ||
}); | ||
describe("$send.snowflake", () => { | ||
const checker = $sendConfigRuntimeTypeChecker.snowflake | ||
let config | ||
const checker = $sendConfigRuntimeTypeChecker.snowflake; | ||
let config; | ||
beforeEach(() => { | ||
@@ -253,67 +255,67 @@ config = { | ||
user: randString(), | ||
} | ||
}) | ||
}; | ||
}); | ||
it("should fail with empty config", () => { | ||
expect(() => checker({})).toThrow() | ||
}) | ||
expect(() => checker({})).toThrow(); | ||
}); | ||
it("should pass with full config", () => { | ||
expect(() => checker(config)).not.toThrow() | ||
}) | ||
expect(() => checker(config)).not.toThrow(); | ||
}); | ||
xit("should fail with .__extra", () => { | ||
config.__extra = randString() | ||
expect(() => checker(config)).toThrow() | ||
}) | ||
it.skip("should fail with .__extra", () => { | ||
config.__extra = randString(); | ||
expect(() => checker(config)).toThrow(); | ||
}); | ||
it("should fail without .account", () => { | ||
delete config.account | ||
expect(() => checker(config)).toThrow() | ||
}) | ||
delete config.account; | ||
expect(() => checker(config)).toThrow(); | ||
}); | ||
it("should fail without .database", () => { | ||
delete config.database | ||
expect(() => checker(config)).toThrow() | ||
}) | ||
delete config.database; | ||
expect(() => checker(config)).toThrow(); | ||
}); | ||
it("should fail without .host", () => { | ||
delete config.host | ||
expect(() => checker(config)).toThrow() | ||
}) | ||
delete config.host; | ||
expect(() => checker(config)).toThrow(); | ||
}); | ||
it("should fail without .payload", () => { | ||
delete config.payload | ||
expect(() => checker(config)).toThrow() | ||
}) | ||
delete config.payload; | ||
expect(() => checker(config)).toThrow(); | ||
}); | ||
it("should fail without .pipe_name", () => { | ||
delete config.pipe_name | ||
expect(() => checker(config)).toThrow() | ||
}) | ||
delete config.pipe_name; | ||
expect(() => checker(config)).toThrow(); | ||
}); | ||
it("should fail without .private_key", () => { | ||
delete config.private_key | ||
expect(() => checker(config)).toThrow() | ||
}) | ||
delete config.private_key; | ||
expect(() => checker(config)).toThrow(); | ||
}); | ||
it("should fail without .schema", () => { | ||
delete config.schema | ||
expect(() => checker(config)).toThrow() | ||
}) | ||
delete config.schema; | ||
expect(() => checker(config)).toThrow(); | ||
}); | ||
it("should fail without .stage_name", () => { | ||
delete config.stage_name | ||
expect(() => checker(config)).toThrow() | ||
}) | ||
delete config.stage_name; | ||
expect(() => checker(config)).toThrow(); | ||
}); | ||
it("should fail without .user", () => { | ||
delete config.stage_name | ||
expect(() => checker(config)).toThrow() | ||
}) | ||
}) | ||
delete config.stage_name; | ||
expect(() => checker(config)).toThrow(); | ||
}); | ||
}); | ||
describe("$send.sse", () => { | ||
const checker = $sendConfigRuntimeTypeChecker.sse | ||
let config | ||
const checker = $sendConfigRuntimeTypeChecker.sse; | ||
let config; | ||
beforeEach(() => { | ||
@@ -323,27 +325,27 @@ config = { | ||
payload: randString(), | ||
} | ||
}) | ||
}; | ||
}); | ||
it("should fail with empty config", () => { | ||
expect(() => checker({})).toThrow() | ||
}) | ||
expect(() => checker({})).toThrow(); | ||
}); | ||
it("should pass with full config", () => { | ||
expect(() => checker(config)).not.toThrow() | ||
}) | ||
expect(() => checker(config)).not.toThrow(); | ||
}); | ||
xit("should fail with .__extra", () => { | ||
config.__extra = randString() | ||
expect(() => checker(config)).toThrow() | ||
}) | ||
it.skip("should fail with .__extra", () => { | ||
config.__extra = randString(); | ||
expect(() => checker(config)).toThrow(); | ||
}); | ||
it("should fail without .channel", () => { | ||
delete config.channel | ||
expect(() => checker(config)).toThrow() | ||
}) | ||
delete config.channel; | ||
expect(() => checker(config)).toThrow(); | ||
}); | ||
it("should fail without .payload", () => { | ||
delete config.payload | ||
expect(() => checker(config)).toThrow() | ||
}) | ||
}) | ||
delete config.payload; | ||
expect(() => checker(config)).toThrow(); | ||
}); | ||
}); |
@@ -1,11 +0,21 @@ | ||
const { axios } = require("../dist") | ||
const { axios } = require("../dist"); | ||
describe("axios", () => { | ||
it("should fail as expected", async () => { | ||
const step = {} | ||
const step = {}; | ||
await expect(axios(step, { | ||
url: "http://56371f3c71069f63d769d0b7ddeca4ac.x.pipedream.net/this-should-404", | ||
})).rejects.toThrow() | ||
expect(step.debug).toHaveProperty("config") | ||
}) | ||
}) | ||
})).rejects.toThrow(); | ||
expect(step.debug).toHaveProperty("config"); | ||
}); | ||
it("should contain debug", async () => { | ||
const step = {}; | ||
await axios(step, { | ||
url: "https://api.github.com/users/defunkt", | ||
debug: true, | ||
}); | ||
expect(step).toHaveProperty("debug_config"); | ||
expect(step).toHaveProperty("debug_response"); | ||
}); | ||
}); |
@@ -7,2 +7,3 @@ "use strict"; | ||
const utils_1 = require("./utils"); | ||
const errors_1 = require("./errors"); | ||
function cleanObject(o) { | ||
@@ -23,4 +24,6 @@ for (const k in o || {}) { | ||
// https://stackoverflow.com/a/8649003/387413 | ||
const urlParams = JSON.parse('{"' + queryString.replace(/&/g, '","').replace(/=/g, '":"') + '"}', function (key, value) { | ||
return key === "" ? value : decodeURIComponent(value); | ||
const urlParams = JSON.parse("{\"" + queryString.replace(/&/g, "\",\"").replace(/=/g, "\":\"") + "\"}", function (key, value) { | ||
return key === "" | ||
? value | ||
: decodeURIComponent(value); | ||
}); | ||
@@ -41,4 +44,4 @@ for (const k in urlParams) { | ||
return querystring.stringify(p) | ||
.replace(/\!/g, "%21") | ||
.replace(/\'/g, "%27") | ||
.replace(/!/g, "%21") | ||
.replace(/'/g, "%27") | ||
.replace(/\(/g, "%28") | ||
@@ -55,6 +58,9 @@ .replace(/\)/g, "%29") | ||
} | ||
if (config.body != null) { | ||
throw new errors_1.ConfigurationError("unexpected body, use only data instead"); | ||
} | ||
removeSearchFromUrl(config); | ||
// OAuth1 request | ||
if (signConfig) { | ||
const { oauthSignerUri, token } = signConfig; | ||
const { oauthSignerUri, token, } = signConfig; | ||
const requestData = { | ||
@@ -91,16 +97,32 @@ method: config.method || "get", | ||
try { | ||
return (await axios_1.default(config)).data; | ||
if (config.debug) { | ||
stepExport(step, config, "debug_config"); | ||
} | ||
const { data } = await axios_1.default(config); | ||
if (config.debug) { | ||
stepExport(step, data, "debug_response"); | ||
} | ||
return data; | ||
} | ||
catch (err) { | ||
if (err.response) { | ||
if (step.export) { | ||
step.export("debug", utils_1.cloneSafe(err.response)); | ||
} | ||
else { | ||
step.debug = utils_1.cloneSafe(err.response); | ||
} | ||
throw err; | ||
stepExport(step, err.response, "debug"); | ||
} | ||
throw err; | ||
} | ||
} | ||
exports.default = default_1; | ||
function stepExport(step, message, key) { | ||
message = utils_1.cloneSafe(message); | ||
if (step) { | ||
if (step.export) { | ||
step.export(key, message); | ||
} | ||
else { | ||
step[key] = message; | ||
} | ||
} | ||
else { | ||
console.log(`key: ${key}`, message); | ||
} | ||
} |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.$sendConfigRuntimeTypeChecker = exports.$send = exports.$end = exports.END_NEEDLE = exports.$event = exports.sendTypeMap = exports.SendConfigSSE = exports.SendConfigSnowflake = exports.SendConfigSQL = exports.SendConfigS3 = exports.SendConfigHTTP = exports.HTTP_METHODS = exports.SendConfigEmit = exports.SendConfigEmit_optional = exports.SendConfigEmit_required = exports.SendConfigEmail = exports.axios = void 0; | ||
const t = require("io-ts"); | ||
@@ -7,5 +8,10 @@ const axios_1 = require("./axios"); | ||
var utils_1 = require("./utils"); | ||
exports.cloneSafe = utils_1.cloneSafe; | ||
exports.jsonStringifySafe = utils_1.jsonStringifySafe; | ||
const SendPayload = t.union([t.string, t.object]); | ||
Object.defineProperty(exports, "cloneSafe", { enumerable: true, get: function () { return utils_1.cloneSafe; } }); | ||
Object.defineProperty(exports, "jsonStringifySafe", { enumerable: true, get: function () { return utils_1.jsonStringifySafe; } }); | ||
var errors_1 = require("./errors"); | ||
Object.defineProperty(exports, "ConfigurationError", { enumerable: true, get: function () { return errors_1.ConfigurationError; } }); | ||
const SendPayload = t.union([ | ||
t.string, | ||
t.object, | ||
]); | ||
exports.SendConfigEmail = t.partial({ | ||
@@ -22,3 +28,6 @@ html: t.string, | ||
}); | ||
exports.SendConfigEmit = t.intersection([exports.SendConfigEmit_required, exports.SendConfigEmit_optional]); | ||
exports.SendConfigEmit = t.intersection([ | ||
exports.SendConfigEmit_required, | ||
exports.SendConfigEmit_optional, | ||
]); | ||
// interface SendConfigHTTPKv { | ||
@@ -59,3 +68,6 @@ // [key: string]: string; | ||
}); | ||
exports.SendConfigHTTP = t.intersection([SendConfigHTTP_required, SendConfigHTTP_optional]); | ||
exports.SendConfigHTTP = t.intersection([ | ||
SendConfigHTTP_required, | ||
SendConfigHTTP_optional, | ||
]); | ||
exports.SendConfigS3 = t.strict({ | ||
@@ -108,3 +120,3 @@ bucket: t.string, | ||
const ret = {}; | ||
for (const [sendName, sendConfigType] of Object.entries(exports.sendTypeMap)) { | ||
for (const [sendName, sendConfigType,] of Object.entries(exports.sendTypeMap)) { | ||
ret[sendName] = function (config) { | ||
@@ -138,9 +150,1 @@ const result = sendConfigType.decode(config); | ||
})(); | ||
class ConfigurationError extends Error { | ||
constructor(message, exposeStack = false) { | ||
super(message); | ||
this.name = "ConfigurationError"; | ||
this.exposeStack = exposeStack; | ||
} | ||
} | ||
exports.ConfigurationError = ConfigurationError; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.jsonStringifySafe = exports.cloneSafe = void 0; | ||
function cloneSafe(o) { | ||
const str = jsonStringifySafe(o); | ||
return str ? JSON.parse(str) : null; | ||
return str | ||
? JSON.parse(str) | ||
: null; | ||
} | ||
@@ -7,0 +10,0 @@ exports.cloneSafe = cloneSafe; |
117
lib/axios.ts
@@ -1,11 +0,12 @@ | ||
import axios from "axios" | ||
import { AxiosRequestConfig } from "axios" | ||
import * as buildURL from "axios/lib/helpers/buildURL" | ||
import * as querystring from "querystring" | ||
import { cloneSafe } from "./utils" | ||
import axios from "axios"; | ||
import { AxiosRequestConfig } from "./index"; | ||
import * as buildURL from "axios/lib/helpers/buildURL"; | ||
import * as querystring from "querystring"; | ||
import { cloneSafe } from "./utils"; | ||
import { ConfigurationError } from "./errors"; | ||
function cleanObject(o: { string: any }) { | ||
function cleanObject(o: { string: any; }) { | ||
for (const k in o || {}) { | ||
if (typeof o[k] === "undefined") { | ||
delete o[k] | ||
delete o[k]; | ||
} | ||
@@ -17,17 +18,19 @@ } | ||
function removeSearchFromUrl(config: AxiosRequestConfig) { | ||
if (!config.url) return | ||
const url = new URL(config.url) | ||
const queryString = url.search.substr(1) | ||
if (!config.url) return; | ||
const url = new URL(config.url); | ||
const queryString = url.search.substr(1); | ||
if (queryString) { | ||
// https://stackoverflow.com/a/8649003/387413 | ||
const urlParams = JSON.parse('{"' + queryString.replace(/&/g, '","').replace(/=/g, '":"') + '"}', function (key, value) { | ||
return key === "" ? value : decodeURIComponent(value) | ||
}) | ||
const urlParams = JSON.parse("{\"" + queryString.replace(/&/g, "\",\"").replace(/=/g, "\":\"") + "\"}", function (key, value) { | ||
return key === "" | ||
? value | ||
: decodeURIComponent(value); | ||
}); | ||
for (const k in urlParams) { | ||
if (!config.params) config.params = {} | ||
if (k in config.params) continue // params object > url query params | ||
config.params[k] = urlParams[k] | ||
if (!config.params) config.params = {}; | ||
if (k in config.params) continue; // params object > url query params | ||
config.params[k] = urlParams[k]; | ||
} | ||
url.search = "" | ||
config.url = url.toString() // if ends with ? should be okay, but could be cleaner | ||
url.search = ""; | ||
config.url = url.toString(); // if ends with ? should be okay, but could be cleaner | ||
} | ||
@@ -39,7 +42,7 @@ } | ||
return querystring.stringify(p) | ||
.replace(/\!/g, "%21") | ||
.replace(/\'/g, "%27") | ||
.replace(/!/g, "%21") | ||
.replace(/'/g, "%27") | ||
.replace(/\(/g, "%28") | ||
.replace(/\)/g, "%29") | ||
.replace(/\*/g, "%2A") | ||
.replace(/\*/g, "%2A"); | ||
} | ||
@@ -49,51 +52,73 @@ | ||
export default async function (step: any, config: AxiosRequestConfig, signConfig?: any) { | ||
cleanObject(config.headers) | ||
cleanObject(config.params) | ||
cleanObject(config.headers); | ||
cleanObject(config.params); | ||
if (typeof config.data === "object") { | ||
cleanObject(config.data) | ||
cleanObject(config.data); | ||
} | ||
removeSearchFromUrl(config) | ||
if (config.body != null) { | ||
throw new ConfigurationError("unexpected body, use only data instead"); | ||
} | ||
removeSearchFromUrl(config); | ||
// OAuth1 request | ||
if (signConfig) { | ||
const { oauthSignerUri, token } = signConfig | ||
const { | ||
oauthSignerUri, token, | ||
} = signConfig; | ||
const requestData = { | ||
method: config.method || "get", | ||
url: buildURL(config.url, config.params, oauth1ParamsSerializer), // build url as axios will | ||
} | ||
}; | ||
// the OAuth specification explicitly states that only form-encoded data should be included | ||
let hasContentType = false | ||
let formEncodedContentType = false | ||
let hasContentType = false; | ||
let formEncodedContentType = false; | ||
for (const k in config.headers || {}) { | ||
if (/content-type/i.test(k)) { | ||
hasContentType = true | ||
formEncodedContentType = config.headers[k] === "application/x-www-form-urlencoded" | ||
break | ||
hasContentType = true; | ||
formEncodedContentType = config.headers[k] === "application/x-www-form-urlencoded"; | ||
break; | ||
} | ||
} | ||
if (config.data && typeof config.data === "object" && formEncodedContentType) { | ||
(requestData as any).data = config.data | ||
(requestData as any).data = config.data; | ||
} else if (typeof config.data === "string" && (!hasContentType || formEncodedContentType)) { | ||
(requestData as any).data = querystring.parse(config.data) | ||
(requestData as any).data = querystring.parse(config.data); | ||
} | ||
config.paramsSerializer = oauth1ParamsSerializer | ||
config.paramsSerializer = oauth1ParamsSerializer; | ||
const payload = { | ||
requestData, | ||
token, | ||
} | ||
const oauthSignature = (await axios.post(oauthSignerUri, payload)).data | ||
if (!config.headers) config.headers = {} | ||
config.headers.Authorization = oauthSignature | ||
}; | ||
const oauthSignature = (await axios.post(oauthSignerUri, payload)).data; | ||
if (!config.headers) config.headers = {}; | ||
config.headers.Authorization = oauthSignature; | ||
} | ||
try { | ||
return (await axios(config)).data | ||
if (config.debug) { | ||
stepExport(step, config, "debug_config"); | ||
} | ||
const { data } = await axios(config); | ||
if (config.debug) { | ||
stepExport(step, data, "debug_response"); | ||
} | ||
return data; | ||
} catch (err) { | ||
if (err.response) { | ||
if (step.export) { | ||
step.export("debug", cloneSafe(err.response)) | ||
} else { | ||
step.debug = cloneSafe(err.response) | ||
} | ||
throw err | ||
stepExport(step, err.response, "debug"); | ||
} | ||
throw err; | ||
} | ||
} | ||
function stepExport(step: any, message: any, key: string) { | ||
message = cloneSafe(message); | ||
if (step) { | ||
if (step.export) { | ||
step.export(key, message); | ||
} else { | ||
step[key] = message; | ||
} | ||
} else { | ||
console.log(`key: ${key}`, message); | ||
} | ||
} |
211
lib/index.ts
@@ -1,15 +0,27 @@ | ||
import * as t from "io-ts" | ||
import * as t from "io-ts"; | ||
import axios from "./axios" | ||
import axios from "./axios"; | ||
import { AxiosRequestConfig as AxiosConfig } from "axios"; | ||
export { axios } | ||
export { cloneSafe, jsonStringifySafe } from "./utils" | ||
export { | ||
axios, | ||
}; | ||
export { | ||
cloneSafe, jsonStringifySafe, | ||
} from "./utils"; | ||
const SendPayload = t.union([t.string, t.object]); | ||
export { | ||
ConfigurationError, | ||
} from "./errors"; | ||
const SendPayload = t.union([ | ||
t.string, | ||
t.object, | ||
]); | ||
type SendPayload = t.TypeOf<typeof SendPayload>; | ||
export const SendConfigEmail = t.partial({ | ||
html: t.string, | ||
subject: t.string, | ||
text: t.string, | ||
html: t.string, | ||
subject: t.string, | ||
text: t.string, | ||
}); | ||
@@ -19,9 +31,12 @@ export type SendConfigEmail = t.TypeOf<typeof SendConfigEmail>; | ||
export const SendConfigEmit_required = t.strict({ | ||
raw_event: t.object, | ||
raw_event: t.object, | ||
}); | ||
export const SendConfigEmit_optional = t.partial({ | ||
event: t.object, | ||
event: t.object, | ||
}); | ||
export const SendConfigEmit = t.intersection([SendConfigEmit_required, SendConfigEmit_optional]); | ||
export const SendConfigEmit = t.intersection([ | ||
SendConfigEmit_required, | ||
SendConfigEmit_optional, | ||
]); | ||
export type SendConfigEmit = t.TypeOf<typeof SendConfigEmit>; | ||
@@ -35,4 +50,4 @@ | ||
const SendConfigHTTPAuth = t.strict({ | ||
password: t.string, | ||
username: t.string, | ||
password: t.string, | ||
username: t.string, | ||
}); | ||
@@ -42,29 +57,32 @@ type SendConfigHTTPAuth = t.TypeOf<typeof SendConfigHTTPAuth>; | ||
export const HTTP_METHODS = [ | ||
"GET", | ||
"HEAD", | ||
"POST", | ||
"PUT", | ||
"DELETE", | ||
"CONNECT", | ||
"OPTIONS", | ||
"TRACE", | ||
"PATCH", | ||
] | ||
"GET", | ||
"HEAD", | ||
"POST", | ||
"PUT", | ||
"DELETE", | ||
"CONNECT", | ||
"OPTIONS", | ||
"TRACE", | ||
"PATCH", | ||
]; | ||
// HTTP method must be uppercase (for kotlin in coordinator -- i voted to make it case insensitive, but w.e for now) | ||
const SendConfigHTTPMethod = t.keyof(HTTP_METHODS.reduce((acc, v) => { | ||
acc[v] = null | ||
return acc | ||
}, {})) | ||
acc[v] = null; | ||
return acc; | ||
}, {})); | ||
type SendConfigHTTPMethod = t.TypeOf<typeof SendConfigHTTPMethod>; | ||
const SendConfigHTTP_required = t.strict({ | ||
method: SendConfigHTTPMethod, | ||
url: t.string, | ||
}) | ||
method: SendConfigHTTPMethod, | ||
url: t.string, | ||
}); | ||
const SendConfigHTTP_optional = t.partial({ | ||
auth: SendConfigHTTPAuth, | ||
data: SendPayload, | ||
headers: SendConfigHTTPKv, | ||
params: SendConfigHTTPKv, | ||
}) | ||
export const SendConfigHTTP = t.intersection([SendConfigHTTP_required, SendConfigHTTP_optional]); | ||
auth: SendConfigHTTPAuth, | ||
data: SendPayload, | ||
headers: SendConfigHTTPKv, | ||
params: SendConfigHTTPKv, | ||
}); | ||
export const SendConfigHTTP = t.intersection([ | ||
SendConfigHTTP_required, | ||
SendConfigHTTP_optional, | ||
]); | ||
// Mimics axios config. (for now) | ||
@@ -74,5 +92,5 @@ export type SendConfigHTTP = t.TypeOf<typeof SendConfigHTTP>; | ||
export const SendConfigS3 = t.strict({ | ||
bucket: t.string, | ||
payload: SendPayload, | ||
prefix: t.string, | ||
bucket: t.string, | ||
payload: SendPayload, | ||
prefix: t.string, | ||
}); | ||
@@ -82,4 +100,4 @@ export type SendConfigS3 = t.TypeOf<typeof SendConfigS3>; | ||
export const SendConfigSQL = t.strict({ | ||
payload: SendPayload, | ||
table: t.string, | ||
payload: SendPayload, | ||
table: t.string, | ||
}); | ||
@@ -89,11 +107,11 @@ export type SendConfigSQL = t.TypeOf<typeof SendConfigSQL>; | ||
export const SendConfigSnowflake = t.strict({ | ||
account: t.string, | ||
database: t.string, | ||
host: t.string, | ||
payload: SendPayload, | ||
pipe_name: t.string, | ||
private_key: t.string, | ||
schema: t.string, | ||
stage_name: t.string, | ||
user: t.string, | ||
account: t.string, | ||
database: t.string, | ||
host: t.string, | ||
payload: SendPayload, | ||
pipe_name: t.string, | ||
private_key: t.string, | ||
schema: t.string, | ||
stage_name: t.string, | ||
user: t.string, | ||
}); | ||
@@ -103,4 +121,4 @@ export type SendConfigSnowflake = t.TypeOf<typeof SendConfigSnowflake>; | ||
export const SendConfigSSE = t.strict({ | ||
channel: t.string, | ||
payload: SendPayload, | ||
channel: t.string, | ||
payload: SendPayload, | ||
}); | ||
@@ -121,10 +139,10 @@ export type SendConfigSSE = t.TypeOf<typeof SendConfigSSE>; | ||
export const sendTypeMap = { | ||
email: SendConfigEmail, | ||
emit: SendConfigEmit, | ||
http: SendConfigHTTP, | ||
s3: SendConfigS3, | ||
sql: SendConfigSQL, | ||
snowflake: SendConfigSnowflake, | ||
sse: SendConfigSSE, | ||
} | ||
email: SendConfigEmail, | ||
emit: SendConfigEmit, | ||
http: SendConfigHTTP, | ||
s3: SendConfigS3, | ||
sql: SendConfigSQL, | ||
snowflake: SendConfigSnowflake, | ||
sse: SendConfigSSE, | ||
}; | ||
@@ -134,11 +152,11 @@ // Event object that persists throughout worfklow with observability after each step. | ||
export const END_NEEDLE = "__pd_end" | ||
export const END_NEEDLE = "__pd_end"; | ||
// End workflow with optional message. | ||
export function $end(message?: string): void { | ||
const err = new Error() | ||
err[END_NEEDLE] = { | ||
message, | ||
ts: new Date().toISOString(), | ||
} | ||
throw err | ||
const err = new Error(); | ||
err[END_NEEDLE] = { | ||
message, | ||
ts: new Date().toISOString(), | ||
}; | ||
throw err; | ||
} | ||
@@ -149,36 +167,35 @@ | ||
export const $sendConfigRuntimeTypeChecker = (function() { | ||
const ret = {} | ||
for (const [sendName, sendConfigType] of Object.entries(sendTypeMap)) { | ||
ret[sendName] = function(config) { | ||
const result = sendConfigType.decode(config) | ||
if (!result) throw new Error("io-ts: unexpected decode output") | ||
if (result._tag === "Left") { | ||
for (const err of result.left) { | ||
if (err.message) { | ||
throw new Error(err.message) | ||
} else { | ||
const keyChunks: string[] = [] | ||
for (const ctx of err.context) { | ||
if (!ctx.key) continue | ||
if (!isNaN(+ctx.key)) continue | ||
keyChunks.push(ctx.key) | ||
} | ||
throw new Error(`$send.${sendName}: invalid value ${err.value} for ${keyChunks.join(".")}`) | ||
} | ||
} | ||
throw new Error("io-ts: error but could not produce message") // shouldn't happen... | ||
const ret = {}; | ||
for (const [ | ||
sendName, | ||
sendConfigType, | ||
] of Object.entries(sendTypeMap)) { | ||
ret[sendName] = function(config) { | ||
const result = sendConfigType.decode(config); | ||
if (!result) throw new Error("io-ts: unexpected decode output"); | ||
if (result._tag === "Left") { | ||
for (const err of result.left) { | ||
if (err.message) { | ||
throw new Error(err.message); | ||
} else { | ||
const keyChunks: string[] = []; | ||
for (const ctx of err.context) { | ||
if (!ctx.key) continue; | ||
if (!isNaN(+ctx.key)) continue; | ||
keyChunks.push(ctx.key); | ||
} | ||
// XXX if result !== config they passed extra fields... but expensive | ||
throw new Error(`$send.${sendName}: invalid value ${err.value} for ${keyChunks.join(".")}`); | ||
} | ||
} | ||
} | ||
return ret | ||
})() | ||
throw new Error("io-ts: error but could not produce message"); // shouldn't happen... | ||
} | ||
// XXX if result !== config they passed extra fields... but expensive | ||
}; | ||
} | ||
return ret; | ||
})(); | ||
export class ConfigurationError extends Error { | ||
exposeStack: boolean; | ||
constructor(message: string, exposeStack = false) { | ||
super(message) | ||
this.name = "ConfigurationError" | ||
this.exposeStack = exposeStack | ||
} | ||
export interface AxiosRequestConfig extends AxiosConfig { | ||
debug?: boolean; | ||
body?: any; | ||
} |
export function cloneSafe(o: any) { | ||
const str = jsonStringifySafe(o) | ||
return str ? JSON.parse(str) : null | ||
const str = jsonStringifySafe(o); | ||
return str | ||
? JSON.parse(str) | ||
: null; | ||
} | ||
@@ -14,26 +16,26 @@ | ||
try { | ||
return JSON.stringify(v) | ||
return JSON.stringify(v); | ||
} catch (err) { | ||
set = new Set(set || []) | ||
if (set.has(v)) return | ||
set.add(v) | ||
set = new Set(set || []); | ||
if (set.has(v)) return; | ||
set.add(v); | ||
if (typeof v === "object") { | ||
const strs: Array<String> = [] | ||
const strs: Array<string> = []; | ||
if (Array.isArray(v)) { | ||
for (const el of v) { | ||
const str = jsonStringifySafe(el, set) | ||
if (str) strs.push(str) | ||
const str = jsonStringifySafe(el, set); | ||
if (str) strs.push(str); | ||
} | ||
return `[${strs.join(",")}]` | ||
return `[${strs.join(",")}]`; | ||
} | ||
for (const k in v) { | ||
const str = jsonStringifySafe(v[k], set) | ||
const str = jsonStringifySafe(v[k], set); | ||
if (str) { | ||
const kStr = JSON.stringify(k) | ||
strs.push(`${kStr}:${str}`) | ||
const kStr = JSON.stringify(k); | ||
strs.push(`${kStr}:${str}`); | ||
} | ||
} | ||
return `{${strs.join(",")}}` | ||
return `{${strs.join(",")}}`; | ||
} | ||
} | ||
} |
{ | ||
"name": "@pipedream/platform", | ||
"version": "0.10.0", | ||
"version": "1.0.0", | ||
"description": "Pipedream platform globals (typing and runtime type checking)", | ||
@@ -8,6 +8,2 @@ "homepage": "https://pipedream.com", | ||
"types": "dist/index.d.ts", | ||
"scripts": { | ||
"build": "rm -rf dist/** && tsc", | ||
"test": "jest" | ||
}, | ||
"author": "Pipedream Team", | ||
@@ -27,10 +23,10 @@ "license": "MIT", | ||
"jest": "^24.8.0", | ||
"release-it": "^12.3.3", | ||
"@octokit/core": "^3.6.0", | ||
"typescript": "^3.5.3" | ||
}, | ||
"husky": { | ||
"hooks": { | ||
"pre-commit": "npm run build && npm test && git add dist" | ||
} | ||
"scripts": { | ||
"prebuild": "rm -rf dist", | ||
"build": "tsc", | ||
"test": "npm run build && jest" | ||
} | ||
} | ||
} |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
0
0
31905
980