Skip to content

Instantly share code, notes, and snippets.

@mnikn
Created January 26, 2024 01:50
Show Gist options
  • Save mnikn/bc7ec4d7e7011d38d1d31fb2a57c0f18 to your computer and use it in GitHub Desktop.
Save mnikn/bc7ec4d7e7011d38d1d31fb2a57c0f18 to your computer and use it in GitHub Desktop.
import dayjs, { Dayjs } from 'dayjs';
import { get } from 'lodash';
type FormatOptions = {
reliable: boolean;
};
type BaseSchemaField = {
type: SchemaFieldType;
source?: ((parentSourceData: any, rootSourceData: any) => any) | string;
optional?: boolean;
customConvert?: (fieldData: any, parentSourceData: any, rootSourceData: any) => any;
};
type SchemaFieldType = 'string' | 'number' | 'enum' | 'boolean' | 'date' | 'array' | 'object';
type NumberSchemaField = BaseSchemaField & {
type: 'number';
defaultValue?: number;
};
type EnumSchemaField = BaseSchemaField & {
type: 'enum';
enumType: Record<string, string | number>;
defaultValue?: number | string;
};
type StringSchemaField = BaseSchemaField & {
type: 'string';
defaultValue?: string;
};
type BooleanSchemaField = BaseSchemaField & {
type: 'boolean';
defaultValue?: boolean;
};
type DateSchemaField = BaseSchemaField & {
type: 'date';
defaultValue?: Dayjs;
isUnixTime?: boolean;
};
type ObjectSchemaField = BaseSchemaField & {
type: 'object';
defaultValue?: object;
fields?: Record<string, SchemaField>;
};
type ArraySchemaField = BaseSchemaField & {
type: 'array';
defaultValue?: any[];
itemSchema?: SchemaField;
};
export type SchemaField =
| StringSchemaField
| NumberSchemaField
| BooleanSchemaField
| EnumSchemaField
| DateSchemaField
| ObjectSchemaField
| ArraySchemaField;
// type MapToSchemaField<T extends SchemaFieldType> = T extends 'string'
// ? StringSchemaField
// : T extends 'number'
// ? NumberSchemaField
// : T extends 'enum'
// ? EnumSchemaField
// : T extends 'boolean'
// ? BooleanSchemaField
// : T extends 'date'
// ? DateSchemaField
// : T extends 'object'
// ? ObjectSchemaField
// : T extends 'array'
// ? ArraySchemaField
// : ObjectSchemaField;
type GetSchemaFormatValue<T extends SchemaField> = T extends ObjectSchemaField
? Record<keyof T['fields'], any>
: T extends ArraySchemaField
? any[]
: T extends StringSchemaField
? string
: T extends NumberSchemaField
? number
: T extends EnumSchemaField
? string | number
: T extends BooleanSchemaField
? boolean
: undefined;
export function generateDefaultJson<T extends SchemaField>(
schemaField: T,
options: FormatOptions = { reliable: true }
) {
return formatJson(null, schemaField, options);
}
export function formatJson<T extends SchemaField>(
data: Array<unknown> | Record<string, unknown> | null,
schemaField: T,
options: FormatOptions = { reliable: true }
) {
return doFormatFieldData(data, schemaField, options, null, data);
}
function doFormatFieldData<T extends SchemaField>(
data: any,
schemaField: T,
options: FormatOptions = { reliable: true },
parentSourceData?: any,
rootSourceData?: any
): GetSchemaFormatValue<T> {
let fieldValue: any = schemaField.defaultValue;
if (schemaField.customConvert) {
fieldValue = schemaField.customConvert(data, parentSourceData, rootSourceData);
} else {
if (schemaField.type === 'string') {
const finalSchemaField = schemaField as StringSchemaField;
fieldValue = doCovnertString(data, finalSchemaField, options);
}
if (schemaField.type === 'number') {
const finalSchemaField = schemaField as NumberSchemaField;
fieldValue = doCovnertNumber(data, finalSchemaField, options);
}
if (schemaField.type === 'boolean') {
const finalSchemaField = schemaField as BooleanSchemaField;
fieldValue = doCovnertBoolean(data, finalSchemaField, options);
}
if (schemaField.type === 'enum') {
const finalSchemaField = schemaField as EnumSchemaField;
fieldValue = doCovnertEnum(data, finalSchemaField, options);
}
if (schemaField.type === 'date') {
const finalSchemaField = schemaField as DateSchemaField;
fieldValue = doCovnertDate(data, finalSchemaField, options);
}
if (schemaField.type === 'object') {
const finalSchemaField = schemaField as ObjectSchemaField;
if (!((data === undefined && schemaField.optional) || !options.reliable)) {
fieldValue = Object.keys(finalSchemaField?.fields || {}).reduce((res, key) => {
let subFieldData: unknown;
// const subFieldSchema = toSchemaField(finalSchemaField.fields[key]);
const subFieldSchema = (finalSchemaField?.fields || {})[key];
if (subFieldSchema.source instanceof Function) {
subFieldData = subFieldSchema.source(data, rootSourceData);
} else if (subFieldSchema.source && get(data, subFieldSchema.source, undefined) !== undefined) {
subFieldData = get(data, subFieldSchema.source, undefined);
} else if (get(data, key, undefined) !== undefined) {
subFieldData = get(data, key, undefined);
}
res[key] = doFormatFieldData(subFieldData, subFieldSchema, options, data, rootSourceData);
return res;
}, {} as any);
}
}
if (schemaField.type === 'array') {
const finalSchemaField = schemaField as ArraySchemaField;
const itemSchema = finalSchemaField.itemSchema;
if (itemSchema && !((data === undefined && schemaField.optional) || !options.reliable)) {
const arr = data || [];
fieldValue = arr.map((item: any) => {
return doFormatFieldData(item, itemSchema, options, data, rootSourceData);
});
}
}
}
return fieldValue;
}
function doCovnertNumber(sourceData: any, schema: NumberSchemaField, options?: FormatOptions) {
if (typeof sourceData === 'number') {
return sourceData;
}
let val = 'defaultValue' in schema ? schema.defaultValue : undefined;
if ((val === undefined || val === null) && options?.reliable && !schema.optional) {
val = 0;
}
return val;
}
function doCovnertString(sourceData: any, schema: StringSchemaField, options?: FormatOptions) {
if (typeof sourceData === 'string') {
return sourceData;
}
let val = 'defaultValue' in schema ? schema.defaultValue : undefined;
if ((val === undefined || val === null) && options?.reliable && !schema.optional) {
val = '';
}
return val;
}
function doCovnertBoolean(sourceData: any, schema: BooleanSchemaField, options?: FormatOptions) {
if (typeof sourceData === 'number') {
return sourceData === 1;
}
if (typeof sourceData === 'boolean') {
return sourceData;
}
let val = 'defaultValue' in schema ? schema.defaultValue : undefined;
if ((val === undefined || val === null) && options?.reliable && !schema.optional) {
val = false;
}
return val;
}
function doCovnertEnum(sourceData: any, schema: EnumSchemaField, options?: FormatOptions) {
if (sourceData in schema.enumType) {
return sourceData;
}
let val = 'defaultValue' in schema ? schema.defaultValue : undefined;
if ((val === undefined || val === null) && options?.reliable && !schema.optional) {
if (typeof schema.enumType === 'string') {
val = '';
} else {
val = 0;
}
}
return val;
}
function doCovnertDate(sourceData: any, schema: DateSchemaField, options?: FormatOptions) {
if (typeof sourceData === 'string' || typeof sourceData === 'number') {
if (schema.isUnixTime && typeof sourceData === 'number') {
return dayjs(sourceData * 1000);
}
return dayjs(sourceData);
}
let val = 'defaultValue' in schema ? schema.defaultValue : undefined;
if ((val === undefined || val === null) && options?.reliable && !schema.optional) {
val = dayjs();
}
return val;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment