Skip to content

Commit

Permalink
Feat/combine schemas (#3)
Browse files Browse the repository at this point in the history
* fix: schema json

fix: readme typos

fix: schema fix

chore: add node_modules and dist in .gitignore

chore: ignore lint on generated files

chore: update package lock file

fix: pr edits for date type

fix: include lodash dev-dep

fix: tests and include oa schema

feat: separate file to handle oa and geekout

* fix: gitignore squash issue

* feat: update postinstall for new combined schema

* fix: lint issue

* fix: remove extra oa type generation

* fix: edit publish script

* fix: shift axios to dev dep

* fix: readme

Co-authored-by: Matthea Loo <[email protected]>
  • Loading branch information
Nebulis and dhemutton authored Oct 21, 2020
1 parent 4a6b79f commit 98f4c3b
Show file tree
Hide file tree
Showing 7 changed files with 223 additions and 10 deletions.
38 changes: 37 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
1. Add a file named `schema.json` in the `src` directory. The file must contain the newly added JSON schema and must be placed under a reversed domain name structure. For instance if your schema is related to `tech.gov.sg`, then place the file in `src/sg/gov/tech/<name>/<version>/schema.json`. In that example `<name>` and `<version>` must be replaced by real values.

- The schema file name must strictly be `schema.json`. It will be automatically published.

> _All json files in the src folder will be published._
- The schema will be published at the URL following the structure of the `src` folder. For instance, if your schema is in `src/sg/gov/tech/<name>/<version>/schema.json` then it will be available from `https://domain/sg/gov/tech/<name>/<version>`.
- Always use `1.0` version for new schemata.

Expand All @@ -15,4 +18,37 @@
- Run `npm run postinstall` to generate the types.
- Update `src/index.ts` to export the generated types. In order to prevent name collision, use the `import *` notation to import all the types related to your new schema under a specific name. Then re-export that name.

> Feel free to refer to previously created schemata for help.
> _Feel free to refer to previously created schemata for help._
---

## Including Open Attestation schema requirements into existing schema

1. In the same folder as your `schema.json`, create a json file labelled `<name>-open-attestation.json` with the following attributes:

```json
{
"$id": "...",
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"allOf": [
{
"$ref": "https://schema.openattestation.com/2.0/schema.json"
},
{
"$ref": "..."
}
]
}
```
In the example above:

- `"$id"` field is the URL from which the schema will be available at.

- `"$ref"` field is a reference to the URL where your existing schema is published. The first `"$ref"` field refers to the URL where the Open Attestation schema is at.

2. Add tests for your schema.

>_If you are uncertain of the structure of an Open Attestation document, you can refer to [this](https://openattestation.com/docs/verifiable-document/raw-document) and append your existing sample document with the required fields._

27 changes: 21 additions & 6 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
"@types/lodash": "^4.14.162",
"@typescript-eslint/eslint-plugin": "^2.8.0",
"@typescript-eslint/parser": "^2.8.0",
"axios": "^0.20.0",
"commitizen": "4.0.3",
"eslint": "^6.6.0",
"eslint-config-airbnb-base": "^14.0.0",
Expand Down Expand Up @@ -112,5 +113,6 @@
"error"
]
}
}
}
},
"dependencies": {}
}
2 changes: 1 addition & 1 deletion scripts/publish-schema.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@ mkdir public

