Rename Money/Url to Currency/Link and remove snake_case from composite fields (#2536)

* Rename Money/Url to Currency/Link

* regenerate front types

* renaming money/url field types

* fix double text

* fix tests

* fix server tests

* fix generate-target-column-map

* fix currency convert

* fix: tests

---------

Co-authored-by: Jérémy Magrin <jeremy.magrin@gmail.com>
This commit is contained in:
Weiko
2023-11-17 10:31:17 +01:00
committed by GitHub
parent 31e439681c
commit bc579d64a6
56 changed files with 579 additions and 512 deletions
@@ -0,0 +1,15 @@
/*
Warnings:
- The `currency` column on the `pipelines` table would be dropped and recreated. This will lead to data loss if there is data in the column.
*/
-- CreateEnum
CREATE TYPE "CurrencyCode" AS ENUM ('AED', 'AFN', 'ALL', 'AMD', 'ANG', 'AOA', 'ARS', 'AUD', 'AWG', 'AZN', 'BAM', 'BBD', 'BDT', 'BGN', 'BHD', 'BIF', 'BMD', 'BND', 'BOB', 'BOV', 'BRL', 'BSD', 'BTN', 'BWP', 'BYN', 'BZD', 'CAD', 'CDF', 'CHF', 'CLF', 'CLP', 'CNY', 'COP', 'COU', 'CRC', 'CUC', 'CUP', 'CVE', 'CZK', 'DJF', 'DKK', 'DOP', 'DZD', 'EGP', 'ERN', 'ETB', 'EUR', 'FJD', 'FKP', 'GBP', 'GEL', 'GHS', 'GIP', 'GMD', 'GNF', 'GTQ', 'GYD', 'HKD', 'HNL', 'HRK', 'HTG', 'HUF', 'IDR', 'ILS', 'INR', 'IQD', 'IRR', 'ISK', 'JMD', 'JOD', 'JPY', 'KES', 'KGS', 'KHR', 'KMF', 'KPW', 'KRW', 'KWD', 'KYD', 'KZT', 'LAK', 'LBP', 'LKR', 'LRD', 'LSL', 'LYD', 'MAD', 'MDL', 'MGA', 'MKD', 'MMK', 'MNT', 'MOP', 'MRO', 'MRU', 'MUR', 'MVR', 'MWK', 'MXN', 'MXV', 'MYR', 'MZN', 'NAD', 'NGN', 'NIO', 'NOK', 'NPR', 'NZD', 'OMR', 'PAB', 'PEN', 'PGK', 'PHP', 'PKR', 'PLN', 'PYG', 'QAR', 'RON', 'RSD', 'RUB', 'RWF', 'SAR', 'SBD', 'SCR', 'SDD', 'SDG', 'SEK', 'SGD', 'SHP', 'SLL', 'SOS', 'SRD', 'SSP', 'STD', 'STN', 'SVC', 'SYP', 'SZL', 'THB', 'TJS', 'TMM', 'TMT', 'TND', 'TOP', 'TRY', 'TTD', 'TWD', 'TZS', 'UAH', 'UGX', 'USD', 'UYU', 'UZS', 'VEF', 'VES', 'VND', 'VUV', 'WST', 'XAF', 'XCD', 'XOF', 'XPF', 'XSU', 'XUA', 'YER', 'ZAR', 'ZMW', 'ZWL');
-- AlterTable
ALTER TABLE "pipelines" DROP COLUMN "currency",
ADD COLUMN "currency" "CurrencyCode" NOT NULL DEFAULT 'USD';
-- DropEnum
DROP TYPE "Currency";
+4 -2
View File
@@ -446,7 +446,7 @@ model ActivityTarget {
}
// All of the world's currently active currencies based on the ISO 4217 standard
enum Currency {
enum CurrencyCode {
AED
AFN
ALL
@@ -615,6 +615,8 @@ enum Currency {
ZAR
ZMW
ZWL
@@map("CurrencyCode")
}
model Pipeline {
@@ -640,7 +642,7 @@ model Pipeline {
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
currency Currency @default(USD)
currency CurrencyCode @default(USD)
@@map("pipelines")
}
@@ -27,8 +27,8 @@ export enum FieldMetadataType {
NUMBER = 'NUMBER',
PROBABILITY = 'PROBABILITY',
ENUM = 'ENUM',
URL = 'URL',
MONEY = 'MONEY',
LINK = 'LINK',
CURRENCY = 'CURRENCY',
RELATION = 'RELATION',
}
@@ -23,21 +23,21 @@ export type FieldMetadataDynamicDefaultValue =
| { type: 'uuid' }
| { type: 'now' };
interface FieldMetadataDefaultValueUrl {
text: string;
link: string;
interface FieldMetadataDefaultValueLink {
label: string;
url: string;
}
interface FieldMetadataDefaultValueMoney {
amount: number;
currency: string;
interface FieldMetadataDefaultValueCurrency {
amountMicros: number;
currencyCode: string;
}
type AllFieldMetadataDefaultValueTypes =
| FieldMetadataScalarDefaultValue
| FieldMetadataDynamicDefaultValue
| FieldMetadataDefaultValueUrl
| FieldMetadataDefaultValueMoney;
| FieldMetadataDefaultValueLink
| FieldMetadataDefaultValueCurrency;
type FieldMetadataDefaultValueMapping = {
[FieldMetadataType.UUID]: FieldMetadataDefaultValueString;
@@ -49,8 +49,8 @@ type FieldMetadataDefaultValueMapping = {
[FieldMetadataType.NUMBER]: FieldMetadataDefaultValueNumber;
[FieldMetadataType.PROBABILITY]: FieldMetadataDefaultValueNumber;
[FieldMetadataType.ENUM]: FieldMetadataDefaultValueString;
[FieldMetadataType.URL]: FieldMetadataDefaultValueUrl;
[FieldMetadataType.MONEY]: FieldMetadataDefaultValueMoney;
[FieldMetadataType.LINK]: FieldMetadataDefaultValueLink;
[FieldMetadataType.CURRENCY]: FieldMetadataDefaultValueCurrency;
};
type DefaultValueByFieldMetadata<T extends FieldMetadataType | 'default'> = [
@@ -4,14 +4,14 @@ export interface FieldMetadataTargetColumnMapValue {
value: string;
}
export interface FieldMetadataTargetColumnMapUrl {
text: string;
link: string;
export interface FieldMetadataTargetColumnMapLink {
label: string;
url: string;
}
export interface FieldMetadataTargetColumnMapMoney {
amount: string;
currency: string;
export interface FieldMetadataTargetColumnMapCurrency {
amountMicros: string;
currencyCode: string;
}
type AllFieldMetadataTypes = {
@@ -19,8 +19,8 @@ type AllFieldMetadataTypes = {
};
type FieldMetadataTypeMapping = {
[FieldMetadataType.URL]: FieldMetadataTargetColumnMapUrl;
[FieldMetadataType.MONEY]: FieldMetadataTargetColumnMapMoney;
[FieldMetadataType.LINK]: FieldMetadataTargetColumnMapLink;
[FieldMetadataType.CURRENCY]: FieldMetadataTargetColumnMapCurrency;
};
type TypeByFieldMetadata<T extends FieldMetadataType | 'default'> =
@@ -19,46 +19,49 @@ describe('convertFieldMetadataToColumnActions', () => {
]);
});
it('should convert URL field metadata to column actions', () => {
it('should convert LINK field metadata to column actions', () => {
const fieldMetadata = {
type: FieldMetadataType.URL,
targetColumnMap: { text: 'url_text', link: 'url_link' },
defaultValue: { text: 'http://example.com', link: 'Example' },
type: FieldMetadataType.LINK,
targetColumnMap: { label: 'linkLabel', url: 'linkURL' },
defaultValue: { label: 'http://example.com', url: 'Example' },
} as any;
const columnActions = convertFieldMetadataToColumnActions(fieldMetadata);
expect(columnActions).toEqual([
{
action: 'CREATE',
columnName: 'url_text',
columnName: 'linkLabel',
columnType: 'varchar',
defaultValue: "'http://example.com'",
},
{
action: 'CREATE',
columnName: 'url_link',
columnName: 'linkURL',
columnType: 'varchar',
defaultValue: "'Example'",
},
]);
});
it('should convert MONEY field metadata to column actions', () => {
it('should convert CURRENCY field metadata to column actions', () => {
const fieldMetadata = {
type: FieldMetadataType.MONEY,
targetColumnMap: { amount: 'money_amount', currency: 'money_currency' },
defaultValue: { amount: 100, currency: 'USD' },
type: FieldMetadataType.CURRENCY,
targetColumnMap: {
amountMicros: 'moneyAmountMicros',
currencyCode: 'moneyCurrencyCode',
},
defaultValue: { amountMicros: 100 * 1_000_000, currencyCode: 'USD' },
} as any;
const columnActions = convertFieldMetadataToColumnActions(fieldMetadata);
expect(columnActions).toEqual([
{
action: 'CREATE',
columnName: 'money_amount',
columnName: 'moneyAmountMicros',
columnType: 'integer',
defaultValue: 100,
defaultValue: 100 * 1_000_000,
},
{
action: 'CREATE',
columnName: 'money_currency',
columnName: 'moneyCurrencyCode',
columnType: 'varchar',
defaultValue: "'USD'",
},
@@ -12,21 +12,21 @@ describe('generateTargetColumnMap', () => {
);
expect(textMap).toEqual({ value: 'name' });
const urlMap = generateTargetColumnMap(
FieldMetadataType.URL,
const linkMap = generateTargetColumnMap(
FieldMetadataType.LINK,
false,
'website',
);
expect(urlMap).toEqual({ text: 'website_text', link: 'website_link' });
expect(linkMap).toEqual({ label: 'websiteLabel', url: 'websiteUrl' });
const moneyMap = generateTargetColumnMap(
FieldMetadataType.MONEY,
const currencyMap = generateTargetColumnMap(
FieldMetadataType.CURRENCY,
true,
'price',
);
expect(moneyMap).toEqual({
amount: '_price_amount',
currency: '_price_currency',
expect(currencyMap).toEqual({
amountMicros: '_priceAmountMicros',
currencyCode: '_priceCurrencyCode',
});
});
@@ -8,7 +8,9 @@ describe('serializeDefaultValue', () => {
});
it('should handle uuid dynamic default value', () => {
expect(serializeDefaultValue({ type: 'uuid' })).toBe('uuid_generate_v4()');
expect(serializeDefaultValue({ type: 'uuid' })).toBe(
'public.uuid_generate_v4()',
);
});
it('should handle now dynamic default value', () => {
@@ -124,44 +124,44 @@ describe('validateDefaultValueBasedOnType', () => {
).toBe(false);
});
// URL type
it('should validate URL default value', () => {
// LINK type
it('should validate LINK default value', () => {
expect(
validateDefaultValueBasedOnType(
{ text: 'http://example.com', link: 'Example' },
FieldMetadataType.URL,
{ label: 'http://example.com', url: 'Example' },
FieldMetadataType.LINK,
),
).toBe(true);
});
it('should return false for invalid URL default value', () => {
it('should return false for invalid LINK default value', () => {
expect(
validateDefaultValueBasedOnType(
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error Just for testing purposes
{ text: 123, link: {} },
FieldMetadataType.URL,
{ label: 123, url: {} },
FieldMetadataType.LINK,
),
).toBe(false);
});
// MONEY type
it('should validate MONEY default value', () => {
// CURRENCY type
it('should validate CURRENCY default value', () => {
expect(
validateDefaultValueBasedOnType(
{ amount: 100, currency: 'USD' },
FieldMetadataType.MONEY,
{ amountMicros: 100, currencyCode: 'USD' },
FieldMetadataType.CURRENCY,
),
).toBe(true);
});
it('should return false for invalid MONEY default value', () => {
it('should return false for invalid CURRENCY default value', () => {
expect(
validateDefaultValueBasedOnType(
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error Just for testing purposes
{ amount: '100', currency: 'USD' },
FieldMetadataType.MONEY,
{ amountMicros: '100', currencyCode: 'USD' },
FieldMetadataType.CURRENCY,
),
).toBe(false);
});
@@ -85,41 +85,41 @@ export function convertFieldMetadataToColumnActions(
},
];
}
case FieldMetadataType.URL: {
case FieldMetadataType.LINK: {
const defaultValue =
fieldMetadata.defaultValue as FieldMetadataDefaultValue<FieldMetadataType.URL>;
fieldMetadata.defaultValue as FieldMetadataDefaultValue<FieldMetadataType.LINK>;
return [
{
action: TenantMigrationColumnActionType.CREATE,
columnName: fieldMetadata.targetColumnMap.text,
columnName: fieldMetadata.targetColumnMap.label,
columnType: 'varchar',
defaultValue: serializeDefaultValue(defaultValue?.text),
defaultValue: serializeDefaultValue(defaultValue?.label),
},
{
action: TenantMigrationColumnActionType.CREATE,
columnName: fieldMetadata.targetColumnMap.link,
columnName: fieldMetadata.targetColumnMap.url,
columnType: 'varchar',
defaultValue: serializeDefaultValue(defaultValue?.link),
defaultValue: serializeDefaultValue(defaultValue?.url),
},
];
}
case FieldMetadataType.MONEY: {
case FieldMetadataType.CURRENCY: {
const defaultValue =
fieldMetadata.defaultValue as FieldMetadataDefaultValue<FieldMetadataType.MONEY>;
fieldMetadata.defaultValue as FieldMetadataDefaultValue<FieldMetadataType.CURRENCY>;
return [
{
action: TenantMigrationColumnActionType.CREATE,
columnName: fieldMetadata.targetColumnMap.amount,
columnName: fieldMetadata.targetColumnMap.amountMicros,
columnType: 'integer',
defaultValue: serializeDefaultValue(defaultValue?.amount),
defaultValue: serializeDefaultValue(defaultValue?.amountMicros),
},
{
action: TenantMigrationColumnActionType.CREATE,
columnName: fieldMetadata.targetColumnMap.currency,
columnName: fieldMetadata.targetColumnMap.currencyCode,
columnType: 'varchar',
defaultValue: serializeDefaultValue(defaultValue?.currency),
defaultValue: serializeDefaultValue(defaultValue?.currencyCode),
},
];
}
@@ -29,15 +29,15 @@ export function generateTargetColumnMap(
return {
value: columnName,
};
case FieldMetadataType.URL:
case FieldMetadataType.LINK:
return {
text: `${columnName}_text`,
link: `${columnName}_link`,
label: `${columnName}Label`,
url: `${columnName}Url`,
};
case FieldMetadataType.MONEY:
case FieldMetadataType.CURRENCY:
return {
amount: `${columnName}_amount`,
currency: `${columnName}_currency`,
amountMicros: `${columnName}AmountMicros`,
currencyCode: `${columnName}CurrencyCode`,
};
default:
throw new BadRequestException(`Unknown type ${type}`);
@@ -13,7 +13,7 @@ export const serializeDefaultValue = (
if (typeof defaultValue === 'object' && 'type' in defaultValue) {
switch (defaultValue.type) {
case 'uuid':
return 'uuid_generate_v4()';
return 'public.uuid_generate_v4()';
case 'now':
return 'now()';
default:
@@ -54,22 +54,22 @@ export const validateDefaultValueBasedOnType = (
defaultValue.value instanceof Date
);
case FieldMetadataType.URL:
case FieldMetadataType.LINK:
return (
typeof defaultValue === 'object' &&
'text' in defaultValue &&
typeof defaultValue.text === 'string' &&
'link' in defaultValue &&
typeof defaultValue.link === 'string'
'label' in defaultValue &&
typeof defaultValue.label === 'string' &&
'url' in defaultValue &&
typeof defaultValue.url === 'string'
);
case FieldMetadataType.MONEY:
case FieldMetadataType.CURRENCY:
return (
typeof defaultValue === 'object' &&
'amount' in defaultValue &&
typeof defaultValue.amount === 'number' &&
'currency' in defaultValue &&
typeof defaultValue.currency === 'string'
'amountMicros' in defaultValue &&
typeof defaultValue.amountMicros === 'number' &&
'currencyCode' in defaultValue &&
typeof defaultValue.currencyCode === 'string'
);
default:
@@ -75,7 +75,7 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
isNullable: true,
isActive: true,
isCustom: false,
// isSystem: true,
isSystem: true,
workspaceId: record.workspaceId,
},
{
@@ -53,7 +53,7 @@ const personMetadata = {
{
isCustom: false,
isActive: true,
type: FieldMetadataType.URL,
type: FieldMetadataType.LINK,
name: 'linkedinUrl',
label: 'Linkedin',
targetColumnMap: {
@@ -66,7 +66,7 @@ const personMetadata = {
{
isCustom: false,
isActive: true,
type: FieldMetadataType.URL,
type: FieldMetadataType.LINK,
name: 'xUrl',
label: 'X',
targetColumnMap: {
@@ -0,0 +1,35 @@
import { ObjectMetadataInterface } from 'src/tenant/schema-builder/interfaces/object-metadata.interface';
import { FieldMetadataInterface } from 'src/tenant/schema-builder/interfaces/field-metadata.interface';
import { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.entity';
export const currencyObjectDefinition = {
id: FieldMetadataType.CURRENCY.toString(),
nameSingular: 'currency',
namePlural: 'currency',
labelSingular: 'Currency',
labelPlural: 'Currency',
targetTableName: '',
fields: [
{
id: 'amountMicros',
type: FieldMetadataType.NUMBER,
objectMetadataId: FieldMetadataType.CURRENCY.toString(),
name: 'amountMicros',
label: 'AmountMicros',
targetColumnMap: { value: 'amountMicros' },
isNullable: true,
} satisfies FieldMetadataInterface,
{
id: 'currencyCode',
type: FieldMetadataType.TEXT,
objectMetadataId: FieldMetadataType.CURRENCY.toString(),
name: 'currencyCode',
label: 'Currency Code',
targetColumnMap: { value: 'currencyCode' },
isNullable: true,
} satisfies FieldMetadataInterface,
],
fromRelations: [],
toRelations: [],
} satisfies ObjectMetadataInterface;
@@ -0,0 +1,35 @@
import { ObjectMetadataInterface } from 'src/tenant/schema-builder/interfaces/object-metadata.interface';
import { FieldMetadataInterface } from 'src/tenant/schema-builder/interfaces/field-metadata.interface';
import { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.entity';
export const linkObjectDefinition = {
id: FieldMetadataType.LINK.toString(),
nameSingular: 'link',
namePlural: 'link',
labelSingular: 'Link',
labelPlural: 'Link',
targetTableName: '',
fields: [
{
id: 'label',
type: FieldMetadataType.TEXT,
objectMetadataId: FieldMetadataType.LINK.toString(),
name: 'label',
label: 'Label',
targetColumnMap: { value: 'label' },
isNullable: true,
} satisfies FieldMetadataInterface,
{
id: 'url',
type: FieldMetadataType.TEXT,
objectMetadataId: FieldMetadataType.LINK.toString(),
name: 'url',
label: 'Url',
targetColumnMap: { value: 'url' },
isNullable: true,
} satisfies FieldMetadataInterface,
],
fromRelations: [],
toRelations: [],
} satisfies ObjectMetadataInterface;
@@ -1,34 +0,0 @@
import { ObjectMetadataInterface } from 'src/tenant/schema-builder/interfaces/object-metadata.interface';
import { FieldMetadataInterface } from 'src/tenant/schema-builder/interfaces/field-metadata.interface';
import { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.entity';
export const moneyObjectDefinition = {
id: FieldMetadataType.MONEY.toString(),
nameSingular: 'Money',
namePlural: 'Money',
labelSingular: 'Money',
labelPlural: 'Money',
targetTableName: 'money',
fields: [
{
id: 'amount',
type: FieldMetadataType.NUMBER,
objectMetadataId: FieldMetadataType.MONEY.toString(),
name: 'amount',
label: 'Amount',
targetColumnMap: { value: 'amount' },
isNullable: true,
} satisfies FieldMetadataInterface,
{
id: 'currency',
type: FieldMetadataType.TEXT,
objectMetadataId: FieldMetadataType.MONEY.toString(),
name: 'currency',
label: 'Currency',
targetColumnMap: { value: 'currency' },
} satisfies FieldMetadataInterface,
],
fromRelations: [],
toRelations: [],
} satisfies ObjectMetadataInterface;
@@ -1,33 +0,0 @@
import { ObjectMetadataInterface } from 'src/tenant/schema-builder/interfaces/object-metadata.interface';
import { FieldMetadataInterface } from 'src/tenant/schema-builder/interfaces/field-metadata.interface';
import { FieldMetadataType } from 'src/metadata/field-metadata/field-metadata.entity';
export const urlObjectDefinition = {
id: FieldMetadataType.URL.toString(),
nameSingular: 'Url',
namePlural: 'Url',
labelSingular: 'Url',
labelPlural: 'Url',
targetTableName: 'url',
fields: [
{
id: 'text',
type: FieldMetadataType.TEXT,
objectMetadataId: FieldMetadataType.URL.toString(),
name: 'text',
label: 'Text',
targetColumnMap: { value: 'text' },
} satisfies FieldMetadataInterface,
{
id: 'link',
type: FieldMetadataType.TEXT,
objectMetadataId: FieldMetadataType.URL.toString(),
name: 'link',
label: 'Link',
targetColumnMap: { value: 'link' },
} satisfies FieldMetadataInterface,
],
fromRelations: [],
toRelations: [],
} satisfies ObjectMetadataInterface;
@@ -52,7 +52,7 @@ export class TypeMapperService {
const numberScalar =
numberScalarMode === 'float' ? GraphQLFloat : GraphQLInt;
// URL and MONEY are handled in the factories because they are objects
// LINK and CURRENCY are handled in the factories because they are objects
const typeScalarMapping = new Map<FieldMetadataType, GraphQLScalarType>([
[FieldMetadataType.UUID, GraphQLID],
[FieldMetadataType.TEXT, GraphQLString],
@@ -78,7 +78,7 @@ export class TypeMapperService {
const numberScalar =
numberScalarMode === 'float' ? FloatFilterType : IntFilterType;
// URL and MONEY are handled in the factories because they are objects
// LINK and CURRENCY are handled in the factories because they are objects
const typeFilterMapping = new Map<
FieldMetadataType,
GraphQLInputObjectType | GraphQLScalarType<boolean, boolean>
@@ -100,7 +100,7 @@ export class TypeMapperService {
mapToOrderByType(
fieldMetadataType: FieldMetadataType,
): GraphQLInputType | undefined {
// URL and MONEY are handled in the factories because they are objects
// LINK and CURRENCY are handled in the factories because they are objects
const typeOrderByMapping = new Map<FieldMetadataType, GraphQLEnumType>([
[FieldMetadataType.UUID, OrderByDirectionType],
[FieldMetadataType.TEXT, OrderByDirectionType],
@@ -14,8 +14,8 @@ import {
} from './factories/input-type-definition.factory';
import { getFieldMetadataType } from './utils/get-field-metadata-type.util';
import { BuildSchemaOptions } from './interfaces/build-schema-optionts.interface';
import { moneyObjectDefinition } from './object-definitions/money.object-definition';
import { urlObjectDefinition } from './object-definitions/url.object-definition';
import { currencyObjectDefinition } from './object-definitions/currency.object-definition';
import { linkObjectDefinition } from './object-definitions/link.object-definition';
import { ObjectMetadataInterface } from './interfaces/object-metadata.interface';
import { FieldMetadataInterface } from './interfaces/field-metadata.interface';
import { FilterTypeDefinitionFactory } from './factories/filter-type-definition.factory';
@@ -61,8 +61,8 @@ export class TypeDefinitionsGenerator {
private generateStaticObjectTypeDefs(options: BuildSchemaOptions) {
const staticObjectMetadataCollection = [
moneyObjectDefinition,
urlObjectDefinition,
currencyObjectDefinition,
linkObjectDefinition,
];
this.logger.log(