|
|
|
@ -1,10 +1,59 @@ |
|
|
|
export const convertJSONSchemaToTableSchema = ( |
|
|
|
jsonSchema, |
|
|
|
squashObjects = false |
|
|
|
) => { |
|
|
|
/** |
|
|
|
* Gets the schema for a datasource which is targeting a JSON array, including |
|
|
|
* nested JSON arrays. The returned schema is a squashed, table-like schema |
|
|
|
* which is fully compatible with the rest of the platform. |
|
|
|
* @param tableSchema the full schema for the table this JSON field is in |
|
|
|
* @param datasource the datasource configuration |
|
|
|
*/ |
|
|
|
export const getJSONArrayDatasourceSchema = (tableSchema, datasource) => { |
|
|
|
let jsonSchema = tableSchema |
|
|
|
let keysToSchema = [] |
|
|
|
|
|
|
|
// If we are already deep inside a JSON field then we need to account
|
|
|
|
// for the keys that brought us here, so we can get the schema for the
|
|
|
|
// depth we're actually at
|
|
|
|
if (datasource.prefixKeys) { |
|
|
|
keysToSchema = datasource.prefixKeys.concat(["schema"]) |
|
|
|
} |
|
|
|
|
|
|
|
// We parse the label of the datasource to work out where we are inside
|
|
|
|
// the structure. We can use this to know which part of the schema
|
|
|
|
// is available underneath our current position.
|
|
|
|
keysToSchema = keysToSchema.concat(datasource.label.split(".").slice(2)) |
|
|
|
|
|
|
|
// Follow the JSON key path until we reach the schema for the level
|
|
|
|
// we are at
|
|
|
|
for (let i = 0; i < keysToSchema.length; i++) { |
|
|
|
jsonSchema = jsonSchema?.[keysToSchema[i]] |
|
|
|
if (jsonSchema?.schema) { |
|
|
|
jsonSchema = jsonSchema.schema |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// We need to convert the JSON schema into a more typical looking table
|
|
|
|
// schema so that it works with the rest of the platform
|
|
|
|
return convertJSONSchemaToTableSchema(jsonSchema, { |
|
|
|
squashObjects: true, |
|
|
|
prefixKeys: keysToSchema, |
|
|
|
}) |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* Converts a JSON field schema (or sub-schema of a nested field) into a schema |
|
|
|
* that looks like a typical table schema. |
|
|
|
* @param jsonSchema the JSON field schema or sub-schema |
|
|
|
* @param options |
|
|
|
*/ |
|
|
|
export const convertJSONSchemaToTableSchema = (jsonSchema, options) => { |
|
|
|
if (!jsonSchema) { |
|
|
|
return null |
|
|
|
} |
|
|
|
|
|
|
|
// Add default options
|
|
|
|
options = { squashObjects: false, prefixKeys: null, ...options } |
|
|
|
|
|
|
|
// Immediately strip the wrapper schema for objects, or wrap shallow values in
|
|
|
|
// a fake "value" schema
|
|
|
|
if (jsonSchema.schema) { |
|
|
|
jsonSchema = jsonSchema.schema |
|
|
|
} else { |
|
|
|
@ -12,34 +61,60 @@ export const convertJSONSchemaToTableSchema = ( |
|
|
|
value: jsonSchema, |
|
|
|
} |
|
|
|
} |
|
|
|
const keys = extractJSONSchemaKeys(jsonSchema, squashObjects) |
|
|
|
|
|
|
|
// Extract all deep keys from the schema
|
|
|
|
const keys = extractJSONSchemaKeys(jsonSchema, options.squashObjects) |
|
|
|
|
|
|
|
// Form a full schema from all the deep schema keys
|
|
|
|
let schema = {} |
|
|
|
keys.forEach(({ key, type }) => { |
|
|
|
schema[key] = { type, name: key } |
|
|
|
schema[key] = { type, name: key, prefixKeys: options.prefixKeys } |
|
|
|
}) |
|
|
|
return schema |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* Recursively builds paths to all leaf fields in a JSON field schema structure, |
|
|
|
* stopping when leaf nodes or arrays are reached. |
|
|
|
* @param jsonSchema the JSON field schema or sub-schema |
|
|
|
* @param squashObjects whether to recurse into objects or not |
|
|
|
*/ |
|
|
|
const extractJSONSchemaKeys = (jsonSchema, squashObjects = false) => { |
|
|
|
if (!jsonSchema || !Object.keys(jsonSchema).length) { |
|
|
|
return [] |
|
|
|
} |
|
|
|
|
|
|
|
// Iterate through every schema key
|
|
|
|
let keys = [] |
|
|
|
Object.keys(jsonSchema).forEach(key => { |
|
|
|
const type = jsonSchema[key].type |
|
|
|
|
|
|
|
// If we encounter an object, then only go deeper if we want to squash
|
|
|
|
// object paths
|
|
|
|
if (type === "json" && squashObjects) { |
|
|
|
// Find all keys within this objects schema
|
|
|
|
const childKeys = extractJSONSchemaKeys( |
|
|
|
jsonSchema[key].schema, |
|
|
|
squashObjects |
|
|
|
) |
|
|
|
|
|
|
|
// Append child paths onto the current path to build the full path
|
|
|
|
keys = keys.concat( |
|
|
|
childKeys.map(childKey => ({ |
|
|
|
key: `${key}.${childKey.key}`, |
|
|
|
type: childKey.type, |
|
|
|
})) |
|
|
|
) |
|
|
|
} else if (type !== "array") { |
|
|
|
keys.push({ key, type }) |
|
|
|
} |
|
|
|
|
|
|
|
// Otherwise add this as a lead node.
|
|
|
|
// We transform array types from "array" into "jsonarray" here to avoid
|
|
|
|
// confusion with the existing "array" type that represents a multi-select.
|
|
|
|
else { |
|
|
|
keys.push({ |
|
|
|
key, |
|
|
|
type: type === "array" ? "jsonarray" : type, |
|
|
|
}) |
|
|
|
} |
|
|
|
}) |
|
|
|
return keys |
|
|
|
|