Initial commit
This commit is contained in:
@@ -0,0 +1,268 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <hello@stalw.art>
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
||||
*/
|
||||
|
||||
import type { Schema, ObjectTypeObject, ObjectTypeSingleton, Fields, Form, List } from '@/types/schema';
|
||||
|
||||
export interface ResolvedObject {
|
||||
viewName: string;
|
||||
objectName: string;
|
||||
objectType: ObjectTypeObject | ObjectTypeSingleton;
|
||||
permissionPrefix: string;
|
||||
enterprise: boolean;
|
||||
}
|
||||
|
||||
export function resolveObject(schema: Schema, viewName: string): ResolvedObject | null {
|
||||
const entry = schema.objects[viewName];
|
||||
if (!entry) return null;
|
||||
|
||||
if (entry.type === 'view') {
|
||||
const parent = schema.objects[entry.objectName];
|
||||
if (!parent || parent.type === 'view') return null;
|
||||
return {
|
||||
viewName,
|
||||
objectName: entry.objectName,
|
||||
objectType: parent,
|
||||
permissionPrefix: parent.permissionPrefix,
|
||||
enterprise: parent.enterprise ?? false,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
viewName,
|
||||
objectName: viewName,
|
||||
objectType: entry,
|
||||
permissionPrefix: entry.permissionPrefix,
|
||||
enterprise: entry.enterprise ?? false,
|
||||
};
|
||||
}
|
||||
|
||||
export interface ResolvedSingleSchema {
|
||||
type: 'single';
|
||||
schemaName: string;
|
||||
fields: Fields;
|
||||
}
|
||||
|
||||
export interface ResolvedMultipleSchema {
|
||||
type: 'multiple';
|
||||
variants: {
|
||||
name: string;
|
||||
label: string;
|
||||
schemaName?: string;
|
||||
fields: Fields | null;
|
||||
}[];
|
||||
}
|
||||
|
||||
export type ResolvedSchema = ResolvedSingleSchema | ResolvedMultipleSchema;
|
||||
|
||||
export function resolveSchema(schema: Schema, objectName: string): ResolvedSchema | null {
|
||||
const schemaEntry = schema.schemas[objectName];
|
||||
if (!schemaEntry) return null;
|
||||
|
||||
if (schemaEntry.type === 'single') {
|
||||
const fields = schema.fields[schemaEntry.schemaName];
|
||||
if (!fields) return null;
|
||||
return { type: 'single', schemaName: schemaEntry.schemaName, fields };
|
||||
}
|
||||
|
||||
return {
|
||||
type: 'multiple',
|
||||
variants: schemaEntry.variants.map((v) => ({
|
||||
name: v.name,
|
||||
label: v.label,
|
||||
schemaName: v.schemaName,
|
||||
fields: v.schemaName ? (schema.fields[v.schemaName] ?? null) : null,
|
||||
})),
|
||||
};
|
||||
}
|
||||
|
||||
export function resolveList(schema: Schema, viewName: string, objectName: string): List | null {
|
||||
return schema.lists[viewName] ?? schema.lists[objectName] ?? null;
|
||||
}
|
||||
|
||||
export function resolveForm(schema: Schema, viewName: string, objectName: string, schemaName: string): Form | null {
|
||||
return schema.forms[viewName] ?? schema.forms[objectName] ?? schema.forms[schemaName] ?? null;
|
||||
}
|
||||
|
||||
export function resolveVariantForm(
|
||||
schema: Schema,
|
||||
_viewName: string,
|
||||
_objectName: string,
|
||||
variantSchemaName?: string,
|
||||
): Form | null {
|
||||
if (variantSchemaName && schema.forms[variantSchemaName]) {
|
||||
return schema.forms[variantSchemaName];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export function deepMerge(target: Record<string, unknown>, source: Record<string, unknown>): Record<string, unknown> {
|
||||
const result = { ...target };
|
||||
for (const key of Object.keys(source)) {
|
||||
const sourceVal = source[key];
|
||||
const targetVal = result[key];
|
||||
if (
|
||||
sourceVal &&
|
||||
typeof sourceVal === 'object' &&
|
||||
!Array.isArray(sourceVal) &&
|
||||
targetVal &&
|
||||
typeof targetVal === 'object' &&
|
||||
!Array.isArray(targetVal)
|
||||
) {
|
||||
result[key] = deepMerge(targetVal as Record<string, unknown>, sourceVal as Record<string, unknown>);
|
||||
} else {
|
||||
result[key] = sourceVal;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
export function buildCreateDefaults(
|
||||
schema: Schema,
|
||||
resolvedObject: ResolvedObject,
|
||||
resolvedSchema: ResolvedSchema,
|
||||
variantName?: string,
|
||||
filtersStatic?: Record<string, unknown>,
|
||||
): Record<string, unknown> {
|
||||
let defaults: Record<string, unknown> = {};
|
||||
let activeFields: Fields | null = null;
|
||||
|
||||
if (resolvedSchema.type === 'single') {
|
||||
activeFields = resolvedSchema.fields;
|
||||
const childDefaults = resolvedSchema.fields.defaults ?? {};
|
||||
defaults = deepMerge(defaults, childDefaults);
|
||||
} else if (resolvedSchema.type === 'multiple' && variantName) {
|
||||
const variant = resolvedSchema.variants.find((v) => v.name === variantName);
|
||||
activeFields = variant?.fields ?? null;
|
||||
if (variant?.fields?.defaults) {
|
||||
defaults = deepMerge(defaults, variant.fields.defaults);
|
||||
}
|
||||
defaults['@type'] = variantName;
|
||||
}
|
||||
|
||||
const parentSchemaEntry = schema.schemas[resolvedObject.objectName];
|
||||
if (parentSchemaEntry?.type === 'single') {
|
||||
const parentFields = schema.fields[parentSchemaEntry.schemaName];
|
||||
if (parentFields?.defaults) {
|
||||
defaults = deepMerge(defaults, parentFields.defaults);
|
||||
}
|
||||
}
|
||||
|
||||
if (activeFields) {
|
||||
for (const [propName, propDef] of Object.entries(activeFields.properties)) {
|
||||
const t = propDef.type;
|
||||
if (t.type !== 'object') continue;
|
||||
|
||||
if (defaults[propName] === null) continue;
|
||||
|
||||
const parentChildOverride =
|
||||
defaults[propName] && typeof defaults[propName] === 'object' && !Array.isArray(defaults[propName])
|
||||
? (defaults[propName] as Record<string, unknown>)
|
||||
: {};
|
||||
|
||||
const nestedSchemaEntry = schema.schemas[t.objectName];
|
||||
let nestedVariant: string | undefined;
|
||||
if (nestedSchemaEntry?.type === 'multiple') {
|
||||
nestedVariant = (parentChildOverride['@type'] as string | undefined) ?? nestedSchemaEntry.variants[0]?.name;
|
||||
}
|
||||
|
||||
const nested = buildEmbeddedDefaults(schema, t.objectName, parentChildOverride, nestedVariant);
|
||||
|
||||
if (Object.keys(nested).length > 0) {
|
||||
defaults[propName] = nested;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (filtersStatic && Object.keys(filtersStatic).length > 0) {
|
||||
const validKeys = new Set<string>(['@type']);
|
||||
if (activeFields) {
|
||||
for (const k of Object.keys(activeFields.properties)) validKeys.add(k);
|
||||
}
|
||||
if (resolvedSchema.type === 'multiple') {
|
||||
const parentSchemaName =
|
||||
parentSchemaEntry?.type === 'single' ? parentSchemaEntry.schemaName : resolvedObject.objectName;
|
||||
const parentProps = schema.fields[parentSchemaName]?.properties;
|
||||
if (parentProps) {
|
||||
for (const k of Object.keys(parentProps)) validKeys.add(k);
|
||||
}
|
||||
}
|
||||
|
||||
const filtered: Record<string, unknown> = {};
|
||||
for (const [k, v] of Object.entries(filtersStatic)) {
|
||||
if (validKeys.has(k)) filtered[k] = v;
|
||||
}
|
||||
if (Object.keys(filtered).length > 0) {
|
||||
defaults = deepMerge(defaults, filtered);
|
||||
}
|
||||
}
|
||||
|
||||
return defaults;
|
||||
}
|
||||
|
||||
export function buildEmbeddedDefaults(
|
||||
schema: Schema,
|
||||
objectName: string,
|
||||
parentOverrides: Record<string, unknown> = {},
|
||||
variantName?: string,
|
||||
): Record<string, unknown> {
|
||||
const schemaEntry = schema.schemas[objectName];
|
||||
if (!schemaEntry) return { ...parentOverrides };
|
||||
|
||||
let fields: Fields | null = null;
|
||||
let result: Record<string, unknown> = {};
|
||||
|
||||
if (schemaEntry.type === 'single') {
|
||||
fields = schema.fields[schemaEntry.schemaName] ?? null;
|
||||
} else {
|
||||
const variant = variantName ? schemaEntry.variants.find((v) => v.name === variantName) : schemaEntry.variants[0];
|
||||
if (!variant) return { '@type': variantName ?? '', ...parentOverrides };
|
||||
result['@type'] = variant.name;
|
||||
if (variant.schemaName) {
|
||||
fields = schema.fields[variant.schemaName] ?? null;
|
||||
}
|
||||
}
|
||||
|
||||
if (fields) {
|
||||
if (fields.defaults) {
|
||||
result = deepMerge(result, fields.defaults);
|
||||
}
|
||||
|
||||
for (const [propName, propDef] of Object.entries(fields.properties)) {
|
||||
const t = propDef.type;
|
||||
if (t.type !== 'object') continue;
|
||||
|
||||
const parentChildOverride =
|
||||
result[propName] && typeof result[propName] === 'object' && !Array.isArray(result[propName])
|
||||
? (result[propName] as Record<string, unknown>)
|
||||
: {};
|
||||
|
||||
const nestedSchemaEntry = schema.schemas[t.objectName];
|
||||
let nestedVariant: string | undefined;
|
||||
if (nestedSchemaEntry?.type === 'multiple') {
|
||||
nestedVariant = (parentChildOverride['@type'] as string | undefined) ?? nestedSchemaEntry.variants[0]?.name;
|
||||
}
|
||||
|
||||
const nestedDefaults = buildEmbeddedDefaults(schema, t.objectName, parentChildOverride, nestedVariant);
|
||||
|
||||
if (Object.keys(nestedDefaults).length > 0) {
|
||||
result[propName] = nestedDefaults;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Object.keys(parentOverrides).length > 0) {
|
||||
result = deepMerge(result, parentOverrides);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
export function getDisplayProperty(schema: Schema, objectName: string): string {
|
||||
const list = schema.lists[objectName];
|
||||
if (list?.labelProperty) return list.labelProperty;
|
||||
if (list?.columns?.[0]) return list.columns[0].name;
|
||||
return 'name';
|
||||
}
|
||||
Reference in New Issue
Block a user