Skip to content

Commit

Permalink
fix: add guard and utils
Browse files Browse the repository at this point in the history
  • Loading branch information
ChenRen93 authored Sep 7, 2021
1 parent 6d6ed36 commit 7546878
Show file tree
Hide file tree
Showing 9 changed files with 252 additions and 82 deletions.
2 changes: 2 additions & 0 deletions src/shared/utils/@types/diagnose.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export type Kind = "raw" | "wrapped" | "signed";
export type Mode = "strict" | "non-strict";
163 changes: 91 additions & 72 deletions src/shared/utils/__tests__/diagnose.test.ts
Original file line number Diff line number Diff line change
@@ -1,80 +1,22 @@
import { diagnose } from "../diagnose";
import {
__unsafe__use__it__at__your__own__risks__wrapDocument,
SchemaId,
signDocument,
SUPPORTED_SIGNING_ALGORITHM,
wrapDocument,
WrappedDocument,
} from "../../..";
import * as v3 from "../../../3.0/types";
import { signDocument, SUPPORTED_SIGNING_ALGORITHM } from "../../..";
import * as v2 from "../../../2.0/types";
import { omit } from "lodash";

import * as v2RawDocument from "../../../../test/fixtures/v2/raw-document.json";
import * as v3RawDocument from "../../../../test/fixtures/v3/raw-document.json";
import * as v2WrappedVerifiableDocument from "../../../../test/fixtures/v2/not-obfuscated-wrapped.json";
import * as v3WrappedVerifiableDocument from "../../../../test/fixtures/v3/not-obfuscated-wrapped.json";
describe("diagnose", () => {
let wrappedV3Document: WrappedDocument<v3.OpenAttestationDocument>;
let signedV2Document: v2.SignedWrappedDocument;
const wrappedV2Document: WrappedDocument<v2.OpenAttestationDocument> = wrapDocument({
issuers: [
{
name: "John",
documentStore: "0xabcdabcdabcdabcdabcdabcdabcdabcdabcdabcd",
identityProof: {
type: v2.IdentityProofType.DNSTxt,
location: "example.com",
},
},
],
});
let v2SignedDocument: v2.SignedWrappedDocument;

beforeAll(async () => {
wrappedV3Document = await __unsafe__use__it__at__your__own__risks__wrapDocument(
{
"@context": [
"https://www.w3.org/2018/credentials/v1",
"https://www.w3.org/2018/credentials/examples/v1",
"https://schemata.openattestation.com/com/openattestation/1.0/OpenAttestation.v3.json",
],
issuer: {
name: "name",
type: "OpenAttestationIssuer",
id: "https://example.com",
},
issuanceDate: "2010-01-01T19:23:24Z",
type: ["VerifiableCredential", "UniversityDegreeCredential", "OpenAttestationCredential"],
credentialSubject: {
id: "did:example:ebfeb1f712ebc6f1c276e12ec21",
degree: {
type: "BachelorDegree",
name: "Bachelor of Science and Arts",
},
},
openAttestationMetadata: {
proof: {
value: "0xabcf",
type: v3.ProofType.OpenAttestationProofMethod,
method: v3.Method.DocumentStore,
},
template: {
url: "https://",
name: "",
type: v3.TemplateType.EmbeddedRenderer,
},
identityProof: {
identifier: "whatever",
type: v2.IdentityProofType.DNSTxt,
},
},
name: "",
reference: "",
validFrom: "2010-01-01T19:23:24Z",
},
{ version: SchemaId.v3 }
);
signedV2Document = await signDocument(wrappedV2Document, SUPPORTED_SIGNING_ALGORITHM.Secp256k1VerificationKey2018, {
const wrappedDocument: v2.WrappedDocument<v2.OpenAttestationDocument> = v2WrappedVerifiableDocument as any;
v2SignedDocument = await signDocument(wrappedDocument, SUPPORTED_SIGNING_ALGORITHM.Secp256k1VerificationKey2018, {
public: "did:ethr:0xE712878f6E8d5d4F9e87E10DA604F9cB564C9a89#controller",
private: "0x497c85ed89f1874ba37532d1e33519aba15bd533cdcb90774cc497bfe3cde655",
});
});

describe("3.0", () => {
it("should return an error when document is empty", () => {
expect(diagnose({ version: "3.0", kind: "wrapped", document: null, mode: "non-strict" })).toMatchInlineSnapshot(`
Expand All @@ -85,14 +27,21 @@ describe("diagnose", () => {
]
`);
});

it("should not return an error when document is valid", () => {
expect(
diagnose({ version: "3.0", kind: "wrapped", document: wrappedV3Document, mode: "non-strict" })
diagnose({ version: "3.0", kind: "wrapped", document: v3WrappedVerifiableDocument, mode: "non-strict" })
).toMatchInlineSnapshot(`Array []`);
});

it("should return an error when document does not have issuer", () => {
expect(
diagnose({ version: "3.0", kind: "wrapped", document: omit(wrappedV3Document, "issuer"), mode: "non-strict" })
diagnose({
version: "3.0",
kind: "wrapped",
document: omit(v3WrappedVerifiableDocument, "issuer"),
mode: "non-strict",
})
).toMatchInlineSnapshot(`
Array [
Object {
Expand All @@ -104,7 +53,43 @@ describe("diagnose", () => {
]
`);
});

it("should return an error when raw document is not version 3", () => {
expect(diagnose({ version: "3.0", kind: "raw", document: v2RawDocument, mode: "non-strict" }))
.toMatchInlineSnapshot(`
Array [
Object {
"message": "The document does not match OpenAttestation schema 3.0",
},
Object {
"message": "document - must have required property '@context'",
},
Object {
"message": "document - must have required property 'type'",
},
Object {
"message": "document - must have required property 'credentialSubject'",
},
Object {
"message": "document - must have required property 'issuer'",
},
Object {
"message": "document - must have required property 'issuanceDate'",
},
Object {
"message": "document - must have required property 'openAttestationMetadata'",
},
]
`);
});

it("should not return an error when raw document is version 3", () => {
expect(
diagnose({ version: "3.0", kind: "raw", document: v3RawDocument, mode: "non-strict" })
).toMatchInlineSnapshot(`Array []`);
});
});

describe("2.0", () => {
it("should return an error when document is empty", () => {
expect(diagnose({ version: "2.0", kind: "wrapped", document: null, mode: "non-strict" })).toMatchInlineSnapshot(`
Expand All @@ -115,17 +100,19 @@ describe("diagnose", () => {
]
`);
});

it("should not return an error when document is valid", () => {
expect(
diagnose({ version: "2.0", kind: "signed", document: signedV2Document, mode: "non-strict" })
diagnose({ version: "2.0", kind: "signed", document: v2SignedDocument, mode: "non-strict" })
).toMatchInlineSnapshot(`Array []`);
});

it("should return an error when document does not have issuer", () => {
expect(
diagnose({
version: "2.0",
kind: "signed",
document: omit(signedV2Document, "data.issuers"),
document: omit(v2SignedDocument, "data.issuers"),
mode: "non-strict",
})
).toMatchInlineSnapshot(`
Expand All @@ -139,5 +126,37 @@ describe("diagnose", () => {
]
`);
});

it("should return an error when raw document is not version 2", () => {
expect(diagnose({ version: "2.0", kind: "raw", document: v3RawDocument, mode: "non-strict" }))
.toMatchInlineSnapshot(`
Array [
Object {
"message": "The document does not match OpenAttestation schema 2.0",
},
Object {
"message": "document - must have required property 'issuers'",
},
Object {
"message": "/attachments/0 - must have required property 'filename'",
},
Object {
"message": "/attachments/0 - must have required property 'type'",
},
Object {
"message": "/attachments/0 - must NOT have additional properties",
},
Object {
"message": "/attachments/0 - must NOT have additional properties",
},
]
`);
});

it("should not return an error when raw document is version 2", () => {
expect(
diagnose({ version: "2.0", kind: "raw", document: v2RawDocument, mode: "non-strict" })
).toMatchInlineSnapshot(`Array []`);
});
});
});
34 changes: 34 additions & 0 deletions src/shared/utils/__tests__/utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import { wrapDocument } from "../../..";
import { WrappedDocument } from "../../../shared/@types/document";
import * as v2 from "../../../__generated__/schema.2.0";
import * as v3 from "../../../__generated__/schema.3.0";
import * as v2RawDocument from "../../../../test/fixtures/v2/raw-document.json";
import * as v3RawDocument from "../../../../test/fixtures/v3/raw-document.json";
import * as v2WrappedVerifiableDocument from "../../../../test/fixtures/v2/not-obfuscated-wrapped.json";
import * as v3WrappedVerifiableDocument from "../../../../test/fixtures/v3/not-obfuscated-wrapped.json";
import * as v2WrappedDidDocument from "../../../../test/fixtures/v2/did-wrapped.json";
Expand Down Expand Up @@ -250,6 +252,38 @@ describe("Util Functions", () => {
});
});