# copy from src to preserve the structure
cd src || exit 10
find . -name schema.json -exec rsync -R {} ../public \; # https://stackoverflow.com/questions/11246070/cp-parents-option-on-mac/13855290#13855290
find . -name *.json -exec rsync -R {} ../public \; # https://stackoverflow.com/questions/11246070/cp-parents-option-on-mac/13855290#13855290
cd ..
31 changes: 31 additions & 0 deletions src/sg/gov/tech/geekout/1.0/geekout-open-attestation-document.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{
"recipient": {
"name": "Matthea Loo"
},
"programme": {
"name": "GeekOut 2020",
"startDate": "2020-10-12",
"endDate": "2020-10-14"
},
"signatory": {
"name": "Alice",
"position": "Boss",
"organisation": "ABC",
"signature": "signature"
},
"issuers": [
{
"name": "GovTech",
"documentStore": "0x8Fc57204c35fb9317D91285eF52D6b892EC08cD3",
"identityProof": {
"type": "DNS-TXT",
"location": "example.openattestation.com"
}
}
],
"$template": {
"name": "GEEK_OUT_2020",
"type": "EMBEDDED_RENDERER",
"url": "https://stoic-lumiere-531096.netlify.app"
}
}
13 changes: 13 additions & 0 deletions src/sg/gov/tech/geekout/1.0/geekout-open-attestation.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"$id": "https://schemata.openattestation.com/sg/gov/tech/geekout/1.0/geekout-open-attestation",
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"allOf": [
{
"$ref": "https://schema.openattestation.com/2.0/schema.json"
},
{
"$ref": "https://schemata.openattestation.com/sg/gov/tech/geekout/1.0/schema.json"
}
]
}
116 changes: 116 additions & 0 deletions src/sg/gov/tech/geekout/1.0/geekout-open-attestation.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
import Ajv from "ajv";
import { cloneDeep, omit } from "lodash";
import schema from "./geekout-open-attestation.json";
import sampleDocJson from "./geekout-open-attestation-document.json";
import axios from "axios";

function loadSchema(uri: string) {
return axios.get(uri).then(res => {
return res.data;
});
}
const ajv = new Ajv({ allErrors: true, loadSchema: loadSchema });
let validator: Ajv.ValidateFunction;

describe("schema", () => {
beforeAll(async () => {
validator = await ajv.compileAsync(schema);
});
it("should work with valid json", () => {
expect(validator(sampleDocJson)).toBe(true);
});

//To test if geekout schema is correctly merged
it("should return array of errors without recipient name", () => {
const badDoc = omit(cloneDeep(sampleDocJson), "recipient.name");
expect(validator(badDoc)).toBe(false);
expect(validator.errors).toStrictEqual([
{
keyword: "required",
dataPath: ".recipient",
schemaPath:
"https://schemata.openattestation.com/sg/gov/tech/geekout/1.0/schema.json/properties/recipient/required",
params: { missingProperty: "name" },
message: "should have required property 'name'"
}
]);
});

//To test if oa schema is correctly merged
it("should return array of errors without issuer name", () => {
const badDoc = omit(cloneDeep(sampleDocJson), "issuers[0].name");
expect(validator(badDoc)).toBe(false);
expect(validator.errors).toStrictEqual([
{
keyword: "required",
dataPath: ".issuers[0]",
schemaPath: "#/required",
params: { missingProperty: "name" },
message: "should have required property 'name'"
},
{
keyword: "required",
dataPath: ".issuers[0]",
schemaPath: "#/allOf/1/required",
params: { missingProperty: "tokenRegistry" },
message: "should have required property 'tokenRegistry'"
},
{
keyword: "required",
dataPath: ".issuers[0]",
schemaPath: "#/required",
params: { missingProperty: "name" },
message: "should have required property 'name'"
},
{
keyword: "required",
dataPath: ".issuers[0]",
schemaPath: "#/definitions/certificateStore/required",
params: { missingProperty: "name" },
message: "should have required property 'name'"
},
{
keyword: "required",
dataPath: ".issuers[0]",
schemaPath: "#/definitions/certificateStore/required",
params: { missingProperty: "certificateStore" },
message: "should have required property 'certificateStore'"
},
{
keyword: "required",
dataPath: ".issuers[0]",
schemaPath: "#/required",
params: { missingProperty: "name" },
message: "should have required property 'name'"
},
{
keyword: "required",
dataPath: ".issuers[0]",
schemaPath: "#/allOf/1/required",
params: { missingProperty: "revocationStore" },
message: "should have required property 'revocationStore'"
},
{
keyword: "required",
dataPath: ".issuers[0]",
schemaPath: "#/required",
params: { missingProperty: "name" },
message: "should have required property 'name'"
},
{
keyword: "not",
dataPath: ".issuers[0]",
schemaPath: "#/properties/issuers/items/oneOf/4/allOf/1/not",
params: {},
message: "should NOT be valid"
},
{
keyword: "oneOf",
dataPath: ".issuers[0]",
schemaPath: "#/properties/issuers/items/oneOf",
params: { passingSchemas: null },
message: "should match exactly one schema in oneOf"
}
]);
});
});

0 comments on commit 98f4c3b

Please sign in to comment.