Skip to content

Commit

Permalink
Minor restructuring for schema definition validation
Browse files Browse the repository at this point in the history
  • Loading branch information
javagl committed Sep 25, 2023
1 parent 7cbacb5 commit bdc6544
Show file tree
Hide file tree
Showing 4 changed files with 205 additions and 148 deletions.
95 changes: 0 additions & 95 deletions src/validation/SchemaResolver.ts

This file was deleted.

71 changes: 20 additions & 51 deletions src/validation/TilesetValidator.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { defined } from "3d-tiles-tools";
import { Buffers } from "3d-tiles-tools";

import { Validator } from "./Validator";
import { ValidationState } from "./ValidationState";
Expand All @@ -12,7 +11,7 @@ import { TilesetTraversingValidator } from "./TilesetTraversingValidator";
import { RootPropertyValidator } from "./RootPropertyValidator";
import { ExtendedObjectsValidators } from "./ExtendedObjectsValidators";

import { SchemaValidator } from "./metadata/SchemaValidator";
import { SchemaDefinitionValidator } from "./metadata/SchemaDefinitionValidator";
import { MetadataEntityValidator } from "./metadata/MetadataEntityValidator";

import { Tileset } from "3d-tiles-tools";
Expand All @@ -21,9 +20,7 @@ import { Group } from "3d-tiles-tools";

import { IoValidationIssues } from "../issues/IoValidationIssue";
import { StructureValidationIssues } from "../issues/StructureValidationIssues";
import { JsonValidationIssues } from "../issues/JsonValidationIssues";
import { SemanticValidationIssues } from "../issues/SemanticValidationIssues";
import { SchemaResolver } from "./SchemaResolver";

