diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8ffdfec19..802bb245d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,45 @@
# Changelog
+## [1.13.0](https://github.com/apigee/devrel/compare/v1.12.0...v1.13.0) (2023-08-17)
+
+
+### Features
+
+* added missing licenses ([cf6d1ff](https://github.com/apigee/devrel/commit/cf6d1ff5cdb940de93b9fa9c7a97aaaaacd3dffa))
+* added pip package install to pipeline-runner ([06fd5fa](https://github.com/apigee/devrel/commit/06fd5fa70278c2b20c1defb6240389aa0811fcc0))
+* Added proxy-endpoint-unifier source ([d9489f2](https://github.com/apigee/devrel/commit/d9489f281a150604a2ebff532e7517e6fa6491e1))
+* added support for PostClientFlow ([d80c5b4](https://github.com/apigee/devrel/commit/d80c5b4b7e255ae7f0bba5d7221fa0f5c72495ab))
+* added test scripts for proxy-endpoint-unifier ([740316e](https://github.com/apigee/devrel/commit/740316e0c2a9413a47484a18c3a0d9c0fcd343a8))
+* added zip packaged to pipeline runner ([af3e94e](https://github.com/apigee/devrel/commit/af3e94ecb7a5c3851e478c0099c5a48d69c2c2dd))
+* addressed flake8 for main proxy-endpoint-unifier wrapper ([18badee](https://github.com/apigee/devrel/commit/18badeef1adad545e94bec8374cd22ca554990a9))
+* addressed flake8 for utils proxy-endpoint-unifier wrapper ([b812452](https://github.com/apigee/devrel/commit/b81245213b2653b7595652dfa79326e9d96f0355))
+* addressed flake8 for xorhybrid proxy-endpoint-unifier wrapper ([7ccef07](https://github.com/apigee/devrel/commit/7ccef07a097041fc323388433dce7f8abca32a8a))
+* fixed shell lint and updated README.md ([f3ff9a0](https://github.com/apigee/devrel/commit/f3ff9a01f28e9c015b16da09df61f98bb9b0f127))
+* modified pipeline.sh & README ([fd3ba09](https://github.com/apigee/devrel/commit/fd3ba097b36aa6f311160f1af690b57cde934470))
+* removed comments & added newlines in the proxy-endpoint-unifier wrapper ([b39a38f](https://github.com/apigee/devrel/commit/b39a38fed5bbb05f22c3e84771aca68cc0d6b938))
+* updated CODEOWNERS ([655e5af](https://github.com/apigee/devrel/commit/655e5aff29d6a0273fefbf87b919b5dc3a9cce09))
+* updated Licenses in the proxy-endpoint-unifier wrapper ([a7c8a03](https://github.com/apigee/devrel/commit/a7c8a030e40e55aede631d5df438265012ac7ddd))
+
+
+### Bug Fixes
+
+* added execute permissions on pipeline.sh ([cbae732](https://github.com/apigee/devrel/commit/cbae7326cf2475efddc21a018034cc85a40d0512))
+* added support for FS read when proxy root xml is empty ([f821756](https://github.com/apigee/devrel/commit/f82175625f80d44fdeb78f25788d5846c56042a2))
+* addressed PR comments ([d2db718](https://github.com/apigee/devrel/commit/d2db7181d950b742641b36ad9f2e55a63900cc92))
+* changes to proxy endpoint unifier readme ([b245415](https://github.com/apigee/devrel/commit/b245415ceac9fce9b5247d6261497058faf9fe79))
+* removing manifests , fixing Cache Policy & target endpoint ([5f92893](https://github.com/apigee/devrel/commit/5f92893d1fe0fdcc164feea9ff41d7595e6477d9))
+
+## [1.12.0](https://github.com/apigee/devrel/compare/v1.11.0...v1.12.0) (2023-08-08)
+
+
+### Features
+
+* cloud build job to delete stale image artifacts ([2e1e923](https://github.com/apigee/devrel/commit/2e1e923e46d5e8405edd513253c9ac83804c4d55))
+* fix semantic changes from httpbin to mocktarget ([87a376f](https://github.com/apigee/devrel/commit/87a376f93f041ec7d91c36ffc854250890d278cd))
+* remove hackish fix since mocktarget now supports query param args ([e4c2280](https://github.com/apigee/devrel/commit/e4c2280f3dc736bba4a05f9ba6f97c4c2afadb3f))
+* replace httpbin with mocktarget for identity facade and envoy quickstart ([b3fcfb9](https://github.com/apigee/devrel/commit/b3fcfb91a8afd126fca0588cd5c2e1375d243c24))
+* replace httpbin.org with mocktarget.apigee.net ([58a8185](https://github.com/apigee/devrel/commit/58a81857cb4fdff4edaa6542aeb32707eefb3d48))
+
## [1.11.0](https://github.com/apigee/devrel/compare/v1.10.1...v1.11.0) (2023-07-05)
diff --git a/CODEOWNERS b/CODEOWNERS
index caf8da8f1..8e5385587 100644
--- a/CODEOWNERS
+++ b/CODEOWNERS
@@ -36,4 +36,5 @@
/tools/oas-configurable-proxy @danistrebel
/tools/pipeline-linter @seymen @danistrebel
/tools/pipeline-runner @seymen @danistrebel
-/tools/sf-dependency-list @yuriylesyuk
\ No newline at end of file
+/tools/sf-dependency-list @yuriylesyuk
+/tools/proxy-endpoint-unifier @anaik91
diff --git a/README.md b/README.md
index 144532a37..4c707765c 100644
--- a/README.md
+++ b/README.md
@@ -91,6 +91,8 @@ Apigee products.
A tool to generate topologically sorted Shared Flow dependencies.
- [Apigee Envoy Quickstart Toolkit](tools/apigee-envoy-quickstart) -
A tool to set up the sample deployments of Apigee Envoy.
+- [Apigee API Proxy Endpoint Unifier](tools/proxy-endpoint-unifier) -
+ A tool to unify/split proxy endpoints based on API basepath.
## Labs
diff --git a/references/cicd-pipeline/package-lock.json b/references/cicd-pipeline/package-lock.json
index 2107fd9ae..8d033e269 100644
--- a/references/cicd-pipeline/package-lock.json
+++ b/references/cicd-pipeline/package-lock.json
@@ -12,7 +12,6 @@
"apickli": "^3.0.1",
"apigeelint": "^2.13.0",
"eslint": "^7.0.0",
- "eslint-config-google": "^0.14.0",
"eslint-config-prettier": "^6.11.0",
"mocha": "^7.2.0",
"nyc": "^15.1.0",
@@ -2564,15 +2563,6 @@
"url": "https://opencollective.com/eslint"
}
},
- "node_modules/eslint-config-google": {
- "version": "0.14.0",
- "resolved": "https://registry.npmjs.org/eslint-config-google/-/eslint-config-google-0.14.0.tgz",
- "integrity": "sha512-WsbX4WbjuMvTdeVL6+J3rK1RGhCTqjsFjX7UMSMgZiyxxaNLkoJENbrGExzERFeoTpGw3F3FypTiWAP9ZXzkEw==",
- "dev": true,
- "engines": {
- "node": ">=0.10.0"
- }
- },
"node_modules/eslint-config-prettier": {
"version": "6.11.0",
"resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-6.11.0.tgz",
@@ -7179,9 +7169,9 @@
}
},
"node_modules/word-wrap": {
- "version": "1.2.3",
- "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
- "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==",
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
+ "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==",
"dev": true,
"engines": {
"node": ">=0.10.0"
@@ -9642,12 +9632,6 @@
}
}
},
- "eslint-config-google": {
- "version": "0.14.0",
- "resolved": "https://registry.npmjs.org/eslint-config-google/-/eslint-config-google-0.14.0.tgz",
- "integrity": "sha512-WsbX4WbjuMvTdeVL6+J3rK1RGhCTqjsFjX7UMSMgZiyxxaNLkoJENbrGExzERFeoTpGw3F3FypTiWAP9ZXzkEw==",
- "dev": true
- },
"eslint-config-prettier": {
"version": "6.11.0",
"resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-6.11.0.tgz",
@@ -13474,9 +13458,9 @@
}
},
"word-wrap": {
- "version": "1.2.3",
- "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
- "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==",
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
+ "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==",
"dev": true
},
"wrap-ansi": {
diff --git a/references/cicd-sharedflow-pipeline/package-lock.json b/references/cicd-sharedflow-pipeline/package-lock.json
index 84c2a126f..b8fcaf0e2 100644
--- a/references/cicd-sharedflow-pipeline/package-lock.json
+++ b/references/cicd-sharedflow-pipeline/package-lock.json
@@ -5625,9 +5625,9 @@
}
},
"node_modules/word-wrap": {
- "version": "1.2.3",
- "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
- "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==",
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
+ "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==",
"dev": true,
"engines": {
"node": ">=0.10.0"
@@ -10426,9 +10426,9 @@
}
},
"word-wrap": {
- "version": "1.2.3",
- "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
- "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==",
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
+ "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==",
"dev": true
},
"wrappy": {
diff --git a/references/data-converters-shared-flow/package-lock.json b/references/data-converters-shared-flow/package-lock.json
index 5a7e47c18..41b3bbb28 100644
--- a/references/data-converters-shared-flow/package-lock.json
+++ b/references/data-converters-shared-flow/package-lock.json
@@ -1738,9 +1738,9 @@
}
},
"node_modules/word-wrap": {
- "version": "1.2.3",
- "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
- "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==",
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
+ "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==",
"dev": true,
"engines": {
"node": ">=0.10.0"
@@ -3263,9 +3263,9 @@
}
},
"word-wrap": {
- "version": "1.2.3",
- "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
- "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==",
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
+ "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==",
"dev": true
},
"workerpool": {
diff --git a/references/js-callout/package-lock.json b/references/js-callout/package-lock.json
index ca307121a..c71106f59 100644
--- a/references/js-callout/package-lock.json
+++ b/references/js-callout/package-lock.json
@@ -908,6 +908,15 @@
"@sinonjs/commons": "^1.7.0"
}
},
+ "node_modules/@tootallnate/once": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz",
+ "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==",
+ "dev": true,
+ "engines": {
+ "node": ">= 6"
+ }
+ },
"node_modules/@types/babel__core": {
"version": "7.1.9",
"resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.9.tgz",
@@ -1043,9 +1052,9 @@
}
},
"node_modules/abab": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.3.tgz",
- "integrity": "sha512-tsFzPpcttalNjFBCFMqsKYQcWxxen1pgJR56by//QwvJc4/OUS3kPOOttx2tSIfjsylB0pYu7f5D3K1RCxUnUg==",
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz",
+ "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==",
"dev": true
},
"node_modules/acorn": {
@@ -1079,6 +1088,18 @@
"node": ">=0.4.0"
}
},
+ "node_modules/agent-base": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
+ "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
+ "dev": true,
+ "dependencies": {
+ "debug": "4"
+ },
+ "engines": {
+ "node": ">= 6.0.0"
+ }
+ },
"node_modules/ajv": {
"version": "6.12.3",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.3.tgz",
@@ -1974,9 +1995,9 @@
}
},
"node_modules/decimal.js": {
- "version": "10.2.0",
- "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.2.0.tgz",
- "integrity": "sha512-vDPw+rDgn3bZe1+F/pyEwb1oMG2XTlRVgAa6B4KccTEpYgF8w6eQllVbQcfIJnZyvzFtFpxnpGtx8dd7DJp/Rw==",
+ "version": "10.4.3",
+ "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz",
+ "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==",
"dev": true
},
"node_modules/decode-uri-component": {
@@ -1988,12 +2009,6 @@
"node": ">=0.10"
}
},
- "node_modules/deep-is": {
- "version": "0.1.3",
- "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz",
- "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=",
- "dev": true
- },
"node_modules/deepmerge": {
"version": "4.2.2",
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz",
@@ -2282,23 +2297,24 @@
}
},
"node_modules/escodegen": {
- "version": "1.14.3",
- "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.3.tgz",
- "integrity": "sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==",
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz",
+ "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==",
"dev": true,
"dependencies": {
"esprima": "^4.0.1",
- "estraverse": "^4.2.0",
- "esutils": "^2.0.2",
- "optionator": "^0.8.1",
- "source-map": "~0.6.1"
+ "estraverse": "^5.2.0",
+ "esutils": "^2.0.2"
},
"bin": {
"escodegen": "bin/escodegen.js",
"esgenerate": "bin/esgenerate.js"
},
"engines": {
- "node": ">=4.0"
+ "node": ">=6.0"
+ },
+ "optionalDependencies": {
+ "source-map": "~0.6.1"
}
},
"node_modules/esprima": {
@@ -2315,9 +2331,9 @@
}
},
"node_modules/estraverse": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz",
- "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==",
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+ "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
"dev": true,
"engines": {
"node": ">=4.0"
@@ -2596,12 +2612,6 @@
"integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
"dev": true
},
- "node_modules/fast-levenshtein": {
- "version": "2.0.6",
- "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
- "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=",
- "dev": true
- },
"node_modules/fb-watchman": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.1.tgz",
@@ -3007,6 +3017,20 @@
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
"dev": true
},
+ "node_modules/http-proxy-agent": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz",
+ "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==",
+ "dev": true,
+ "dependencies": {
+ "@tootallnate/once": "1",
+ "agent-base": "6",
+ "debug": "4"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
"node_modules/http-signature": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
@@ -3022,6 +3046,19 @@
"npm": ">=1.3.7"
}
},
+ "node_modules/https-proxy-agent": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
+ "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==",
+ "dev": true,
+ "dependencies": {
+ "agent-base": "6",
+ "debug": "4"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
"node_modules/human-signals": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz",
@@ -3093,15 +3130,6 @@
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
"dev": true
},
- "node_modules/ip-regex": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz",
- "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=",
- "dev": true,
- "engines": {
- "node": ">=4"
- }
- },
"node_modules/is-accessor-descriptor": {
"version": "0.1.6",
"resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
@@ -3299,9 +3327,9 @@
}
},
"node_modules/is-potential-custom-element-name": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.0.tgz",
- "integrity": "sha1-DFLlS8yjkbssSUsh6GJtczbG45c=",
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz",
+ "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==",
"dev": true
},
"node_modules/is-property": {
@@ -4104,40 +4132,75 @@
"dev": true
},
"node_modules/jsdom": {
- "version": "16.3.0",
- "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.3.0.tgz",
- "integrity": "sha512-zggeX5UuEknpdZzv15+MS1dPYG0J/TftiiNunOeNxSl3qr8Z6cIlQpN0IdJa44z9aFxZRIVqRncvEhQ7X5DtZg==",
+ "version": "16.7.0",
+ "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.7.0.tgz",
+ "integrity": "sha512-u9Smc2G1USStM+s/x1ru5Sxrl6mPYCbByG1U/hUmqaVsm4tbNyS7CicOSRyuGQYZhTu0h84qkZZQ/I+dzizSVw==",
"dev": true,
"dependencies": {
- "abab": "^2.0.3",
- "acorn": "^7.1.1",
+ "abab": "^2.0.5",
+ "acorn": "^8.2.4",
"acorn-globals": "^6.0.0",
"cssom": "^0.4.4",
- "cssstyle": "^2.2.0",
+ "cssstyle": "^2.3.0",
"data-urls": "^2.0.0",
- "decimal.js": "^10.2.0",
+ "decimal.js": "^10.2.1",
"domexception": "^2.0.1",
- "escodegen": "^1.14.1",
+ "escodegen": "^2.0.0",
+ "form-data": "^3.0.0",
"html-encoding-sniffer": "^2.0.1",
- "is-potential-custom-element-name": "^1.0.0",
+ "http-proxy-agent": "^4.0.1",
+ "https-proxy-agent": "^5.0.0",
+ "is-potential-custom-element-name": "^1.0.1",
"nwsapi": "^2.2.0",
- "parse5": "5.1.1",
- "request": "^2.88.2",
- "request-promise-native": "^1.0.8",
- "saxes": "^5.0.0",
+ "parse5": "6.0.1",
+ "saxes": "^5.0.1",
"symbol-tree": "^3.2.4",
- "tough-cookie": "^3.0.1",
+ "tough-cookie": "^4.0.0",
"w3c-hr-time": "^1.0.2",
"w3c-xmlserializer": "^2.0.0",
"webidl-conversions": "^6.1.0",
"whatwg-encoding": "^1.0.5",
"whatwg-mimetype": "^2.3.0",
- "whatwg-url": "^8.0.0",
- "ws": "^7.2.3",
+ "whatwg-url": "^8.5.0",
+ "ws": "^7.4.6",
"xml-name-validator": "^3.0.0"
},
"engines": {
"node": ">=10"
+ },
+ "peerDependencies": {
+ "canvas": "^2.5.0"
+ },
+ "peerDependenciesMeta": {
+ "canvas": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/jsdom/node_modules/acorn": {
+ "version": "8.10.0",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz",
+ "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==",
+ "dev": true,
+ "bin": {
+ "acorn": "bin/acorn"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/jsdom/node_modules/form-data": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz",
+ "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==",
+ "dev": true,
+ "dependencies": {
+ "asynckit": "^0.4.0",
+ "combined-stream": "^1.0.8",
+ "mime-types": "^2.1.12"
+ },
+ "engines": {
+ "node": ">= 6"
}
},
"node_modules/jsesc": {
@@ -4291,19 +4354,6 @@
"node": ">=6"
}
},
- "node_modules/levn": {
- "version": "0.3.0",
- "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz",
- "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=",
- "dev": true,
- "dependencies": {
- "prelude-ls": "~1.1.2",
- "type-check": "~0.3.2"
- },
- "engines": {
- "node": ">= 0.8.0"
- }
- },
"node_modules/lines-and-columns": {
"version": "1.1.6",
"resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz",
@@ -4582,12 +4632,6 @@
"integrity": "sha1-T7VPgWZS5a4Q6PcvcXo4jHMmU4o=",
"dev": true
},
- "node_modules/lodash.sortby": {
- "version": "4.7.0",
- "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz",
- "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=",
- "dev": true
- },
"node_modules/lodash.support": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/lodash.support/-/lodash.support-2.4.1.tgz",
@@ -5076,23 +5120,6 @@
"node": ">=6"
}
},
- "node_modules/optionator": {
- "version": "0.8.3",
- "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz",
- "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==",
- "dev": true,
- "dependencies": {
- "deep-is": "~0.1.3",
- "fast-levenshtein": "~2.0.6",
- "levn": "~0.3.0",
- "prelude-ls": "~1.1.2",
- "type-check": "~0.3.2",
- "word-wrap": "~1.2.3"
- },
- "engines": {
- "node": ">= 0.8.0"
- }
- },
"node_modules/p-each-series": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-2.1.0.tgz",
@@ -5172,9 +5199,9 @@
}
},
"node_modules/parse5": {
- "version": "5.1.1",
- "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz",
- "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==",
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz",
+ "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==",
"dev": true
},
"node_modules/parseurl": {
@@ -5302,15 +5329,6 @@
"node": ">=0.10.0"
}
},
- "node_modules/prelude-ls": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz",
- "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=",
- "dev": true,
- "engines": {
- "node": ">= 0.8.0"
- }
- },
"node_modules/pretty-format": {
"version": "26.6.2",
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz",
@@ -5419,6 +5437,12 @@
"node": ">=0.6"
}
},
+ "node_modules/querystringify": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz",
+ "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==",
+ "dev": true
+ },
"node_modules/range-parser": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
@@ -5546,45 +5570,6 @@
"node": ">= 6"
}
},
- "node_modules/request-promise-core": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.3.tgz",
- "integrity": "sha512-QIs2+ArIGQVp5ZYbWD5ZLCY29D5CfWizP8eWnm8FoGD1TX61veauETVQbrV60662V0oFBkrDOuaBI8XgtuyYAQ==",
- "dev": true,
- "dependencies": {
- "lodash": "^4.17.15"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/request-promise-native": {
- "version": "1.0.8",
- "resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.8.tgz",
- "integrity": "sha512-dapwLGqkHtwL5AEbfenuzjTYg35Jd6KPytsC2/TLkVMz8rm+tNt72MGUWT1RP/aYawMpN6HqbNGBQaRcBtjQMQ==",
- "dev": true,
- "dependencies": {
- "request-promise-core": "1.1.3",
- "stealthy-require": "^1.1.1",
- "tough-cookie": "^2.3.3"
- },
- "engines": {
- "node": ">=0.12.0"
- }
- },
- "node_modules/request-promise-native/node_modules/tough-cookie": {
- "version": "2.5.0",
- "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz",
- "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==",
- "dev": true,
- "dependencies": {
- "psl": "^1.1.28",
- "punycode": "^2.1.1"
- },
- "engines": {
- "node": ">=0.8"
- }
- },
"node_modules/request/node_modules/tough-cookie": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz",
@@ -5622,6 +5607,12 @@
"integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==",
"dev": true
},
+ "node_modules/requires-port": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
+ "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==",
+ "dev": true
+ },
"node_modules/resolve": {
"version": "1.20.0",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz",
@@ -6446,15 +6437,6 @@
"node": ">= 0.6"
}
},
- "node_modules/stealthy-require": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz",
- "integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=",
- "dev": true,
- "engines": {
- "node": ">=0.10.0"
- }
- },
"node_modules/streamsearch": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz",
@@ -6962,23 +6944,24 @@
}
},
"node_modules/tough-cookie": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-3.0.1.tgz",
- "integrity": "sha512-yQyJ0u4pZsv9D4clxO69OEjLWYw+jbgspjTue4lTQZLfV0c5l1VmK2y1JK8E9ahdpltPOaAThPcp5nKPUgSnsg==",
+ "version": "4.1.3",
+ "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.3.tgz",
+ "integrity": "sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==",
"dev": true,
"dependencies": {
- "ip-regex": "^2.1.0",
- "psl": "^1.1.28",
- "punycode": "^2.1.1"
+ "psl": "^1.1.33",
+ "punycode": "^2.1.1",
+ "universalify": "^0.2.0",
+ "url-parse": "^1.5.3"
},
"engines": {
"node": ">=6"
}
},
"node_modules/tr46": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/tr46/-/tr46-2.0.2.tgz",
- "integrity": "sha512-3n1qG+/5kg+jrbTzwAykB5yRYtQCTqOGKq5U5PE3b0a1/mzo6snDhjGS0zJVJunO0NrT3Dg1MLy5TjWP/UJppg==",
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/tr46/-/tr46-2.1.0.tgz",
+ "integrity": "sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw==",
"dev": true,
"dependencies": {
"punycode": "^2.1.1"
@@ -7017,18 +7000,6 @@
"integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==",
"dev": true
},
- "node_modules/type-check": {
- "version": "0.3.2",
- "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz",
- "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=",
- "dev": true,
- "dependencies": {
- "prelude-ls": "~1.1.2"
- },
- "engines": {
- "node": ">= 0.8.0"
- }
- },
"node_modules/type-detect": {
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz",
@@ -7090,6 +7061,15 @@
"node": ">=0.10.0"
}
},
+ "node_modules/universalify": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz",
+ "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==",
+ "dev": true,
+ "engines": {
+ "node": ">= 4.0.0"
+ }
+ },
"node_modules/unpipe": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
@@ -7171,6 +7151,16 @@
"integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=",
"dev": true
},
+ "node_modules/url-parse": {
+ "version": "1.5.10",
+ "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz",
+ "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==",
+ "dev": true,
+ "dependencies": {
+ "querystringify": "^2.1.1",
+ "requires-port": "^1.0.0"
+ }
+ },
"node_modules/use": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz",
@@ -7327,28 +7317,19 @@
"dev": true
},
"node_modules/whatwg-url": {
- "version": "8.1.0",
- "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.1.0.tgz",
- "integrity": "sha512-vEIkwNi9Hqt4TV9RdnaBPNt+E2Sgmo3gePebCRgZ1R7g6d23+53zCTnuB0amKI4AXq6VM8jj2DUAa0S1vjJxkw==",
+ "version": "8.7.0",
+ "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.7.0.tgz",
+ "integrity": "sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg==",
"dev": true,
"dependencies": {
- "lodash.sortby": "^4.7.0",
- "tr46": "^2.0.2",
- "webidl-conversions": "^5.0.0"
+ "lodash": "^4.7.0",
+ "tr46": "^2.1.0",
+ "webidl-conversions": "^6.1.0"
},
"engines": {
"node": ">=10"
}
},
- "node_modules/whatwg-url/node_modules/webidl-conversions": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz",
- "integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==",
- "dev": true,
- "engines": {
- "node": ">=8"
- }
- },
"node_modules/which": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
@@ -7371,9 +7352,9 @@
"dev": true
},
"node_modules/word-wrap": {
- "version": "1.2.3",
- "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
- "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==",
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
+ "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==",
"dev": true,
"engines": {
"node": ">=0.10.0"
@@ -8329,6 +8310,12 @@
"@sinonjs/commons": "^1.7.0"
}
},
+ "@tootallnate/once": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz",
+ "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==",
+ "dev": true
+ },
"@types/babel__core": {
"version": "7.1.9",
"resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.9.tgz",
@@ -8461,9 +8448,9 @@
"dev": true
},
"abab": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.3.tgz",
- "integrity": "sha512-tsFzPpcttalNjFBCFMqsKYQcWxxen1pgJR56by//QwvJc4/OUS3kPOOttx2tSIfjsylB0pYu7f5D3K1RCxUnUg==",
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz",
+ "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==",
"dev": true
},
"acorn": {
@@ -8488,6 +8475,15 @@
"integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==",
"dev": true
},
+ "agent-base": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
+ "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
+ "dev": true,
+ "requires": {
+ "debug": "4"
+ }
+ },
"ajv": {
"version": "6.12.3",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.3.tgz",
@@ -9260,9 +9256,9 @@
"dev": true
},
"decimal.js": {
- "version": "10.2.0",
- "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.2.0.tgz",
- "integrity": "sha512-vDPw+rDgn3bZe1+F/pyEwb1oMG2XTlRVgAa6B4KccTEpYgF8w6eQllVbQcfIJnZyvzFtFpxnpGtx8dd7DJp/Rw==",
+ "version": "10.4.3",
+ "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz",
+ "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==",
"dev": true
},
"decode-uri-component": {
@@ -9271,12 +9267,6 @@
"integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=",
"dev": true
},
- "deep-is": {
- "version": "0.1.3",
- "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz",
- "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=",
- "dev": true
- },
"deepmerge": {
"version": "4.2.2",
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz",
@@ -9523,15 +9513,14 @@
"dev": true
},
"escodegen": {
- "version": "1.14.3",
- "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.3.tgz",
- "integrity": "sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==",
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz",
+ "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==",
"dev": true,
"requires": {
"esprima": "^4.0.1",
- "estraverse": "^4.2.0",
+ "estraverse": "^5.2.0",
"esutils": "^2.0.2",
- "optionator": "^0.8.1",
"source-map": "~0.6.1"
}
},
@@ -9542,9 +9531,9 @@
"dev": true
},
"estraverse": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz",
- "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==",
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+ "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
"dev": true
},
"esutils": {
@@ -9777,12 +9766,6 @@
"integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
"dev": true
},
- "fast-levenshtein": {
- "version": "2.0.6",
- "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
- "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=",
- "dev": true
- },
"fb-watchman": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.1.tgz",
@@ -10110,6 +10093,17 @@
}
}
},
+ "http-proxy-agent": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz",
+ "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==",
+ "dev": true,
+ "requires": {
+ "@tootallnate/once": "1",
+ "agent-base": "6",
+ "debug": "4"
+ }
+ },
"http-signature": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
@@ -10121,6 +10115,16 @@
"sshpk": "^1.7.0"
}
},
+ "https-proxy-agent": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
+ "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==",
+ "dev": true,
+ "requires": {
+ "agent-base": "6",
+ "debug": "4"
+ }
+ },
"human-signals": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz",
@@ -10174,12 +10178,6 @@
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
"dev": true
},
- "ip-regex": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz",
- "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=",
- "dev": true
- },
"is-accessor-descriptor": {
"version": "0.1.6",
"resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
@@ -10335,9 +10333,9 @@
}
},
"is-potential-custom-element-name": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.0.tgz",
- "integrity": "sha1-DFLlS8yjkbssSUsh6GJtczbG45c=",
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz",
+ "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==",
"dev": true
},
"is-property": {
@@ -10989,37 +10987,57 @@
"dev": true
},
"jsdom": {
- "version": "16.3.0",
- "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.3.0.tgz",
- "integrity": "sha512-zggeX5UuEknpdZzv15+MS1dPYG0J/TftiiNunOeNxSl3qr8Z6cIlQpN0IdJa44z9aFxZRIVqRncvEhQ7X5DtZg==",
+ "version": "16.7.0",
+ "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.7.0.tgz",
+ "integrity": "sha512-u9Smc2G1USStM+s/x1ru5Sxrl6mPYCbByG1U/hUmqaVsm4tbNyS7CicOSRyuGQYZhTu0h84qkZZQ/I+dzizSVw==",
"dev": true,
"requires": {
- "abab": "^2.0.3",
- "acorn": "^7.1.1",
+ "abab": "^2.0.5",
+ "acorn": "^8.2.4",
"acorn-globals": "^6.0.0",
"cssom": "^0.4.4",
- "cssstyle": "^2.2.0",
+ "cssstyle": "^2.3.0",
"data-urls": "^2.0.0",
- "decimal.js": "^10.2.0",
+ "decimal.js": "^10.2.1",
"domexception": "^2.0.1",
- "escodegen": "^1.14.1",
+ "escodegen": "^2.0.0",
+ "form-data": "^3.0.0",
"html-encoding-sniffer": "^2.0.1",
- "is-potential-custom-element-name": "^1.0.0",
+ "http-proxy-agent": "^4.0.1",
+ "https-proxy-agent": "^5.0.0",
+ "is-potential-custom-element-name": "^1.0.1",
"nwsapi": "^2.2.0",
- "parse5": "5.1.1",
- "request": "^2.88.2",
- "request-promise-native": "^1.0.8",
- "saxes": "^5.0.0",
+ "parse5": "6.0.1",
+ "saxes": "^5.0.1",
"symbol-tree": "^3.2.4",
- "tough-cookie": "^3.0.1",
+ "tough-cookie": "^4.0.0",
"w3c-hr-time": "^1.0.2",
"w3c-xmlserializer": "^2.0.0",
"webidl-conversions": "^6.1.0",
"whatwg-encoding": "^1.0.5",
"whatwg-mimetype": "^2.3.0",
- "whatwg-url": "^8.0.0",
- "ws": "^7.2.3",
+ "whatwg-url": "^8.5.0",
+ "ws": "^7.4.6",
"xml-name-validator": "^3.0.0"
+ },
+ "dependencies": {
+ "acorn": {
+ "version": "8.10.0",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz",
+ "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==",
+ "dev": true
+ },
+ "form-data": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz",
+ "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==",
+ "dev": true,
+ "requires": {
+ "asynckit": "^0.4.0",
+ "combined-stream": "^1.0.8",
+ "mime-types": "^2.1.12"
+ }
+ }
}
},
"jsesc": {
@@ -11136,16 +11154,6 @@
"integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==",
"dev": true
},
- "levn": {
- "version": "0.3.0",
- "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz",
- "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=",
- "dev": true,
- "requires": {
- "prelude-ls": "~1.1.2",
- "type-check": "~0.3.2"
- }
- },
"lines-and-columns": {
"version": "1.1.6",
"resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz",
@@ -11421,12 +11429,6 @@
"integrity": "sha1-T7VPgWZS5a4Q6PcvcXo4jHMmU4o=",
"dev": true
},
- "lodash.sortby": {
- "version": "4.7.0",
- "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz",
- "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=",
- "dev": true
- },
"lodash.support": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/lodash.support/-/lodash.support-2.4.1.tgz",
@@ -11823,20 +11825,6 @@
"mimic-fn": "^2.1.0"
}
},
- "optionator": {
- "version": "0.8.3",
- "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz",
- "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==",
- "dev": true,
- "requires": {
- "deep-is": "~0.1.3",
- "fast-levenshtein": "~2.0.6",
- "levn": "~0.3.0",
- "prelude-ls": "~1.1.2",
- "type-check": "~0.3.2",
- "word-wrap": "~1.2.3"
- }
- },
"p-each-series": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-2.1.0.tgz",
@@ -11895,9 +11883,9 @@
}
},
"parse5": {
- "version": "5.1.1",
- "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz",
- "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==",
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz",
+ "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==",
"dev": true
},
"parseurl": {
@@ -11998,12 +11986,6 @@
"integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=",
"dev": true
},
- "prelude-ls": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz",
- "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=",
- "dev": true
- },
"pretty-format": {
"version": "26.6.2",
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz",
@@ -12090,6 +12072,12 @@
"integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==",
"dev": true
},
+ "querystringify": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz",
+ "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==",
+ "dev": true
+ },
"range-parser": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
@@ -12213,38 +12201,6 @@
}
}
},
- "request-promise-core": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.3.tgz",
- "integrity": "sha512-QIs2+ArIGQVp5ZYbWD5ZLCY29D5CfWizP8eWnm8FoGD1TX61veauETVQbrV60662V0oFBkrDOuaBI8XgtuyYAQ==",
- "dev": true,
- "requires": {
- "lodash": "^4.17.15"
- }
- },
- "request-promise-native": {
- "version": "1.0.8",
- "resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.8.tgz",
- "integrity": "sha512-dapwLGqkHtwL5AEbfenuzjTYg35Jd6KPytsC2/TLkVMz8rm+tNt72MGUWT1RP/aYawMpN6HqbNGBQaRcBtjQMQ==",
- "dev": true,
- "requires": {
- "request-promise-core": "1.1.3",
- "stealthy-require": "^1.1.1",
- "tough-cookie": "^2.3.3"
- },
- "dependencies": {
- "tough-cookie": {
- "version": "2.5.0",
- "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz",
- "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==",
- "dev": true,
- "requires": {
- "psl": "^1.1.28",
- "punycode": "^2.1.1"
- }
- }
- }
- },
"require-directory": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
@@ -12257,6 +12213,12 @@
"integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==",
"dev": true
},
+ "requires-port": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
+ "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==",
+ "dev": true
+ },
"resolve": {
"version": "1.20.0",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz",
@@ -12961,12 +12923,6 @@
"integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=",
"dev": true
},
- "stealthy-require": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz",
- "integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=",
- "dev": true
- },
"streamsearch": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz",
@@ -13399,20 +13355,21 @@
"dev": true
},
"tough-cookie": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-3.0.1.tgz",
- "integrity": "sha512-yQyJ0u4pZsv9D4clxO69OEjLWYw+jbgspjTue4lTQZLfV0c5l1VmK2y1JK8E9ahdpltPOaAThPcp5nKPUgSnsg==",
+ "version": "4.1.3",
+ "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.3.tgz",
+ "integrity": "sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==",
"dev": true,
"requires": {
- "ip-regex": "^2.1.0",
- "psl": "^1.1.28",
- "punycode": "^2.1.1"
+ "psl": "^1.1.33",
+ "punycode": "^2.1.1",
+ "universalify": "^0.2.0",
+ "url-parse": "^1.5.3"
}
},
"tr46": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/tr46/-/tr46-2.0.2.tgz",
- "integrity": "sha512-3n1qG+/5kg+jrbTzwAykB5yRYtQCTqOGKq5U5PE3b0a1/mzo6snDhjGS0zJVJunO0NrT3Dg1MLy5TjWP/UJppg==",
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/tr46/-/tr46-2.1.0.tgz",
+ "integrity": "sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw==",
"dev": true,
"requires": {
"punycode": "^2.1.1"
@@ -13445,15 +13402,6 @@
"integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==",
"dev": true
},
- "type-check": {
- "version": "0.3.2",
- "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz",
- "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=",
- "dev": true,
- "requires": {
- "prelude-ls": "~1.1.2"
- }
- },
"type-detect": {
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz",
@@ -13503,6 +13451,12 @@
"set-value": "^2.0.1"
}
},
+ "universalify": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz",
+ "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==",
+ "dev": true
+ },
"unpipe": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
@@ -13573,6 +13527,16 @@
"integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=",
"dev": true
},
+ "url-parse": {
+ "version": "1.5.10",
+ "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz",
+ "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==",
+ "dev": true,
+ "requires": {
+ "querystringify": "^2.1.1",
+ "requires-port": "^1.0.0"
+ }
+ },
"use": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz",
@@ -13709,22 +13673,14 @@
"dev": true
},
"whatwg-url": {
- "version": "8.1.0",
- "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.1.0.tgz",
- "integrity": "sha512-vEIkwNi9Hqt4TV9RdnaBPNt+E2Sgmo3gePebCRgZ1R7g6d23+53zCTnuB0amKI4AXq6VM8jj2DUAa0S1vjJxkw==",
+ "version": "8.7.0",
+ "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.7.0.tgz",
+ "integrity": "sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg==",
"dev": true,
"requires": {
- "lodash.sortby": "^4.7.0",
- "tr46": "^2.0.2",
- "webidl-conversions": "^5.0.0"
- },
- "dependencies": {
- "webidl-conversions": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz",
- "integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==",
- "dev": true
- }
+ "lodash": "^4.7.0",
+ "tr46": "^2.1.0",
+ "webidl-conversions": "^6.1.0"
}
},
"which": {
@@ -13743,9 +13699,9 @@
"dev": true
},
"word-wrap": {
- "version": "1.2.3",
- "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
- "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==",
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
+ "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==",
"dev": true
},
"wrap-ansi": {
diff --git a/references/product-recommendations/package-lock.json b/references/product-recommendations/package-lock.json
index 8596946b2..1de450bc9 100644
--- a/references/product-recommendations/package-lock.json
+++ b/references/product-recommendations/package-lock.json
@@ -5120,9 +5120,9 @@
}
},
"node_modules/word-wrap": {
- "version": "1.2.3",
- "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
- "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==",
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
+ "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==",
"dev": true,
"engines": {
"node": ">=0.10.0"
@@ -9429,9 +9429,9 @@
}
},
"word-wrap": {
- "version": "1.2.3",
- "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
- "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==",
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
+ "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==",
"dev": true
},
"wrappy": {
diff --git a/tools/pipeline-runner/Dockerfile b/tools/pipeline-runner/Dockerfile
index 9d2fc55c2..9e454b160 100644
--- a/tools/pipeline-runner/Dockerfile
+++ b/tools/pipeline-runner/Dockerfile
@@ -33,7 +33,9 @@ RUN apk add --no-cache \
freetype-dev \
harfbuzz \
ca-certificates \
- ttf-freefont
+ ttf-freefont \
+ py-pip \
+ zip
# Reduce nighly log (note: -ntp requires maven 3.6.1+)
RUN mv /usr/bin/mvn /usr/bin/_mvn &&\
diff --git a/tools/proxy-endpoint-unifier/README.md b/tools/proxy-endpoint-unifier/README.md
new file mode 100644
index 000000000..9a43155e2
--- /dev/null
+++ b/tools/proxy-endpoint-unifier/README.md
@@ -0,0 +1,44 @@
+# Apigee API Proxy Endpoint Unifier
+
+Apigee X and hybrid have a limitation of hosting up to 5 Proxy Endpoints per API Proxy. Apigee Edge has no such limitation.
+The objective of this tool is to take a proxy bundle and intelligently convert its proxy endpoints into logically
+grouped conditional flows, in order to stay within the Proxy Endpoint limit.
+
+## Disclaimer
+This is not an officially supported Google product.
+
+## Prerequisites
+* `python3`
+* Please install the required Python dependencies
+```
+ python3 -m pip install -r requirements.txt
+```
+* Please fill in `input.properties`
+```
+ [common]
+ input_apis=apis # Folder Containing exported & unzipped Proxy Bundles
+ processed_apis=transformed # Folder to export transformed Proxies to
+ proxy_bundle_directory=transformed_zipped_bundles # Folder to export transformed Proxies Bundles (zip) to
+ proxy_endpoint_count=4 # Number of Proxy Endpoints to retain while transforming (1-5)
+ debug=false # Flag to export debug logs
+
+ [validate]
+ enabled=true # Flag to enable proxy validation
+ gcp_project_id=xxx-xxx-xxx # Apigee Project for proxy validation
+```
+
+* If enabling validation, please run the following command to authenticate against Apigee APIs:
+
+```
+ export APIGEE_ACCESS_TOKEN=$(gcloud auth print-access-token)
+```
+
+
+## Usage
+Run the script as below
+```
+python3 main.py
+```
+
+## Limitations
+* This tool does not currently handle the resources within API proxies.
diff --git a/tools/proxy-endpoint-unifier/apigee.py b/tools/proxy-endpoint-unifier/apigee.py
new file mode 100644
index 000000000..e3f5f0b41
--- /dev/null
+++ b/tools/proxy-endpoint-unifier/apigee.py
@@ -0,0 +1,42 @@
+#!/usr/bin/python
+
+# Copyright 2023 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import requests
+import os
+
+
+class Apigee:
+ def __init__(self, org):
+ self.baseurl = f"https://apigee.googleapis.com/v1/organizations/{org}"
+ self.auth_header = {}
+
+ def set_auth_header(self, token):
+ self.auth_header = {
+ 'Authorization': f"Bearer {token}"
+ }
+
+ def validate_api(self, api_type, proxy_bundle_path):
+ api_name = os.path.basename(proxy_bundle_path).split('.zip')[0]
+ url = f"{self.baseurl}/{api_type}?name={api_name}&action=validate&validate=true" # noqa
+ files = [
+ ('data', (api_name, open(proxy_bundle_path,'rb'), 'application/zip')) # noqa
+ ]
+ response = requests.request("POST", url, headers=self.auth_header,
+ data={}, files=files)
+ if response.status_code == 200:
+ return True
+ else:
+ return response.json()
diff --git a/tools/proxy-endpoint-unifier/input.properties b/tools/proxy-endpoint-unifier/input.properties
new file mode 100644
index 000000000..ae04281de
--- /dev/null
+++ b/tools/proxy-endpoint-unifier/input.properties
@@ -0,0 +1,24 @@
+# Copyright 2023 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+[common]
+input_apis=test/api_bundles
+processed_apis=test/transformed
+proxy_bundle_directory=test/transformed_zipped_bundles
+proxy_endpoint_count=4
+debug=false
+
+[validate]
+enabled=true
+gcp_project_id=xxx-xxx-xxx
diff --git a/tools/proxy-endpoint-unifier/main.py b/tools/proxy-endpoint-unifier/main.py
new file mode 100644
index 000000000..c03240151
--- /dev/null
+++ b/tools/proxy-endpoint-unifier/main.py
@@ -0,0 +1,132 @@
+#!/usr/bin/python
+
+# Copyright 2023 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import os
+import sys
+from apigee import Apigee
+import utils
+
+
+def main():
+ cfg = utils.parse_config('input.properties')
+ proxy_dir = cfg['common']['input_apis']
+ proxy_dest_dir = cfg['common']['processed_apis']
+ proxy_bundle_directory = cfg['common']['proxy_bundle_directory']
+ export_debug_file = cfg.getboolean('common', 'debug')
+ validation_enabled = cfg.getboolean('validate', 'enabled')
+ utils.delete_folder(proxy_dest_dir)
+ utils.delete_folder(proxy_bundle_directory)
+ utils.create_dir(proxy_bundle_directory)
+ proxy_endpoint_count = utils.get_proxy_endpoint_count(cfg)
+ proxies = utils.list_dir(proxy_dir)
+
+ final_dict = {}
+ processed_dict = {}
+
+ for each_dir in proxies:
+ each_proxy_dict = utils.read_proxy_artifacts(
+ f"{proxy_dir}/{each_dir}",
+ utils.parse_proxy_root(f"{proxy_dir}/{each_dir}")
+ )
+ if len(each_proxy_dict) > 0:
+ each_proxy_rel = utils.get_proxy_objects_relationships(
+ each_proxy_dict)
+ final_dict[each_dir] = each_proxy_dict
+ processed_dict[each_dir] = each_proxy_rel
+
+ processing_final_dict = final_dict.copy()
+
+ path_group_map = {}
+ for each_api, each_api_info in processed_dict.items():
+ path_group_map[each_api] = utils.get_api_path_groups(each_api_info)
+
+ grouped_apis = {}
+ for each_api, base_path_info in path_group_map.items():
+ grouped_apis[each_api] = utils.group_paths_by_path(
+ base_path_info, proxy_endpoint_count)
+
+ bundled_group = {}
+ for each_api, grouped_api in grouped_apis.items():
+ bundled_group[each_api] = utils.bundle_path(grouped_api)
+
+ merged_pes = {}
+ merged_objects = {}
+ for each_api, grouped_api in bundled_group.items():
+ print(f'Processing API => {each_api} with {len(grouped_api)} groups')
+ for index, each_group in enumerate(grouped_api):
+ merged_objects[f"{each_api}_{index}"] = {
+ 'Policies': [],
+ 'TargetEndpoints': [],
+ 'ProxyEndpoints': []
+ }
+ for each_path, pes in each_group.items():
+ each_pe = '-'.join(pes)
+ merged_pes[each_pe] = utils.merge_proxy_endpoints(
+ processing_final_dict[each_api],
+ each_path,
+ pes
+ )
+ merged_objects[f"{each_api}_{index}"]['Name'] = f"{final_dict[each_api]['proxyName']}_{index}" # noqa
+ merged_objects[f"{each_api}_{index}"]['Policies'].extend( # noqa
+ [ item for pe in pes for item in processed_dict[each_api][pe]['Policies']]) # noqa
+ merged_objects[f"{each_api}_{index}"]['TargetEndpoints'].extend( # noqa
+ [ item for pe in pes for item in processed_dict[each_api][pe]['TargetEndpoints']]) # noqa
+ merged_objects[f"{each_api}_{index}"]['Policies'] = list(set(merged_objects[f"{each_api}_{index}"]['Policies'])) # noqa
+ merged_objects[f"{each_api}_{index}"]['TargetEndpoints'] = list(set(merged_objects[f"{each_api}_{index}"]['TargetEndpoints'])) # noqa
+ merged_objects[f"{each_api}_{index}"]['ProxyEndpoints'].append(each_pe) # noqa
+
+ for each_api, grouped_api in bundled_group.items():
+ for index, each_group in enumerate(grouped_api):
+ utils.clone_proxies(
+ f"{proxy_dir}/{each_api}",
+ f"{proxy_dest_dir}/{each_api}_{index}",
+ merged_objects[f"{each_api}_{index}"],
+ merged_pes,
+ proxy_bundle_directory
+ )
+
+ files = {
+ 'final_dict': final_dict,
+ 'processed_dict': processed_dict,
+ 'path_group_map': path_group_map,
+ 'grouped_apis': grouped_apis,
+ 'bundled_group': bundled_group,
+ 'merged_pes': merged_pes,
+ 'merged_objects': merged_objects,
+ }
+ if export_debug_file:
+ utils.export_debug_log(files)
+
+ if validation_enabled:
+ errors = {}
+ gcp_project_id = cfg['validate']['gcp_project_id']
+ x = Apigee(gcp_project_id)
+ x.set_auth_header(os.getenv('APIGEE_ACCESS_TOKEN'))
+ result = {}
+ bundled_proxies = utils.list_dir(proxy_bundle_directory)
+ for each_bundle in bundled_proxies:
+ validation = x.validate_api('apis',f"{proxy_bundle_directory}/{each_bundle}") # noqa
+ if not isinstance(validation, bool):
+ errors[each_bundle] = validation
+ result[each_bundle] = validation
+ print(f"{each_bundle} ==> Validation : {validation}")
+ if len(errors) > 0:
+ print('ERROR: Some Validations have failed')
+ sys.exit(1)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/tools/proxy-endpoint-unifier/pipeline.sh b/tools/proxy-endpoint-unifier/pipeline.sh
new file mode 100755
index 000000000..dd83d7c43
--- /dev/null
+++ b/tools/proxy-endpoint-unifier/pipeline.sh
@@ -0,0 +1,65 @@
+#!/bin/sh
+
+# Copyright 2023 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+set -e
+
+SCRIPTPATH="$( cd "$(dirname "$0")" || exit >/dev/null 2>&1 ; pwd -P )"
+
+# Clean up previously generated files
+rm -rf "$SCRIPTPATH/input.properties"
+rm -rf "$SCRIPTPATH/transformed"
+rm -rf "$SCRIPTPATH/transformed_bundles"
+
+# Generate input file
+cat > "$SCRIPTPATH/input.properties" << EOF
+[common]
+input_apis=$SCRIPTPATH/test/api_bundles
+processed_apis=$SCRIPTPATH/transformed
+proxy_bundle_directory=$SCRIPTPATH/transformed_bundles
+proxy_endpoint_count=4
+debug=true
+
+[validate]
+enabled=true
+gcp_project_id=$APIGEE_X_ORG
+EOF
+
+# Install Dependencies
+python3 -m pip install -r "$SCRIPTPATH/requirements.txt"
+
+# Generate Gcloud Acccess Token
+APIGEE_ACCESS_TOKEN="$(gcloud config config-helper --force-auth-refresh --format json | jq -r '.credential.access_token')"
+export APIGEE_ACCESS_TOKEN
+
+# Building API Proxy Bundle for Proxy containing more than 5 Proxy Endpoints
+cd "$SCRIPTPATH/test/api_bundles"
+rm -rf "$SCRIPTPATH/test/api_bundles/test.zip"
+echo "Building original proxy bundle"
+zip -q -r test.zip apiproxy/
+cd "$SCRIPTPATH"
+
+# Validating API Proxy Bundle for Proxy containing more than 5 Proxy Endpoints
+echo "Validating the original proxy bundle"
+python3 -c "import os, sys ,json; \
+ from apigee import Apigee; \
+ x = Apigee(os.getenv('APIGEE_X_ORG')); \
+ x.set_auth_header(os.getenv('APIGEE_ACCESS_TOKEN')); \
+ r=x.validate_api('apis','test/api_bundles/test.zip'); \
+ print(json.dumps(r,indent=2))"
+rm -rf "$SCRIPTPATH/test/api_bundles/test.zip"
+
+# Running and Validating API Proxy Bundle after splitting the proxies
+python3 "$SCRIPTPATH/main.py"
diff --git a/tools/proxy-endpoint-unifier/requirements.txt b/tools/proxy-endpoint-unifier/requirements.txt
new file mode 100644
index 000000000..ef1bdd28b
--- /dev/null
+++ b/tools/proxy-endpoint-unifier/requirements.txt
@@ -0,0 +1,16 @@
+# Copyright 2023 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+xmltodict==0.13.0
+requests==2.31.0
diff --git a/tools/proxy-endpoint-unifier/test/api_bundles/apiproxy/policies/ExtractVariables-3.xml b/tools/proxy-endpoint-unifier/test/api_bundles/apiproxy/policies/ExtractVariables-3.xml
new file mode 100644
index 000000000..d6dc23438
--- /dev/null
+++ b/tools/proxy-endpoint-unifier/test/api_bundles/apiproxy/policies/ExtractVariables-3.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+ $.results[0].geometry.location.lat
+
+
+ $.results[0].geometry.location.lng
+
+
+ geocoderesponse
+
\ No newline at end of file
diff --git a/tools/proxy-endpoint-unifier/test/api_bundles/apiproxy/policies/Message-Logging-1.xml b/tools/proxy-endpoint-unifier/test/api_bundles/apiproxy/policies/Message-Logging-1.xml
new file mode 100644
index 000000000..c092f9fc5
--- /dev/null
+++ b/tools/proxy-endpoint-unifier/test/api_bundles/apiproxy/policies/Message-Logging-1.xml
@@ -0,0 +1,21 @@
+
+
+
+ Message-Logging-1
+
+ Message.id = {request.header.id}
+ IP
+ 556
+
+
\ No newline at end of file
diff --git a/tools/proxy-endpoint-unifier/test/api_bundles/apiproxy/policies/Populate-Cache-1.xml b/tools/proxy-endpoint-unifier/test/api_bundles/apiproxy/policies/Populate-Cache-1.xml
new file mode 100644
index 000000000..589cb0edf
--- /dev/null
+++ b/tools/proxy-endpoint-unifier/test/api_bundles/apiproxy/policies/Populate-Cache-1.xml
@@ -0,0 +1,27 @@
+
+
+
+ Populate Cache-1
+
+
+ my
+ test
+
+ test-cache
+ Exclusive
+
+ 3600
+
+
+
\ No newline at end of file
diff --git a/tools/proxy-endpoint-unifier/test/api_bundles/apiproxy/policies/Quota-1.xml b/tools/proxy-endpoint-unifier/test/api_bundles/apiproxy/policies/Quota-1.xml
new file mode 100644
index 000000000..1d85c2be4
--- /dev/null
+++ b/tools/proxy-endpoint-unifier/test/api_bundles/apiproxy/policies/Quota-1.xml
@@ -0,0 +1,27 @@
+
+
+
+ Quota-1
+
+
+ 1
+ false
+ false
+ month
+ 2023-8-7 12:00:00
+
+ 20
+ 5
+
+
diff --git a/tools/proxy-endpoint-unifier/test/api_bundles/apiproxy/policies/Statistics-Collector-1.xml b/tools/proxy-endpoint-unifier/test/api_bundles/apiproxy/policies/Statistics-Collector-1.xml
new file mode 100644
index 000000000..693be0e90
--- /dev/null
+++ b/tools/proxy-endpoint-unifier/test/api_bundles/apiproxy/policies/Statistics-Collector-1.xml
@@ -0,0 +1,20 @@
+
+
+
+ Statistics Collector-1
+
+
+ value
+
+
\ No newline at end of file
diff --git a/tools/proxy-endpoint-unifier/test/api_bundles/apiproxy/proxies/ProxyEndpoint-1.xml b/tools/proxy-endpoint-unifier/test/api_bundles/apiproxy/proxies/ProxyEndpoint-1.xml
new file mode 100644
index 000000000..6d6742543
--- /dev/null
+++ b/tools/proxy-endpoint-unifier/test/api_bundles/apiproxy/proxies/ProxyEndpoint-1.xml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ /test-policy-path2
+
+ default
+
+
+ default
+
+
\ No newline at end of file
diff --git a/tools/proxy-endpoint-unifier/test/api_bundles/apiproxy/proxies/ProxyEndpoint-2.xml b/tools/proxy-endpoint-unifier/test/api_bundles/apiproxy/proxies/ProxyEndpoint-2.xml
new file mode 100644
index 000000000..dd7958e0c
--- /dev/null
+++ b/tools/proxy-endpoint-unifier/test/api_bundles/apiproxy/proxies/ProxyEndpoint-2.xml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ /test-policy-3
+
+ default
+
+
+ default
+
+
\ No newline at end of file
diff --git a/tools/proxy-endpoint-unifier/test/api_bundles/apiproxy/proxies/ProxyEndpoint-3.xml b/tools/proxy-endpoint-unifier/test/api_bundles/apiproxy/proxies/ProxyEndpoint-3.xml
new file mode 100644
index 000000000..2eaffbc11
--- /dev/null
+++ b/tools/proxy-endpoint-unifier/test/api_bundles/apiproxy/proxies/ProxyEndpoint-3.xml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ /test-policy-4
+
+ default
+
+
+ default
+
+
\ No newline at end of file
diff --git a/tools/proxy-endpoint-unifier/test/api_bundles/apiproxy/proxies/ProxyEndpoint-4.xml b/tools/proxy-endpoint-unifier/test/api_bundles/apiproxy/proxies/ProxyEndpoint-4.xml
new file mode 100644
index 000000000..bb4b2afec
--- /dev/null
+++ b/tools/proxy-endpoint-unifier/test/api_bundles/apiproxy/proxies/ProxyEndpoint-4.xml
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Message-Logging-1
+
+
+
+
+
+ /test-policy-5
+
+ default
+
+
+ default
+
+
\ No newline at end of file
diff --git a/tools/proxy-endpoint-unifier/test/api_bundles/apiproxy/proxies/ProxyEndpoint-5.xml b/tools/proxy-endpoint-unifier/test/api_bundles/apiproxy/proxies/ProxyEndpoint-5.xml
new file mode 100644
index 000000000..f9f51ccb4
--- /dev/null
+++ b/tools/proxy-endpoint-unifier/test/api_bundles/apiproxy/proxies/ProxyEndpoint-5.xml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ /test-policy-6
+
+ default
+
+
+ default
+
+
\ No newline at end of file
diff --git a/tools/proxy-endpoint-unifier/test/api_bundles/apiproxy/proxies/default.xml b/tools/proxy-endpoint-unifier/test/api_bundles/apiproxy/proxies/default.xml
new file mode 100644
index 000000000..137f89681
--- /dev/null
+++ b/tools/proxy-endpoint-unifier/test/api_bundles/apiproxy/proxies/default.xml
@@ -0,0 +1,53 @@
+
+
+
+
+
+
+
+
+ Quota-1
+
+
+ Populate-Cache-1
+
+
+
+
+
+
+
+
+ Populate-Cache-1
+
+
+
+
+
+
+
+ Message-Logging-1
+
+
+
+
+
+ /test-policy
+
+ default
+
+
+ default
+
+
\ No newline at end of file
diff --git a/tools/proxy-endpoint-unifier/test/api_bundles/apiproxy/targets/default.xml b/tools/proxy-endpoint-unifier/test/api_bundles/apiproxy/targets/default.xml
new file mode 100644
index 000000000..ff5aa88a7
--- /dev/null
+++ b/tools/proxy-endpoint-unifier/test/api_bundles/apiproxy/targets/default.xml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ExtractVariables-3
+
+
+
+
+
+
+ https://mocktarget.apigee.net/
+
+
\ No newline at end of file
diff --git a/tools/proxy-endpoint-unifier/test/api_bundles/apiproxy/test-proxy.xml b/tools/proxy-endpoint-unifier/test/api_bundles/apiproxy/test-proxy.xml
new file mode 100644
index 000000000..ae884bfc1
--- /dev/null
+++ b/tools/proxy-endpoint-unifier/test/api_bundles/apiproxy/test-proxy.xml
@@ -0,0 +1,17 @@
+
+
+
\ No newline at end of file
diff --git a/tools/proxy-endpoint-unifier/utils.py b/tools/proxy-endpoint-unifier/utils.py
new file mode 100644
index 000000000..2dd439a0c
--- /dev/null
+++ b/tools/proxy-endpoint-unifier/utils.py
@@ -0,0 +1,563 @@
+#!/usr/bin/python
+
+# Copyright 2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import configparser
+import os
+import sys
+import xmltodict
+import json
+import shutil
+import zipfile
+
+
+def parse_config(config_file):
+ config = configparser.ConfigParser()
+ config.read(config_file)
+ return config
+
+
+def get_proxy_endpoint_count(cfg):
+ try:
+ proxy_endpoint_count = cfg.getint('common', 'proxy_endpoint_count')
+ if not (proxy_endpoint_count > 0 and proxy_endpoint_count <= 5):
+ print('ERROR: Proxy Endpoints should be > Zero(0) & < Five(5)')
+ sys.exit(1)
+ except ValueError:
+ print('proxy_endpoint_count should be a Number')
+ sys.exit(1)
+ return proxy_endpoint_count
+
+
+def create_dir(dir):
+ try:
+ os.makedirs(dir)
+ except FileExistsError:
+ print(f"INFO: {dir} already exists")
+
+
+def list_dir(dir, isok=False):
+ try:
+ return os.listdir(dir)
+ except FileNotFoundError:
+ if isok:
+ print(f"Ignoring: Directory \"{dir}\" not found")
+ return []
+ print(f"ERROR: Directory \"{dir}\" not found")
+ sys.exit(1)
+
+
+def get_proxy_entrypoint(dir):
+ files = list_dir(dir)
+ ent = []
+ for eachfile in files:
+ if eachfile.endswith(".xml"):
+ ent.append(eachfile)
+ if len(ent) == 1:
+ return os.path.join(dir, ent[0])
+ else:
+ if len(ent) > 1:
+ print(f"ERROR: Directory \"{dir}\" contains multiple xml files at root") # noqa
+ else:
+ print(f"ERROR: Directory \"{dir}\" has no xml file at root") # noqa
+ return None
+
+
+def get_proxy_files(dir, file_type='proxies'):
+ target_dir = os.path.join(dir, file_type)
+ files = list_dir(target_dir)
+ xml_files = []
+ for eachfile in files:
+ if eachfile.endswith(".xml"):
+ xml_files.append(os.path.splitext(eachfile)[0])
+ if len(xml_files) == 0:
+ print(f"ERROR: Directory \"{target_dir}\" has no xml files") # noqa
+ return []
+ else:
+ return xml_files
+
+
+def parse_json(file):
+ try:
+ with open(file) as fl:
+ doc = json.loads(fl.read())
+ return doc
+ except FileNotFoundError:
+ print(f"ERROR: File \"{file}\" not found")
+ return {}
+
+
+def parse_xml(file):
+ try:
+ with open(file) as fl:
+ doc = xmltodict.parse(fl.read())
+ return doc
+ except FileNotFoundError:
+ print(f"ERROR: File \"{file}\" not found")
+ return {}
+
+
+def write_json(file, data):
+ try:
+ with open(file, 'w') as fl:
+ fl.write(json.dumps(data, indent=2))
+ except FileNotFoundError:
+ print(f"ERROR: File \"{file}\" not found")
+ return False
+ return True
+
+
+def write_xml_from_dict(file, data):
+ try:
+ with open(file, 'w') as fl:
+ fl.write(xmltodict.unparse(data, pretty=True))
+ except FileNotFoundError:
+ print(f"ERROR: File \"{file}\" not found")
+ return False
+ return True
+
+
+def parse_proxy_root(dir):
+ file = get_proxy_entrypoint(dir)
+ if file is None:
+ return {}
+ doc = parse_xml(file)
+ api_proxy = doc.get('APIProxy', {})
+ proxy_endpoints = api_proxy.get('ProxyEndpoints', {}).get('ProxyEndpoint', {}) # noqa
+ target_endpoints = api_proxy.get('TargetEndpoints', {}).get('TargetEndpoint', {}) # noqa
+ policies = api_proxy.get('Policies', {}).get('Policy', {})
+ if len(proxy_endpoints) == 0:
+ print('Proceeding with Filesystem parse of ProxyEndpoints')
+ doc['APIProxy']['ProxyEndpoints'] = {}
+ proxies = get_proxy_files(dir)
+ doc['APIProxy']['ProxyEndpoints']['ProxyEndpoint'] = proxies
+ else:
+ print('Skipping with Filesystem parse of ProxyEndpoints')
+ if len(target_endpoints) == 0:
+ print('Proceeding with Filesystem parse of TargetEndpoints')
+ doc['APIProxy']['TargetEndpoints'] = {}
+ targets = get_proxy_files(dir, 'targets')
+ doc['APIProxy']['TargetEndpoints']['TargetEndpoint'] = targets
+ else:
+ print('Skipping with Filesystem parse of TargetEndpoints')
+ if len(policies) == 0:
+ print('Proceeding with Filesystem parse of Policies')
+ doc['APIProxy']['Policies'] = {}
+ policies_list = get_proxy_files(dir, 'policies')
+ doc['APIProxy']['Policies']['Policy'] = policies_list
+ else:
+ print('Skipping with Filesystem parse of Policies')
+ return doc
+
+
+def read_proxy_artifacts(dir, entrypoint):
+ APIProxy = entrypoint['APIProxy']
+ # Check if proxy has multiple endpoints
+ if isinstance(APIProxy['ProxyEndpoints']['ProxyEndpoint'], list):
+ proxyName = entrypoint['APIProxy']['@name']
+ proxy_dict = {
+ 'ProxyEndpoints': {},
+ 'TargetEndpoints': {},
+ 'proxyName': proxyName
+ }
+ ProxyEndpoints = APIProxy['ProxyEndpoints']['ProxyEndpoint']
+ ProxyEndpoints = ([ProxyEndpoints] if isinstance(ProxyEndpoints,str) else ProxyEndpoints) # noqa
+ for each_pe in ProxyEndpoints:
+ proxy_dict['ProxyEndpoints'][each_pe] = parse_xml(os.path.join(dir,'proxies',f"{each_pe}.xml")) # noqa
+
+ TargetEndpoints = APIProxy['TargetEndpoints']['TargetEndpoint']
+ TargetEndpoints = ([TargetEndpoints] if isinstance(TargetEndpoints,str) else TargetEndpoints) # noqa
+ for each_te in TargetEndpoints:
+ proxy_dict['TargetEndpoints'][each_te]=parse_xml(os.path.join(dir,'targets',f"{each_te}.xml")) # noqa
+
+ # Skip when proxy has one endpoints
+ else:
+ print(f"Skipping Proxy ==> {entrypoint['APIProxy']['@name']}")
+ return {}
+ return proxy_dict
+
+
+def get_all_policies_from_step(Step):
+ policies = []
+ StepData = ([Step] if isinstance(Step, dict) else Step)
+ for eachStep in StepData:
+ policies.append(eachStep['Name'])
+ return policies
+
+
+def get_all_policies_from_flow(Flow, fault_rule=False):
+ policies = []
+ if not fault_rule:
+ Request = ([] if Flow['Request'] is None else (
+ [Flow['Request']['Step']] if isinstance(Flow['Request']['Step'], dict) # noqa
+ else Flow['Request']['Step']))
+ Response = ([] if Flow['Response'] is None else (
+ [Flow['Response']['Step']] if isinstance(Flow['Response']['Step'], dict) # noqa
+ else Flow['Response']['Step']))
+ for each_flow in Request:
+ policies.extend(get_all_policies_from_step(each_flow))
+ for each_flow in Response:
+ policies.extend(get_all_policies_from_step(each_flow))
+ else:
+ FaultRules = ([] if Flow is None else (
+ [Flow['Step']] if isinstance(Flow['Step'], dict)
+ else Flow['Step']))
+ for each_step in FaultRules:
+ policies.extend(get_all_policies_from_step(each_step))
+ return policies
+
+
+def get_all_policies_from_endpoint(endpointData, endpointType):
+ policies = []
+ policies.extend(
+ get_all_policies_from_flow(
+ endpointData[endpointType]['PreFlow']
+ )
+ )
+ policies.extend(
+ get_all_policies_from_flow(
+ endpointData[endpointType]['PostFlow']
+ )
+ )
+
+ if (endpointType == 'ProxyEndpoint' and
+ 'PostClientFlow' in endpointData[endpointType]):
+ policies.extend(
+ get_all_policies_from_flow(
+ endpointData[endpointType]['PostClientFlow']
+ )
+ )
+
+ Flows = (
+ [] if endpointData[endpointType]['Flows'] is None else (
+ [endpointData[endpointType]['Flows']['Flow']] if isinstance(
+ endpointData[endpointType]['Flows']['Flow'],
+ dict)
+ else
+ endpointData[endpointType]['Flows']['Flow']
+ ))
+
+ for eachFlow in Flows:
+ policies.extend(
+ get_all_policies_from_flow(
+ eachFlow
+ )
+ )
+ if 'DefaultFaultRule' in endpointData[endpointType]:
+ policies.extend(
+ get_all_policies_from_flow(endpointData[endpointType]['DefaultFaultRule'], True) # noqa
+ )
+ return policies
+
+
+def get_target_endpoints(ProxyEndpointData):
+ target_endpoints = []
+ routes = (
+ [ProxyEndpointData['RouteRule']]
+ if isinstance(ProxyEndpointData['RouteRule'], dict)
+ else ProxyEndpointData['RouteRule']
+ )
+ for eachRoute in routes:
+ if 'TargetEndpoint' in eachRoute:
+ target_endpoints.append(eachRoute['TargetEndpoint'])
+ return target_endpoints
+
+
+def get_proxy_objects_relationships(proxy_dict):
+ proxy_object_map = {}
+ ProxyEndpoints = proxy_dict['ProxyEndpoints']
+ for ProxyEndpoint, ProxyEndpointData in ProxyEndpoints.items():
+ proxy_object_map[ProxyEndpoint] = {}
+ target_endpoints = get_target_endpoints(ProxyEndpointData['ProxyEndpoint']) # noqa
+ TargetEndpointsData = {te: proxy_dict['TargetEndpoints'][te] for te in target_endpoints} # noqa
+ policies = []
+ policies.extend(get_all_policies_from_endpoint(ProxyEndpointData, 'ProxyEndpoint')) # noqa
+ for _, each_te in TargetEndpointsData.items():
+ policies.extend(get_all_policies_from_endpoint(each_te, 'TargetEndpoint')) # noqa
+ proxy_object_map[ProxyEndpoint] = {
+ 'Policies': policies,
+ 'BasePath': ProxyEndpointData['ProxyEndpoint']['HTTPProxyConnection']['BasePath'], # noqa
+ 'TargetEndpoints': target_endpoints,
+ }
+ return proxy_object_map
+
+
+def get_api_path_groups(each_api_info):
+ api_path_group_map = {}
+ for pe, pe_info in each_api_info.items():
+ if pe_info['BasePath'] is None:
+ if '_null_' in api_path_group_map:
+ api_path_group_map['_null_'].append({pe: None})
+ else:
+ api_path_group_map['_null_'] = [{pe: None}]
+ else:
+ base_path_split = [ i for i in pe_info['BasePath'].split('/') if i != ""] # noqa
+ if base_path_split[0] in api_path_group_map:
+ api_path_group_map[base_path_split[0]].append(
+ {pe: base_path_split[0]})
+ else:
+ api_path_group_map[base_path_split[0]] = [{pe: base_path_split[0]}] # noqa
+ return api_path_group_map
+
+
+def group_paths_by_path(api_info, pe_count_limit):
+ result = []
+ paths = list(api_info.keys())
+ path_count = len(paths)
+ if path_count > pe_count_limit:
+ for i in range(0, path_count, pe_count_limit):
+ each_result = []
+ if i+pe_count_limit > path_count:
+ for k in paths[i:path_count]:
+ each_result.extend(api_info[k])
+ else:
+ for k in paths[i:i+pe_count_limit]:
+ each_result.extend(api_info[k])
+ result.append(each_result)
+ else:
+ each_result = []
+ for _, v in api_info.items():
+ each_result.extend(v)
+ result.append(each_result)
+ return result
+
+
+def bundle_path(each_group_bundle):
+ outer_group = []
+ for each_group in each_group_bundle:
+ subgroups = {}
+ for each_pe in each_group:
+ path = list(each_pe.values())[0]
+ proxy_ep = list(each_pe.keys())[0]
+ if path in subgroups:
+ subgroups[path].append(proxy_ep)
+ else:
+ subgroups[path] = [proxy_ep]
+ outer_group.append(subgroups)
+ return outer_group
+
+
+def apply_condition(step, condition):
+ step_or_rule = step.copy()
+ if 'Condition' in step_or_rule:
+ if step_or_rule['Condition'] is None:
+ step_or_rule['Condition'] = condition
+ elif len(step_or_rule['Condition'].strip()) > 0:
+ if step_or_rule['Condition'].strip().startswith('('):
+ step_or_rule['Condition'] = f"{condition} and {step_or_rule['Condition']}" # noqa
+ else:
+ step_or_rule['Condition'] = f"{condition} and {step_or_rule['Condition']}" # noqa
+ else:
+ step_or_rule['Condition'] = condition
+ else:
+ step_or_rule['Condition'] = condition
+ return step_or_rule
+
+
+def process_steps(step, condition):
+ processed_step = []
+ if step is None:
+ return processed_step
+ elif isinstance(step['Step'], dict):
+ processed_step = [apply_condition(step['Step'], condition)]
+ elif isinstance(step['Step'], list):
+ processed_step = [apply_condition(i, condition) for i in step['Step']]
+ else:
+ return processed_step
+ return processed_step
+
+
+def process_flow(flow, condition):
+ processed_flow = flow.copy()
+ if flow['Request'] is not None:
+ processed_flow['Request']['Step'] = process_steps(flow['Request'],
+ condition)
+ if flow['Response'] is not None:
+ processed_flow['Response']['Step'] = process_steps(flow['Response'],
+ condition)
+ processed_flow_with_condition = apply_condition(processed_flow,
+ condition)
+ return processed_flow_with_condition
+
+
+def process_route_rules(route_rules, condition):
+ processed_rr = []
+ for each_rr in (route_rules if isinstance(route_rules, list)
+ else [route_rules]):
+ each_processed_rr = apply_condition(each_rr, condition)
+ processed_rr.append(each_processed_rr)
+ return processed_rr
+
+
+def merge_proxy_endpoints(api_dict, basepath, pes):
+ merged_pe = {'ProxyEndpoint': {}}
+ for each_pe, each_pe_info in api_dict['ProxyEndpoints'].items():
+ if each_pe in pes:
+ original_basepath = each_pe_info['ProxyEndpoint']['HTTPProxyConnection']['BasePath'] # noqa
+ # TODO : Build full Request path
+ condition=(original_basepath if original_basepath is None else f'(request.path Matches "{original_basepath}*")') # noqa
+ copied_flows = (
+ None if each_pe_info['ProxyEndpoint']['Flows'] is None else each_pe_info['ProxyEndpoint']['Flows'].copy() # noqa
+ )
+ original_flows = ([] if copied_flows is None else
+ ([copied_flows['Flow']] if isinstance(copied_flows['Flow'],dict) else copied_flows['Flow'])) # noqa
+
+ if len(merged_pe['ProxyEndpoint']) == 0:
+ merged_pe['ProxyEndpoint'] = {
+ '@name': [],
+ 'Description': None,
+ 'FaultRules': None,
+ 'PreFlow': {
+ '@name': 'PreFlow',
+ 'Request': {'Step': []},
+ 'Response': {'Step': []},
+ },
+ 'PostFlow': {
+ '@name': 'PostFlow',
+ 'Request': {'Step': []},
+ 'Response': {'Step': []},
+ },
+ 'Flows': {'Flow': []},
+ 'HTTPProxyConnection': {'BasePath': '',
+ 'Properties': {},
+ 'VirtualHost': ''},
+ 'RouteRule': []
+ }
+
+ merged_pe['ProxyEndpoint']['Description'] = each_pe_info['ProxyEndpoint']['Description'] # noqa
+ merged_pe['ProxyEndpoint']['FaultRules'] = each_pe_info['ProxyEndpoint']['FaultRules'] # noqa
+ merged_pe['ProxyEndpoint']['HTTPProxyConnection']['BasePath'] = (basepath if basepath is None else f'/{basepath}') # noqa
+ merged_pe['ProxyEndpoint']['HTTPProxyConnection']['Properties'] = each_pe_info['ProxyEndpoint']['HTTPProxyConnection']['Properties'] # noqa
+ merged_pe['ProxyEndpoint']['HTTPProxyConnection']['VirtualHost'] = each_pe_info['ProxyEndpoint']['HTTPProxyConnection']['VirtualHost'] # noqa
+
+ merged_pe['ProxyEndpoint']['@name'].append(each_pe_info['ProxyEndpoint']['@name']) # noqa
+ merged_pe['ProxyEndpoint']['RouteRule'].extend(
+ process_route_rules(each_pe_info['ProxyEndpoint']['RouteRule'],condition) # noqa
+ )
+ merged_pe['ProxyEndpoint']['PreFlow']['Request']['Step'].extend(
+ process_steps(each_pe_info['ProxyEndpoint']['PreFlow']['Request'],condition) # noqa
+ )
+ merged_pe['ProxyEndpoint']['PreFlow']['Response']['Step'].extend(
+ process_steps(each_pe_info['ProxyEndpoint']['PreFlow']['Response'],condition) # noqa
+ )
+ merged_pe['ProxyEndpoint']['PostFlow']['Request']['Step'].extend(
+ process_steps(each_pe_info['ProxyEndpoint']['PostFlow']['Request'],condition) # noqa
+ )
+ merged_pe['ProxyEndpoint']['PostFlow']['Response']['Step'].extend(
+ process_steps(each_pe_info['ProxyEndpoint']['PostFlow']['Response'],condition) # noqa
+ )
+ if 'PostClientFlow' in each_pe_info['ProxyEndpoint']:
+ merged_pe['ProxyEndpoint']['PostClientFlow'] = {
+ '@name': 'PostClientFlow',
+ 'Request': {'Step': []},
+ 'Response': {'Step': []},
+ }
+ merged_pe['ProxyEndpoint']['PostClientFlow']['Response']['Step'].extend( # noqa
+ process_steps(each_pe_info['ProxyEndpoint']['PostClientFlow']['Response'], None) # noqa
+ )
+ for each_flow in original_flows:
+ merged_pe['ProxyEndpoint']['Flows']['Flow'].append(
+ process_flow(each_flow, condition)
+ )
+ merged_pe['ProxyEndpoint']['@name'] = "-".join(merged_pe['ProxyEndpoint']['@name']) # noqa
+ return merged_pe
+
+
+def copy_folder(src, dst):
+ try:
+ shutil.copytree(src, dst)
+ except FileNotFoundError as e:
+ print(e)
+ sys.exit(1)
+
+
+def delete_folder(src):
+ try:
+ shutil.rmtree(src)
+ except FileNotFoundError as e:
+ print(f'Ignoring : {e}')
+ return
+
+
+def delete_file(src):
+ try:
+ os.remove(src)
+ except FileNotFoundError as e:
+ print(f'Ignoring : {e}')
+ return
+
+
+def clean_up_artifacts(target_dir, artifacts_to_retains):
+ for file in list_dir(target_dir, True):
+ each_policy_file = file.split('.xml')[0]
+ if each_policy_file not in artifacts_to_retains:
+ delete_file(f"{target_dir}/{file}")
+
+
+def filter_objects(obj_data, obj_type, targets):
+ result = None
+ if obj_data is None:
+ return result
+ elif isinstance(obj_data[obj_type], str):
+ result = ({ obj_type: obj_data[obj_type] } if obj_data[obj_type] in targets else None ) # noqa
+ elif isinstance(obj_data[obj_type], list):
+ result = {obj_type: [v for v in obj_data[obj_type] if v in targets]}
+ else:
+ return result
+ return result
+
+
+def zipdir(path, ziph):
+ # ziph is zipfile handle
+ for root, dirs, files in os.walk(path):
+ for file in files:
+ ziph.write(os.path.join(root, file),
+ os.path.relpath(os.path.join(root, file),
+ os.path.join(path, '..')))
+
+
+def clone_proxies(source_dir, target_dir,
+ objects, merged_pes, proxy_bundle_directory):
+ target_dir = f"{target_dir}/apiproxy"
+ copy_folder(source_dir, target_dir)
+ file = get_proxy_entrypoint(target_dir)
+ # root = parse_xml(file)
+ root = parse_proxy_root(target_dir)
+ delete_file(file)
+ root['APIProxy']['@name'] = objects['Name']
+ root['APIProxy']['Policies'] = filter_objects(
+ root['APIProxy']['Policies'], 'Policy', objects['Policies'])
+ root['APIProxy']['TargetEndpoints'] = filter_objects(
+ root['APIProxy']['TargetEndpoints'], 'TargetEndpoint', objects['TargetEndpoints']) # noqa
+ clean_up_artifacts(f"{target_dir}/policies", objects['Policies'])
+ clean_up_artifacts(f"{target_dir}/targets", objects['TargetEndpoints'])
+ for pe in objects['ProxyEndpoints']:
+ write_xml_from_dict(f"{target_dir}/proxies/{pe}.xml", merged_pes[pe])
+ clean_up_artifacts(f"{target_dir}/proxies", objects['ProxyEndpoints'])
+ root['APIProxy']['ProxyEndpoints'] = {'ProxyEndpoint': (
+ objects['ProxyEndpoints'] if len(objects['ProxyEndpoints']) > 1 else objects['ProxyEndpoints'][0] )} # noqa
+ transformed_file = file.split('/')
+ transformed_file[-1] = f"{objects['Name']}.xml"
+ write_xml_from_dict("/".join(transformed_file), root)
+ delete_folder(f"{target_dir}/manifests")
+ with zipfile.ZipFile(f"{proxy_bundle_directory}/{objects['Name']}.zip", 'w', zipfile.ZIP_DEFLATED) as zipf: # noqa
+ zipdir(target_dir, zipf)
+
+
+def export_debug_log(files, log_path='logs'):
+ create_dir(log_path)
+ for file, data in files.items():
+ file_name = f'{log_path}/{file}.json'
+ write_json(file_name, data)