Skip to content

Commit e3393b7

Browse files
committed
feat(errors): create proper error classes
1 parent 3ed93c9 commit e3393b7

File tree

4 files changed

+125
-19
lines changed

4 files changed

+125
-19
lines changed

src/generate-method-id.ts

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,37 @@ const makeNotFoundError = (method: types.MethodObject, contentDescriptor: types.
1111
return new Error(errorMessage);
1212
};
1313

14+
/**
15+
* Create a unique identifier for a parameter within a given method.
16+
* This is typically used to create hashmap keys for method to parameter mappings.
17+
*
18+
* @param method The OpenRPC Method which encloses the content descriptor
19+
* @param contentDescriptor The OpenRPC Content Descriptor that is a param in the method
20+
*
21+
* @returns an ID for the param/method combo.
22+
* It follows the format `{method.name}/{indexWithinParams}|{contentDescriptor.name}` where:
23+
* 1. if the method's parameter structure is "by-name", the format returned uses the contentDescriptor.name
24+
* 1. otherwise, the return value will use the params index in the list of params.
25+
*
26+
* @example
27+
* ```typescript
28+
*
29+
* const { generateMethodParamId }
30+
* const methodObject = {
31+
* name: "foo",
32+
* params: [{
33+
* name: "fooParam",
34+
* schema: { type: "integer" }
35+
* }],
36+
* result: {}
37+
* };
38+
* const paramId = generateMethodParamId(methodObject, methodObject.params[0]);
39+
* console.log(paramId);
40+
* // outputs:
41+
* // "foo/0/fooParam"
42+
* ```
43+
*
44+
*/
1445
export function generateMethodParamId(
1546
method: types.MethodObject,
1647
contentDescriptor: types.ContentDescriptorObject,
@@ -25,6 +56,35 @@ export function generateMethodParamId(
2556
return `${method.name}/${paramId}`;
2657
}
2758

59+
/**
60+
* Create a unique identifier for a result within a given method.
61+
* This is typically used to create hashmap keys for method to result mappings.
62+
*
63+
* @param method The OpenRPC Method which encloses the content descriptor
64+
* @param contentDescriptor The OpenRPC Content Descriptor (either a method param or the result).
65+
*
66+
* @returns an ID for the result/method combo.
67+
* It follows the format `{method.name}/result`.
68+
*
69+
* @example
70+
* ```typescript
71+
*
72+
* const { generateMethodResultId }
73+
* const methodObject = {
74+
* name: "foo",
75+
* params: [],
76+
* result: {
77+
* name: "fooResult",
78+
* schema: { type: "string" }
79+
* }
80+
* };
81+
* const resultId = generateMethodResultId(methodObject, methodObject.result);
82+
* console.log(paramId);
83+
* // outputs:
84+
* // "foo/result"
85+
* ```
86+
*
87+
*/
2888
export function generateMethodResultId(
2989
method: types.MethodObject,
3090
contentDescriptor: types.ContentDescriptorObject,

src/get-open-rpc-document.ts

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,6 @@ import { types } from "@open-rpc/meta-schema";
44

55
type TGetOpenRPCDocument = (schema: string) => Promise<types.OpenRPC>;
66

7-
/**
8-
*
9-
*/
107
const fetchUrlSchemaFile: TGetOpenRPCDocument = async (schema) => {
118
try {
129
const response = await fetch(schema);
@@ -16,9 +13,6 @@ const fetchUrlSchemaFile: TGetOpenRPCDocument = async (schema) => {
1613
}
1714
};
1815

19-
/**
20-
*
21-
*/
2216
const readSchemaFromFile = async (schema: string) => {
2317
try {
2418
return await readJson(schema);

src/parse-open-rpc-document.ts

Lines changed: 34 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,35 @@
11
import { pathExists } from "fs-extra";
22
import refParser from "json-schema-ref-parser";
3-
import validateOpenRPCDocument from "./validate-open-rpc-document";
3+
import validateOpenRPCDocument, { OpenRPCDocumentValidationError } from "./validate-open-rpc-document";
44
import { types } from "@open-rpc/meta-schema";
55
import isUrl = require("is-url");
66
import { fetchUrlSchemaFile, readSchemaFromFile } from "./get-open-rpc-document";
77

8+
/**
9+
* @ignore
10+
*/
811
const cwd = process.cwd();
912

13+
/**
14+
* @ignore
15+
*/
1016
const isJson = (jsonString: string) => {
1117
try { JSON.parse(jsonString); return true; } catch (e) { return false; }
1218
};
1319

20+
/**
21+
* Provides an error interface for OpenRPC Document dereferencing problems
22+
*/
23+
export class OpenRPCDocumentDereferencingError extends Error {
24+
25+
/**
26+
* @param e The error that originated from jsonSchemaRefParser
27+
*/
28+
constructor(e: Error) {
29+
super(`The json schema provided cannot be dereferenced. Received Error: \n ${e.message}`);
30+
}
31+
}
32+
1433
/**
1534
* Resolves an OpenRPC document from a variety of input types. The resolved OpenRPC document
1635
* will be dereferenced and validated against the [meta-schema](https://github.com/open-rpc/meta-schema).
@@ -23,6 +42,11 @@ const isJson = (jsonString: string) => {
2342
* 2. schema is a url that resolves to an OpenRPC document.
2443
* 3. schema is a file path, where the file at the path contains an OpenRPC document.
2544
*
45+
* @returns The same OpenRPC Document that was passed in, but with all $ref's dereferenced.
46+
*
47+
* @throws [[OpenRPCDocumentValidationError]]
48+
* @throws [[OpenRPCDocumentDereferencingError]]
49+
*
2650
* @example
2751
* ```typescript
2852
*
@@ -56,15 +80,15 @@ export default async function parseOpenRPCDocument(
5680
parsedSchema = await readSchemaFromFile(schema as string);
5781
}
5882

59-
const errors = validateOpenRPCDocument(parsedSchema);
60-
if (errors) {
61-
throw new Error(`Error Validating schema against meta-schema. \n ${JSON.stringify(errors, undefined, " ")}`);
62-
}
83+
const isValid = validateOpenRPCDocument(parsedSchema);
6384

64-
try {
65-
const openrpcDocument = await refParser.dereference(parsedSchema) as types.OpenRPC;
66-
return openrpcDocument;
67-
} catch (e) {
68-
throw new Error(`The json schema provided cannot be dereferenced. Received Error: \n ${e.message}`);
85+
if (isValid === true) {
86+
try {
87+
return await refParser.dereference(parsedSchema) as types.OpenRPC;
88+
} catch (e) {
89+
throw new OpenRPCDocumentDereferencingError(e);
90+
}
91+
} else {
92+
throw isValid as OpenRPCDocumentValidationError;
6993
}
7094
}

src/validate-open-rpc-document.ts

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,35 @@ import metaSchema, { types } from "@open-rpc/meta-schema";
22
import JsonSchemaDraft07 from "../lib/json-schema-draft-07.json";
33
import Ajv from "ajv";
44

5+
/**
6+
* @ignore
7+
*/
58
const ajv = new Ajv();
69
ajv.addMetaSchema(JsonSchemaDraft07, "https://json-schema.org/draft-07/schema#");
710

11+
/**
12+
* Provides an error interface for OpenRPC Document validation
13+
*/
14+
export class OpenRPCDocumentValidationError extends Error {
15+
16+
/**
17+
* @param errors The errors received by ajv.errors.
18+
*/
19+
constructor(private errors: Ajv.ErrorObject[]) {
20+
super([
21+
"Error validating OpenRPC Document against @open-rpc/meta-schema.",
22+
"The errors found are as follows:",
23+
JSON.stringify(errors, undefined, " "),
24+
].join("\n"));
25+
}
26+
}
27+
828
/**
929
* Returns any JSON Schema validation errors that are found with the OpenRPC document passed in.
1030
*
11-
* @param document OpenRPC Document to validate
31+
* @param document OpenRPC Document to validate.
32+
*
33+
* @returns Either true if everything checks out, or a well formatted error.
1234
*
1335
* @example
1436
* ```typescript
@@ -23,8 +45,14 @@ ajv.addMetaSchema(JsonSchemaDraft07, "https://json-schema.org/draft-07/schema#")
2345
* ```
2446
*
2547
*/
26-
export default function validateOpenRPCDocument(document: types.OpenRPC): Ajv.ErrorObject[] | null | undefined {
48+
export default function validateOpenRPCDocument(
49+
document: types.OpenRPC,
50+
): OpenRPCDocumentValidationError | true {
2751
const result = ajv.validate(metaSchema, document);
2852

29-
return ajv.errors;
53+
if (ajv.errors) {
54+
return new OpenRPCDocumentValidationError(ajv.errors);
55+
} else {
56+
return true;
57+
}
3058
}

0 commit comments

Comments
 (0)