feat: update links field (#5212)

Closes #5113

---------

Co-authored-by: Jérémy Magrin <jeremy.magrin@gmail.com>
This commit is contained in:
Thaïs
2024-05-01 14:56:55 +02:00
committed by GitHub
parent 8853226d17
commit 0d023e5e77
11 changed files with 146 additions and 56 deletions

View File

@ -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

View File

@ -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: [],
}); });
}; };

View File

@ -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;

View File

@ -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;

View File

@ -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 = (

View File

@ -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,
]; ];

View File

@ -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);
},
});

View File

@ -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));
}
},
});

View File

@ -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);

View File

@ -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;
}; };

View File

@ -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;
} }