describe("getTemplateURL", () => {
test("should return template url for raw v2 document", async () => {
expect(utils.getTemplateURL(v2RawDocument)).toStrictEqual("https://tutorial-renderer.openattestation.com");
});
test("should return template url for wrapped v2 document", async () => {
expect(utils.getTemplateURL(v2WrappedVerifiableDocument)).toStrictEqual(
"https://tutorial-renderer.openattestation.com"
);
});
test("should return template url for raw v3 document", async () => {
expect(utils.getTemplateURL(v3RawDocument)).toStrictEqual("https://tutorial-renderer.openattestation.com");
});
test("should return template url for wrapped v3 document", async () => {
expect(utils.getTemplateURL(v3WrappedVerifiableDocument)).toStrictEqual(
"https://tutorial-renderer.openattestation.com"
);
});
test("should return error when document is not OpenAttestation document", async () => {
const document: WrappedDocument<any> = {
signature: {
type: "SHA3MerkleProof",
targetHash: "c5d53262962b192c5c977f2252acd4862f41cc1ccce7e87c5b406905a2726692",
proof: [],
merkleRoot: "c5d53262962b192c5c977f2252acd4862f41cc1ccce7e87c5b406905a2726692",
},
};
expect(() => utils.getTemplateURL(document)).toThrow(
new Error("Unsupported document type: Only can retrieve template url from OpenAttestation v2 & v3 documents.")
);
});
});

