Merge pull request #60 from payloadcms/fix/localized-within-blocks

Fix/localized within blocks
This commit is contained in:
James Mikrut
2021-02-05 15:15:49 -06:00
committed by GitHub
14 changed files with 176 additions and 28 deletions

2
.vscode/launch.json vendored
View File

@@ -14,7 +14,7 @@
"--runInBand"
],
"env": {
"PAYLOAD_CONFIG_PATH": "demo/payload.config.js"
"PAYLOAD_CONFIG_PATH": "demo/payload.config.ts"
},
"console": "integratedTerminal",
"internalConsoleOptions": "neverOpen",

View File

@@ -1,4 +1,20 @@
import { PayloadCollectionConfig } from '../../src/collections/config/types';
import { Block } from '../../src/fields/config/types';
const RichTextBlock: Block = {
slug: 'richTextBlock',
labels: {
singular: 'Rich Text Block',
plural: 'Rich Text Blocks',
},
fields: [
{
name: 'content',
localized: true,
type: 'richText',
},
],
};
const LocalizedPosts: PayloadCollectionConfig = {
slug: 'localized-posts',
@@ -48,6 +64,54 @@ const LocalizedPosts: PayloadCollectionConfig = {
required: true,
localized: true,
},
{
name: 'localizedGroup',
label: 'Localized Group',
type: 'group',
localized: true,
fields: [
{
type: 'text',
name: 'text',
label: 'Text',
},
],
},
{
name: 'nonLocalizedGroup',
label: 'Non-Localized Group',
type: 'group',
fields: [
{
type: 'text',
name: 'text',
label: 'Text',
localized: true,
},
],
},
{
type: 'array',
label: 'Non-Localized Array',
name: 'nonLocalizedArray',
maxRows: 3,
fields: [
{
type: 'text',
name: 'localizedEmbeddedText',
label: 'Localized Embedded Text',
localized: true,
},
],
},
{
label: 'Blocks',
name: 'richTextBlocks',
type: 'blocks',
blocks: [
RichTextBlock,
],
},
],
timestamps: true,
};

View File

