Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/schema/applyRules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ function evalJsonCheck(
}

const json: Record<string, unknown> = sidecarRule ? context.sidecar : context.json
for (const [key, requirement] of Object.entries(rule.fields)) {
for (const [key, requirement] of Object.entries(rule.fields ?? {})) {
// @ts-expect-error dynamic nested index access on GenericSchema is not typed
const metadataDef = schema.objects.metadata[key]
const keyName: string = metadataDef.name
Expand Down
33 changes: 33 additions & 0 deletions src/schema/pruneSchema.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { assert } from '@std/assert'
import type { GenericRule, GenericSchema } from '../types/schema.ts'
import { loadSchema } from '../setup/loadSchema.ts'
import { pruneSchema, type SchemaFilter } from './pruneSchema.ts'

Deno.test('Test schema pruning', async (t) => {
const schema = await loadSchema() as unknown as GenericSchema
await t.step('Test delete path', async () => {
const path = 'rules.sidecars'
const filter: SchemaFilter = {path}
assert(schema[path])
const newSchema = pruneSchema(schema, [filter])
assert(schema[path])
assert(newSchema[path] == undefined)
})
await t.step('Test delete matching rules', async () => {
const path = 'rules.sidecars'
const testPath = `${path}.derivatives.atlas.TemplateNonStandard`
const filter: SchemaFilter = {
path,
match: {
selectors: [
'dataset.dataset_description.DatasetType == "derivative"'
]
}
}
assert(schema[testPath])
const newSchema = pruneSchema(schema, [filter])
assert(schema[path])
assert(schema[testPath])
assert(newSchema[testPath] == undefined)
})
})
67 changes: 67 additions & 0 deletions src/schema/pruneSchema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import type { GenericRule, GenericSchema } from '../types/schema.ts'
import { objectPathHandler } from '../utils/objectPathHandler.ts'

export interface SchemaFilter {
path?: string,
match?: Partial<GenericRule>,
update?: Partial<GenericRule>
}

export function pruneSchema(
schema: GenericSchema,
filters: SchemaFilter[]
): GenericSchema {
let newSchema = JSON.parse(JSON.stringify(schema))
newSchema = new Proxy( newSchema as object, objectPathHandler) as GenericSchema
filters.map(applyFilter.bind(null, newSchema))
return newSchema
}

function applyFilter(
schema: GenericSchema,
filter: SchemaFilter
): GenericSchema {
if (filter.path && !filter.match) {
if (filter.update) {
Object.assign(schema[filter.path], filter.update)
} else {
delete schema[filter.path]
}
return schema
}
const path = filter.path ?? 'rules'
_applyFilter(schema, filter, path)
return schema
}

function _applyFilter(
schema: GenericSchema,
filter: SchemaFilter,
path: string
) {
if (typeof schema[path] != 'object') {
return
}
if (filter.match && ruleMatch(schema[path], filter.match)) {
if (filter.update) {
Object.assign(schema[path], filter.update)
} else {
delete schema[path]
}
}
for (const key in schema[path]) {
_applyFilter(schema, filter, `${path}.${key}`)
}
return
}

function ruleMatch(rule: GenericRule, match: Partial<GenericRule>): boolean {
return (Object.keys(match) as (keyof GenericRule)[]).every((key) => {
if (typeof rule[key] == 'undefined') return false
if (Array.isArray(match[key])) {
return match[key].every(entry => Array.isArray(rule[key]) && rule[key].includes(entry))
}
// TODO matches for rule fields that are strings or records
return false
})
}
2 changes: 1 addition & 1 deletion src/types/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ export interface GenericRule {
columns?: Record<string, string>
additional_columns?: string
initial_columns?: string[]
fields: Record<string, SchemaFields>
fields?: Record<string, SchemaFields>
issue?: SchemaIssue
extensions?: string[]
suffixes?: string[]
Expand Down
19 changes: 19 additions & 0 deletions src/utils/objectPathHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,23 @@ export const objectPathHandler = {
}
return res
},
deleteProperty(target: unknown, property: string): boolean {
if (typeof property === 'symbol') {
return true
}
const props = property.split('.')
const toDelete = props.pop()
if (toDelete === undefined) {
return true
}
let parent = target
for (const prop of props) {
if (hasProp(parent, prop)) {
parent = parent[prop]
} else {
return true
}
}
return delete (parent as Record<string, unknown>)[toDelete]
}
}
Loading