From ded788da1868c91a03b2020ffeef7419a664cdb3 Mon Sep 17 00:00:00 2001 From: Nebulis Date: Fri, 29 Nov 2019 16:07:16 +0800 Subject: [PATCH] feat: update schema and generate type --- .circleci/config.yml | 4 +- .eslintignore | 3 +- package-lock.json | 689 ++++++++++++++++++++++ package.json | 4 +- src/@types/document.ts | 23 + src/__generated__/,gitignore | 4 + src/digest/digest.test.ts | 2 +- src/digest/digest.ts | 2 +- src/index.ts | 26 +- src/privacy/privacy.test.ts | 3 +- src/privacy/privacy.ts | 42 +- src/schema/1.0/sample-document.json | 9 +- src/schema/1.0/schema.json | 163 +++-- src/schema/1.0/schema.test.ts | 883 ++++++++++++++++++++++------ src/schema/schema.test.ts | 6 +- src/schema/schema.ts | 7 +- src/signature/signature.test.ts | 2 +- src/signature/signature.ts | 19 +- test/e2e.test.ts | 34 +- 19 files changed, 1630 insertions(+), 295 deletions(-) create mode 100644 src/@types/document.ts create mode 100644 src/__generated__/,gitignore diff --git a/.circleci/config.yml b/.circleci/config.yml index a19410a9..3234ecf3 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -16,10 +16,10 @@ jobs: command: npm run lint - run: name: test - command: npm run build + command: npm run test - run: name: build - command: npm run test + command: npm run build - run: name: release command: npx --no-install semantic-release diff --git a/.eslintignore b/.eslintignore index a4916e63..fd4405cf 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,3 +1,4 @@ node_modules coverage -dist \ No newline at end of file +dist +src/__generated__ diff --git a/package-lock.json b/package-lock.json index 218f237b..f57bf275 100644 --- a/package-lock.json +++ b/package-lock.json @@ -901,6 +901,167 @@ "rimraf": "^2.5.2" } }, + "@mark.probst/typescript-json-schema": { + "version": "0.32.0", + "resolved": "https://registry.npmjs.org/@mark.probst/typescript-json-schema/-/typescript-json-schema-0.32.0.tgz", + "integrity": "sha512-OoD+5D7Mka80FIcmvPyuAKV7g5Of5S04R74S4DTAG8pr9REDWySUh9pOloro7SNFwWt/+2f90wyP+DtGHykVfg==", + "dev": true, + "requires": { + "glob": "~7.1.3", + "json-stable-stringify": "^1.0.1", + "typescript": "~3.2.1", + "yargs": "^12.0.5" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + }, + "cliui": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", + "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", + "dev": true, + "requires": { + "string-width": "^2.1.1", + "strip-ansi": "^4.0.0", + "wrap-ansi": "^2.0.0" + } + }, + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "get-caller-file": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", + "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", + "dev": true + }, + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "p-limit": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.1.tgz", + "integrity": "sha512-85Tk+90UCVWvbDavCLKPOLC9vvY8OwEX/RtKF+/1OADJMVlFfEHOiMTPVyxg7mk/dKa+ipdHm0OUkTvCpMTuwg==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + }, + "require-main-filename": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", + "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", + "dev": true + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + }, + "typescript": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.2.4.tgz", + "integrity": "sha512-0RNDbSdEokBeEAkgNbxJ+BLwSManFy9TeXz8uW+48j/xhEXv1ePME60olyzw2XzUqUBNAYFeJadIqAgNqIACwg==", + "dev": true + }, + "yargs": { + "version": "12.0.5", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.5.tgz", + "integrity": "sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==", + "dev": true, + "requires": { + "cliui": "^4.0.0", + "decamelize": "^1.2.0", + "find-up": "^3.0.0", + "get-caller-file": "^1.0.1", + "os-locale": "^3.0.0", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^2.0.0", + "which-module": "^2.0.0", + "y18n": "^3.2.1 || ^4.0.0", + "yargs-parser": "^11.1.1" + } + }, + "yargs-parser": { + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-11.1.1.tgz", + "integrity": "sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + } + } + }, + "@mark.probst/unicode-properties": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@mark.probst/unicode-properties/-/unicode-properties-1.1.0.tgz", + "integrity": "sha512-7AQsO0hMmpqDledV7AhBuSYqYPFsKP9PaltMecX9nlnsyFxqtsqUg9/pvB2L/jxvskrDrNkdKYz2KTbQznCtng==", + "dev": true, + "requires": { + "brfs": "^1.4.0", + "unicode-trie": "^0.3.0" + } + }, "@nodelib/fs.scandir": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.3.tgz", @@ -1974,6 +2135,15 @@ "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", "dev": true }, + "array-back": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-2.0.0.tgz", + "integrity": "sha512-eJv4pLLufP3g5kcZry0j6WXpIbzYw9GUB4mVJZno9wfwiBxbizTnHCw3VJb07cBihbFX48Y7oSrW9y+gt4glyw==", + "dev": true, + "requires": { + "typical": "^2.6.1" + } + }, "array-equal": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/array-equal/-/array-equal-1.0.0.tgz", @@ -2357,6 +2527,18 @@ } } }, + "brfs": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/brfs/-/brfs-1.6.1.tgz", + "integrity": "sha512-OfZpABRQQf+Xsmju8XE9bDjs+uU4vLREGolP7bDgcpsI17QREyZ4Bl+2KLxxx1kCgA0fAIhKQBaBYh+PEcCqYQ==", + "dev": true, + "requires": { + "quote-stream": "^1.0.1", + "resolve": "^1.1.5", + "static-module": "^2.2.0", + "through2": "^2.0.0" + } + }, "browser-process-hrtime": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-0.1.3.tgz", @@ -2404,6 +2586,12 @@ "integrity": "sha1-M3dm2hWAEhD92VbCLpxokaudAzc=", "dev": true }, + "buffer-equal": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-0.0.1.tgz", + "integrity": "sha1-kbx0sR6kBbyRa8aqkI+q+ltKrEs=", + "dev": true + }, "buffer-from": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", @@ -2659,6 +2847,12 @@ "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", "dev": true }, + "collection-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/collection-utils/-/collection-utils-1.0.1.tgz", + "integrity": "sha512-LA2YTIlR7biSpXkKYwwuzGjwL5rjWEZVOSnvdUc7gObvWe4WkjxOpfrdhoP7Hs09YWDVfg0Mal9BpAqLfVEzQg==", + "dev": true + }, "collection-visit": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", @@ -2699,6 +2893,29 @@ "delayed-stream": "~1.0.0" } }, + "command-line-args": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/command-line-args/-/command-line-args-4.0.7.tgz", + "integrity": "sha512-aUdPvQRAyBvQd2n7jXcsMDz68ckBJELXNzBybCHOibUWEg0mWTnaYCSRU8h9R+aNRSvDihJtssSRCiDRpLaezA==", + "dev": true, + "requires": { + "array-back": "^2.0.0", + "find-replace": "^1.0.3", + "typical": "^2.6.1" + } + }, + "command-line-usage": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/command-line-usage/-/command-line-usage-5.0.5.tgz", + "integrity": "sha512-d8NrGylA5oCXSbGoKz05FkehDAzSmIm4K03S5VDh4d5lZAtTWfc3D1RuETtuQCn8129nYfJfDdF7P/lwcz1BlA==", + "dev": true, + "requires": { + "array-back": "^2.0.0", + "chalk": "^2.4.1", + "table-layout": "^0.4.3", + "typical": "^2.6.1" + } + }, "commander": { "version": "2.20.3", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", @@ -2849,6 +3066,18 @@ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", "dev": true }, + "concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, "confusing-browser-globals": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.9.tgz", @@ -3407,6 +3636,15 @@ "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", "dev": true }, + "encoding": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz", + "integrity": "sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=", + "dev": true, + "requires": { + "iconv-lite": "~0.4.13" + } + }, "end-of-stream": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", @@ -4188,6 +4426,32 @@ "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", "dev": true }, + "falafel": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/falafel/-/falafel-2.1.0.tgz", + "integrity": "sha1-lrsXdh2rqU9G0AFzizzt86Z/4Gw=", + "dev": true, + "requires": { + "acorn": "^5.0.0", + "foreach": "^2.0.5", + "isarray": "0.0.1", + "object-keys": "^1.0.6" + }, + "dependencies": { + "acorn": { + "version": "5.7.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.3.tgz", + "integrity": "sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw==", + "dev": true + }, + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "dev": true + } + } + }, "fast-deep-equal": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", @@ -4337,6 +4601,27 @@ "merge": "^1.2.1" } }, + "find-replace": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/find-replace/-/find-replace-1.0.3.tgz", + "integrity": "sha1-uI5zZNLZyVlVnziMZmcNYTBEH6A=", + "dev": true, + "requires": { + "array-back": "^1.0.4", + "test-value": "^2.1.0" + }, + "dependencies": { + "array-back": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-1.0.4.tgz", + "integrity": "sha1-ZEun8JX3/898Q7Xw3DnTwfA8Bjs=", + "dev": true, + "requires": { + "typical": "^2.6.0" + } + } + } + }, "find-root": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", @@ -5237,6 +5522,15 @@ "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", "dev": true }, + "graphql": { + "version": "0.11.7", + "resolved": "https://registry.npmjs.org/graphql/-/graphql-0.11.7.tgz", + "integrity": "sha512-x7uDjyz8Jx+QPbpCFCMQ8lltnQa4p4vSYHx6ADe8rVYRTdsyhCJbvSty5DAsLVmU6cGakl+r8HQYolKHxk/tiw==", + "dev": true, + "requires": { + "iterall": "1.1.3" + } + }, "growly": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz", @@ -5782,6 +6076,12 @@ "loose-envify": "^1.0.0" } }, + "invert-kv": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz", + "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==", + "dev": true + }, "is-accessor-descriptor": { "version": "0.1.6", "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", @@ -5996,6 +6296,12 @@ "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", "dev": true }, + "is-url": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/is-url/-/is-url-1.2.4.tgz", + "integrity": "sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww==", + "dev": true + }, "is-utf8": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", @@ -6141,6 +6447,12 @@ "handlebars": "^4.1.2" } }, + "iterall": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/iterall/-/iterall-1.1.3.tgz", + "integrity": "sha512-Cu/kb+4HiNSejAPhSaN1VukdNTTi/r4/e+yykqjlG/IW+1gZH5b4+Bq3whDX4tvbYugta3r8KTMUiqT3fIGxuQ==", + "dev": true + }, "java-properties": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/java-properties/-/java-properties-1.0.2.tgz", @@ -6638,6 +6950,12 @@ } } }, + "js-base64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.5.1.tgz", + "integrity": "sha512-M7kLczedRMYX4L8Mdh4MzyAMM9O5osx+4FcOQuTvr3A9F2D9S5JXheN0ewNbrvK2UatkTRhL5ejGmGSjNMiZuw==", + "dev": true + }, "js-sha3": { "version": "0.8.0", "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz", @@ -6730,6 +7048,15 @@ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" }, + "json-stable-stringify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", + "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=", + "dev": true, + "requires": { + "jsonify": "~0.0.0" + } + }, "json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", @@ -6768,6 +7095,12 @@ "graceful-fs": "^4.1.6" } }, + "jsonify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", + "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=", + "dev": true + }, "jsonparse": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", @@ -6801,6 +7134,15 @@ "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", "dev": true }, + "lcid": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz", + "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==", + "dev": true, + "requires": { + "invert-kv": "^2.0.0" + } + }, "left-pad": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/left-pad/-/left-pad-1.3.0.tgz", @@ -6922,6 +7264,12 @@ "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=", "dev": true }, + "lodash.padend": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/lodash.padend/-/lodash.padend-4.6.1.tgz", + "integrity": "sha1-U8y6BH0G4VjTEfRdpiX05J5vFm4=", + "dev": true + }, "lodash.set": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/lodash.set/-/lodash.set-4.3.2.tgz", @@ -7108,6 +7456,15 @@ "tmpl": "1.0.x" } }, + "map-age-cleaner": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", + "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==", + "dev": true, + "requires": { + "p-defer": "^1.0.0" + } + }, "map-cache": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", @@ -7149,6 +7506,31 @@ "supports-hyperlinks": "^1.0.1" } }, + "mem": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/mem/-/mem-4.3.0.tgz", + "integrity": "sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w==", + "dev": true, + "requires": { + "map-age-cleaner": "^0.1.1", + "mimic-fn": "^2.0.0", + "p-is-promise": "^2.0.0" + }, + "dependencies": { + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true + }, + "p-is-promise": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-2.1.0.tgz", + "integrity": "sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg==", + "dev": true + } + } + }, "meow": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/meow/-/meow-4.0.1.tgz", @@ -7190,6 +7572,23 @@ "integrity": "sha512-VjFo4P5Whtj4vsLzsYBu5ayHhoHJ0UqNm7ibvShmbmoz7tGi0vXaoJbGdB+GmDMLUdg8DpQXEIeVDAe8MaABvQ==", "dev": true }, + "merge-source-map": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/merge-source-map/-/merge-source-map-1.0.4.tgz", + "integrity": "sha1-pd5GU42uhNQRTMXqArR3KmNGcB8=", + "dev": true, + "requires": { + "source-map": "^0.5.6" + }, + "dependencies": { + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + } + } + }, "merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", @@ -7328,6 +7727,12 @@ "integrity": "sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw==", "dev": true }, + "moment": { + "version": "2.24.0", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz", + "integrity": "sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg==", + "dev": true + }, "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", @@ -11023,6 +11428,12 @@ } } }, + "object-inspect": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.4.1.tgz", + "integrity": "sha512-wqdhLpfCUbEsoEwl3FXwGyv8ief1k/1aUdIPCqVnupM6e8l63BEJdiF/0swtn04/8p05tG/T0FrpTlfwvljOdw==", + "dev": true + }, "object-keys": { "version": "1.0.12", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.12.tgz", @@ -11187,6 +11598,17 @@ "wordwrap": "~1.0.0" } }, + "os-locale": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz", + "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==", + "dev": true, + "requires": { + "execa": "^1.0.0", + "lcid": "^2.0.0", + "mem": "^4.0.0" + } + }, "os-name": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/os-name/-/os-name-3.1.0.tgz", @@ -11203,6 +11625,12 @@ "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", "dev": true }, + "p-defer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", + "integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=", + "dev": true + }, "p-each-series": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-1.0.0.tgz", @@ -11279,6 +11707,12 @@ "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", "dev": true }, + "pako": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.10.tgz", + "integrity": "sha512-0DTvPVU3ed8+HNXOu5Bs+o//Mbdj9VNQMUOe9oKCwh8l0GNwpTDMKCWbRjgtD291AWnkAgkqA/LOnQS8AmS1tw==", + "dev": true + }, "parent-module": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.0.tgz", @@ -11394,6 +11828,12 @@ "find-up": "^2.1.0" } }, + "pluralize": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-7.0.0.tgz", + "integrity": "sha512-ARhBOdzS3e41FbkW/XWrTEtukqqLoK5+Z/4UeDaLuSW+39JPeFgs4gCGqsrJHVZX0fUrx//4OF0K1CUGwlIFow==", + "dev": true + }, "pn": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/pn/-/pn-1.1.0.tgz", @@ -11514,6 +11954,65 @@ "integrity": "sha1-Q2CxfGETatOAeDl/8RQW4Ybc+7g=", "dev": true }, + "quicktype": { + "version": "15.0.207", + "resolved": "https://registry.npmjs.org/quicktype/-/quicktype-15.0.207.tgz", + "integrity": "sha512-HqWffbfL5D4HtkzV+Suw7xWie4RAgkmGCL8PiIwT1wOwE1t4kfzAL4EWGH3rMY+trKccQFNfYrQboGERmxJukg==", + "dev": true, + "requires": { + "@mark.probst/typescript-json-schema": "~0.32.0", + "@mark.probst/unicode-properties": "~1.1.0", + "chalk": "^2.4.1", + "collection-utils": "^1.0.1", + "command-line-args": "^4.0.6", + "command-line-usage": "^5.0.5", + "graphql": "^0.11.7", + "is-url": "^1.2.4", + "js-base64": "^2.4.3", + "lodash": "^4.17.10", + "moment": "^2.22.1", + "node-fetch": "^1.7.1", + "pako": "^1.0.6", + "pluralize": "^7.0.0", + "stream-json": "1.1.3", + "string-to-stream": "^1.1.0", + "urijs": "^1.19.1", + "uuid": "^3.2.1", + "wordwrap": "^1.0.0", + "yaml": "^1.5.0" + }, + "dependencies": { + "node-fetch": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.3.tgz", + "integrity": "sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ==", + "dev": true, + "requires": { + "encoding": "^0.1.11", + "is-stream": "^1.0.1" + } + } + } + }, + "quote-stream": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/quote-stream/-/quote-stream-1.0.2.tgz", + "integrity": "sha1-hJY/jJwmuULhU/7rU6rnRlK34LI=", + "dev": true, + "requires": { + "buffer-equal": "0.0.1", + "minimist": "^1.1.3", + "through2": "^2.0.0" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + } + } + }, "rc": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", @@ -11641,6 +12140,12 @@ "esprima": "~4.0.0" } }, + "reduce-flatten": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/reduce-flatten/-/reduce-flatten-1.0.1.tgz", + "integrity": "sha1-JYx479FT3fk8tWEjf2EYTzaW4yc=", + "dev": true + }, "regenerator-runtime": { "version": "0.11.1", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", @@ -12538,6 +13043,12 @@ } } }, + "shallow-copy": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/shallow-copy/-/shallow-copy-0.0.1.tgz", + "integrity": "sha1-QV9CcC1z2BAzApLMXuhurhoRoXA=", + "dev": true + }, "shebang-command": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", @@ -12868,6 +13379,15 @@ "integrity": "sha512-MTX+MeG5U994cazkjd/9KNAapsHnibjMLnfXodlkXw76JEea0UiNzrqidzo1emMwk7w5Qhc9jd4Bn9TBb1MFwA==", "dev": true }, + "static-eval": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/static-eval/-/static-eval-2.0.3.tgz", + "integrity": "sha512-zsxDGucfAh8T339sSKgpFbvg15Fms2IVaJGC+jqp0bVsxhcpM+iMeAI8weNo8dmf4OblgifTBUoyk1vGVtYw2w==", + "dev": true, + "requires": { + "escodegen": "^1.11.1" + } + }, "static-extend": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", @@ -12889,12 +13409,70 @@ } } }, + "static-module": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/static-module/-/static-module-2.2.5.tgz", + "integrity": "sha512-D8vv82E/Kpmz3TXHKG8PPsCPg+RAX6cbCOyvjM6x04qZtQ47EtJFVwRsdov3n5d6/6ynrOY9XB4JkaZwB2xoRQ==", + "dev": true, + "requires": { + "concat-stream": "~1.6.0", + "convert-source-map": "^1.5.1", + "duplexer2": "~0.1.4", + "escodegen": "~1.9.0", + "falafel": "^2.1.0", + "has": "^1.0.1", + "magic-string": "^0.22.4", + "merge-source-map": "1.0.4", + "object-inspect": "~1.4.0", + "quote-stream": "~1.0.2", + "readable-stream": "~2.3.3", + "shallow-copy": "~0.0.1", + "static-eval": "^2.0.0", + "through2": "~2.0.3" + }, + "dependencies": { + "escodegen": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.9.1.tgz", + "integrity": "sha512-6hTjO1NAWkHnDk3OqQ4YrCuwwmGHL9S3nPlzBOUG/R44rda3wLNrfvQ5fkSGjyhHFKM7ALPKcKGrwvCLe0lC7Q==", + "dev": true, + "requires": { + "esprima": "^3.1.3", + "estraverse": "^4.2.0", + "esutils": "^2.0.2", + "optionator": "^0.8.1", + "source-map": "~0.6.1" + } + }, + "esprima": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz", + "integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM=", + "dev": true + }, + "magic-string": { + "version": "0.22.5", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.22.5.tgz", + "integrity": "sha512-oreip9rJZkzvA8Qzk9HFs8fZGF/u7H/gtrE8EN6RjKJ9kh2HlC+yQ2QezifqTZfGyiuAV0dRv5a+y/8gBb1m9w==", + "dev": true, + "requires": { + "vlq": "^0.2.2" + } + } + } + }, "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 }, + "stream-chain": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/stream-chain/-/stream-chain-2.2.1.tgz", + "integrity": "sha512-LW24AKjJHBrihU8xGLPs/o7OSCNPzBpJ/5JtjMyt8/1WLzcSIRKRRvugeBQjTCFJMn0nFzvN0rd6oCSWguKmxw==", + "dev": true + }, "stream-combiner2": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/stream-combiner2/-/stream-combiner2-1.1.1.tgz", @@ -12905,6 +13483,15 @@ "readable-stream": "^2.0.2" } }, + "stream-json": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/stream-json/-/stream-json-1.1.3.tgz", + "integrity": "sha512-y+ChhCov2A5nDqC2aZ6HKXs3OvDlvAp0Ps3BF1P/Iv8tUZJQQsMVaSzk0WryVTVoGITKv01UYahCXMpAs7I0lQ==", + "dev": true, + "requires": { + "stream-chain": "^2.0.3" + } + }, "string-length": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/string-length/-/string-length-2.0.0.tgz", @@ -12932,6 +13519,16 @@ } } }, + "string-to-stream": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string-to-stream/-/string-to-stream-1.1.1.tgz", + "integrity": "sha512-QySF2+3Rwq0SdO3s7BAp4x+c3qsClpPQ6abAmb0DGViiSBAkT5kL6JT2iyzEVP+T1SmzHrQD1TwlP9QAHCc+Sw==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "readable-stream": "^2.1.0" + } + }, "string-width": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", @@ -13080,6 +13677,19 @@ } } }, + "table-layout": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/table-layout/-/table-layout-0.4.5.tgz", + "integrity": "sha512-zTvf0mcggrGeTe/2jJ6ECkJHAQPIYEwDoqsiqBjI24mvRmQbInK5jq33fyypaCBxX08hMkfmdOqj6haT33EqWw==", + "dev": true, + "requires": { + "array-back": "^2.0.0", + "deep-extend": "~0.6.0", + "lodash.padend": "^4.6.1", + "typical": "^2.6.1", + "wordwrapjs": "^3.0.0" + } + }, "temp-dir": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-1.0.0.tgz", @@ -13186,6 +13796,27 @@ } } }, + "test-value": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/test-value/-/test-value-2.1.0.tgz", + "integrity": "sha1-Edpv9nDzRxpztiXKTz/c97t0gpE=", + "dev": true, + "requires": { + "array-back": "^1.0.3", + "typical": "^2.6.0" + }, + "dependencies": { + "array-back": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-1.0.4.tgz", + "integrity": "sha1-ZEun8JX3/898Q7Xw3DnTwfA8Bjs=", + "dev": true, + "requires": { + "typical": "^2.6.0" + } + } + } + }, "text-extensions": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/text-extensions/-/text-extensions-1.9.0.tgz", @@ -13220,6 +13851,12 @@ "xtend": "~4.0.1" } }, + "tiny-inflate": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tiny-inflate/-/tiny-inflate-1.0.3.tgz", + "integrity": "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==", + "dev": true + }, "tmp": { "version": "0.0.33", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", @@ -13407,12 +14044,24 @@ "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", "dev": true }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", + "dev": true + }, "typescript": { "version": "3.7.2", "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.7.2.tgz", "integrity": "sha512-ml7V7JfiN2Xwvcer+XAf2csGO1bPBdRbFCkYBczNZggrBZ9c7G3riSUeJmqEU5uOtXNPMhE3n+R4FA/3YOAWOQ==", "dev": true }, + "typical": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/typical/-/typical-2.6.1.tgz", + "integrity": "sha1-XAgOXWYcu+OCWdLnCjxyU+hziB0=", + "dev": true + }, "uglify-js": { "version": "3.6.9", "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.6.9.tgz", @@ -13424,6 +14073,24 @@ "source-map": "~0.6.1" } }, + "unicode-trie": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/unicode-trie/-/unicode-trie-0.3.1.tgz", + "integrity": "sha1-1nHd3YkQGgi6w3tqUWEBBgIFIIU=", + "dev": true, + "requires": { + "pako": "^0.2.5", + "tiny-inflate": "^1.0.0" + }, + "dependencies": { + "pako": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz", + "integrity": "sha1-8/dSL073gjSNqBYbrZ7P1Rv4OnU=", + "dev": true + } + } + }, "union-value": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.0.tgz", @@ -13537,6 +14204,12 @@ "punycode": "^2.1.0" } }, + "urijs": { + "version": "1.19.2", + "resolved": "https://registry.npmjs.org/urijs/-/urijs-1.19.2.tgz", + "integrity": "sha512-s/UIq9ap4JPZ7H1EB5ULo/aOUbWqfDi7FKzMC2Nz+0Si8GiT1rIEaprt8hy3Vy2Ex2aJPpOQv4P4DuOZ+K1c6w==", + "dev": true + }, "urix": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", @@ -13619,6 +14292,12 @@ "extsprintf": "^1.2.0" } }, + "vlq": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/vlq/-/vlq-0.2.3.tgz", + "integrity": "sha512-DRibZL6DsNhIgYQ+wNdWDL2SL3bKPlVrRiBqV5yuMm++op8W4kGFtaQfCs4KEJn0wBZcHVHJ3eoywX8983k1ow==", + "dev": true + }, "vorpal": { "version": "1.12.0", "resolved": "https://registry.npmjs.org/vorpal/-/vorpal-1.12.0.tgz", @@ -13840,6 +14519,16 @@ "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", "dev": true }, + "wordwrapjs": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/wordwrapjs/-/wordwrapjs-3.0.0.tgz", + "integrity": "sha512-mO8XtqyPvykVCsrwj5MlOVWvSnCdT+C+QVbm6blradR7JExAhbkZ7hZ9A+9NUtwzSqrlUo9a67ws0EiILrvRpw==", + "dev": true, + "requires": { + "reduce-flatten": "^1.0.1", + "typical": "^2.6.1" + } + }, "wrap-ansi": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", diff --git a/package.json b/package.json index a7469df2..8ab3abac 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,8 @@ "test:coverage": "jest --coverage", "test:watch": "jest --watch", "lint": "eslint . --ext .ts --max-warnings 0", - "lint:fix": "npm run lint -- --fix" + "lint:fix": "npm run lint -- --fix", + "postinstall": "quicktype -s schema -o src/__generated__/schema.ts -t OpenAttestationSchemaV1 --just-types ./src/schema/1.0/schema.json --explicit-unions" }, "files": [ "/dist" @@ -52,6 +53,7 @@ "git-cz": "^3.3.0", "jest": "^24.9.0", "prettier": "^1.19.1", + "quicktype": "^15.0.207", "rollup": "^1.27.3", "rollup-plugin-commonjs": "^10.1.0", "semantic-release": "^15.13.31", diff --git a/src/@types/document.ts b/src/@types/document.ts new file mode 100644 index 00000000..aaef0030 --- /dev/null +++ b/src/@types/document.ts @@ -0,0 +1,23 @@ +export type SignatureProofAlgorithm = "SHA3MerkleProof"; + +export interface SignedDocument extends SchematisedDocument { + signature: Signature; +} + +export interface Signature { + type: SignatureProofAlgorithm; + targetHash: string; + proof: string[]; + merkleRoot: string; +} + +export interface ObfuscationMetadata { + obfuscatedData?: string[]; +} + +export interface SchematisedDocument { + version: string; + data: T; + schema?: string; + privacy?: ObfuscationMetadata; +} diff --git a/src/__generated__/,gitignore b/src/__generated__/,gitignore new file mode 100644 index 00000000..5e7d2734 --- /dev/null +++ b/src/__generated__/,gitignore @@ -0,0 +1,4 @@ +# Ignore everything in this directory +* +# Except this file +!.gitignore diff --git a/src/digest/digest.test.ts b/src/digest/digest.test.ts index 03dac6c7..ec63fa33 100644 --- a/src/digest/digest.test.ts +++ b/src/digest/digest.test.ts @@ -1,5 +1,5 @@ import { digestDocument, flattenHashArray } from "./digest"; -import { SchematisedDocument } from "../privacy"; +import { SchematisedDocument } from "../@types/document"; describe("digest", () => { describe("flattenHashArray", () => { diff --git a/src/digest/digest.ts b/src/digest/digest.ts index 49a9fe54..75f1076a 100644 --- a/src/digest/digest.ts +++ b/src/digest/digest.ts @@ -1,7 +1,7 @@ import { get, omitBy, sortBy } from "lodash"; import { keccak256 } from "js-sha3"; import { flatten } from "../serialize/flatten"; -import { SchematisedDocument } from "../privacy"; +import { SchematisedDocument } from "../@types/document"; const isKeyOrValueUndefined = (value: any, key: any) => value === undefined || key === undefined; diff --git a/src/index.ts b/src/index.ts index 3e256b18..c7e5c2fc 100644 --- a/src/index.ts +++ b/src/index.ts @@ -2,10 +2,12 @@ import Ajv from "ajv"; import { digestDocument } from "./digest"; import { getSchema, validateSchema as validate } from "./schema"; import { sign } from "./signature"; -import { Document, SchematisedDocument, setData, SignedDocument } from "./privacy"; +import { SchematisedDocument, SignedDocument } from "./@types/document"; import { saltData } from "./privacy/salt"; import * as utils from "./utils"; import openAttestationSchema from "./schema/1.0/schema.json"; +import { OpenAttestationSchemaV1 } from "./__generated__/schema"; +import { setData } from "./privacy"; const createDocument = (data: any, schemaId?: string) => { const version = openAttestationSchema.$id.split("/")?.[1]; @@ -24,7 +26,7 @@ const createDocument = (data: any, schemaId?: string) => { }; class SchemaValidationError extends Error { - constructor(message: string, public validationErrors: Ajv.ErrorObject[], public document: Document) { + constructor(message: string, public validationErrors: Ajv.ErrorObject[], public document: any) { super(message); } } @@ -35,8 +37,11 @@ const isSchemaValidationError = (error: any): error is SchemaValidationError => interface IssueDocumentOption { externalSchemaId?: string; } -export const issueDocument = (data: any, options?: IssueDocumentOption) => { - const document = createDocument(data, options?.externalSchemaId); +export const issueDocument = ( + data: unknown, + options?: IssueDocumentOption +): SignedDocument => { + const document: SchematisedDocument = createDocument(data, options?.externalSchemaId); const errors = validate(document, getSchema("open-attestation/1.0")); if (errors.length > 0) { throw new SchemaValidationError("Invalid document", errors, document); @@ -44,7 +49,10 @@ export const issueDocument = (data: any, options?: IssueDocumentOption) => { return sign(document, [digestDocument(document)]); }; -export const issueDocuments = (dataArray: any[], options?: IssueDocumentOption) => { +export const issueDocuments = ( + dataArray: unknown[], + options?: IssueDocumentOption +): SignedDocument[] => { const documents = dataArray.map(data => createDocument(data, options?.externalSchemaId)); documents.forEach(document => { const errors = validate(document, getSchema("open-attestation/1.0")); @@ -57,11 +65,13 @@ export const issueDocuments = (dataArray: any[], options?: IssueDocumentOption) return documents.map(doc => sign(doc, batchHashes)); }; -export const validateSchema = (document: SignedDocument): boolean => { - return validate(document, getSchema(`open-attestation/${document.version}`)).length === 0; +export const validateSchema = (document: any): boolean => { + return validate(document, getSchema(`open-attestation/${document?.version ?? ""}`)).length === 0; }; export { digestDocument } from "./digest"; -export { getData, obfuscateDocument, Document, SchematisedDocument, SignedDocument } from "./privacy"; +export { getData, obfuscateDocument } from "./privacy"; export { checkProof, MerkleTree, sign, verify as verifySignature } from "./signature"; export { utils, isSchemaValidationError }; +export * from "./__generated__/schema"; +export * from "./@types/document"; diff --git a/src/privacy/privacy.test.ts b/src/privacy/privacy.test.ts index b48f63b4..385e7043 100644 --- a/src/privacy/privacy.test.ts +++ b/src/privacy/privacy.test.ts @@ -1,4 +1,5 @@ -import { getData, obfuscateData, obfuscateDocument, setData, SignedDocument } from "./privacy"; +import { getData, obfuscateData, obfuscateDocument, setData } from "./privacy"; +import { SignedDocument } from "../@types/document"; describe("privacy", () => { describe("obfuscateData", () => { diff --git a/src/privacy/privacy.ts b/src/privacy/privacy.ts index e467129a..a7a43340 100644 --- a/src/privacy/privacy.ts +++ b/src/privacy/privacy.ts @@ -1,31 +1,10 @@ -import { get, cloneDeep, pick, unset } from "lodash"; +import { cloneDeep, pick, unset } from "lodash"; import { flatten } from "../serialize/flatten"; import { toBuffer } from "../utils"; import { unsaltData } from "./salt"; -import { Signature } from "../signature"; +import { SchematisedDocument, SignedDocument } from "../@types/document"; -export interface SignedDocument extends SchematisedDocument { - signature: Signature; -} - -export interface ObfuscationMetadata { - obfuscatedData?: string[]; -} - -export interface SchematisedDocument extends Document { - version: string; - data: any; -} - -export interface Document { - data: any; - privacy?: ObfuscationMetadata; - schema?: string; -} - -export type OpenAttestationData = any; // input data can take any format - -export const getData = (document: Document) => unsaltData(document.data); +export const getData = (document: any) => unsaltData(document.data); /** * Takes a partial originating document, possibly only with a schema.id and returns a document with the given data and obfuscated data @@ -35,11 +14,11 @@ export const getData = (document: Document) => unsaltData(document.data); */ // TODO: split into two separate functions for the two different use cases -export const setData = ( +export const setData = | SignedDocument, U = any>( document: T, - data: OpenAttestationData, + data: U, obfuscatedData: string[] = [] -): T => { +) => { const privacy = { ...document.privacy, obfuscatedData: obfuscatedData && obfuscatedData.length > 0 ? obfuscatedData : [] @@ -74,12 +53,15 @@ export const obfuscateData = (_data: any, fields: string[] | string) => { }; }; -export const obfuscateDocument = (document: SignedDocument, fields: string[] | string): SignedDocument => { +// TODO shouldn't document be any and shouldn't we validate first it's a valid document ? +export const obfuscateDocument = ( + document: SignedDocument, + fields: string[] | string +): SignedDocument => { const existingData = document.data; const { data, obfuscatedData } = obfuscateData(existingData, fields); - // we use lodash.get because document might not have the correct fields when coming from external input - const currentObfuscatedData = get(document, "privacy.obfuscatedData", []); + const currentObfuscatedData = document?.privacy?.obfuscatedData ?? []; const newObfuscatedData = currentObfuscatedData.concat(obfuscatedData); return setData(document, data, newObfuscatedData); diff --git a/src/schema/1.0/sample-document.json b/src/schema/1.0/sample-document.json index 7202e978..54b0f317 100644 --- a/src/schema/1.0/sample-document.json +++ b/src/schema/1.0/sample-document.json @@ -8,13 +8,18 @@ "issuers": [ { "name": "DEMO STORE", - "documentStore": "0x9178F546D3FF57D7A6352bD61B80cCCD46199C2d", "identityProof": { "type": "DNS-TXT", - "location": "tradetrust.io" + "location": "tradetrust.io", + "subject": "proof.value" } } ], + "proof": { + "type": "OpenAttestationSignature2018", + "method": "DOCUMENT_STORE", + "value": "0x9178F546D3FF57D7A6352bD61B80cCCD46199C2d" + }, "recipient": { "name": "Recipient Name" }, diff --git a/src/schema/1.0/schema.json b/src/schema/1.0/schema.json index 658d2633..0ad2e707 100644 --- a/src/schema/1.0/schema.json +++ b/src/schema/1.0/schema.json @@ -1,35 +1,10 @@ { + "title": "Open Attestation Schema v1", "$id": "open-attestation/1.0", "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", - "properties": { - "id": { - "type": "string", - "description": "Internal reference, usually serial number, of this document" - }, - "$template": { - "type": "object", - "properties": { - "name": { - "type": "string", - "description": "Template name to be use by template renderer to determine the template to use" - }, - "type": { - "type": "string", - "description": "Type of renderer template", - "enum": ["EMBEDDED_RENDERER"] - }, - "url": { - "type": "string", - "description": "URL of a decentralised renderer to render this document" - } - }, - "required": ["name", "type"] - }, - "documentUrl": { - "type": "string", - "description": "URL of the stored tt document" - }, + + "definitions": { "issuers": { "type": "array", "items": { @@ -39,11 +14,6 @@ "type": "string", "description": "Issuer's name" }, - "documentStore": { - "type": "string", - "pattern": "^0x[a-fA-F0-9]{40}$", - "description": "Smart contract address of document store" - }, "identityProof": { "type": "object", "properties": { @@ -54,29 +24,68 @@ "location": { "type": "string", "description": "Url of the website referencing to document store" + }, + "subject": { + "type": "string", + "description": "Path to the property holding the proof value. The value must follow json path syntax" } }, - "required": ["type", "location"] - }, - "tokenRegistry": { - "type": "string", - "pattern": "^0x[a-fA-F0-9]{40}$", - "description": "Smart contract address of token registry" + "additionalProperties": false, + "required": ["type", "location", "subject"] } }, - "anyOf": [ - { - "required": ["name", "documentStore", "identityProof"], - "not": { "required": ["tokenRegistry"] } - }, - { - "required": ["name", "tokenRegistry", "identityProof"], - "not": { "required": ["documentStore"] } - } - ], - "additionalProperties": true + "required": ["name", "identityProof"], + "additionalProperties": false + } + } + }, + + "properties": { + "id": { + "type": "string", + "description": "Internal reference, usually serial number, of this document" + }, + "$template": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Template name to be use by template renderer to determine the template to use" + }, + "type": { + "type": "string", + "description": "Type of renderer template", + "enum": ["EMBEDDED_RENDERER"] + }, + "url": { + "type": "string", + "description": "URL of a decentralised renderer to render this document", + "format": "uri", + "pattern": "^(https?)://" + } }, - "minItems": 1 + "required": ["name", "type", "url"], + "additionalProperties": false + }, + "proof": { + "type": "object", + "properties": { + "type": { + "type": "string", + "description": "Proof method name as specified by w3c vc", + "enum": ["OpenAttestationSignature2018"] + }, + "method": { + "type": "string", + "description": "Proof open attestation method", + "enum": ["TOKEN_REGISTRY", "DOCUMENT_STORE"] + }, + "value": { + "description": "Proof value for issuer(s)" + } + }, + "required": ["type", "method", "value"], + "additionalProperties": false }, "recipient": { "type": "object", @@ -112,6 +121,56 @@ } } }, - "required": ["issuers"], + + "oneOf": [ + { + "properties": { + "issuers": { + "allOf": [ + { "$ref": "#/definitions/issuers" }, + + { + "minItems": 1, + "maxItems": 1 + } + ] + }, + "proof": { + "type": "object", + "properties": { + "value": { + "type": "string" + } + } + } + } + }, + { + "properties": { + "issuers": { + "allOf": [ + { "$ref": "#/definitions/issuers" }, + + { + "minItems": 2 + } + ] + }, + "proof": { + "type": "object", + "properties": { + "value": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + } + } + ], + + "required": ["id", "issuers", "$template", "proof"], "additionalProperties": true } diff --git a/src/schema/1.0/schema.test.ts b/src/schema/1.0/schema.test.ts index 9f6b4104..e02c7ca7 100644 --- a/src/schema/1.0/schema.test.ts +++ b/src/schema/1.0/schema.test.ts @@ -1,192 +1,743 @@ -import { cloneDeep, merge, omit } from "lodash"; +// disable to check error properties, tried with objectContaining but didnt work +/* eslint-disable jest/no-try-expect */ import { issueDocument } from "../../index"; import { $id } from "./schema.json"; -import sampleToken from "./sample-token.json"; import sampleDoc from "./sample-document.json"; -describe("$id/1.0", () => { +describe("open-attestation/1.0", () => { it("should be valid with sample document", () => { const issuedDocument = issueDocument(sampleDoc, { externalSchemaId: $id }); expect(issuedDocument.version).toStrictEqual("1.0"); }); - - it("should be invalid if identity type is other than DNS-TXT", () => { - const document = merge(sampleDoc, { - issuers: [ - { - identityProof: { - type: "ABC", - location: "abc.com" - } - } - ] - }); - expect(() => { - issueDocument(document, { externalSchemaId: $id }); - }).toThrow("Invalid document"); - }); - - it("should be valid if identity type is DNS-TXT", () => { - const document = merge(sampleDoc, { - issuers: [ - { - identityProof: { - type: "DNS-TXT", - location: "abc.com" - } - } - ] - }); - const issuedDocument = issueDocument(document, { externalSchemaId: $id }); - expect(issuedDocument.version).toStrictEqual("1.0"); - }); - - it("should not be valid without identityProof", () => { - const document = cloneDeep(sampleDoc); - delete document.issuers[0].identityProof; - expect(() => { - issueDocument(document, { externalSchemaId: $id }); - }).toThrow("Invalid document"); - }); - - it("should be valid with sample token", () => { - const issuedToken = issueDocument(sampleToken, { externalSchemaId: $id }); - expect(issuedToken.version).toStrictEqual("1.0"); - }); - - it("should not be valid with document with both documentStore and tokenRegistry", () => { - expect(() => { - issueDocument( - { - ...sampleToken, - issuers: [ - { - name: "DEMO STORE", - documentStore: "0x9178F546D3FF57D7A6352bD61B80cCCD46199C2d", - tokenRegistry: "0x9178F546D3FF57D7A6352bD61B80cCCD46199C2d" - } - ], - identityProof: { - type: "DNS-TXT", - location: "abc.com" - } - }, - { externalSchemaId: $id } - ); - }).toThrow("Invalid document"); - }); - - it("should be valid with additonal key:value", () => { - const issuedDocument = issueDocument({ ...sampleDoc, foo: "bar" }, { externalSchemaId: $id }); - // expect(valid).toBe(true); + it("should be valid when adding any additional data", () => { + const issuedDocument = issueDocument({ ...sampleDoc, key1: "some" }, { externalSchemaId: $id }); expect(issuedDocument.version).toStrictEqual("1.0"); }); - it("should be valid without $template (will use default view)", () => { - const document = omit(sampleDoc, "$template"); - const issuedDocument = issueDocument(document, { externalSchemaId: $id }); - expect(issuedDocument.version).toStrictEqual("1.0"); - }); - - it("should be valid without attachments", () => { - const document = omit(sampleDoc, "attachments"); - const issuedDocument = issueDocument(document, { externalSchemaId: $id }); - expect(issuedDocument.version).toStrictEqual("1.0"); + describe("id", () => { + it("should be invalid if id is missing", () => { + expect.assertions(2); + const document = { ...sampleDoc }; + delete document.id; + try { + issueDocument(document, { externalSchemaId: $id }); + } catch (e) { + expect(e).toHaveProperty("message", "Invalid document"); + expect(e).toHaveProperty("validationErrors", [ + { + keyword: "required", + dataPath: "", + schemaPath: "#/required", + params: { missingProperty: "id" }, + message: "should have required property 'id'" + } + ]); + } + }); + it("should be invalid if id is undefined", () => { + expect.assertions(2); + const document = { ...sampleDoc, id: undefined }; + try { + issueDocument(document, { externalSchemaId: $id }); + } catch (e) { + expect(e).toHaveProperty("message", "Invalid document"); + expect(e).toHaveProperty("validationErrors", [ + { + keyword: "required", + dataPath: "", + schemaPath: "#/required", + params: { missingProperty: "id" }, + message: "should have required property 'id'" + } + ]); + } + }); + it("should be invalid if id is null", () => { + expect.assertions(2); + const document = { ...sampleDoc, id: null }; + try { + issueDocument(document, { externalSchemaId: $id }); + } catch (e) { + expect(e).toHaveProperty("message", "Invalid document"); + expect(e).toHaveProperty("validationErrors", [ + { + keyword: "type", + dataPath: ".id", + schemaPath: "#/properties/id/type", + params: { type: "string" }, + message: "should be string" + } + ]); + } + }); }); - it("should be invalid if $template does not have name or type", () => { - const documentWithoutName = omit(sampleDoc, "$template.name"); - expect(() => { - issueDocument(documentWithoutName, { externalSchemaId: $id }); - }).toThrow("Invalid document"); + describe("$template", () => { + it("should be valid when type is EMBEDDED_RENDERER", () => { + const document = { ...sampleDoc, $template: { ...sampleDoc.$template, type: "EMBEDDED_RENDERER" } }; + const issuedDocument = issueDocument(document, { externalSchemaId: $id }); + expect(issuedDocument.version).toStrictEqual("1.0"); + }); + it("should be valid when url starts with http", () => { + const document = { ...sampleDoc, $template: { ...sampleDoc.$template, url: "http://some.example.com" } }; + const issuedDocument = issueDocument(document, { externalSchemaId: $id }); + expect(issuedDocument.version).toStrictEqual("1.0"); + }); + it("should be valid when url starts with https", () => { + const document = { ...sampleDoc, $template: { ...sampleDoc.$template, url: "https://some.example.com" } }; + const issuedDocument = issueDocument(document, { externalSchemaId: $id }); + expect(issuedDocument.version).toStrictEqual("1.0"); + }); - const documentWithoutType = omit(sampleDoc, "$template.type"); - expect(() => { - issueDocument(documentWithoutType, { externalSchemaId: $id }); - }).toThrow("Invalid document"); - }); + it("should be invalid when adding additional data", () => { + expect.assertions(2); + const document = { ...sampleDoc, $template: { ...sampleDoc.$template, key: "any" } }; + try { + issueDocument(document, { externalSchemaId: $id }); + } catch (e) { + expect(e).toHaveProperty("message", "Invalid document"); + expect(e).toHaveProperty("validationErrors", [ + { + keyword: "additionalProperties", + dataPath: ".$template", + schemaPath: "#/properties/%24template/additionalProperties", + params: { additionalProperty: "key" }, + message: "should NOT have additional properties" + } + ]); + } + }); - it("should be invalid with invalid template type", () => { - const document = { - ...sampleDoc, - $template: { - name: "CUSTOM_TEMPLATE", - type: "INVALID_RENDERER" - } - }; - expect(() => { - issueDocument(document, { externalSchemaId: $id }); - }).toThrow("Invalid document"); + it("should be invalid if $template is missing", () => { + expect.assertions(2); + const document = { ...sampleDoc }; + delete document.$template; + try { + issueDocument(document, { externalSchemaId: $id }); + } catch (e) { + expect(e).toHaveProperty("message", "Invalid document"); + expect(e).toHaveProperty("validationErrors", [ + { + keyword: "required", + dataPath: "", + schemaPath: "#/required", + params: { missingProperty: "$template" }, + message: "should have required property '$template'" + } + ]); + } + }); + it("should be invalid if $template.name is missing", () => { + expect.assertions(2); + const document = { ...sampleDoc, $template: { ...sampleDoc.$template } }; + delete document.$template.name; + try { + issueDocument(document, { externalSchemaId: $id }); + } catch (e) { + expect(e).toHaveProperty("message", "Invalid document"); + expect(e).toHaveProperty("validationErrors", [ + { + keyword: "required", + dataPath: ".$template", + schemaPath: "#/properties/%24template/required", + params: { missingProperty: "name" }, + message: "should have required property 'name'" + } + ]); + } + }); + it("should be invalid if $template.type is missing", () => { + expect.assertions(2); + const document = { ...sampleDoc, $template: { ...sampleDoc.$template } }; + delete document.$template.type; + try { + issueDocument(document, { externalSchemaId: $id }); + } catch (e) { + expect(e).toHaveProperty("message", "Invalid document"); + expect(e).toHaveProperty("validationErrors", [ + { + keyword: "required", + dataPath: ".$template", + schemaPath: "#/properties/%24template/required", + params: { missingProperty: "type" }, + message: "should have required property 'type'" + } + ]); + } + }); + it("should be invalid if $template.type is not equal to EMBEDDED_RENDERER", () => { + expect.assertions(2); + const document = { ...sampleDoc, $template: { ...sampleDoc.$template } }; + document.$template.type = "SOMETHING"; + try { + issueDocument(document, { externalSchemaId: $id }); + } catch (e) { + expect(e).toHaveProperty("message", "Invalid document"); + expect(e).toHaveProperty("validationErrors", [ + { + keyword: "enum", + dataPath: ".$template.type", + schemaPath: "#/properties/%24template/properties/type/enum", + params: { allowedValues: ["EMBEDDED_RENDERER"] }, + message: "should be equal to one of the allowed values" + } + ]); + } + }); + it("should be invalid if $template.url is missing", () => { + expect.assertions(2); + const document = { ...sampleDoc, $template: { ...sampleDoc.$template } }; + delete document.$template.url; + try { + issueDocument(document, { externalSchemaId: $id }); + } catch (e) { + expect(e).toHaveProperty("message", "Invalid document"); + expect(e).toHaveProperty("validationErrors", [ + { + keyword: "required", + dataPath: ".$template", + schemaPath: "#/properties/%24template/required", + params: { missingProperty: "url" }, + message: "should have required property 'url'" + } + ]); + } + }); + it("should be invalid if $template.url is not an http url", () => { + expect.assertions(2); + const document = { ...sampleDoc, $template: { ...sampleDoc.$template } }; + document.$template.url = "ftp://some"; + try { + issueDocument(document, { externalSchemaId: $id }); + } catch (e) { + expect(e).toHaveProperty("message", "Invalid document"); + expect(e).toHaveProperty("validationErrors", [ + { + keyword: "pattern", + dataPath: ".$template.url", + schemaPath: "#/properties/%24template/properties/url/pattern", + params: { pattern: "^(https?)://" }, + message: 'should match pattern "^(https?)://"' + } + ]); + } + }); }); - it("should be invalid with invalid file type", () => { - const document = { - ...sampleDoc, - attachments: [ - { - filename: "sample.aac", - type: "audio/aac", - data: "BASE64_ENCODED_FILE" - } - ] - }; - expect(() => { - issueDocument(document, { externalSchemaId: $id }); - }).toThrow("Invalid document"); - }); + describe("issuers", () => { + it("should be valid when type is DNS-TXT", () => { + const document = { + ...sampleDoc, + issuers: [ + { ...sampleDoc.issuers[0], identityProof: { ...sampleDoc.issuers[0].identityProof, type: "DNS-TXT" } } + ] + }; + const issuedDocument = issueDocument(document, { externalSchemaId: $id }); + expect(issuedDocument.version).toStrictEqual("1.0"); + }); + it("should be invalid when adding additional data", () => { + expect.assertions(2); + const document = { ...sampleDoc, issuers: [{ ...sampleDoc.issuers[0], key: "any" }] }; + try { + issueDocument(document, { externalSchemaId: $id }); + } catch (e) { + expect(e).toHaveProperty("message", "Invalid document"); + expect(e).toHaveProperty("validationErrors.0", { + keyword: "additionalProperties", + dataPath: ".issuers[0]", + schemaPath: "#/definitions/issuers/items/additionalProperties", + params: { additionalProperty: "key" }, + message: "should NOT have additional properties" + }); + } + }); + it("should be invalid when adding additional data in identity proof", () => { + expect.assertions(2); + const document = { + ...sampleDoc, + issuers: [{ ...sampleDoc.issuers[0], identityProof: { ...sampleDoc.issuers[0].identityProof, key1: "any" } }] + }; + try { + issueDocument(document, { externalSchemaId: $id }); + } catch (e) { + expect(e).toHaveProperty("message", "Invalid document"); + expect(e).toHaveProperty("validationErrors.0", { + keyword: "additionalProperties", + dataPath: ".issuers[0].identityProof", + schemaPath: "#/definitions/issuers/items/properties/identityProof/additionalProperties", + params: { additionalProperty: "key1" }, + message: "should NOT have additional properties" + }); + } + }); - it("should be invalid with invalid documentStore address", () => { - const document = { - ...sampleDoc, - issuers: [ - { - name: "DEMO STORE", - documentStore: "Invalid Address", - identityProof: { - type: "DNS-TXT", - location: "abc.com" - } - } - ] - }; - expect(() => { - issueDocument(document, { externalSchemaId: $id }); - }).toThrow("Invalid document"); + it("should be invalid if issuers is missing", () => { + expect.assertions(2); + const document = { ...sampleDoc }; + delete document.issuers; + try { + issueDocument(document, { externalSchemaId: $id }); + } catch (e) { + expect(e).toHaveProperty("message", "Invalid document"); + expect(e).toHaveProperty("validationErrors", [ + { + keyword: "required", + dataPath: "", + schemaPath: "#/required", + params: { missingProperty: "issuers" }, + message: "should have required property 'issuers'" + } + ]); + } + }); + it("should be invalid if issuers is an empty array", () => { + expect.assertions(2); + const document = { ...sampleDoc, issuers: [] }; + try { + issueDocument(document, { externalSchemaId: $id }); + } catch (e) { + expect(e).toHaveProperty("message", "Invalid document"); + expect(e).toHaveProperty("validationErrors.0", { + keyword: "minItems", + dataPath: ".issuers", + schemaPath: "#/oneOf/0/properties/issuers/allOf/1/minItems", + params: { limit: 1 }, + message: "should NOT have fewer than 1 items" + }); + } + }); + it("should be invalid if issuer has no name", () => { + expect.assertions(2); + const document = { ...sampleDoc, issuers: [{ ...sampleDoc.issuers[0] }] }; + delete document.issuers[0].name; + try { + issueDocument(document, { externalSchemaId: $id }); + } catch (e) { + expect(e).toHaveProperty("message", "Invalid document"); + expect(e).toHaveProperty("validationErrors.0", { + keyword: "required", + dataPath: ".issuers[0]", + schemaPath: "#/definitions/issuers/items/required", + params: { missingProperty: "name" }, + message: "should have required property 'name'" + }); + } + }); + it("should be invalid if issuer has no identityProof", () => { + expect.assertions(2); + const document = { ...sampleDoc, issuers: [{ ...sampleDoc.issuers[0] }] }; + delete document.issuers[0].identityProof; + try { + issueDocument(document, { externalSchemaId: $id }); + } catch (e) { + expect(e).toHaveProperty("message", "Invalid document"); + expect(e).toHaveProperty("validationErrors.0", { + keyword: "required", + dataPath: ".issuers[0]", + schemaPath: "#/definitions/issuers/items/required", + params: { missingProperty: "identityProof" }, + message: "should have required property 'identityProof'" + }); + } + }); + it("should be invalid if identityProof has no type", () => { + expect.assertions(2); + const document = { + ...sampleDoc, + issuers: [{ ...sampleDoc.issuers[0], identityProof: { ...sampleDoc.issuers[0].identityProof } }] + }; + delete document.issuers[0].identityProof.type; + try { + issueDocument(document, { externalSchemaId: $id }); + } catch (e) { + expect(e).toHaveProperty("message", "Invalid document"); + expect(e).toHaveProperty("validationErrors.0", { + keyword: "required", + dataPath: ".issuers[0].identityProof", + schemaPath: "#/definitions/issuers/items/properties/identityProof/required", + params: { missingProperty: "type" }, + message: "should have required property 'type'" + }); + } + }); + it("should be invalid if identityProof type is not DNS-TXT", () => { + expect.assertions(2); + const document = { + ...sampleDoc, + issuers: [{ ...sampleDoc.issuers[0], identityProof: { ...sampleDoc.issuers[0].identityProof } }] + }; + document.issuers[0].identityProof.type = "OTHER"; + try { + issueDocument(document, { externalSchemaId: $id }); + } catch (e) { + expect(e).toHaveProperty("message", "Invalid document"); + expect(e).toHaveProperty("validationErrors.0", { + keyword: "enum", + dataPath: ".issuers[0].identityProof.type", + schemaPath: "#/definitions/issuers/items/properties/identityProof/properties/type/enum", + params: { allowedValues: ["DNS-TXT"] }, + message: "should be equal to one of the allowed values" + }); + } + }); + it("should be invalid if identityProof has no location", () => { + expect.assertions(2); + const document = { + ...sampleDoc, + issuers: [{ ...sampleDoc.issuers[0], identityProof: { ...sampleDoc.issuers[0].identityProof } }] + }; + delete document.issuers[0].identityProof.location; + try { + issueDocument(document, { externalSchemaId: $id }); + } catch (e) { + expect(e).toHaveProperty("message", "Invalid document"); + expect(e).toHaveProperty("validationErrors.0", { + keyword: "required", + dataPath: ".issuers[0].identityProof", + schemaPath: "#/definitions/issuers/items/properties/identityProof/required", + params: { missingProperty: "location" }, + message: "should have required property 'location'" + }); + } + }); + it("should be invalid if identityProof has no subject", () => { + expect.assertions(2); + const document = { + ...sampleDoc, + issuers: [{ ...sampleDoc.issuers[0], identityProof: { ...sampleDoc.issuers[0].identityProof } }] + }; + delete document.issuers[0].identityProof.subject; + try { + issueDocument(document, { externalSchemaId: $id }); + } catch (e) { + expect(e).toHaveProperty("message", "Invalid document"); + expect(e).toHaveProperty("validationErrors.0", { + keyword: "required", + dataPath: ".issuers[0].identityProof", + schemaPath: "#/definitions/issuers/items/properties/identityProof/required", + params: { missingProperty: "subject" }, + message: "should have required property 'subject'" + }); + } + }); }); - it("should be invalid without issuer", () => { - const documentWithoutKey = omit(sampleDoc, "issuers"); - expect(() => { - issueDocument(documentWithoutKey, { externalSchemaId: $id }); - }).toThrow("Invalid document"); - - const documentWithZeroIssuer = { ...sampleDoc, issuers: [] }; - expect(() => { - issueDocument(documentWithZeroIssuer, { externalSchemaId: $id }); - }).toThrow("Invalid document"); - }); + describe("proof", () => { + it("should be valid when type is OpenAttestationSignature2018", () => { + const document = { ...sampleDoc, proof: { ...sampleDoc.proof, type: "OpenAttestationSignature2018" } }; + const issuedDocument = issueDocument(document, { externalSchemaId: $id }); + expect(issuedDocument.version).toStrictEqual("1.0"); + }); + it("should be valid when method is TOKEN_REGISTRY", () => { + const document = { ...sampleDoc, proof: { ...sampleDoc.proof, method: "TOKEN_REGISTRY" } }; + const issuedDocument = issueDocument(document, { externalSchemaId: $id }); + expect(issuedDocument.version).toStrictEqual("1.0"); + }); + it("should be valid when method is DOCUMENT_STORE", () => { + const document = { ...sampleDoc, proof: { ...sampleDoc.proof, method: "DOCUMENT_STORE" } }; + const issuedDocument = issueDocument(document, { externalSchemaId: $id }); + expect(issuedDocument.version).toStrictEqual("1.0"); + }); + it("should be valid when there is one issuer and proof value is a string", () => { + const document = { + ...sampleDoc, + issuers: [sampleDoc.issuers[0]], + proof: { ...sampleDoc.proof, value: "any value" } + }; + const issuedDocument = issueDocument(document, { externalSchemaId: $id }); + expect(issuedDocument.version).toStrictEqual("1.0"); + }); + it("should be valid when there are two issuers and proof value is an array", () => { + const document = { + ...sampleDoc, + issuers: [sampleDoc.issuers[0], sampleDoc.issuers[0]], + proof: { ...sampleDoc.proof, value: ["any value", "any value 2"] } + }; + const issuedDocument = issueDocument(document, { externalSchemaId: $id }); + expect(issuedDocument.version).toStrictEqual("1.0"); + }); - it("should be invalid without documentStore in issuer", () => { - const document = omit(sampleDoc, "issuers[0].documentStore"); - expect(() => { - issueDocument(document, { externalSchemaId: $id }); - }).toThrow("Invalid document"); + it("should be invalid when adding additional data", () => { + expect.assertions(2); + const document = { ...sampleDoc, proof: { ...sampleDoc.proof, key: "any" } }; + try { + issueDocument(document, { externalSchemaId: $id }); + } catch (e) { + expect(e).toHaveProperty("message", "Invalid document"); + expect(e).toHaveProperty("validationErrors", [ + { + keyword: "additionalProperties", + dataPath: ".proof", + schemaPath: "#/properties/proof/additionalProperties", + params: { additionalProperty: "key" }, + message: "should NOT have additional properties" + } + ]); + } + }); + it("should be invalid if proof is missing", () => { + expect.assertions(2); + const document = { ...sampleDoc }; + delete document.proof; + try { + issueDocument(document, { externalSchemaId: $id }); + } catch (e) { + expect(e).toHaveProperty("message", "Invalid document"); + expect(e).toHaveProperty("validationErrors", [ + { + keyword: "required", + dataPath: "", + schemaPath: "#/required", + params: { missingProperty: "proof" }, + message: "should have required property 'proof'" + } + ]); + } + }); + it("should be invalid if proof type is missing", () => { + expect.assertions(2); + const document = { ...sampleDoc, proof: { ...sampleDoc.proof } }; + delete document.proof.type; + try { + issueDocument(document, { externalSchemaId: $id }); + } catch (e) { + expect(e).toHaveProperty("message", "Invalid document"); + expect(e).toHaveProperty("validationErrors", [ + { + keyword: "required", + dataPath: ".proof", + schemaPath: "#/properties/proof/required", + params: { missingProperty: "type" }, + message: "should have required property 'type'" + } + ]); + } + }); + it("should be invalid if proof type is not OpenAttestationSignature2018", () => { + expect.assertions(2); + const document = { ...sampleDoc, proof: { ...sampleDoc.proof } }; + document.proof.type = "Something"; + try { + issueDocument(document, { externalSchemaId: $id }); + } catch (e) { + expect(e).toHaveProperty("message", "Invalid document"); + expect(e).toHaveProperty("validationErrors", [ + { + keyword: "enum", + dataPath: ".proof.type", + schemaPath: "#/properties/proof/properties/type/enum", + params: { allowedValues: ["OpenAttestationSignature2018"] }, + message: "should be equal to one of the allowed values" + } + ]); + } + }); + it("should be invalid if proof method is missing", () => { + expect.assertions(2); + const document = { ...sampleDoc, proof: { ...sampleDoc.proof } }; + delete document.proof.method; + try { + issueDocument(document, { externalSchemaId: $id }); + } catch (e) { + expect(e).toHaveProperty("message", "Invalid document"); + expect(e).toHaveProperty("validationErrors", [ + { + keyword: "required", + dataPath: ".proof", + schemaPath: "#/properties/proof/required", + params: { missingProperty: "method" }, + message: "should have required property 'method'" + } + ]); + } + }); + it("should be invalid if proof type is not TOKEN_REGISTRY or DOCUMENT_STORE", () => { + expect.assertions(2); + const document = { ...sampleDoc, proof: { ...sampleDoc.proof } }; + document.proof.method = "Something"; + try { + issueDocument(document, { externalSchemaId: $id }); + } catch (e) { + expect(e).toHaveProperty("message", "Invalid document"); + expect(e).toHaveProperty("validationErrors", [ + { + keyword: "enum", + dataPath: ".proof.method", + schemaPath: "#/properties/proof/properties/method/enum", + params: { allowedValues: ["TOKEN_REGISTRY", "DOCUMENT_STORE"] }, + message: "should be equal to one of the allowed values" + } + ]); + } + }); + it("should be invalid if proof value is missing", () => { + expect.assertions(2); + const document = { ...sampleDoc, proof: { ...sampleDoc.proof } }; + delete document.proof.value; + try { + issueDocument(document, { externalSchemaId: $id }); + } catch (e) { + expect(e).toHaveProperty("message", "Invalid document"); + expect(e).toHaveProperty("validationErrors.0", { + keyword: "required", + dataPath: ".proof", + schemaPath: "#/properties/proof/required", + params: { missingProperty: "value" }, + message: "should have required property 'value'" + }); + } + }); + it("should be invalid if there are 2 issuers and proof is a string", () => { + expect.assertions(2); + const document = { + ...sampleDoc, + issuers: [sampleDoc.issuers[0], sampleDoc.issuers[0]], + proof: { ...sampleDoc.proof, value: "lkl" } + }; + try { + issueDocument(document, { externalSchemaId: $id }); + } catch (e) { + expect(e).toHaveProperty("message", "Invalid document"); + expect(e).toHaveProperty("validationErrors.0", { + keyword: "maxItems", + dataPath: ".issuers", + schemaPath: "#/oneOf/0/properties/issuers/allOf/1/maxItems", + params: { limit: 1 }, + message: "should NOT have more than 1 items" + }); + } + }); + it("should be invalid if there are 1 issuer and proof is an array", () => { + expect.assertions(2); + const document = { + ...sampleDoc, + proof: { ...sampleDoc.proof, value: ["lkl"] } + }; + try { + issueDocument(document, { externalSchemaId: $id }); + } catch (e) { + expect(e).toHaveProperty("message", "Invalid document"); + expect(e).toHaveProperty("validationErrors.0", { + keyword: "type", + dataPath: ".proof.value", + schemaPath: "#/oneOf/0/properties/proof/properties/value/type", + params: { type: "string" }, + message: "should be string" + }); + } + }); }); - it("should be invalid without attachments filename, type or data", () => { - const documentWithoutName = omit(sampleDoc, "attachments[0].filename"); - expect(() => { - issueDocument(documentWithoutName, { externalSchemaId: $id }); - }).toThrow("Invalid document"); - - const documentWithoutData = omit(sampleDoc, "attachments[0].data"); - expect(() => { - issueDocument(documentWithoutData, { externalSchemaId: $id }); - }).toThrow("Invalid document"); + describe("attachments", () => { + it("should be valid when type is application/pdf", () => { + const document = { ...sampleDoc, attachments: [{ ...sampleDoc.attachments[0], type: "application/pdf" }] }; + const issuedDocument = issueDocument(document, { externalSchemaId: $id }); + expect(issuedDocument.version).toStrictEqual("1.0"); + }); + it("should be valid when type is image/png", () => { + const document = { ...sampleDoc, attachments: [{ ...sampleDoc.attachments[0], type: "image/png" }] }; + const issuedDocument = issueDocument(document, { externalSchemaId: $id }); + expect(issuedDocument.version).toStrictEqual("1.0"); + }); + it("should be valid when type is image/jpeg", () => { + const document = { ...sampleDoc, attachments: [{ ...sampleDoc.attachments[0], type: "image/jpeg" }] }; + const issuedDocument = issueDocument(document, { externalSchemaId: $id }); + expect(issuedDocument.version).toStrictEqual("1.0"); + }); - const documentWithoutType = omit(sampleDoc, "attachments[0].type"); - expect(() => { - issueDocument(documentWithoutType, { externalSchemaId: $id }); - }).toThrow("Invalid document"); + it("should be invalid when adding additional data", () => { + expect.assertions(2); + const document = { ...sampleDoc, attachments: [{ ...sampleDoc.attachments[0], key: "any" }] }; + try { + issueDocument(document, { externalSchemaId: $id }); + } catch (e) { + expect(e).toHaveProperty("message", "Invalid document"); + expect(e).toHaveProperty("validationErrors", [ + { + keyword: "additionalProperties", + dataPath: ".attachments[0]", + schemaPath: "#/properties/attachments/items/additionalProperties", + params: { additionalProperty: "key" }, + message: "should NOT have additional properties" + } + ]); + } + }); + it("should be invalid if filename is missing", () => { + expect.assertions(2); + const document = { ...sampleDoc, attachments: [{ ...sampleDoc.attachments[0] }] }; + delete document.attachments[0].filename; + try { + issueDocument(document, { externalSchemaId: $id }); + } catch (e) { + expect(e).toHaveProperty("message", "Invalid document"); + expect(e).toHaveProperty("validationErrors", [ + { + keyword: "required", + dataPath: ".attachments[0]", + schemaPath: "#/properties/attachments/items/required", + params: { missingProperty: "filename" }, + message: "should have required property 'filename'" + } + ]); + } + }); + it("should be invalid if type is missing", () => { + expect.assertions(2); + const document = { ...sampleDoc, attachments: [{ ...sampleDoc.attachments[0] }] }; + delete document.attachments[0].type; + try { + issueDocument(document, { externalSchemaId: $id }); + } catch (e) { + expect(e).toHaveProperty("message", "Invalid document"); + expect(e).toHaveProperty("validationErrors", [ + { + keyword: "required", + dataPath: ".attachments[0]", + schemaPath: "#/properties/attachments/items/required", + params: { missingProperty: "type" }, + message: "should have required property 'type'" + } + ]); + } + }); + it("should be invalid if type is not one of the specified enum value", () => { + expect.assertions(2); + const document = { ...sampleDoc, attachments: [{ ...sampleDoc.attachments[0] }] }; + document.attachments[0].type = "Something"; + try { + issueDocument(document, { externalSchemaId: $id }); + } catch (e) { + expect(e).toHaveProperty("message", "Invalid document"); + expect(e).toHaveProperty("validationErrors", [ + { + keyword: "enum", + dataPath: ".attachments[0].type", + schemaPath: "#/properties/attachments/items/properties/type/enum", + params: { allowedValues: ["application/pdf", "image/png", "image/jpeg"] }, + message: "should be equal to one of the allowed values" + } + ]); + } + }); + it("should be invalid if data is missing", () => { + expect.assertions(2); + const document = { ...sampleDoc, attachments: [{ ...sampleDoc.attachments[0] }] }; + delete document.attachments[0].data; + try { + issueDocument(document, { externalSchemaId: $id }); + } catch (e) { + expect(e).toHaveProperty("message", "Invalid document"); + expect(e).toHaveProperty("validationErrors", [ + { + keyword: "required", + dataPath: ".attachments[0]", + schemaPath: "#/properties/attachments/items/required", + params: { missingProperty: "data" }, + message: "should have required property 'data'" + } + ]); + } + }); }); }); diff --git a/src/schema/schema.test.ts b/src/schema/schema.test.ts index da244f14..a4f39c60 100644 --- a/src/schema/schema.test.ts +++ b/src/schema/schema.test.ts @@ -1,6 +1,6 @@ import Ajv from "ajv"; import { validateSchema } from "./schema"; -import { Document, SchematisedDocument } from "../privacy"; +import { SchematisedDocument } from "../@types/document"; const schemaV1 = { $id: "http://example.com/schemaV1.json", @@ -18,7 +18,9 @@ const schemaV1 = { describe("schema", () => { describe("validateSchema", () => { test("throws when the schema cannot be found/has not been added", () => { - const document: Document = { + // eslint-disable-next-line @typescript-eslint/ban-ts-ignore + // @ts-ignore ignore typescript for this test + const document: SchematisedDocument = { schema: "http://example.com/schemaV1.json", data: { key1: 2 diff --git a/src/schema/schema.ts b/src/schema/schema.ts index d22fd8e7..39a58d8c 100644 --- a/src/schema/schema.ts +++ b/src/schema/schema.ts @@ -1,14 +1,11 @@ import Ajv from "ajv"; -import { getData, SchematisedDocument, SignedDocument } from "../privacy"; +import { getData } from "../privacy"; import { getLogger } from "../logger"; import openAttestationSchema from "./1.0/schema.json"; const logger = getLogger("validate"); -export const validateSchema = ( - document: SignedDocument | SchematisedDocument, - validator: Ajv.ValidateFunction -): Ajv.ErrorObject[] => { +export const validateSchema = (document: any, validator: Ajv.ValidateFunction): Ajv.ErrorObject[] => { if (!validator) { throw new Error("No schema validator provided"); } diff --git a/src/signature/signature.test.ts b/src/signature/signature.test.ts index 6bd9f77a..867d968e 100644 --- a/src/signature/signature.test.ts +++ b/src/signature/signature.test.ts @@ -1,5 +1,5 @@ import { sign, verify } from "./signature"; -import { SchematisedDocument } from "../privacy"; +import { SchematisedDocument } from "../@types/document"; const unsignedDocument: SchematisedDocument = { version: "1.0", diff --git a/src/signature/signature.ts b/src/signature/signature.ts index 97f28c87..15db795a 100644 --- a/src/signature/signature.ts +++ b/src/signature/signature.ts @@ -3,18 +3,13 @@ import { keccak256 } from "js-sha3"; import { digestDocument } from "../digest"; import { MerkleTree } from "./merkle"; import { hashToBuffer, bufSortJoin } from "../utils"; -import { Document, SchematisedDocument, SignedDocument } from "../privacy"; +import { SchematisedDocument, Signature, SignedDocument } from "../@types/document"; +import { OpenAttestationSchemaV1 } from ".."; -export type SignatureProofAlgorithm = "SHA3MerkleProof"; - -export interface Signature { - type: SignatureProofAlgorithm; - targetHash: string; - proof: string[]; - merkleRoot: string; -} - -export const sign = (document: SchematisedDocument, batch?: string[]): SignedDocument => { +export const sign = ( + document: SchematisedDocument, + batch?: string[] +): SignedDocument => { const digest = digestDocument(document); if (batch && !batch.includes(digest)) { @@ -37,7 +32,7 @@ export const sign = (document: SchematisedDocument, batch?: string[]): SignedDoc return { ...document, signature }; }; -export const verify = (document: any): document is Document => { +export const verify = (document: any): document is SignedDocument => { const signature = get(document, "signature"); if (!signature) { return false; diff --git a/test/e2e.test.ts b/test/e2e.test.ts index f2efe4fe..ca866477 100644 --- a/test/e2e.test.ts +++ b/test/e2e.test.ts @@ -8,21 +8,38 @@ import { // eslint-disable-next-line @typescript-eslint/ban-ts-ignore // @ts-ignore } from "../dist/cjs/index"; -import { Document } from "../src/privacy"; +import { + IdentityProofType, + Method, + OpenAttestationSchemaV1, + ProofType, + TemplateType +} from "../src/__generated__/schema"; // Disable tslint import/no-unresolved for this because it usually doesn't exist until build runs type IssueDocumentReturnType = ReturnType; -const openAttestationData = { +const openAttestationData: OpenAttestationSchemaV1 = { + id: "document identifier", + $template: { + name: "any", + type: TemplateType.EmbeddedRenderer, + url: "http://some.example.com" + }, issuers: [ { name: "DEMO STORE", - documentStore: "0x9178F546D3FF57D7A6352bD61B80cCCD46199C2d", identityProof: { - type: "DNS-TXT", - location: "tradetrust.io" + type: IdentityProofType.DNSTxt, + location: "tradetrust.io", + subject: "proof.value" } } - ] + ], + proof: { + type: ProofType.OpenAttestationSignature2018, + value: "0x9178F546D3FF57D7A6352bD61B80cCCD46199C2d", + method: Method.TokenRegistry + } }; const datum = [ @@ -148,10 +165,7 @@ describe("E2E Test Scenarios", () => { expect(verified).toBe(true); }); test("checks that documents conforms to the schema", () => { - const validatedSchema = signedDocuments.reduce( - (prev: boolean, curr: Document) => validateSchema(curr) && prev, - true - ); + const validatedSchema = signedDocuments.reduce((prev: boolean, curr: any) => validateSchema(curr) && prev, true); expect(validatedSchema).toBe(true); });