@@ -27,7 +27,8 @@ const LocalizedArrays: PayloadCollectionConfig = {
label: 'Array Text 1',
type: 'text',
required: true,
}, {
},
{
name: 'arrayText2',
label: 'Array Text 2',
type: 'text',

View File

@@ -99,6 +99,7 @@
"jest": "26.6.3",
"joi": "^17.3.0",
"jsonwebtoken": "^8.5.1",
"lodash.merge": "^4.6.2",
"method-override": "^3.0.0",
"micro-memoize": "^4.0.9",
"mini-css-extract-plugin": "1.3.3",

View File

@@ -4,7 +4,7 @@ import localizationPlugin from '../localization/plugin';
import buildSchema from '../mongoose/buildSchema';
const buildCollectionSchema = (collection, config, schemaOptions = {}) => {
const schema = buildSchema(collection.fields, { timestamps: collection.timestamps !== false, ...schemaOptions });
const schema = buildSchema(config, collection.fields, { timestamps: collection.timestamps !== false, ...schemaOptions });
schema.plugin(paginate)
.plugin(buildQueryPlugin);

View File

@@ -1,5 +1,6 @@
import httpStatus from 'http-status';
import deepmerge from 'deepmerge';
import merge from 'lodash.merge';
import path from 'path';
import { UploadedFile } from 'express-fileupload';
import { Where, Document } from '../../types';
@@ -103,7 +104,7 @@ async function update(incomingArgs: Arguments): Promise<Document> {
if (!doc && hasWherePolicy) throw new Forbidden();
if (locale && doc.setLocale) {
doc.setLocale(locale, fallbackLocale);
doc.setLocale(locale, null);
}
let originalDoc: Document = doc.toJSON({ virtuals: true });
@@ -245,10 +246,14 @@ async function update(incomingArgs: Arguments): Promise<Document> {
// Update
// /////////////////////////////////////
Object.assign(doc, data);
merge(doc, data);
await doc.save();
if (locale && doc.setLocale) {
doc.setLocale(locale, fallbackLocale);
}
let result: Document = doc.toJSON({ virtuals: true });
result = removeInternalFields(result);

View File

@@ -44,6 +44,26 @@ describe('Collections - REST', () => {
title: 'title',
description: englishPostDesc,
priority: 1,
nonLocalizedGroup: {
text: 'english',
},
localizedGroup: {
text: 'english',
},
nonLocalizedArray: [
{
localizedEmbeddedText: 'english',
},
],
richTextBlocks: [
{
blockType: 'richTextBlock',
blockName: 'Test Block Name',
content: [{
children: [{ text: 'english' }],
}],
},
],
}),
headers,
method: 'post',
@@ -54,6 +74,11 @@ describe('Collections - REST', () => {
expect(response.status).toBe(201);
expect(data.doc.title).not.toBeNull();
expect(data.doc.id).not.toBeNull();
expect(data.doc.nonLocalizedGroup.text).toStrictEqual('english');
expect(data.doc.localizedGroup.text).toStrictEqual('english');
expect(data.doc.nonLocalizedArray[0].localizedEmbeddedText).toStrictEqual('english');
expect(data.doc.richTextBlocks[0].content[0].children[0].text).toStrictEqual('english');
const timestampRegex = /^(\d{4})(?:-?W(\d+)(?:-?(\d+)D?)?|(?:-(\d+))?-(\d+))(?:[T ](\d+):(\d+)(?::(\d+)(?:\.(\d+))?)?)?(?:Z(-?\d*))?$/;
expect(data.doc.createdAt).toStrictEqual(expect.stringMatching(timestampRegex));
expect(data.doc.updatedAt).toStrictEqual(expect.stringMatching(timestampRegex));
@@ -101,6 +126,26 @@ describe('Collections - REST', () => {
title: 'title',
description: spanishPostDesc,
priority: 1,
nonLocalizedGroup: {
text: 'spanish',
},
localizedGroup: {
text: 'spanish',
},
nonLocalizedArray: [
{
localizedEmbeddedText: 'spanish',
},
],
richTextBlocks: [
{
blockType: 'richTextBlock',
blockName: 'Test Block Name',
content: [{
children: [{ text: 'spanish' }],
}],
},
],
}),
headers,
method: 'put',
@@ -110,6 +155,10 @@ describe('Collections - REST', () => {
expect(response.status).toBe(200);
expect(data.doc.description).toBe(spanishPostDesc);
expect(data.doc.nonLocalizedGroup.text).toStrictEqual('spanish');
expect(data.doc.localizedGroup.text).toStrictEqual('spanish');
expect(data.doc.nonLocalizedArray[0].localizedEmbeddedText).toStrictEqual('spanish');
expect(data.doc.richTextBlocks[0].content[0].children[0].text).toStrictEqual('spanish');
});
});
@@ -121,6 +170,10 @@ describe('Collections - REST', () => {
expect(response.status).toBe(200);
expect(data.description).toBe(englishPostDesc);
expect(data.nonLocalizedGroup.text).toStrictEqual('english');
expect(data.localizedGroup.text).toStrictEqual('english');
expect(data.nonLocalizedArray[0].localizedEmbeddedText).toStrictEqual('english');
expect(data.richTextBlocks[0].content[0].children[0].text).toStrictEqual('english');
});
it('should allow a localized post to be retrieved in specified English locale', async () => {
@@ -129,6 +182,10 @@ describe('Collections - REST', () => {
expect(response.status).toBe(200);
expect(data.description).toBe(englishPostDesc);
expect(data.nonLocalizedGroup.text).toStrictEqual('english');
expect(data.localizedGroup.text).toStrictEqual('english');
expect(data.nonLocalizedArray[0].localizedEmbeddedText).toStrictEqual('english');
expect(data.richTextBlocks[0].content[0].children[0].text).toStrictEqual('english');
});
it('should allow a localized post to be retrieved in Spanish', async () => {
@@ -137,6 +194,10 @@ describe('Collections - REST', () => {
expect(response.status).toBe(200);
expect(data.description).toBe(spanishPostDesc);
expect(data.nonLocalizedGroup.text).toStrictEqual('spanish');
expect(data.localizedGroup.text).toStrictEqual('spanish');
expect(data.nonLocalizedArray[0].localizedEmbeddedText).toStrictEqual('spanish');
expect(data.richTextBlocks[0].content[0].children[0].text).toStrictEqual('spanish');
});
it('should allow a localized post to be retrieved in all locales', async () => {

View File

@@ -171,7 +171,7 @@ const traverseFields = (args: Arguments): void => {
if ((operation === 'create' || operation === 'update') && field.name) {
const updatedData = data;
if (data[field.name] === undefined && originalDoc[field.name] === undefined && field.defaultValue) {
if (data?.[field.name] === undefined && originalDoc?.[field.name] === undefined && field.defaultValue) {
updatedData[field.name] = field.defaultValue;
}

View File

@@ -22,8 +22,8 @@ const validationPromise = async ({
const hasCondition = field.admin && field.admin.condition;
const shouldValidate = field.validate && !hasCondition;
let valueToValidate = newData[field.name];
if (valueToValidate === undefined) valueToValidate = existingData[field.name];
let valueToValidate = newData?.[field.name];
if (valueToValidate === undefined) valueToValidate = existingData?.[field.name];
if (valueToValidate === undefined) valueToValidate = field.defaultValue;
const result = shouldValidate ? await field.validate(valueToValidate, field) : true;

View File

@@ -14,7 +14,7 @@ const buildModel = (config: Config): mongoose.PaginateModel<any> | null => {
const Globals = mongoose.model('globals', globalsSchema);
Object.values(config.globals).forEach((globalConfig) => {
const globalSchema = buildSchema(globalConfig.fields);
const globalSchema = buildSchema(config, globalConfig.fields, {});
if (config.localization) {
globalSchema.plugin(localizationPlugin, config.localization);

View File

@@ -1,8 +1,8 @@
import deepmerge from 'deepmerge';
import merge from 'lodash.merge';
import overwriteMerge from '../../utilities/overwriteMerge';
import executeAccess from '../../auth/executeAccess';
import removeInternalFields from '../../utilities/removeInternalFields';
import { AfterChangeHook, BeforeValidateHook } from '../../collections/config/types';
async function update(args) {
const { globals: { Model } } = this;
@@ -39,7 +39,7 @@ async function update(args) {
}
if (locale && global.setLocale) {
global.setLocale(locale, fallbackLocale);
global.setLocale(locale, null);
}
const globalJSON = global.toJSON({ virtuals: true });
@@ -100,10 +100,14 @@ async function update(args) {
// 7. Perform database operation
// /////////////////////////////////////
Object.assign(global, data);
merge(global, data);
await global.save();
if (locale && global.setLocale) {
global.setLocale(locale, fallbackLocale);
}
global = global.toJSON({ virtuals: true });
// /////////////////////////////////////

View File

@@ -1,5 +1,5 @@
const sanitizeFallbackLocale = (fallbackLocale) => {
if (fallbackLocale === 'null' || fallbackLocale === 'none' || fallbackLocale === 'false' || fallbackLocale === false) {
if (fallbackLocale === 'null' || fallbackLocale === 'none' || fallbackLocale === 'false' || fallbackLocale === false || fallbackLocale === null) {
return null;
}

View File

@@ -1,11 +1,13 @@
/* eslint-disable no-use-before-define */
import { Schema, SchemaDefinition } from 'mongoose';
import { Config } from '../config/types';
import { MissingFieldInputOptions } from '../errors';
import { ArrayField, Block, BlockField, Field, GroupField, RadioField, RelationshipField, RowField, SelectField, UploadField } from '../fields/config/types';
import localizationPlugin from '../localization/plugin';
type FieldSchemaGenerator = (field: Field, fields: SchemaDefinition) => SchemaDefinition;
type FieldSchemaGenerator = (field: Field, fields: SchemaDefinition, config: Config) => SchemaDefinition;
const setBlockDiscriminators = (fields: Field[], schema: Schema) => {
const setBlockDiscriminators = (fields: Field[], schema: Schema, config: Config) => {
fields.forEach((field) => {
const blockFieldType = field as BlockField;
if (blockFieldType.type === 'blocks' && blockFieldType.blocks && blockFieldType.blocks.length > 0) {
@@ -15,16 +17,21 @@ const setBlockDiscriminators = (fields: Field[], schema: Schema) => {
blockItem.fields.forEach((blockField) => {
const fieldSchema: FieldSchemaGenerator = fieldToSchemaMap[blockField.type];
if (fieldSchema) {
blockSchemaFields = fieldSchema(blockField, blockSchemaFields);
blockSchemaFields = fieldSchema(blockField, blockSchemaFields, config);
}
});
const blockSchema = new Schema(blockSchemaFields, { _id: false });
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore Possible incorrect typing in mongoose types, this works
schema.path(field.name).discriminator(blockItem.slug, blockSchema);
setBlockDiscriminators(blockItem.fields, blockSchema);
if (config.localization) {
blockSchema.plugin(localizationPlugin, config.localization);
}
setBlockDiscriminators(blockItem.fields, blockSchema, config);
});
}
});
@@ -44,20 +51,20 @@ const formatBaseSchema = (field: Field) => {
};
};
const buildSchema = (configFields: Field[], options = {}): Schema => {
const buildSchema = (config: Config, configFields: Field[], options = {}): Schema => {
let fields = {};
configFields.forEach((field) => {
const fieldSchema: FieldSchemaGenerator = fieldToSchemaMap[field.type];
if (fieldSchema) {
fields = fieldSchema(field, fields);
fields = fieldSchema(field, fields, config);
}
});
const schema = new Schema(fields, options);
setBlockDiscriminators(configFields, schema);
setBlockDiscriminators(configFields, schema, config);
return schema;
};
@@ -153,22 +160,22 @@ const fieldToSchemaMap = {
[field.name]: schema,
};
},
row: (field: RowField, fields: SchemaDefinition): SchemaDefinition => {
row: (field: RowField, fields: SchemaDefinition, config: Config): SchemaDefinition => {
const newFields = { ...fields };
field.fields.forEach((rowField: Field) => {
const fieldSchemaMap: FieldSchemaGenerator = fieldToSchemaMap[rowField.type];
if (fieldSchemaMap) {
const fieldSchema = fieldSchemaMap(rowField, fields);
const fieldSchema = fieldSchemaMap(rowField, fields, config);
newFields[rowField.name] = fieldSchema[rowField.name];
}
});
return newFields;
},
array: (field: ArrayField, fields: SchemaDefinition) => {
const schema = buildSchema(field.fields, { _id: false, id: false });
array: (field: ArrayField, fields: SchemaDefinition, config: Config) => {
const schema = buildSchema(config, field.fields, { _id: false, id: false });
return {
...fields,
@@ -178,8 +185,8 @@ const fieldToSchemaMap = {
},
};
},
group: (field: GroupField, fields: SchemaDefinition): SchemaDefinition => {
const schema = buildSchema(field.fields, { _id: false, id: false });
group: (field: GroupField, fields: SchemaDefinition, config: Config): SchemaDefinition => {
const schema = buildSchema(config, field.fields, { _id: false, id: false });
return {
...fields,
@@ -210,12 +217,12 @@ const fieldToSchemaMap = {
};
},
blocks: (field: BlockField, fields: SchemaDefinition) => {
const flexibleSchema = new Schema({ blockName: String }, { discriminatorKey: 'blockType', _id: false, id: false });
const blocksSchema = new Schema({ blockName: String }, { discriminatorKey: 'blockType', _id: false, id: false });
return {
...fields,
[field.name]: {
type: [flexibleSchema],
type: [blocksSchema],
localized: field.localized || false,
},
};

View File

@@ -8222,6 +8222,11 @@ lodash.memoize@^4.1.2:
resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe"
integrity sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=
lodash.merge@^4.6.2:
version "4.6.2"
resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a"
integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==
lodash.once@^4.0.0:
version "4.1.1"
resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac"