forked from airtasker/spot
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[TT-278] Generate spot contract hash (airtasker#281)
* [TT-278] - Implement function to generate hashes based on contracts - Add command line script to interact with it
- Loading branch information
Showing
4 changed files
with
186 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
import { Command, flags } from "@oclif/command"; | ||
import { hashContractDefinition } from "../../../lib/src/tagging/hash"; | ||
import { safeParse } from "../common/safe-parse"; | ||
|
||
const ARG_API = "spot_contract"; | ||
|
||
/** | ||
* oclif command to generate a tag based on a Spot contract | ||
*/ | ||
export default class Checksum extends Command { | ||
static description = "Generate a version tag based on a Spot contract"; | ||
|
||
static examples = ["$ spot mock api.ts"]; | ||
|
||
static args = [ | ||
{ | ||
name: ARG_API, | ||
required: true, | ||
description: "path to Spot contract", | ||
hidden: false | ||
} | ||
]; | ||
|
||
static flags = { | ||
help: flags.help({ char: "h" }) | ||
}; | ||
|
||
async run() { | ||
const { args } = this.parse(Checksum); | ||
try { | ||
const contract = safeParse.call(this, args[ARG_API]).definition; | ||
const hash = hashContractDefinition(contract); | ||
this.log(hash); | ||
} catch (e) { | ||
this.error(e, { exit: 1 }); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
import { cleanse } from "../cleansers/cleanser"; | ||
import { EndpointDefinition } from "../models/definitions"; | ||
import { TypeKind } from "../models/types/kinds"; // | ||
import { parse } from "../parsers/parser"; | ||
import { hashContractDefinition } from "./hash"; | ||
|
||
describe("Hash", () => { | ||
describe("hashContractDefinition", () => { | ||
it("returns a consistent hash", () => { | ||
const result = parse("./lib/src/test/examples/contract.ts"); | ||
const contractDefinition = cleanse(result); | ||
const hash0 = hashContractDefinition(contractDefinition); | ||
const hash1 = hashContractDefinition(contractDefinition); | ||
expect(hash0).toEqual(hash1); | ||
}); | ||
|
||
it("returns a new hash when a new endpoint is added", () => { | ||
const result = parse("./lib/src/test/examples/contract.ts"); | ||
const contractDefinition = cleanse(result); | ||
|
||
const hash0 = hashContractDefinition(contractDefinition); | ||
|
||
const endpointDefinition: EndpointDefinition = { | ||
name: "testEndpoint", | ||
description: "test endpoint", | ||
tags: ["test"], | ||
method: "GET", | ||
path: "/test", | ||
request: { | ||
headers: [], | ||
pathParams: [], | ||
queryParams: [], | ||
body: { | ||
description: "test response body", | ||
type: { | ||
kind: TypeKind.STRING | ||
} | ||
} | ||
}, | ||
responses: [], | ||
tests: [] | ||
}; | ||
|
||
contractDefinition.endpoints.push(endpointDefinition); | ||
|
||
const hash1 = hashContractDefinition(contractDefinition); | ||
|
||
expect(hash0).not.toEqual(hash1); | ||
}); | ||
|
||
it("returns a new hash when an endpoint request body gets udpated", () => { | ||
const result = parse("./lib/src/test/examples/contract.ts"); | ||
const contractDefinition = cleanse(result); | ||
|
||
const hash0 = hashContractDefinition(contractDefinition); | ||
|
||
contractDefinition.endpoints[0].request.body = { | ||
type: { | ||
kind: TypeKind.STRING | ||
} | ||
}; | ||
|
||
const hash1 = hashContractDefinition(contractDefinition); | ||
|
||
expect(hash0).not.toEqual(hash1); | ||
}); | ||
|
||
it("returns a new hash when an endpoint response body gets udpated", () => { | ||
const result = parse("./lib/src/test/examples/contract.ts"); | ||
const contractDefinition = cleanse(result); | ||
|
||
const hash0 = hashContractDefinition(contractDefinition); | ||
|
||
contractDefinition.endpoints[0].responses[0].description = | ||
"test response"; | ||
|
||
const hash1 = hashContractDefinition(contractDefinition); | ||
|
||
expect(hash0).not.toEqual(hash1); | ||
}); | ||
|
||
it("returns a new hash when an endpoint request header gets udpated", () => { | ||
const result = parse("./lib/src/test/examples/contract.ts"); | ||
const contractDefinition = cleanse(result); | ||
|
||
const hash0 = hashContractDefinition(contractDefinition); | ||
|
||
contractDefinition.endpoints[0].request.headers[0].description = | ||
"test request header"; | ||
|
||
const hash1 = hashContractDefinition(contractDefinition); | ||
|
||
expect(hash0).not.toEqual(hash1); | ||
}); | ||
|
||
it("returns a new hash when an endpoint request parameter gets udpated", () => { | ||
const result = parse("./lib/src/test/examples/contract.ts"); | ||
const contractDefinition = cleanse(result); | ||
|
||
const hash0 = hashContractDefinition(contractDefinition); | ||
|
||
contractDefinition.endpoints[0].request.pathParams[0].description = | ||
"test path parameter"; | ||
|
||
const hash1 = hashContractDefinition(contractDefinition); | ||
|
||
expect(hash0).not.toEqual(hash1); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
import { createHash } from "crypto"; | ||
import { ContractDefinition } from "../models/definitions"; | ||
|
||
export function hashContractDefinition( | ||
contractDefinition: ContractDefinition | ||
): string { | ||
// Remove unnecessary whitespace and aim to make the output JSON-formatting independent. | ||
const contractDefinitionString = JSON.stringify(contractDefinition).replace( | ||
/\s/g, | ||
"" | ||
); | ||
|
||
const hash = createHash("sha1") | ||
.update(contractDefinitionString) | ||
.digest("hex"); | ||
|
||
return hash; | ||
} |