feat: update links field (#5212)
Closes #5113 --------- Co-authored-by: Jérémy Magrin <jeremy.magrin@gmail.com>
This commit is contained in:
@ -2,7 +2,10 @@ import { isUndefined } from '@sniptt/guards';
|
|||||||
|
|
||||||
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||||
import { mapObjectMetadataToGraphQLQuery } from '@/object-metadata/utils/mapObjectMetadataToGraphQLQuery';
|
import { mapObjectMetadataToGraphQLQuery } from '@/object-metadata/utils/mapObjectMetadataToGraphQLQuery';
|
||||||
import { FieldMetadataType } from '~/generated-metadata/graphql';
|
import {
|
||||||
|
FieldMetadataType,
|
||||||
|
RelationMetadataType,
|
||||||
|
} from '~/generated-metadata/graphql';
|
||||||
|
|
||||||
import { FieldMetadataItem } from '../types/FieldMetadataItem';
|
import { FieldMetadataItem } from '../types/FieldMetadataItem';
|
||||||
|
|
||||||
@ -23,29 +26,29 @@ export const mapFieldMetadataToGraphQLQuery = ({
|
|||||||
}): any => {
|
}): any => {
|
||||||
const fieldType = field.type;
|
const fieldType = field.type;
|
||||||
|
|
||||||
const fieldIsSimpleValue = (
|
const fieldIsSimpleValue = [
|
||||||
[
|
FieldMetadataType.Uuid,
|
||||||
'UUID',
|
FieldMetadataType.Text,
|
||||||
'TEXT',
|
FieldMetadataType.Phone,
|
||||||
'PHONE',
|
FieldMetadataType.DateTime,
|
||||||
'DATE_TIME',
|
FieldMetadataType.Date,
|
||||||
'DATE',
|
FieldMetadataType.Email,
|
||||||
'EMAIL',
|
FieldMetadataType.Number,
|
||||||
'NUMBER',
|
FieldMetadataType.Boolean,
|
||||||
'BOOLEAN',
|
FieldMetadataType.Rating,
|
||||||
'RATING',
|
FieldMetadataType.Select,
|
||||||
'SELECT',
|
FieldMetadataType.MultiSelect,
|
||||||
'MULTI_SELECT',
|
FieldMetadataType.Position,
|
||||||
'POSITION',
|
FieldMetadataType.RawJson,
|
||||||
'RAW_JSON',
|
].includes(fieldType);
|
||||||
] as FieldMetadataType[]
|
|
||||||
).includes(fieldType);
|
|
||||||
|
|
||||||
if (fieldIsSimpleValue) {
|
if (fieldIsSimpleValue) {
|
||||||
return field.name;
|
return field.name;
|
||||||
} else if (
|
}
|
||||||
fieldType === 'RELATION' &&
|
|
||||||
field.toRelationMetadata?.relationType === 'ONE_TO_MANY'
|
if (
|
||||||
|
fieldType === FieldMetadataType.Relation &&
|
||||||
|
field.toRelationMetadata?.relationType === RelationMetadataType.OneToMany
|
||||||
) {
|
) {
|
||||||
const relationMetadataItem = objectMetadataItems.find(
|
const relationMetadataItem = objectMetadataItems.find(
|
||||||
(objectMetadataItem) =>
|
(objectMetadataItem) =>
|
||||||
@ -65,9 +68,11 @@ ${mapObjectMetadataToGraphQLQuery({
|
|||||||
computeReferences: computeReferences,
|
computeReferences: computeReferences,
|
||||||
isRootLevel: false,
|
isRootLevel: false,
|
||||||
})}`;
|
})}`;
|
||||||
} else if (
|
}
|
||||||
fieldType === 'RELATION' &&
|
|
||||||
field.fromRelationMetadata?.relationType === 'ONE_TO_MANY'
|
if (
|
||||||
|
fieldType === FieldMetadataType.Relation &&
|
||||||
|
field.fromRelationMetadata?.relationType === RelationMetadataType.OneToMany
|
||||||
) {
|
) {
|
||||||
const relationMetadataItem = objectMetadataItems.find(
|
const relationMetadataItem = objectMetadataItems.find(
|
||||||
(objectMetadataItem) =>
|
(objectMetadataItem) =>
|
||||||
@ -91,26 +96,43 @@ ${mapObjectMetadataToGraphQLQuery({
|
|||||||
})}
|
})}
|
||||||
}
|
}
|
||||||
}`;
|
}`;
|
||||||
} else if (fieldType === 'LINK') {
|
}
|
||||||
|
|
||||||
|
if (fieldType === FieldMetadataType.Link) {
|
||||||
return `${field.name}
|
return `${field.name}
|
||||||
{
|
{
|
||||||
label
|
label
|
||||||
url
|
url
|
||||||
}`;
|
}`;
|
||||||
} else if (fieldType === 'CURRENCY') {
|
}
|
||||||
|
|
||||||
|
if (fieldType === FieldMetadataType.Links) {
|
||||||
|
return `${field.name}
|
||||||
|
{
|
||||||
|
primaryLinkUrl
|
||||||
|
primaryLinkLabel
|
||||||
|
secondaryLinks
|
||||||
|
}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fieldType === FieldMetadataType.Currency) {
|
||||||
return `${field.name}
|
return `${field.name}
|
||||||
{
|
{
|
||||||
amountMicros
|
amountMicros
|
||||||
currencyCode
|
currencyCode
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
} else if (fieldType === 'FULL_NAME') {
|
}
|
||||||
|
|
||||||
|
if (fieldType === FieldMetadataType.FullName) {
|
||||||
return `${field.name}
|
return `${field.name}
|
||||||
{
|
{
|
||||||
firstName
|
firstName
|
||||||
lastName
|
lastName
|
||||||
}`;
|
}`;
|
||||||
} else if (fieldType === 'ADDRESS') {
|
}
|
||||||
|
|
||||||
|
if (fieldType === FieldMetadataType.Address) {
|
||||||
return `${field.name}
|
return `${field.name}
|
||||||
{
|
{
|
||||||
addressStreet1
|
addressStreet1
|
||||||
|
|||||||
@ -27,6 +27,7 @@ export const LinksFieldInput = ({
|
|||||||
persistLinksField({
|
persistLinksField({
|
||||||
primaryLinkUrl: url,
|
primaryLinkUrl: url,
|
||||||
primaryLinkLabel: '',
|
primaryLinkLabel: '',
|
||||||
|
secondaryLinks: [],
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@ -36,6 +37,7 @@ export const LinksFieldInput = ({
|
|||||||
persistLinksField({
|
persistLinksField({
|
||||||
primaryLinkUrl: url,
|
primaryLinkUrl: url,
|
||||||
primaryLinkLabel: '',
|
primaryLinkLabel: '',
|
||||||
|
secondaryLinks: [],
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@ -45,6 +47,7 @@ export const LinksFieldInput = ({
|
|||||||
persistLinksField({
|
persistLinksField({
|
||||||
primaryLinkUrl: url,
|
primaryLinkUrl: url,
|
||||||
primaryLinkLabel: '',
|
primaryLinkLabel: '',
|
||||||
|
secondaryLinks: [],
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@ -54,6 +57,7 @@ export const LinksFieldInput = ({
|
|||||||
persistLinksField({
|
persistLinksField({
|
||||||
primaryLinkUrl: url,
|
primaryLinkUrl: url,
|
||||||
primaryLinkLabel: '',
|
primaryLinkLabel: '',
|
||||||
|
secondaryLinks: [],
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@ -63,6 +67,7 @@ export const LinksFieldInput = ({
|
|||||||
persistLinksField({
|
persistLinksField({
|
||||||
primaryLinkUrl: url,
|
primaryLinkUrl: url,
|
||||||
primaryLinkLabel: '',
|
primaryLinkLabel: '',
|
||||||
|
secondaryLinks: [],
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@ -71,6 +76,7 @@ export const LinksFieldInput = ({
|
|||||||
setDraftValue({
|
setDraftValue({
|
||||||
primaryLinkUrl: url,
|
primaryLinkUrl: url,
|
||||||
primaryLinkLabel: '',
|
primaryLinkLabel: '',
|
||||||
|
secondaryLinks: [],
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -30,7 +30,7 @@ export type FieldLinkDraftValue = { url: string; label: string };
|
|||||||
export type FieldLinksDraftValue = {
|
export type FieldLinksDraftValue = {
|
||||||
primaryLinkLabel: string;
|
primaryLinkLabel: string;
|
||||||
primaryLinkUrl: string;
|
primaryLinkUrl: string;
|
||||||
secondaryLinks?: string | null;
|
secondaryLinks?: { label: string; url: string }[] | null;
|
||||||
};
|
};
|
||||||
export type FieldCurrencyDraftValue = {
|
export type FieldCurrencyDraftValue = {
|
||||||
currencyCode: CurrencyCode;
|
currencyCode: CurrencyCode;
|
||||||
|
|||||||
@ -151,7 +151,7 @@ export type FieldLinkValue = { url: string; label: string };
|
|||||||
export type FieldLinksValue = {
|
export type FieldLinksValue = {
|
||||||
primaryLinkLabel: string;
|
primaryLinkLabel: string;
|
||||||
primaryLinkUrl: string;
|
primaryLinkUrl: string;
|
||||||
secondaryLinks?: string | null;
|
secondaryLinks?: { label: string; url: string }[] | null;
|
||||||
};
|
};
|
||||||
export type FieldCurrencyValue = {
|
export type FieldCurrencyValue = {
|
||||||
currencyCode: CurrencyCode;
|
currencyCode: CurrencyCode;
|
||||||
|
|||||||
@ -7,7 +7,9 @@ import { FieldLinksValue } from '../FieldMetadata';
|
|||||||
export const linksSchema = z.object({
|
export const linksSchema = z.object({
|
||||||
primaryLinkLabel: z.string(),
|
primaryLinkLabel: z.string(),
|
||||||
primaryLinkUrl: absoluteUrlSchema,
|
primaryLinkUrl: absoluteUrlSchema,
|
||||||
secondaryLinks: z.string().optional().nullable(),
|
secondaryLinks: z
|
||||||
|
.array(z.object({ label: z.string(), url: absoluteUrlSchema }))
|
||||||
|
.nullable(),
|
||||||
}) satisfies z.ZodType<FieldLinksValue>;
|
}) satisfies z.ZodType<FieldLinksValue>;
|
||||||
|
|
||||||
export const isFieldLinksValue = (
|
export const isFieldLinksValue = (
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { JsonScalarType } from './json.scalar';
|
import { RawJSONScalar } from './raw-json.scalar';
|
||||||
import { PositionScalarType } from './position.scalar';
|
import { PositionScalarType } from './position.scalar';
|
||||||
import { CursorScalarType } from './cursor.scalar';
|
import { CursorScalarType } from './cursor.scalar';
|
||||||
import { BigFloatScalarType } from './big-float.scalar';
|
import { BigFloatScalarType } from './big-float.scalar';
|
||||||
@ -25,5 +25,5 @@ export const scalars = [
|
|||||||
UUIDScalarType,
|
UUIDScalarType,
|
||||||
CursorScalarType,
|
CursorScalarType,
|
||||||
PositionScalarType,
|
PositionScalarType,
|
||||||
JsonScalarType,
|
RawJSONScalar,
|
||||||
];
|
];
|
||||||
|
|||||||
@ -1,14 +0,0 @@
|
|||||||
import { isString } from 'class-validator';
|
|
||||||
import { GraphQLScalarType } from 'graphql';
|
|
||||||
import GraphQLJSON from 'graphql-type-json';
|
|
||||||
|
|
||||||
export const JsonScalarType = new GraphQLScalarType({
|
|
||||||
...GraphQLJSON,
|
|
||||||
parseValue: (value) => {
|
|
||||||
if (isString(value) && isString(JSON.parse(value))) {
|
|
||||||
throw new Error(`Strings are not supported as JSON: ${value}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
return GraphQLJSON.parseValue(value);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
@ -0,0 +1,74 @@
|
|||||||
|
import { GraphQLScalarType } from 'graphql';
|
||||||
|
import { Maybe } from 'graphql-yoga';
|
||||||
|
import { ObjMap } from 'graphql/jsutils/ObjMap';
|
||||||
|
import { ASTNode, Kind, ValueNode } from 'graphql/language';
|
||||||
|
|
||||||
|
const parseLiteral = (
|
||||||
|
ast: ValueNode,
|
||||||
|
variables?: Maybe<ObjMap<unknown>>,
|
||||||
|
): any => {
|
||||||
|
switch (ast.kind) {
|
||||||
|
case Kind.STRING:
|
||||||
|
case Kind.BOOLEAN:
|
||||||
|
return ast.value;
|
||||||
|
case Kind.INT:
|
||||||
|
case Kind.FLOAT:
|
||||||
|
return parseFloat(ast.value);
|
||||||
|
case Kind.OBJECT:
|
||||||
|
return parseObject(ast as any, variables);
|
||||||
|
case Kind.LIST:
|
||||||
|
return (ast as any).values.map((n: ValueNode) =>
|
||||||
|
parseLiteral(n, variables),
|
||||||
|
);
|
||||||
|
case Kind.NULL:
|
||||||
|
return null;
|
||||||
|
case Kind.VARIABLE:
|
||||||
|
return variables ? variables[ast.name.value] : undefined;
|
||||||
|
default:
|
||||||
|
throw new TypeError(
|
||||||
|
`JSONStringify cannot represent value: ${JSON.stringify(ast)}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const parseObject = (
|
||||||
|
ast: ASTNode,
|
||||||
|
variables?: Maybe<ObjMap<unknown>>,
|
||||||
|
): object => {
|
||||||
|
const value = Object.create(null);
|
||||||
|
|
||||||
|
if ('fields' in ast) {
|
||||||
|
ast.fields?.forEach((field: any) => {
|
||||||
|
value[field.name.value] = parseLiteral(field.value, variables);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
};
|
||||||
|
|
||||||
|
const stringify = (value: any): string => {
|
||||||
|
return JSON.stringify(value);
|
||||||
|
};
|
||||||
|
|
||||||
|
const parseJSON = (value: string): object => {
|
||||||
|
try {
|
||||||
|
return JSON.parse(value);
|
||||||
|
} catch (e) {
|
||||||
|
throw new TypeError(`Value is not valid JSON: ${value}`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const RawJSONScalar = new GraphQLScalarType({
|
||||||
|
name: 'RawJSONScalar',
|
||||||
|
description:
|
||||||
|
'The `RawJSONScalar` scalar type represents JSON values, but stringifies inputs and parses outputs.',
|
||||||
|
serialize: parseJSON,
|
||||||
|
parseValue: stringify,
|
||||||
|
parseLiteral: (ast, variables) => {
|
||||||
|
if (ast.kind === Kind.STRING) {
|
||||||
|
return stringify(ast.value);
|
||||||
|
} else {
|
||||||
|
return stringify(parseLiteral(ast, variables));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
@ -34,7 +34,7 @@ import {
|
|||||||
UUIDScalarType,
|
UUIDScalarType,
|
||||||
} from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars';
|
} from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars';
|
||||||
import { PositionScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars/position.scalar';
|
import { PositionScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars/position.scalar';
|
||||||
import { JsonScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars/json.scalar';
|
import { RawJSONScalar } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars/raw-json.scalar';
|
||||||
import { IDFilterType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/input/id-filter.input-type';
|
import { IDFilterType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/input/id-filter.input-type';
|
||||||
|
|
||||||
export interface TypeOptions<T = any> {
|
export interface TypeOptions<T = any> {
|
||||||
@ -76,7 +76,7 @@ export class TypeMapperService {
|
|||||||
[FieldMetadataType.NUMERIC, BigFloatScalarType],
|
[FieldMetadataType.NUMERIC, BigFloatScalarType],
|
||||||
[FieldMetadataType.PROBABILITY, GraphQLFloat],
|
[FieldMetadataType.PROBABILITY, GraphQLFloat],
|
||||||
[FieldMetadataType.POSITION, PositionScalarType],
|
[FieldMetadataType.POSITION, PositionScalarType],
|
||||||
[FieldMetadataType.RAW_JSON, JsonScalarType],
|
[FieldMetadataType.RAW_JSON, RawJSONScalar],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return typeScalarMapping.get(fieldMetadataType);
|
return typeScalarMapping.get(fieldMetadataType);
|
||||||
|
|||||||
@ -29,5 +29,5 @@ export const linksCompositeType: CompositeType = {
|
|||||||
export type LinksMetadata = {
|
export type LinksMetadata = {
|
||||||
primaryLinkLabel: string;
|
primaryLinkLabel: string;
|
||||||
primaryLinkUrl: string;
|
primaryLinkUrl: string;
|
||||||
secondaryLinks: JSON | null;
|
secondaryLinks: object | null;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -2,10 +2,10 @@ import {
|
|||||||
IsArray,
|
IsArray,
|
||||||
IsBoolean,
|
IsBoolean,
|
||||||
IsDate,
|
IsDate,
|
||||||
IsJSON,
|
|
||||||
IsNotEmpty,
|
IsNotEmpty,
|
||||||
IsNumber,
|
IsNumber,
|
||||||
IsNumberString,
|
IsNumberString,
|
||||||
|
IsObject,
|
||||||
IsString,
|
IsString,
|
||||||
Matches,
|
Matches,
|
||||||
ValidateIf,
|
ValidateIf,
|
||||||
@ -28,9 +28,9 @@ export class FieldMetadataDefaultValueString {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class FieldMetadataDefaultValueRawJson {
|
export class FieldMetadataDefaultValueRawJson {
|
||||||
@ValidateIf((object, value) => value !== null)
|
@ValidateIf((_object, value) => value !== null)
|
||||||
@IsJSON()
|
@IsObject()
|
||||||
value: JSON | null;
|
value: object | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class FieldMetadataDefaultValueNumber {
|
export class FieldMetadataDefaultValueNumber {
|
||||||
@ -149,6 +149,6 @@ export class FieldMetadataDefaultValueLinks {
|
|||||||
primaryLinkUrl: string | null;
|
primaryLinkUrl: string | null;
|
||||||
|
|
||||||
@ValidateIf((_object, value) => value !== null)
|
@ValidateIf((_object, value) => value !== null)
|
||||||
@IsJSON()
|
@IsObject()
|
||||||
secondaryLinks: JSON | null;
|
secondaryLinks: object | null;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user