diff --git a/package-lock.json b/package-lock.json index 51dc531..29a8301 100644 --- a/package-lock.json +++ b/package-lock.json @@ -329,11 +329,20 @@ "dev": true }, "@sindresorhus/is": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.7.0.tgz", - "integrity": "sha512-ONhaKPIufzzrlNbqtWFFd+jlnemX6lJAgq9ZeiZtS7I1PIf/la7CW4m83rTXRnVnsMbW2k56pGYu7AUFJD9Pow==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-3.1.2.tgz", + "integrity": "sha512-JiX9vxoKMmu8Y3Zr2RVathBL1Cdu4Nt4MuNWemt1Nc06A0RAin9c5FArkhGsyMBWfCu4zj+9b+GxtjAnE4qqLQ==", "dev": true }, + "@szmarczak/http-timer": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.5.tgz", + "integrity": "sha512-PyRA9sm1Yayuj5OIoJ1hGt2YISX45w9WcFbh6ddT0Z/0yaFxOtGLInr4jUfU1EAFVs0Yfyfev4RNwBlUaHdlDQ==", + "dev": true, + "requires": { + "defer-to-connect": "^2.0.0" + } + }, "@types/ajv": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@types/ajv/-/ajv-1.0.0.tgz", @@ -352,6 +361,18 @@ "axios": "*" } }, + "@types/cacheable-request": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.1.tgz", + "integrity": "sha512-ykFq2zmBGOCbpIXtoVbz4SKY5QriWPh3AjyU4G74RYbtt5yOc5OfaY75ftjg7mikMOla1CTGpX3lLbuJh8DTrQ==", + "dev": true, + "requires": { + "@types/http-cache-semantics": "*", + "@types/keyv": "*", + "@types/node": "*", + "@types/responselike": "*" + } + }, "@types/chai": { "version": "4.2.11", "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.2.11.tgz", @@ -364,6 +385,12 @@ "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==", "dev": true }, + "@types/cookiejar": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@types/cookiejar/-/cookiejar-2.1.1.tgz", + "integrity": "sha512-aRnpPa7ysx3aNW60hTiCtLHlQaIFsXFCgQlpakNgDNVFzbtusSY8PwjAQgRWfSk0ekNoBjO51eQRB6upA9uuyw==", + "dev": true + }, "@types/cucumber": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/@types/cucumber/-/cucumber-6.0.1.tgz", @@ -376,12 +403,51 @@ "integrity": "sha512-OCutwjDZ4aFS6PB1UZ988C4YgwlBHJd6wCeQqaLdmadZ/7e+w79+hbMUFC1QXDNCmdyoRfAFdm0RypzwR+Qpag==", "dev": true }, + "@types/got": { + "version": "9.6.11", + "resolved": "https://registry.npmjs.org/@types/got/-/got-9.6.11.tgz", + "integrity": "sha512-dr3IiDNg5TDesGyuwTrN77E1Cd7DCdmCFtEfSGqr83jMMtcwhf/SGPbN2goY4JUWQfvxwY56+e5tjfi+oXeSdA==", + "dev": true, + "requires": { + "@types/node": "*", + "@types/tough-cookie": "*", + "form-data": "^2.5.0" + }, + "dependencies": { + "form-data": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz", + "integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==", + "dev": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + } + } + }, + "@types/http-cache-semantics": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.0.tgz", + "integrity": "sha512-c3Xy026kOF7QOTn00hbIllV1dLR9hG9NkSrLQgCVs8NF6sBU+VGWjD3wLPhmh1TYAc7ugCFsvHYMN4VcBN1U1A==", + "dev": true + }, "@types/json-schema": { "version": "7.0.5", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.5.tgz", "integrity": "sha512-7+2BITlgjgDhH0vvwZU/HZJVyk+2XUlvxXe8dFMedNX/aMkaOq++rMAFXc0tM7ij15QaWlbdQASBR9dihi+bDQ==", "dev": true }, + "@types/keyv": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.1.tgz", + "integrity": "sha512-MPtoySlAZQ37VoLaPcTHCu1RWJ4llDkULYZIzOYxlhxBqYPB0RsRlmMU0R6tahtFe27mIdkHV+551ZWV4PLmVw==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, "@types/minimist": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.0.tgz", @@ -406,6 +472,31 @@ "integrity": "sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA==", "dev": true }, + "@types/responselike": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz", + "integrity": "sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/superagent": { + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@types/superagent/-/superagent-4.1.10.tgz", + "integrity": "sha512-xAgkb2CMWUMCyVc/3+7iQfOEBE75NvuZeezvmixbUw3nmENf2tCnQkW5yQLTYqvXUQ+R6EXxdqKKbal2zM5V/g==", + "dev": true, + "requires": { + "@types/cookiejar": "*", + "@types/node": "*" + } + }, + "@types/tough-cookie": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.0.tgz", + "integrity": "sha512-I99sngh224D0M7XgW1s120zxCt3VYQ3IQsuw3P3jbq5GG4yc79+ZjyKznyOGIQrflfylLgcfekeZW/vk0yng6A==", + "dev": true + }, "@types/uuid": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.0.0.tgz", @@ -1022,6 +1113,12 @@ "dev": true, "optional": true }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "dev": true + }, "at-least-node": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", @@ -1417,32 +1514,35 @@ "unset-value": "^1.0.0" } }, + "cacheable-lookup": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.3.tgz", + "integrity": "sha512-W+JBqF9SWe18A72XFzN/V/CULFzPm7sBXzzR6ekkE+3tLG72wFZrBiBZhrZuDoYexop4PHJVdFAKb/Nj9+tm9w==", + "dev": true + }, "cacheable-request": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-2.1.4.tgz", - "integrity": "sha1-DYCIAbY0KtM8kd+dC0TcCbkeXD0=", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.1.tgz", + "integrity": "sha512-lt0mJ6YAnsrBErpTMWeu5kl/tg9xMAWjavYTN6VQXM1A/teBITuNcccXsCxF0tDQQJf9DfAaX5O4e0zp0KlfZw==", "dev": true, "requires": { - "clone-response": "1.0.2", - "get-stream": "3.0.0", - "http-cache-semantics": "3.8.1", - "keyv": "3.0.0", - "lowercase-keys": "1.0.0", - "normalize-url": "2.0.1", - "responselike": "1.0.2" + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^4.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^4.1.0", + "responselike": "^2.0.0" }, "dependencies": { "get-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", - "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", - "dev": true - }, - "lowercase-keys": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.0.tgz", - "integrity": "sha1-TjNms55/VFfjXxMkvfb4jQv8cwY=", - "dev": true + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } } } }, @@ -1695,6 +1795,15 @@ "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", "dev": true }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "requires": { + "delayed-stream": "~1.0.0" + } + }, "commander": { "version": "2.20.3", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", @@ -2043,6 +2152,12 @@ "safe-buffer": "~5.1.1" } }, + "cookiejar": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.2.tgz", + "integrity": "sha512-Mw+adcfzPxcPeI+0WlvRrr/3lGVO0bD75SxX6811cxSh1Wbxx7xZBGK1eVtDf6si8rg2lhnUjsVLMFMfbRIuwA==", + "dev": true + }, "copy-concurrently": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/copy-concurrently/-/copy-concurrently-1.0.5.tgz", @@ -2366,12 +2481,20 @@ } }, "decompress-response": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", - "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", "dev": true, "requires": { - "mimic-response": "^1.0.0" + "mimic-response": "^3.1.0" + }, + "dependencies": { + "mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "dev": true + } } }, "decompress-tar": { @@ -2513,6 +2636,12 @@ } } }, + "defer-to-connect": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.0.tgz", + "integrity": "sha512-bYL2d05vOSf1JEZNx5vSAtPuBMkX8K9EUutg7zlKvTqKXHt7RhWJFbmd7qakVuf13i+IkGmp6FwSsONOf6VYIg==", + "dev": true + }, "define-properties": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", @@ -2563,6 +2692,12 @@ } } }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "dev": true + }, "des.js": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz", @@ -2715,6 +2850,116 @@ "pify": "^4.0.1" }, "dependencies": { + "@sindresorhus/is": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.7.0.tgz", + "integrity": "sha512-ONhaKPIufzzrlNbqtWFFd+jlnemX6lJAgq9ZeiZtS7I1PIf/la7CW4m83rTXRnVnsMbW2k56pGYu7AUFJD9Pow==", + "dev": true + }, + "cacheable-request": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-2.1.4.tgz", + "integrity": "sha1-DYCIAbY0KtM8kd+dC0TcCbkeXD0=", + "dev": true, + "requires": { + "clone-response": "1.0.2", + "get-stream": "3.0.0", + "http-cache-semantics": "3.8.1", + "keyv": "3.0.0", + "lowercase-keys": "1.0.0", + "normalize-url": "2.0.1", + "responselike": "1.0.2" + }, + "dependencies": { + "get-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", + "dev": true + }, + "lowercase-keys": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.0.tgz", + "integrity": "sha1-TjNms55/VFfjXxMkvfb4jQv8cwY=", + "dev": true + } + } + }, + "decompress-response": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", + "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", + "dev": true, + "requires": { + "mimic-response": "^1.0.0" + } + }, + "got": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/got/-/got-8.3.2.tgz", + "integrity": "sha512-qjUJ5U/hawxosMryILofZCkm3C84PLJS/0grRIpjAwu+Lkxxj5cxeCU25BG0/3mDSpXKTyZr8oh8wIgLaH0QCw==", + "dev": true, + "requires": { + "@sindresorhus/is": "^0.7.0", + "cacheable-request": "^2.1.1", + "decompress-response": "^3.3.0", + "duplexer3": "^0.1.4", + "get-stream": "^3.0.0", + "into-stream": "^3.1.0", + "is-retry-allowed": "^1.1.0", + "isurl": "^1.0.0-alpha5", + "lowercase-keys": "^1.0.0", + "mimic-response": "^1.0.0", + "p-cancelable": "^0.4.0", + "p-timeout": "^2.0.1", + "pify": "^3.0.0", + "safe-buffer": "^5.1.1", + "timed-out": "^4.0.1", + "url-parse-lax": "^3.0.0", + "url-to-options": "^1.0.1" + }, + "dependencies": { + "get-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", + "dev": true + }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true + } + } + }, + "http-cache-semantics": { + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-3.8.1.tgz", + "integrity": "sha512-5ai2iksyV8ZXmnZhHH4rWPoxxistEexSi5936zIQ1bnNTW5VnA85B6P/VpXiRM017IgRvb2kKo1a//y+0wSp3w==", + "dev": true + }, + "json-buffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", + "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=", + "dev": true + }, + "keyv": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.0.0.tgz", + "integrity": "sha512-eguHnq22OE3uVoSYG0LVWNP+4ppamWr9+zWBe1bsNcovIMy6huUJFPgy4mGwCd/rnl3vOLGW1MTlu4c57CT1xA==", + "dev": true, + "requires": { + "json-buffer": "3.0.0" + } + }, + "lowercase-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", + "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", + "dev": true + }, "make-dir": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", @@ -2725,11 +2970,46 @@ "semver": "^5.6.0" } }, + "normalize-url": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-2.0.1.tgz", + "integrity": "sha512-D6MUW4K/VzoJ4rJ01JFKxDrtY1v9wrgzCX5f2qj/lzH1m/lW6MhUZFKerVsnyjOhOsYzI9Kqqak+10l4LvLpMw==", + "dev": true, + "requires": { + "prepend-http": "^2.0.0", + "query-string": "^5.0.1", + "sort-keys": "^2.0.0" + } + }, + "p-cancelable": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-0.4.1.tgz", + "integrity": "sha512-HNa1A8LvB1kie7cERyy21VNeHb2CWJJYqyyC2o3klWFfMGlFmWv2Z7sFgZH8ZiaYL95ydToKTFVXgMV/Os0bBQ==", + "dev": true + }, "pify": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", "dev": true + }, + "responselike": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", + "integrity": "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=", + "dev": true, + "requires": { + "lowercase-keys": "^1.0.0" + } + }, + "sort-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-2.0.0.tgz", + "integrity": "sha1-ZYU1WEhh7JfXMNbPQYIuH1ZoQSg=", + "dev": true, + "requires": { + "is-plain-obj": "^1.0.0" + } } } }, @@ -3636,6 +3916,12 @@ "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", "dev": true }, + "fast-safe-stringify": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz", + "integrity": "sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA==", + "dev": true + }, "fd-slicer": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", @@ -3898,6 +4184,23 @@ "signal-exit": "^3.0.2" } }, + "form-data": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.0.tgz", + "integrity": "sha512-CKMFDglpbMi6PyN+brwB9Q/GOw0eAnsrEZDgcsH5Krhz5Od/haKHAX0NmQfha2zPPz0JpWzA7GJHGSnvCRLWsg==", + "dev": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + }, + "formidable": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.2.2.tgz", + "integrity": "sha512-V8gLm+41I/8kguQ4/o1D3RIHRmhYFG4pnNyonvua+40rqcEmT4+V71yaZ3B457xbbgCsCfjSPi65u/W6vK1U5Q==", + "dev": true + }, "fragment-cache": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", @@ -4573,42 +4876,22 @@ "dev": true }, "got": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/got/-/got-8.3.2.tgz", - "integrity": "sha512-qjUJ5U/hawxosMryILofZCkm3C84PLJS/0grRIpjAwu+Lkxxj5cxeCU25BG0/3mDSpXKTyZr8oh8wIgLaH0QCw==", + "version": "11.7.0", + "resolved": "https://registry.npmjs.org/got/-/got-11.7.0.tgz", + "integrity": "sha512-7en2XwH2MEqOsrK0xaKhbWibBoZqy+f1RSUoIeF1BLcnf+pyQdDsljWMfmOh+QKJwuvDIiKx38GtPh5wFdGGjg==", "dev": true, "requires": { - "@sindresorhus/is": "^0.7.0", - "cacheable-request": "^2.1.1", - "decompress-response": "^3.3.0", - "duplexer3": "^0.1.4", - "get-stream": "^3.0.0", - "into-stream": "^3.1.0", - "is-retry-allowed": "^1.1.0", - "isurl": "^1.0.0-alpha5", - "lowercase-keys": "^1.0.0", - "mimic-response": "^1.0.0", - "p-cancelable": "^0.4.0", - "p-timeout": "^2.0.1", - "pify": "^3.0.0", - "safe-buffer": "^5.1.1", - "timed-out": "^4.0.1", - "url-parse-lax": "^3.0.0", - "url-to-options": "^1.0.1" - }, - "dependencies": { - "get-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", - "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", - "dev": true - }, - "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", - "dev": true - } + "@sindresorhus/is": "^3.1.1", + "@szmarczak/http-timer": "^4.0.5", + "@types/cacheable-request": "^6.0.1", + "@types/responselike": "^1.0.0", + "cacheable-lookup": "^5.0.3", + "cacheable-request": "^7.0.1", + "decompress-response": "^6.0.0", + "http2-wrapper": "^1.0.0-beta.5.2", + "lowercase-keys": "^2.0.0", + "p-cancelable": "^2.0.0", + "responselike": "^2.0.0" } }, "graceful-fs": { @@ -4834,9 +5117,9 @@ "dev": true }, "http-cache-semantics": { - "version": "3.8.1", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-3.8.1.tgz", - "integrity": "sha512-5ai2iksyV8ZXmnZhHH4rWPoxxistEexSi5936zIQ1bnNTW5VnA85B6P/VpXiRM017IgRvb2kKo1a//y+0wSp3w==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", + "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==", "dev": true }, "http-parser-js": { @@ -4845,6 +5128,24 @@ "integrity": "sha512-opCO9ASqg5Wy2FNo7A0sxy71yGbbkJJXLdgMK04Tcypw9jr2MgWbyubb0+WdmDmGnFflO7fRbqbaihh/ENDlRQ==", "dev": true }, + "http2-wrapper": { + "version": "1.0.0-beta.5.2", + "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.0-beta.5.2.tgz", + "integrity": "sha512-xYz9goEyBnC8XwXDTuC/MZ6t+MrKVQZOk4s7+PaDkwIsQd8IwqvM+0M6bA/2lvG8GHXcPdf+MejTUeO2LCPCeQ==", + "dev": true, + "requires": { + "quick-lru": "^5.1.1", + "resolve-alpn": "^1.0.0" + }, + "dependencies": { + "quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", + "dev": true + } + } + }, "https-browserify": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", @@ -5453,9 +5754,9 @@ "dev": true }, "json-buffer": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", - "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", "dev": true }, "json-parse-better-errors": { @@ -5513,12 +5814,12 @@ "dev": true }, "keyv": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.0.0.tgz", - "integrity": "sha512-eguHnq22OE3uVoSYG0LVWNP+4ppamWr9+zWBe1bsNcovIMy6huUJFPgy4mGwCd/rnl3vOLGW1MTlu4c57CT1xA==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.0.3.tgz", + "integrity": "sha512-zdGa2TOpSZPq5mU6iowDARnMBZgtCqJ11dJROFi6tg6kTn4nuUdU09lFyLFSaHrWqpIJ+EBq4E8/Dc0Vx5vLdA==", "dev": true, "requires": { - "json-buffer": "3.0.0" + "json-buffer": "3.0.1" } }, "kind-of": { @@ -5680,9 +5981,9 @@ "dev": true }, "lowercase-keys": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", - "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", "dev": true }, "lru-cache": { @@ -5954,6 +6255,12 @@ } } }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", + "dev": true + }, "micromatch": { "version": "3.1.10", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", @@ -6083,12 +6390,27 @@ } } }, + "mime": { + "version": "2.4.6", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.6.tgz", + "integrity": "sha512-RZKhC3EmpBchfTGBVb8fb+RL2cWyw/32lshnsETttkBAyAUXSGHxbEJWWRXc751DrIxG1q04b8QwMbAwkRPpUA==", + "dev": true + }, "mime-db": { "version": "1.44.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==", "dev": true }, + "mime-types": { + "version": "2.1.27", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz", + "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==", + "dev": true, + "requires": { + "mime-db": "1.44.0" + } + }, "mimic-fn": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", @@ -6585,26 +6907,10 @@ "dev": true }, "normalize-url": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-2.0.1.tgz", - "integrity": "sha512-D6MUW4K/VzoJ4rJ01JFKxDrtY1v9wrgzCX5f2qj/lzH1m/lW6MhUZFKerVsnyjOhOsYzI9Kqqak+10l4LvLpMw==", - "dev": true, - "requires": { - "prepend-http": "^2.0.0", - "query-string": "^5.0.1", - "sort-keys": "^2.0.0" - }, - "dependencies": { - "sort-keys": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-2.0.0.tgz", - "integrity": "sha1-ZYU1WEhh7JfXMNbPQYIuH1ZoQSg=", - "dev": true, - "requires": { - "is-plain-obj": "^1.0.0" - } - } - } + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.0.tgz", + "integrity": "sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ==", + "dev": true }, "npm-run-path": { "version": "2.0.2", @@ -6996,9 +7302,9 @@ } }, "p-cancelable": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-0.4.1.tgz", - "integrity": "sha512-HNa1A8LvB1kie7cERyy21VNeHb2CWJJYqyyC2o3klWFfMGlFmWv2Z7sFgZH8ZiaYL95ydToKTFVXgMV/Os0bBQ==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.0.0.tgz", + "integrity": "sha512-wvPXDmbMmu2ksjkB4Z3nZWTSkJEb9lqVdMaCKpZUGJG9TMiNp9XcbG3fn9fPKjem04fJMJnXoyFPk2FmgiaiNg==", "dev": true }, "p-defer": { @@ -7435,6 +7741,12 @@ "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=", "dev": true }, + "qs": { + "version": "6.9.4", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.4.tgz", + "integrity": "sha512-A1kFqHekCTM7cz0udomYUoYNWjBebHm/5wzU/XqrBRBNWectVH0QIiN+NEcZ0Dte5hvzHwbr8+XQmguPhJ6WdQ==", + "dev": true + }, "query-string": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/query-string/-/query-string-5.1.1.tgz", @@ -7623,6 +7935,12 @@ "path-parse": "^1.0.6" } }, + "resolve-alpn": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.0.0.tgz", + "integrity": "sha512-rTuiIEqFmGxne4IovivKSDzld2lWW9QCjqv80SYjPgf+gS35eaCAjaP54CCwGAwBtnCsvNLYtqxe1Nw+i6JEmA==", + "dev": true + }, "resolve-cwd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz", @@ -7676,12 +7994,12 @@ "dev": true }, "responselike": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", - "integrity": "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.0.tgz", + "integrity": "sha512-xH48u3FTB9VsZw7R+vvgaKeLKzT6jOogbQhEe/jewwnZgzPcnyWui2Av6JpoYZF/91uueC+lqhWqeURw5/qhCw==", "dev": true, "requires": { - "lowercase-keys": "^1.0.0" + "lowercase-keys": "^2.0.0" } }, "ret": { @@ -8716,6 +9034,48 @@ } } }, + "superagent": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/superagent/-/superagent-6.1.0.tgz", + "integrity": "sha512-OUDHEssirmplo3F+1HWKUrUjvnQuA+nZI6i/JJBdXb5eq9IyEQwPyPpqND+SSsxf6TygpBEkUjISVRN4/VOpeg==", + "dev": true, + "requires": { + "component-emitter": "^1.3.0", + "cookiejar": "^2.1.2", + "debug": "^4.1.1", + "fast-safe-stringify": "^2.0.7", + "form-data": "^3.0.0", + "formidable": "^1.2.2", + "methods": "^1.1.2", + "mime": "^2.4.6", + "qs": "^6.9.4", + "readable-stream": "^3.6.0", + "semver": "^7.3.2" + }, + "dependencies": { + "debug": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", + "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "semver": { + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", + "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==", + "dev": true + } + } + }, "supports-color": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.0.0.tgz", diff --git a/package.json b/package.json index 70aaca9..e65b172 100644 --- a/package.json +++ b/package.json @@ -107,8 +107,10 @@ "@types/axios": "^0.14.0", "@types/chai": "^4.2.11", "@types/cucumber": "^6.0.1", + "@types/got": "^9.6.11", "@types/mocha": "^7.0.2", "@types/node": "^13.13.9", + "@types/superagent": "^4.1.10", "@types/uuid": "^8.0.0", "@typescript-eslint/eslint-plugin": "^3.4.0", "@typescript-eslint/parser": "^3.4.0", @@ -123,12 +125,14 @@ "eslint-plugin-import": "^2.20.2", "eslint-plugin-node": "^11.1.0", "eslint-plugin-prettier": "^3.1.4", + "got": "^11.7.0", "http-parser-js": "^0.5.2", "mocha": "~7.1.1", "nock": "~12.0.3", "nyc": "~15.0.0", "prettier": "^2.0.5", "standard-version": "^9.0.0", + "superagent": "^6.1.0", "ts-node": "^8.10.2", "typedoc": "^0.18.0", "typedoc-clarity-theme": "~1.1.0", diff --git a/src/index.ts b/src/index.ts index 11149ff..b7813ff 100644 --- a/src/index.ts +++ b/src/index.ts @@ -2,7 +2,14 @@ import { CloudEvent, Version } from "./event/cloudevent"; import { ValidationError } from "./event/validation"; import { CloudEventV03, CloudEventV03Attributes, CloudEventV1, CloudEventV1Attributes } from "./event/interfaces"; -import { Emitter, TransportOptions } from "./transport/emitter"; +import { + Emitter, + TransportOptions, + Options, + TransportFunction, + EmitterFunction, + emitterFor, +} from "./transport/emitter"; import { Receiver } from "./transport/receiver"; import { Protocol } from "./transport/protocols"; import { Headers, Mode, Binding, HTTP, Message, Serializer, Deserializer, headersFor } from "./message"; @@ -32,6 +39,10 @@ export { Receiver, // TODO: Deprecated. Remove for 4.0 Protocol, // TODO: Deprecated. Remove for 4.0 TransportOptions, // TODO: Deprecated. Remove for 4.0 + TransportFunction, + EmitterFunction, + emitterFor, + Options, // From Constants CONSTANTS, }; diff --git a/src/message/http/index.ts b/src/message/http/index.ts index 492f491..97c005b 100644 --- a/src/message/http/index.ts +++ b/src/message/http/index.ts @@ -8,10 +8,14 @@ import { Base64Parser, JSONParser, MappedParser, Parser, parserByContentType } f // implements Serializer export function binary(event: CloudEvent): Message { const contentType: Headers = { [CONSTANTS.HEADER_CONTENT_TYPE]: CONSTANTS.DEFAULT_CONTENT_TYPE }; - const headers: Headers = headersFor(event); + const headers: Headers = { ...contentType, ...headersFor(event) }; + let body = asData(event.data, event.datacontenttype as string); + if (typeof body === "object") { + body = JSON.stringify(body); + } return { - headers: { ...contentType, ...headers }, - body: asData(event.data, event.datacontenttype as string), + headers, + body, }; } diff --git a/src/transport/emitter.ts b/src/transport/emitter.ts index ad1b8a7..03f2721 100644 --- a/src/transport/emitter.ts +++ b/src/transport/emitter.ts @@ -1,14 +1,15 @@ import { CloudEvent } from "../event/cloudevent"; -import { emitBinary, emitStructured } from "./http"; +import { axiosEmitter } from "./http"; import { Protocol } from "./protocols"; -import { AxiosResponse } from "axios"; import { Agent } from "http"; +import { HTTP, Message, Mode } from "../message"; /** * Options supplied to the Emitter when sending an event. * In addition to url and protocol, TransportOptions may * also accept custom options that will be passed to the * Node.js http functions. + * @deprecated will be removed in 4.0.0 */ export interface TransportOptions { /** @@ -26,8 +27,62 @@ export interface TransportOptions { [key: string]: string | Record | Protocol | Agent | undefined; } -interface EmitterFunction { - (event: CloudEvent, options: TransportOptions): Promise; +/** + * Options is an additional, optional dictionary of options that may + * be passed to an EmitterFunction and TransportFunction + */ +export interface Options { + [key: string]: string | Record | unknown; +} + +/** + * EmitterFunction is an invokable interface returned by the emitterFactory + * function. Invoke an EmitterFunction with a CloudEvent and optional transport + * options to send the event as a Message across supported transports. + */ +export interface EmitterFunction { + (event: CloudEvent, options?: Options): Promise; +} + +/** + * TransportFunction is an invokable interface provided to the emitterFactory. + * A TransportFunction's responsiblity is to send a JSON encoded event Message + * across the wire. + */ +export interface TransportFunction { + (message: Message, options?: Options): Promise; +} + +/** + * emitterFactory creates and returns an EmitterFunction using the supplied + * TransportFunction. The returned EmitterFunction will invoke the Binding's + * `binary` or `structured` function to convert a CloudEvent into a JSON + * Message based on the Mode provided, and invoke the TransportFunction with + * the Message and any supplied options. + * + * @param {TransportFunction} fn a TransportFunction that can accept an event Message + * @param { {Binding, Mode} } options network binding and message serialization options + * @param {Binding} options.binding a transport binding, e.g. HTTP + * @param {Mode} options.mode the encoding mode (Mode.BINARY or Mode.STRUCTURED) + * @returns {EmitterFunction} an EmitterFunction to send events with + */ +export function emitterFor(fn: TransportFunction, options = { binding: HTTP, mode: Mode.BINARY }): EmitterFunction { + if (!fn) { + throw new TypeError("A TransportFunction is required"); + } + const { binding, mode } = options; + return function emit(event: CloudEvent, options?: Options): Promise { + options = options || {}; + + switch (mode) { + case Mode.BINARY: + return fn(binding.binary(event), options); + case Mode.STRUCTURED: + return fn(binding.structured(event), options); + default: + throw new TypeError(`Unexpected transport mode: ${mode}`); + } + }; } /** @@ -36,19 +91,21 @@ interface EmitterFunction { * * @see https://github.com/cloudevents/spec/blob/v1.0/http-protocol-binding.md * @see https://github.com/cloudevents/spec/blob/v1.0/http-protocol-binding.md#13-content-modes + * @deprecated Will be removed in 4.0.0. Consider using the emitterFactory + * */ export class Emitter { url?: string; protocol: Protocol; - emitter: EmitterFunction; + binaryEmitter: EmitterFunction; + structuredEmitter: EmitterFunction; constructor(options: TransportOptions = { protocol: Protocol.HTTPBinary }) { this.protocol = options.protocol as Protocol; this.url = options.url; - this.emitter = emitBinary; - if (this.protocol === Protocol.HTTPStructured) { - this.emitter = emitStructured; - } + + this.binaryEmitter = emitterFor(axiosEmitter(this.url as string)); + this.structuredEmitter = emitterFor(axiosEmitter(this.url as string), { binding: HTTP, mode: Mode.STRUCTURED }); } /** @@ -63,15 +120,15 @@ export class Emitter { * In that case, it will be used as the recipient endpoint. The endpoint can * be overridden by providing a URL here. * @returns {Promise} Promise with an eventual response from the receiver - * @deprecated Will be removed in 4.0.0. Consider using the Message interface with HTTP.[binary|structured](event) + * @deprecated Will be removed in 4.0.0. Consider using the emitterFactory */ - send(event: CloudEvent, options?: TransportOptions): Promise { + send(event: CloudEvent, options?: TransportOptions): Promise { options = options || {}; options.url = options.url || this.url; if (options.protocol != this.protocol) { - if (this.protocol === Protocol.HTTPBinary) return emitBinary(event, options); - return emitStructured(event, options); + if (this.protocol === Protocol.HTTPBinary) return this.binaryEmitter(event, options); + return this.structuredEmitter(event, options); } - return this.emitter(event, options); + return this.binaryEmitter(event, options); } } diff --git a/src/transport/http/binary_emitter.ts b/src/transport/http/binary_emitter.ts deleted file mode 100644 index cabf906..0000000 --- a/src/transport/http/binary_emitter.ts +++ /dev/null @@ -1,32 +0,0 @@ -import axios, { AxiosRequestConfig, AxiosResponse } from "axios"; - -import { CloudEvent, Version } from "../../event/cloudevent"; -import { TransportOptions } from "../emitter"; -import { Headers } from "../../message"; -import { headersFor } from "../../message/http/headers"; -import { asData } from "../../event/validation"; -import CONSTANTS from "../../constants"; - -/** - * Send a CloudEvent over HTTP POST to the `options.url` provided. - * @param {CloudEvent} event the event to send to the remote endpoint - * @param {TransportOptions} options options provided to the transport layer - * @returns {Promise} the HTTP response from the transport layer - */ -export async function emitBinary(event: CloudEvent, options: TransportOptions): Promise { - if (event.specversion !== Version.V1 && event.specversion !== Version.V03) { - return Promise.reject(`Unknown spec version ${event.specversion}`); - } - return emit(event, options, headersFor(event)); -} - -async function emit(event: CloudEvent, options: TransportOptions, headers: Headers): Promise { - const contentType: Headers = { [CONSTANTS.HEADER_CONTENT_TYPE]: CONSTANTS.DEFAULT_CONTENT_TYPE }; - const config = { - ...options, - method: "POST", - headers: { ...contentType, ...headers, ...(options.headers as Headers) }, - data: asData(event.data, event.datacontenttype as string), - }; - return axios.request(config as AxiosRequestConfig); -} diff --git a/src/transport/http/index.ts b/src/transport/http/index.ts index f25036d..845475d 100644 --- a/src/transport/http/index.ts +++ b/src/transport/http/index.ts @@ -1,2 +1,17 @@ -export * from "./binary_emitter"; -export * from "./structured_emitter"; +import { Message, Options } from "../.."; +import axios from "axios"; + +export function axiosEmitter(sink: string) { + return function (message: Message, options?: Options): Promise { + options = { ...options }; + const headers = { + ...message.headers, + ...(options.headers as Record), + }; + delete options.headers; + return axios.post(sink, message.body, { + headers: headers, + ...options, + }); + }; +} diff --git a/src/transport/http/structured_emitter.ts b/src/transport/http/structured_emitter.ts deleted file mode 100644 index af6ec6b..0000000 --- a/src/transport/http/structured_emitter.ts +++ /dev/null @@ -1,20 +0,0 @@ -import axios, { AxiosRequestConfig, AxiosResponse } from "axios"; -import { CloudEvent } from "../../event/cloudevent"; -import { TransportOptions } from "../emitter"; -import CONSTANTS from "../../constants"; - -const defaults = { - headers: { - [CONSTANTS.HEADER_CONTENT_TYPE]: CONSTANTS.DEFAULT_CE_CONTENT_TYPE, - }, -}; - -export function emitStructured(event: CloudEvent, options: TransportOptions): Promise { - const config = { - ...defaults, - ...options, - method: "POST", - data: event, - }; - return axios.request(config as AxiosRequestConfig); -} diff --git a/test/integration/emitter_factory_test.ts b/test/integration/emitter_factory_test.ts new file mode 100644 index 0000000..4f6cba3 --- /dev/null +++ b/test/integration/emitter_factory_test.ts @@ -0,0 +1,127 @@ +import "mocha"; +import { expect } from "chai"; +import nock from "nock"; +import axios from "axios"; +import request from "superagent"; +import got from "got"; + +import CONSTANTS from "../../src/constants"; +import { CloudEvent, emitterFor, HTTP, Mode, Message, Options, TransportFunction } from "../../src"; + +const DEFAULT_CE_CONTENT_TYPE = CONSTANTS.DEFAULT_CE_CONTENT_TYPE; +const sink = "https://cloudevents.io/"; +const type = "com.example.test"; +const source = "urn:event:from:myapi/resource/123"; +const ext1Name = "lunch"; +const ext1Value = "tacos"; +const ext2Name = "supper"; +const ext2Value = "sushi"; +const ext3Name = "snack"; +const ext3Value = { value: "chips" }; + +const data = { + lunchBreak: "noon", +}; + +const fixture = new CloudEvent({ + source, + type, + data, + [ext1Name]: ext1Value, + [ext2Name]: ext2Value, + [ext3Name]: ext3Value, +}); + +function axiosEmitter(message: Message, options?: Options): Promise { + return axios.post(sink, message.body, { headers: message.headers, ...options }); +} + +function superagentEmitter(message: Message, options?: Options): Promise { + const post = request.post(sink); + // set any provided options + if (options) { + for (const key of Object.getOwnPropertyNames(options)) { + if (options[key]) { + post.set(key, options[key] as string); + } + } + } + // set headers + for (const key of Object.getOwnPropertyNames(message.headers)) { + post.set(key, message.headers[key]); + } + return post.send(message.body); +} + +function gotEmitter(message: Message, options?: Options): Promise { + return Promise.resolve( + got.post(sink, { headers: message.headers, body: message.body, ...((options as unknown) as Options) }), + ); +} + +describe("HTTP Transport Binding for emitterFactory", () => { + beforeEach(() => { + nock(sink) + .post("/") + .reply(function (uri: string, body: nock.Body) { + // return the request body and the headers so they can be + // examined in the test + if (typeof body === "string") { + body = JSON.parse(body); + } + const returnBody = { ...(body as Record), ...this.req.headers }; + return [201, returnBody]; + }); + }); + + describe("Axios", () => { + testEmitter(axiosEmitter, "data"); + }); + describe("SuperAgent", () => { + testEmitter(superagentEmitter, "body"); + }); + describe("Got", () => { + testEmitter(gotEmitter, "body"); + }); +}); + +function testEmitter(fn: TransportFunction, bodyAttr: string) { + it("Works as a binary event emitter", async () => { + const emitter = emitterFor(fn); + const response = (await emitter(fixture)) as Record>; + let body = response[bodyAttr]; + if (typeof body === "string") { + body = JSON.parse(body); + } + assertBinary(body); + }); + + it("Works as a structured event emitter", async () => { + const emitter = emitterFor(fn, { binding: HTTP, mode: Mode.STRUCTURED }); + const response = (await emitter(fixture)) as Record>>; + let body = response[bodyAttr]; + if (typeof body === "string") { + body = JSON.parse(body); + } + assertStructured(body); + }); +} + +function assertBinary(response: Record) { + expect(response.lunchBreak).to.equal(data.lunchBreak); + expect(response["ce-type"]).to.equal(type); + expect(response["ce-source"]).to.equal(source); + expect(response[`ce-${ext1Name}`]).to.deep.equal(ext1Value); + expect(response[`ce-${ext2Name}`]).to.deep.equal(ext2Value); + expect(response[`ce-${ext3Name}`]).to.deep.equal(ext3Value); +} + +function assertStructured(response: Record>) { + expect(response.data.lunchBreak).to.equal(data.lunchBreak); + expect(response.type).to.equal(type); + expect(response.source).to.equal(source); + expect(response["content-type"]).to.equal(DEFAULT_CE_CONTENT_TYPE); + expect(response[ext1Name]).to.deep.equal(ext1Value); + expect(response[ext2Name]).to.deep.equal(ext2Value); + expect(response[ext3Name]).to.deep.equal(ext3Value); +} diff --git a/test/integration/http_binding_03.ts b/test/integration/http_binding_03.ts deleted file mode 100644 index c3dc2f5..0000000 --- a/test/integration/http_binding_03.ts +++ /dev/null @@ -1,206 +0,0 @@ -import "mocha"; -import { expect } from "chai"; -import nock from "nock"; - -import { emitBinary, emitStructured } from "../../src/transport/http"; -import { CloudEvent, Version } from "../../src"; -import { AxiosResponse } from "axios"; - -const type = "com.github.pull.create"; -const source = "urn:event:from:myapi/resourse/123"; -const contentEncoding = "base64"; -const contentType = "application/cloudevents+json; charset=utf-8"; -const time = new Date().toISOString(); -const schemaurl = "http://cloudevents.io/schema.json"; - -const ceContentType = "application/json"; - -const data = { - foo: "bar", -}; -const dataBase64 = "Y2xvdWRldmVudHMK"; - -const ext1Name = "extension1"; -const ext1Value = "foobar"; -const ext2Name = "extension2"; -const ext2Value = "acme"; - -const cloudevent = new CloudEvent({ - specversion: Version.V03, - type, - source, - datacontenttype: ceContentType, - subject: "subject.ext", - time, - schemaurl, - data, - // set these so that deepEqual works - dataschema: "", - datacontentencoding: "", - data_base64: "", - [ext1Name]: ext1Value, - [ext2Name]: ext2Value, -}); - -const cebase64 = new CloudEvent({ - specversion: Version.V03, - type, - source, - datacontenttype: ceContentType, - datacontentencoding: contentEncoding, - time, - schemaurl, - data: dataBase64, - [ext1Name]: ext1Value, - [ext2Name]: ext2Value, -}); - -const webhook = "https://cloudevents.io/webhook"; -const httpcfg = { - method: "POST", - url: `${webhook}/json`, -}; - -describe("HTTP Transport Binding - Version 0.3", () => { - beforeEach(() => { - // Mocking the webhook - nock(webhook).post("/json").reply(201, { status: "accepted" }); - }); - - describe("Structured", () => { - describe("JSON Format", () => { - it(`requires '${contentType}' Content-Type in the header`, () => - emitStructured(cloudevent, httpcfg).then((response: AxiosResponse) => { - expect(response.config.headers["Content-Type"]).to.equal(contentType); - })); - - it("the request payload should be correct", () => - emitStructured(cloudevent, httpcfg).then((response: AxiosResponse) => { - expect(response.config.data).to.deep.equal(JSON.stringify(cloudevent)); - })); - - describe("'data' attribute with 'base64' encoding", () => { - it("the request payload should be correct", () => - emitStructured(cebase64, httpcfg).then((response: AxiosResponse) => { - expect(JSON.parse(response.config.data).data).to.equal(cebase64.data); - })); - }); - }); - }); - - describe("Binary", () => { - describe("JSON Format", () => { - it(`requires ${cloudevent.datacontenttype} in the header`, () => - emitBinary(cloudevent, httpcfg).then((response: AxiosResponse) => { - expect(response.config.headers["Content-Type"]).to.equal(cloudevent.datacontenttype); - })); - - it("the request payload should be correct", () => - emitBinary(cloudevent, httpcfg).then((response: AxiosResponse) => { - expect(JSON.parse(response.config.data)).to.deep.equal(cloudevent.data); - })); - - it("HTTP Header contains 'ce-type'", () => - emitBinary(cloudevent, httpcfg).then((response: AxiosResponse) => { - expect(response.config.headers).to.have.property("ce-type"); - })); - - it("HTTP Header contains 'ce-specversion'", () => - emitBinary(cloudevent, httpcfg).then((response: AxiosResponse) => { - expect(response.config.headers).to.have.property("ce-specversion"); - })); - - it("HTTP Header contains 'ce-source'", () => - emitBinary(cloudevent, httpcfg).then((response: AxiosResponse) => { - expect(response.config.headers).to.have.property("ce-source"); - })); - - it("HTTP Header contains 'ce-id'", () => - emitBinary(cloudevent, httpcfg).then((response: AxiosResponse) => { - expect(response.config.headers).to.have.property("ce-id"); - })); - - it("HTTP Header contains 'ce-time'", () => - emitBinary(cloudevent, httpcfg).then((response: AxiosResponse) => { - expect(response.config.headers).to.have.property("ce-time"); - })); - - it("HTTP Header contains 'ce-schemaurl'", () => - emitBinary(cloudevent, httpcfg).then((response: AxiosResponse) => { - expect(response.config.headers).to.have.property("ce-schemaurl"); - })); - - it(`HTTP Header contains 'ce-${ext1Name}'`, () => - emitBinary(cloudevent, httpcfg).then((response: AxiosResponse) => { - expect(response.config.headers).to.have.property(`ce-${ext1Name}`); - })); - - it(`HTTP Header contains 'ce-${ext2Name}'`, () => - emitBinary(cloudevent, httpcfg).then((response: AxiosResponse) => { - expect(response.config.headers).to.have.property(`ce-${ext2Name}`); - })); - - it("HTTP Header contains 'ce-subject'", () => - emitBinary(cloudevent, httpcfg).then((response: AxiosResponse) => { - expect(response.config.headers).to.have.property("ce-subject"); - })); - - it("should 'ce-type' have the right value", () => - emitBinary(cloudevent, httpcfg).then((response: AxiosResponse) => { - expect(cloudevent.type).to.equal(response.config.headers["ce-type"]); - })); - - it("should 'ce-specversion' have the right value", () => - emitBinary(cloudevent, httpcfg).then((response: AxiosResponse) => { - expect(cloudevent.specversion).to.equal(response.config.headers["ce-specversion"]); - })); - - it("should 'ce-source' have the right value", () => - emitBinary(cloudevent, httpcfg).then((response: AxiosResponse) => { - expect(cloudevent.source).to.equal(response.config.headers["ce-source"]); - })); - - it("should 'ce-id' have the right value", () => - emitBinary(cloudevent, httpcfg).then((response: AxiosResponse) => { - expect(cloudevent.id).to.equal(response.config.headers["ce-id"]); - })); - - it("should 'ce-time' have the right value", () => - emitBinary(cloudevent, httpcfg).then((response: AxiosResponse) => { - expect(cloudevent.time).to.equal(response.config.headers["ce-time"]); - })); - - it("should 'ce-schemaurl' have the right value", () => - emitBinary(cloudevent, httpcfg).then((response: AxiosResponse) => { - expect(cloudevent.schemaurl).to.equal(response.config.headers["ce-schemaurl"]); - })); - - it(`should 'ce-${ext1Name}' have the right value`, () => - emitBinary(cloudevent, httpcfg).then((response: AxiosResponse) => { - expect(cloudevent[ext1Name]).to.equal(response.config.headers[`ce-${ext1Name}`]); - })); - - it(`should 'ce-${ext2Name}' have the right value`, () => - emitBinary(cloudevent, httpcfg).then((response: AxiosResponse) => { - expect(cloudevent[ext2Name]).to.equal(response.config.headers[`ce-${ext2Name}`]); - })); - - it("should 'ce-subject' have the right value", () => - emitBinary(cloudevent, httpcfg).then((response: AxiosResponse) => { - expect(cloudevent.subject).to.equal(response.config.headers["ce-subject"]); - })); - - describe("'data' attribute with 'base64' encoding", () => { - it("HTTP Header contains 'ce-datacontentencoding'", () => - emitBinary(cebase64, httpcfg).then((response: AxiosResponse) => { - expect(response.config.headers).to.have.property("ce-datacontentencoding"); - })); - - it("should 'ce-datacontentencoding' have the right value", () => - emitBinary(cebase64, httpcfg).then((response: AxiosResponse) => { - expect(cebase64.datacontentencoding).to.equal(response.config.headers["ce-datacontentencoding"]); - })); - }); - }); - }); -}); diff --git a/test/integration/http_binding_1.ts b/test/integration/http_binding_1.ts deleted file mode 100644 index a5a0dec..0000000 --- a/test/integration/http_binding_1.ts +++ /dev/null @@ -1,239 +0,0 @@ -import * as https from "https"; -import "mocha"; -import { expect } from "chai"; -import nock from "nock"; - -import { CloudEvent, Version } from "../../src"; -import { emitBinary, emitStructured } from "../../src/transport/http"; -import { asBase64 } from "../../src/event/validation"; -import { AxiosResponse } from "axios"; - -const type = "com.github.pull.create"; -const source = "urn:event:from:myapi/resource/123"; -const contentType = "application/cloudevents+json; charset=utf-8"; -const time = new Date().toISOString(); -const subject = "subject.ext"; -const dataschema = "http://cloudevents.io/schema.json"; -const datacontenttype = "application/json"; - -const data = { - foo: "bar", -}; - -const ext1Name = "extension1"; -const ext1Value = "foobar"; -const ext2Name = "extension2"; -const ext2Value = "acme"; - -let cloudevent = new CloudEvent({ - specversion: Version.V1, - type, - source, - datacontenttype, - subject, - time, - dataschema, - data, -}); -cloudevent = cloudevent.cloneWith({ [ext1Name]: ext1Value, [ext2Name]: ext2Value }); - -const dataString = ")(*~^my data for ce#@#$%"; - -const webhook = "https://cloudevents.io/webhook/v1"; -const httpcfg = { url: `${webhook}/json` }; - -describe("HTTP Transport Binding - Version 1.0", () => { - beforeEach(() => { - // Mocking the webhook - nock(webhook).post("/json").reply(201, { status: "accepted" }); - }); - - describe("Structured", () => { - it("works with mTLS authentication", () => { - const httpsAgent = new https.Agent({ - cert: "some value", - key: "other value", - }); - - return emitStructured(cloudevent, { ...httpcfg, httpsAgent }).then((response: AxiosResponse) => { - expect(response.config.headers["Content-Type"]).to.equal(contentType); - }); - }); - - describe("JSON Format", () => { - it(`requires '${contentType}' Content-Type in the header`, () => - emitStructured(cloudevent, httpcfg).then((response: AxiosResponse) => { - expect(response.config.headers["Content-Type"]).to.equal(contentType); - })); - - it("the request payload should be correct", () => - emitStructured(cloudevent, httpcfg).then((response: AxiosResponse) => { - expect(response.config.data).to.deep.equal(JSON.stringify(cloudevent)); - })); - - describe("Binary event data", () => { - it("the request payload should be correct when data is binary", () => { - const bindata = Uint32Array.from(dataString as string, (c) => c.codePointAt(0) as number); - const expected = asBase64(bindata); - const binevent = new CloudEvent({ - type, - source, - datacontenttype: "text/plain", - data: bindata, - [ext1Name]: ext1Value, - [ext2Name]: ext2Value, - }); - - return emitStructured(binevent, httpcfg).then((response: AxiosResponse) => { - expect(JSON.parse(response.config.data).data_base64).to.equal(expected); - }); - }); - - it("the payload must have 'data_base64' when data is binary", () => { - const binevent = new CloudEvent({ - type, - source, - datacontenttype: "text/plain", - data: Uint32Array.from(dataString as string, (c) => c.codePointAt(0) as number), - [ext1Name]: ext1Value, - [ext2Name]: ext2Value, - }); - - return emitStructured(binevent, httpcfg).then((response: AxiosResponse) => { - expect(JSON.parse(response.config.data)).to.have.property("data_base64"); - }); - }); - }); - }); - }); - - describe("Binary", () => { - it("works with mTLS authentication", () => - emitBinary(cloudevent, { - url: `${webhook}/json`, - httpsAgent: new https.Agent({ - cert: "some value", - key: "other value", - }), - }).then((response: AxiosResponse) => { - expect(response.config.headers["Content-Type"]).to.equal(cloudevent.datacontenttype); - })); - - describe("JSON Format", () => { - it(`requires '${cloudevent.datacontenttype}' in the header`, () => - emitBinary(cloudevent, httpcfg).then((response: AxiosResponse) => { - expect(response.config.headers["Content-Type"]).to.equal(cloudevent.datacontenttype); - })); - - it("the request payload should be correct", () => - emitBinary(cloudevent, httpcfg).then((response: AxiosResponse) => { - expect(JSON.parse(response.config.data)).to.deep.equal(cloudevent.data); - })); - - it("the request payload should be correct when event data is binary", () => { - const bindata = Uint32Array.from(dataString as string, (c) => c.codePointAt(0) as number); - const expected = asBase64(bindata); - const binevent = new CloudEvent({ - type, - source, - datacontenttype: "text/plain", - data: bindata, - }); - - return emitBinary(binevent, httpcfg).then((response: AxiosResponse) => { - expect(response.config.data).to.equal(expected); - }); - }); - - it("HTTP Header contains 'ce-type'", () => - emitBinary(cloudevent, httpcfg).then((response: AxiosResponse) => { - expect(response.config.headers).to.have.property("ce-type"); - })); - - it("HTTP Header contains 'ce-specversion'", () => - emitBinary(cloudevent, httpcfg).then((response: AxiosResponse) => { - expect(response.config.headers).to.have.property("ce-specversion"); - })); - - it("HTTP Header contains 'ce-source'", () => - emitBinary(cloudevent, httpcfg).then((response: AxiosResponse) => { - expect(response.config.headers).to.have.property("ce-source"); - })); - - it("HTTP Header contains 'ce-id'", () => - emitBinary(cloudevent, httpcfg).then((response: AxiosResponse) => { - expect(response.config.headers).to.have.property("ce-id"); - })); - - it("HTTP Header contains 'ce-time'", () => - emitBinary(cloudevent, httpcfg).then((response: AxiosResponse) => { - expect(response.config.headers).to.have.property("ce-time"); - })); - - it("HTTP Header contains 'ce-dataschema'", () => - emitBinary(cloudevent, httpcfg).then((response: AxiosResponse) => { - expect(response.config.headers).to.have.property("ce-dataschema"); - })); - - it(`HTTP Header contains 'ce-${ext1Name}'`, () => - emitBinary(cloudevent, httpcfg).then((response: AxiosResponse) => { - expect(response.config.headers).to.have.property(`ce-${ext1Name}`); - })); - - it(`HTTP Header contains 'ce-${ext2Name}'`, () => - emitBinary(cloudevent, httpcfg).then((response: AxiosResponse) => { - expect(response.config.headers).to.have.property(`ce-${ext2Name}`); - })); - - it("HTTP Header contains 'ce-subject'", () => - emitBinary(cloudevent, httpcfg).then((response: AxiosResponse) => { - expect(response.config.headers).to.have.property("ce-subject"); - })); - - it("should 'ce-type' have the right value", () => - emitBinary(cloudevent, httpcfg).then((response: AxiosResponse) => { - expect(cloudevent.type).to.equal(response.config.headers["ce-type"]); - })); - - it("should 'ce-specversion' have the right value", () => - emitBinary(cloudevent, httpcfg).then((response: AxiosResponse) => { - expect(cloudevent.specversion).to.equal(response.config.headers["ce-specversion"]); - })); - - it("should 'ce-source' have the right value", () => - emitBinary(cloudevent, httpcfg).then((response: AxiosResponse) => { - expect(cloudevent.source).to.equal(response.config.headers["ce-source"]); - })); - - it("should 'ce-id' have the right value", () => - emitBinary(cloudevent, httpcfg).then((response: AxiosResponse) => { - expect(cloudevent.id).to.equal(response.config.headers["ce-id"]); - })); - - it("should 'ce-time' have the right value", () => - emitBinary(cloudevent, httpcfg).then((response: AxiosResponse) => { - expect(cloudevent.time).to.equal(response.config.headers["ce-time"]); - })); - - it("should 'ce-dataschema' have the right value", () => - emitBinary(cloudevent, httpcfg).then((response: AxiosResponse) => { - expect(cloudevent.dataschema).to.equal(response.config.headers["ce-dataschema"]); - })); - - it(`should 'ce-${ext1Name}' have the right value`, () => - emitBinary(cloudevent, httpcfg).then((response: AxiosResponse) => { - expect(cloudevent[ext1Name]).to.equal(response.config.headers[`ce-${ext1Name}`]); - })); - - it(`should 'ce-${ext2Name}' have the right value`, () => - emitBinary(cloudevent, httpcfg).then((response: AxiosResponse) => { - expect(cloudevent[ext2Name]).to.equal(response.config.headers[`ce-${ext2Name}`]); - })); - - it("should 'ce-subject' have the right value", () => - emitBinary(cloudevent, httpcfg).then((response: AxiosResponse) => { - expect(cloudevent.subject).to.equal(response.config.headers["ce-subject"]); - })); - }); - }); -}); diff --git a/test/integration/http_emitter_test.ts b/test/integration/http_emitter_test.ts index a2d9c05..a014feb 100644 --- a/test/integration/http_emitter_test.ts +++ b/test/integration/http_emitter_test.ts @@ -8,7 +8,6 @@ const DEFAULT_CONTENT_TYPE = CONSTANTS.DEFAULT_CONTENT_TYPE; import { CloudEvent, Version, Emitter, Protocol } from "../../src"; import { headersFor } from "../../src/message/http/headers"; -import { AxiosResponse } from "axios"; const receiver = "https://cloudevents.io/"; const type = "com.example.test"; @@ -20,7 +19,7 @@ const ext2Value = "sushi"; const ext3Name = "snack"; const ext3Value = { value: "chips" }; -const data = { +const eventData = { lunchBreak: "noon", }; @@ -46,7 +45,7 @@ describe("HTTP Transport Binding Emitter for CloudEvents", () => { type, source, time: new Date().toISOString(), - data, + data: eventData, [ext1Name]: ext1Value, [ext2Name]: ext2Value, [ext3Name]: ext3Value, @@ -55,17 +54,24 @@ describe("HTTP Transport Binding Emitter for CloudEvents", () => { it("Sends a binary 1.0 CloudEvent by default", () => emitter .send(event) - .then((response: AxiosResponse) => { + .then((value: unknown) => { + const data = (value as Record< + string, + Record>>> + >).data; + // A binary message will have a ce-id header - expect(response.data["content-type"]).to.equal(DEFAULT_CONTENT_TYPE); - expect(response.data[CONSTANTS.CE_HEADERS.ID]).to.equal(event.id); - expect(response.data[CONSTANTS.CE_HEADERS.SPEC_VERSION]).to.equal(Version.V1); + expect(data["content-type"]).to.equal(DEFAULT_CONTENT_TYPE); + expect(data[CONSTANTS.CE_HEADERS.ID]).to.equal(event.id); + expect(data[CONSTANTS.CE_HEADERS.SPEC_VERSION]).to.equal(Version.V1); // A binary message will have a request body for the data - expect(response.data.lunchBreak).to.equal(data.lunchBreak); + expect(data.lunchBreak).to.equal(data.lunchBreak); // Ensure extensions are handled properly - expect(response.data[`${CONSTANTS.EXTENSIONS_PREFIX}${ext1Name}`]).to.equal(ext1Value); - expect(response.data[`${CONSTANTS.EXTENSIONS_PREFIX}${ext2Name}`]).to.equal(ext2Value); - expect(response.data[`${CONSTANTS.EXTENSIONS_PREFIX}${ext3Name}`].value).to.equal(ext3Value.value); + expect(data[`${CONSTANTS.EXTENSIONS_PREFIX}${ext1Name}`]).to.equal(ext1Value); + expect(data[`${CONSTANTS.EXTENSIONS_PREFIX}${ext2Name}`]).to.equal(ext2Value); + expect((data[`${CONSTANTS.EXTENSIONS_PREFIX}${ext3Name}`] as Record).value).to.equal( + ext3Value.value, + ); }) .catch(expect.fail)); @@ -81,31 +87,41 @@ describe("HTTP Transport Binding Emitter for CloudEvents", () => { it("Sends a binary CloudEvent with Custom Headers", () => emitter .send(event, { headers: { customheader: "value" } }) - .then((response: { data: { [k: string]: string } }) => { + .then((value: unknown) => { + const data = (value as Record< + string, + Record>>> + >).data; + // A binary message will have a ce-id header - expect(response.data.customheader).to.equal("value"); - expect(response.data["content-type"]).to.equal(DEFAULT_CONTENT_TYPE); - expect(response.data[CONSTANTS.CE_HEADERS.ID]).to.equal(event.id); - expect(response.data[CONSTANTS.CE_HEADERS.SPEC_VERSION]).to.equal(Version.V1); + expect(data.customheader).to.equal("value"); + expect(data["content-type"]).to.equal(DEFAULT_CONTENT_TYPE); + expect(data[CONSTANTS.CE_HEADERS.ID]).to.equal(event.id); + expect(data[CONSTANTS.CE_HEADERS.SPEC_VERSION]).to.equal(Version.V1); // A binary message will have a request body for the data - expect(response.data.lunchBreak).to.equal(data.lunchBreak); + expect(data.lunchBreak).to.equal(data.lunchBreak); }) .catch(expect.fail)); it("Sends a structured 1.0 CloudEvent if specified", () => emitter .send(event, { protocol: Protocol.HTTPStructured }) - .then((response: { data: { [k: string]: string | Record; data: { lunchBreak: string } } }) => { + .then((value: unknown) => { + const data = (value as Record>).data as Record< + string, + string | Record + >; + // A structured message will have a cloud event content type - expect(response.data["content-type"]).to.equal(DEFAULT_CE_CONTENT_TYPE); + expect(data["content-type"]).to.equal(DEFAULT_CE_CONTENT_TYPE); // Ensure other CE headers don't exist - just testing for ID - expect(response.data[CONSTANTS.CE_HEADERS.ID]).to.equal(undefined); + expect(data[CONSTANTS.CE_HEADERS.ID]).to.equal(undefined); // The spec version would have been specified in the body - expect(response.data.specversion).to.equal(Version.V1); - expect(response.data.data.lunchBreak).to.equal(data.lunchBreak); + expect(data.specversion).to.equal(Version.V1); + expect((data as Record>).data.lunchBreak).to.equal(eventData.lunchBreak); // Ensure extensions are handled properly - expect(response.data[ext1Name]).to.equal(ext1Value); - expect(response.data[ext2Name]).to.equal(ext2Value); + expect(data[ext1Name]).to.equal(ext1Value); + expect(data[ext2Name]).to.equal(ext2Value); }) .catch(expect.fail)); @@ -124,14 +140,19 @@ describe("HTTP Transport Binding Emitter for CloudEvents", () => { return emitter .send(event, { protocol: Protocol.HTTPStructured, url: `${receiver}alternate` }) - .then((response: AxiosResponse) => { + .then((value: unknown) => { + const data = (value as Record>).data as Record< + string, + string | Record + >; + // A structured message will have a cloud event content type - expect(response.data["content-type"]).to.equal(DEFAULT_CE_CONTENT_TYPE); + expect(data["content-type"]).to.equal(DEFAULT_CE_CONTENT_TYPE); // Ensure other CE headers don't exist - just testing for ID - expect(response.data[CONSTANTS.CE_HEADERS.ID]).to.equal(undefined); + expect(data[CONSTANTS.CE_HEADERS.ID]).to.equal(undefined); // The spec version would have been specified in the body - expect(response.data.specversion).to.equal(Version.V1); - expect(response.data.data.lunchBreak).to.equal(data.lunchBreak); + expect(data.specversion).to.equal(Version.V1); + expect((data as Record>).data.lunchBreak).to.equal(eventData.lunchBreak); }) .catch(expect.fail); }); @@ -144,7 +165,7 @@ describe("HTTP Transport Binding Emitter for CloudEvents", () => { type, source, time: new Date().toISOString(), - data, + data: eventData, [ext1Name]: ext1Value, [ext2Name]: ext2Value, [ext3Name]: ext3Value, @@ -153,16 +174,23 @@ describe("HTTP Transport Binding Emitter for CloudEvents", () => { it("Sends a binary 0.3 CloudEvent", () => emitter .send(event) - .then((response: AxiosResponse) => { + .then((value: unknown) => { + const data = (value as Record>).data as Record< + string, + string | Record + >; + // A binary message will have a ce-id header - expect(response.data[CONSTANTS.CE_HEADERS.ID]).to.equal(event.id); - expect(response.data[CONSTANTS.CE_HEADERS.SPEC_VERSION]).to.equal(Version.V03); + expect(data[CONSTANTS.CE_HEADERS.ID]).to.equal(event.id); + expect(data[CONSTANTS.CE_HEADERS.SPEC_VERSION]).to.equal(Version.V03); // A binary message will have a request body for the data - expect(response.data.lunchBreak).to.equal(data.lunchBreak); + expect(data.lunchBreak).to.equal(data.lunchBreak); // Ensure extensions are handled properly - expect(response.data[`${CONSTANTS.EXTENSIONS_PREFIX}${ext1Name}`]).to.equal(ext1Value); - expect(response.data[`${CONSTANTS.EXTENSIONS_PREFIX}${ext2Name}`]).to.equal(ext2Value); - expect(response.data[`${CONSTANTS.EXTENSIONS_PREFIX}${ext3Name}`].value).to.equal(ext3Value.value); + expect(data[`${CONSTANTS.EXTENSIONS_PREFIX}${ext1Name}`]).to.equal(ext1Value); + expect(data[`${CONSTANTS.EXTENSIONS_PREFIX}${ext2Name}`]).to.equal(ext2Value); + expect((data[`${CONSTANTS.EXTENSIONS_PREFIX}${ext3Name}`] as Record).value).to.equal( + ext3Value.value, + ); }) .catch(expect.fail)); @@ -178,22 +206,23 @@ describe("HTTP Transport Binding Emitter for CloudEvents", () => { it("Sends a structured 0.3 CloudEvent if specified", () => emitter .send(event, { protocol: Protocol.HTTPStructured }) - .then( - (response: { - data: { [k: string]: string | Record; specversion: string; data: { lunchBreak: string } }; - }) => { - // A structured message will have a cloud event content type - expect(response.data["content-type"]).to.equal(DEFAULT_CE_CONTENT_TYPE); - // Ensure other CE headers don't exist - just testing for ID - expect(response.data[CONSTANTS.CE_HEADERS.ID]).to.equal(undefined); - // The spec version would have been specified in the body - expect(response.data.specversion).to.equal(Version.V03); - expect(response.data.data.lunchBreak).to.equal(data.lunchBreak); - // Ensure extensions are handled properly - expect(response.data[ext1Name]).to.equal(ext1Value); - expect(response.data[ext2Name]).to.equal(ext2Value); - }, - ) + .then((value: unknown) => { + const data = (value as Record>).data as Record< + string, + string | Record + >; + + // A structured message will have a cloud event content type + expect(data["content-type"]).to.equal(DEFAULT_CE_CONTENT_TYPE); + // Ensure other CE headers don't exist - just testing for ID + expect(data[CONSTANTS.CE_HEADERS.ID]).to.equal(undefined); + // The spec version would have been specified in the body + expect(data.specversion).to.equal(Version.V03); + expect((data as Record>).data.lunchBreak).to.equal(eventData.lunchBreak); + // Ensure extensions are handled properly + expect(data[ext1Name]).to.equal(ext1Value); + expect(data[ext2Name]).to.equal(ext2Value); + }) .catch(expect.fail)); it("Sends to an alternate URL if specified", () => { @@ -211,19 +240,20 @@ describe("HTTP Transport Binding Emitter for CloudEvents", () => { return emitter .send(event, { protocol: Protocol.HTTPStructured, url: `${receiver}alternate` }) - .then( - (response: { - data: { specversion: string; data: { lunchBreak: string }; [k: string]: string | Record }; - }) => { - // A structured message will have a cloud event content type - expect(response.data["content-type"]).to.equal(DEFAULT_CE_CONTENT_TYPE); - // Ensure other CE headers don't exist - just testing for ID - expect(response.data[CONSTANTS.CE_HEADERS.ID]).to.equal(undefined); - // The spec version would have been specified in the body - expect(response.data.specversion).to.equal(Version.V03); - expect(response.data.data.lunchBreak).to.equal(data.lunchBreak); - }, - ) + .then((value: unknown) => { + const data = (value as Record>).data as Record< + string, + string | Record + >; + + // A structured message will have a cloud event content type + expect(data["content-type"]).to.equal(DEFAULT_CE_CONTENT_TYPE); + // Ensure other CE headers don't exist - just testing for ID + expect(data[CONSTANTS.CE_HEADERS.ID]).to.equal(undefined); + // The spec version would have been specified in the body + expect(data.specversion).to.equal(Version.V03); + expect((data as Record>).data.lunchBreak).to.equal(eventData.lunchBreak); + }) .catch(expect.fail); }); }); diff --git a/test/integration/message_test.ts b/test/integration/message_test.ts index e786034..6495a80 100644 --- a/test/integration/message_test.ts +++ b/test/integration/message_test.ts @@ -102,7 +102,7 @@ describe("HTTP transport", () => { it("Binary Messages can be created from a CloudEvent", () => { const message: Message = HTTP.binary(fixture); - expect(message.body).to.equal(data); + expect(JSON.parse(message.body)).to.deep.equal(data); // validate all headers expect(message.headers[CONSTANTS.HEADER_CONTENT_TYPE]).to.equal(datacontenttype); expect(message.headers[CONSTANTS.CE_HEADERS.SPEC_VERSION]).to.equal(Version.V1); @@ -178,7 +178,7 @@ describe("HTTP transport", () => { it("Binary Messages can be created from a CloudEvent", () => { const message: Message = HTTP.binary(fixture); - expect(message.body).to.equal(data); + expect(message.body).to.equal(JSON.stringify(data)); // validate all headers expect(message.headers[CONSTANTS.HEADER_CONTENT_TYPE]).to.equal(datacontenttype); expect(message.headers[CONSTANTS.CE_HEADERS.SPEC_VERSION]).to.equal(Version.V03);