From fbd1fe1f664fb778705b8b82a24e18e94ca20ad8 Mon Sep 17 00:00:00 2001 From: Mark Probst Date: Tue, 26 Jun 2018 13:24:22 -0700 Subject: [PATCH] Documentation --- doc/Transformers.md | 29 +++++++++++++++++++++++++++ src/cli/index.ts | 2 ++ src/quicktype-core/StringTypes.ts | 7 +++++++ src/quicktype-core/Type.ts | 8 ++++++++ src/quicktype-core/language/CSharp.ts | 4 ++++ test/fixtures.ts | 5 ++++- 6 files changed, 54 insertions(+), 1 deletion(-) create mode 100644 doc/Transformers.md diff --git a/doc/Transformers.md b/doc/Transformers.md new file mode 100644 index 0000000000..25bed649f2 --- /dev/null +++ b/doc/Transformers.md @@ -0,0 +1,29 @@ +# Transformed string types + +quicktype has facilities for transforming JSON strings into other data types, provided a given target language implementation supports it. Right now the most advanced target language in that regard is C#, which supports data/time types and stringified integers. + +There are two different sorts of transformed string types: + +- Those that transform strings into other JSON-representable types, such as stringified integers. + +- Those that transform into types that are not directly representable in JSON (other than as strings), such as date/time types. + +Several pieces need to be implemented to add a new transformed string type: + +1. `TransformedStringTypeTargets` have to be added for the new type kind in the object `transformedStringTypeTargetTypeKinds` in `Type.ts`. The property names of that object are the internal primitive type names. Adding a new property automatically adds a new type. + +2. `inferTransformedStringTypeKindForString` in `StringTypes.ts` can optionally be amended to automatically infer the new transformed type from a given string in JSON input. If this isn't done, the only way to use the new type is through JSON Schema. If it is done, a CLI option to turn it off has to be added. This is currently still more complicated than it needs to be, since it also involves changing the options to `quicktype-core`. + +3. The target languages that should support the new type have to be amended. Currently C# is the only one that allows this easily. See `CSharp.ts` and search for `date-time`. + +4. Test cases have to be added. See `test/inputs/schema/date-time*`, for example. If JSON inference is implemented, there should be at least one JSON test file with a string of that type. + +5. In `fixtures.ts`, `ajv`, which we use to validate JSON against schemas, has to be told about the new JSON Schema string format. Search for `date-time`. + +## Stuff we need to improve + +- Automatic generation of tests. We should have a test generator that produces test files with all string types in all reasonable combinations. + +- One CLI option for all string types. No CLI option work should be necessary to implement a new string type. + +- The AJV thing should be automated, too. We have almost all the validation code necessary in `StringTypes.ts` anyway. diff --git a/src/cli/index.ts b/src/cli/index.ts index 1ff4c3a1a5..86d03bcae5 100644 --- a/src/cli/index.ts +++ b/src/cli/index.ts @@ -385,6 +385,8 @@ function makeOptionDefinitions(targetLanguages: TargetLanguage[]): OptionDefinit type: Boolean, description: "Don't infer enums, always use strings." }, + // We're getting to the point where we should have just one CLI option for + // disabling transformed string types, but right now we have one per type. { name: "no-date-times", type: Boolean, diff --git a/src/quicktype-core/StringTypes.ts b/src/quicktype-core/StringTypes.ts index 8b165242eb..62f00af653 100644 --- a/src/quicktype-core/StringTypes.ts +++ b/src/quicktype-core/StringTypes.ts @@ -177,6 +177,13 @@ function isIntegerString(s: string): boolean { return i >= MIN_INTEGER_STRING && i <= MAX_INTEGER_STRING; } +/** + * JSON inference calls this function to figure out whether a given string is to be + * transformed into a higher level type. Must return undefined if not, otherwise the + * type kind of the transformed string type. + * + * @param s The string for which to determine the transformed string type kind. + */ export function inferTransformedStringTypeKindForString(s: string): TransformedStringTypeKind | undefined { if (s.length === 0 || "0123456789-".indexOf(s[0]) < 0) return undefined; diff --git a/src/quicktype-core/Type.ts b/src/quicktype-core/Type.ts index d43aeceeea..85b38c023a 100644 --- a/src/quicktype-core/Type.ts +++ b/src/quicktype-core/Type.ts @@ -28,6 +28,14 @@ import { TypeAttributes } from "./TypeAttributes"; import { messageAssert } from "./Messages"; import { TypeRef, attributesForTypeRef, derefTypeRef, TypeGraph, typeRefIndex } from "./TypeGraph"; +/** + * `jsonSchema` is the `format` to be used to represent this string type in + * JSON Schema. It's ok to "invent" a new one if the JSON Schema standard doesn't + * have that particular type yet. + * + * For transformed type kinds that map to an existing primitive type, `primitive` + * must specify that type kind. + */ export type TransformedStringTypeTargets = { jsonSchema: string; primitive: PrimitiveNonStringTypeKind | undefined; diff --git a/src/quicktype-core/language/CSharp.ts b/src/quicktype-core/language/CSharp.ts index 4717faff15..d26f57cbe4 100644 --- a/src/quicktype-core/language/CSharp.ts +++ b/src/quicktype-core/language/CSharp.ts @@ -89,6 +89,10 @@ function alwaysApplyTransformation(xf: Transformation): boolean { return false; } +/** + * The C# type for a given transformed string type. The function can + * assume that it will only be called for type kinds that + */ function csTypeForTransformedStringType(t: PrimitiveType): Sourcelike { if (t.kind === "date-time") { return "DateTimeOffset"; diff --git a/test/fixtures.ts b/test/fixtures.ts index 68c5d8fbbb..63038517ed 100644 --- a/test/fixtures.ts +++ b/test/fixtures.ts @@ -369,7 +369,10 @@ class JSONSchemaJSONFixture extends JSONToXToYFixture { let schema = JSON.parse(fs.readFileSync(this.language.output, "utf8")); let ajv = new Ajv({ format: "full", unknownFormats: ["integer"] }); - // Make Ajv's date-time compatible with what we recognize + // Make Ajv's date-time compatible with what we recognize. All non-standard + // JSON formats that we use for transformed type kinds must be registered here + // with a validation function. + // FIXME: Unify this with what's in StringTypes.ts. ajv.addFormat("date-time", isDateTime); let valid = ajv.validate(schema, input); if (!valid) {