describe("isTransferableAsset", () => {
test("should return true for v2 transferable document", async () => {
expect(utils.isTransferableAsset(v2WrappedTransferableDocument)).toStrictEqual(true);
Expand Down
16 changes: 12 additions & 4 deletions src/shared/utils/diagnose.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,9 @@ import {
import { ArrayProof, Signature, SignatureStrict } from "../../2.0/types";
import { clone, cloneDeepWith } from "lodash";
import { buildAjv, getSchema } from "../ajv";
import { Kind, Mode } from "./@types/diagnose";

type Version = "2.0" | "3.0";
type Kind = "wrapped" | "signed";
export type Mode = "strict" | "non-strict";

interface DiagnoseError {
message: string;
Expand Down Expand Up @@ -59,7 +58,7 @@ const ajv = buildAjv({ transform: transformSchema, validateFormats: false });
/**
* Tools to give information about the validity of a document. It will return and eventually output the errors found.
* @param version 2.0 or 3.0
* @param kind wrapped or signed
* @param kind raw, wrapped or signed
* @param debug turn on to output in the console, the errors found
* @param mode strict or non-strict. Strict will perform additional check on the data. For instance strict validation will ensure that a target hash is a 32 bytes hex string while non strict validation will just check that target hash is a string.
* @param document the document to validate
Expand All @@ -80,14 +79,17 @@ export const diagnose = ({
if (!document) {
return handleError(debug, "The document must not be empty");
}

if (typeof document !== "object") {
return handleError(debug, "The document must be an object");
}

const errors = validate(
document,
getSchema(version === "3.0" ? SchemaId.v3 : SchemaId.v2, mode === "non-strict" ? ajv : undefined)
getSchema(version === "3.0" ? SchemaId.v3 : SchemaId.v2, mode === "non-strict" ? ajv : undefined),
kind
);

if (errors.length > 0) {
// TODO this can be improved later
return handleError(
Expand All @@ -97,6 +99,10 @@ export const diagnose = ({
);
}

if (kind === "raw") {
return [];
}

if (version === "3.0") {
return diagnoseV3({ mode, debug, document, kind });
} else {
Expand Down Expand Up @@ -132,6 +138,7 @@ const diagnoseV3 = ({ kind, document, debug, mode }: { kind: Kind; document: any
`The document schema version is wrong. Expected ${SchemaId.v3}, received ${document.version}`
);
}

try {
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
mode === "strict"
Expand All @@ -140,6 +147,7 @@ const diagnoseV3 = ({ kind, document, debug, mode }: { kind: Kind; document: any
} catch (e) {
return handleError(debug, e.message);
}

if (kind === "signed") {
if (!document.proof) {
return handleError(debug, `The document does not have a proof`);
Expand Down
27 changes: 26 additions & 1 deletion src/shared/utils/guard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,32 @@ import {
OpenAttestationDocument as OpenAttestationDocumentV2,
WrappedDocument as WrappedDocumentV2,
} from "../../2.0/types";
import { diagnose, Mode } from "./diagnose";
import { diagnose } from "./diagnose";
import { Mode } from "./@types/diagnose";

/**
*
* @param document
* @param mode strict or non-strict. Strict will perform additional check on the data. For instance strict validation will ensure that a target hash is a 32 bytes hex string while non strict validation will just check that target hash is a string.
*/
export const isRawV2Document = (
document: any,
{ mode }: { mode: Mode } = { mode: "non-strict" }
): document is OpenAttestationDocumentV2 => {
return diagnose({ version: "2.0", kind: "raw", document, debug: false, mode }).length === 0;
};

/**
*
* @param document
* @param mode strict or non-strict. Strict will perform additional check on the data. For instance strict validation will ensure that a target hash is a 32 bytes hex string while non strict validation will just check that target hash is a string.
*/
export const isRawV3Document = (
document: any,
{ mode }: { mode: Mode } = { mode: "non-strict" }
): document is OpenAttestationDocumentV3 => {
return diagnose({ version: "3.0", kind: "raw", document, debug: false, mode }).length === 0;
};

/**
*
Expand Down
Loading

0 comments on commit 7546878

Please sign in to comment.