/**
* A class that can validate a 3D Tiles tileset.
Expand Down Expand Up @@ -136,35 +133,6 @@ export class TilesetValidator implements Validator<Tileset> {
}
}

// The schema and schemaUri MUST NOT be present at the same time
if (defined(tileset.schema) && defined(tileset.schemaUri)) {
const issue = JsonValidationIssues.ONE_OF_ERROR(
path,
"tileset",
"schema",
"schemaUri"
);
context.addIssue(issue);
result = false;
}

// Validate the schemaUri
const schemaUri = tileset.schemaUri;
const schemaUriPath = path + "/schemaUri";
if (defined(schemaUri)) {
// The schemaUri MUST be a string
if (
!BasicValidator.validateString(
schemaUriPath,
"schemaUri",
schemaUri,
context
)
) {
result = false;
}
}

// Create the ValidationState, and fill it with information
// about the presence of a schema, and the validated schema
// (if the schema is valid)
Expand All @@ -174,23 +142,25 @@ export class TilesetValidator implements Validator<Tileset> {
validatedGroups: undefined,
hasGroupsDefinition: false,
};
const schemaResult = await SchemaResolver.resolveSchema(
"",
tileset.schema,
tileset.schemaUri,
context
);
validationState.hasSchemaDefinition = schemaResult.hasSchemaDefinition;

// Validate the schema
const schema = schemaResult.schema;
const schemaPath = path + "/schema";
if (defined(schema)) {
if (SchemaValidator.validateSchema(schemaPath, schema, context)) {
validationState.validatedSchema = schema;
} else {
result = false;
}
const schemaDefinition =
await SchemaDefinitionValidator.validateSchemaDefinition(
path,
"tileset",
tileset.schema,
tileset.schemaUri,
context
);
validationState.hasSchemaDefinition = schemaDefinition.hasSchemaDefinition;
validationState.validatedSchema = schemaDefinition.validatedSchema;

// When there was a schema definition, but the schema
// itself was not valid, then the overall result
// is invalid
if (
schemaDefinition.hasSchemaDefinition &&
!defined(schemaDefinition.validatedSchema)
) {
result = false;
}

// Validate the groups.
Expand Down Expand Up @@ -458,7 +428,6 @@ export class TilesetValidator implements Validator<Tileset> {
return result;
}


/**
* Validates the given `tileset.groups`
*
Expand Down
184 changes: 184 additions & 0 deletions src/validation/metadata/SchemaDefinitionValidator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
import { defined } from "3d-tiles-tools";
import { Buffers } from "3d-tiles-tools";
import { Schema } from "3d-tiles-tools";

import { ValidationContext } from "../ValidationContext";
import { BasicValidator } from "../BasicValidator";
import { SchemaValidator } from "./SchemaValidator";

import { IoValidationIssues } from "../../issues/IoValidationIssue";
import { JsonValidationIssues } from "../../issues/JsonValidationIssues";

/**
* Utility class for validating the definition of a metadata schema.
*
* The metadata schema can either be defined as an inlined `schema`
* object in the JSON, or via a `schemUri` (but not both!).
* This class resolves and validates the schema from either of
* these sources, and returns information about that validation
* result.
*/
export class SchemaDefinitionValidator {
/**
* Validates the given schema definition.
*
* The returned object will contain two properties:
* - `hasSchemaDefintiion`: Whether any schema definition was given
* - `validatedSchema`: The validated `Schema` object
*
* If neither `schema` nor `schemaUri` are given, then the result
* will be `{false, undefined}`.
*
* If the `schema` and `schemaUri` are both given, then an error will be
* added to the given context, `hasSchemaDefintiion` will be `true`,
* and the `validatedSchema` will be `undefined`.
*
* If the `schemaUri` is given but invalid, then an error will be
* added to the given context, `hasSchemaDefintiion` will be `true`,
* and the `validatedSchema` will be `undefined`.
*
* If the `schema` was given, or the schema could be resolved from
* the `schemaUri`, then `hasSchemaDefintiion` will be `true`, and
* the `validatedSchema` will be defined if and only if the
* respective schema could be validated.
*
* @param path - The path for `ValidationIssue` instances
* @param name - A name for the containing object (usually 'tileset')
* @param schema - The `schema` object from the JSON
* @param schemaUri - The `schemaUri` from the JSON
* @param context - The `ValidatonContext`
* @returns The schema definition validation result
*/
static async validateSchemaDefinition(
path: string,
name: string,
schema: any,
schemaUri: any,
context: ValidationContext
): Promise<
{ hasSchemaDefinition: boolean; validatedSchema?: Schema }
> {
// The schema and schemaUri MUST NOT be present at the same time
if (defined(schema) && defined(schemaUri)) {
const issue = JsonValidationIssues.ONE_OF_ERROR(
path,
name,
"schema",
"schemaUri"
);
context.addIssue(issue);
return {
hasSchemaDefinition: true,
validatedSchema: undefined,
};
}

// Validate the schemaUri
const schemaUriPath = path + "/schemaUri";
if (defined(schemaUri)) {
// The schemaUri MUST be a string
if (
!BasicValidator.validateString(
schemaUriPath,
"schemaUri",
schemaUri,
context
)
) {
return {
hasSchemaDefinition: true,
validatedSchema: undefined,
};
}
}

// The schema to validate is either the given one,
// or the one that is resolved from the schemaUri
let schemaToValidate = undefined;
if (defined(schema)) {
schemaToValidate = schema;
} else if (defined(schemaUri)) {
const resolvedSchema = await SchemaDefinitionValidator.resolveSchema(
path,
schemaUri,
context
);
if (!defined(resolvedSchema)) {
return {
hasSchemaDefinition: true,
validatedSchema: undefined,
};
}
schemaToValidate = resolvedSchema;
} else {
// Neither the schema nor the schemaUri have been given
return {
hasSchemaDefinition: false,
validatedSchema: undefined,
};
}

// Validate the schema
let validatedSchema = undefined;
const schemaPath = path + "/schema";
if (defined(schemaToValidate)) {
if (
SchemaValidator.validateSchema(schemaPath, schemaToValidate, context)
) {
validatedSchema = schemaToValidate;
}
}

return {
hasSchemaDefinition: true,
validatedSchema: validatedSchema,
};
}

/**
* Resolves the schema from the given URI
*
* @param path - The path for validation issues
* @param schemaUri - The schema URI
* @param context - The `ValidationContext`
* @returns A promise that resolves with the result object,
* or `undefined` if the schema could not be resolved
*/
private static async resolveSchema(
path: string,
schemaUri: any,
context: ValidationContext
): Promise<Schema | undefined> {
if (defined(schemaUri) && typeof schemaUri === "string") {
const resourceResolver = context.getResourceResolver();
const schemaBuffer = await resourceResolver.resolveData(schemaUri);
if (!defined(schemaBuffer)) {
const schemaUriPath = path + "/schemaUri";
const message = `The 'schemaUri' is '${schemaUri}' and could not be resolved`;
const issue = IoValidationIssues.IO_ERROR(schemaUriPath, message);
context.addIssue(issue);
return undefined;
}

const bom = Buffers.getUnicodeBOMDescription(schemaBuffer);
if (defined(bom)) {
const message = `Unexpected BOM in schema JSON buffer: ${bom}`;
const issue = IoValidationIssues.IO_ERROR(schemaUri, message);
context.addIssue(issue);
return undefined;
}

const schemaString = schemaBuffer.toString();
try {
const resolvedSchema = JSON.parse(schemaString);
return resolvedSchema;
} catch (error) {
//console.log(error);
const issue = IoValidationIssues.JSON_PARSE_ERROR(path, `${error}`);
context.addIssue(issue);
return undefined;
}
}
return undefined;
}
}
Loading

0 comments on commit bdc6544

Please sign in to comment.