Branch i18n bootstrap

This commit is contained in:
Alexandre Bodin 2021-06-28 22:37:19 +02:00
parent d5b96c6e97
commit fe4e355b24
23 changed files with 121 additions and 133 deletions

View File

@ -7,7 +7,9 @@ module.exports = {
'plugin:import/errors',
'plugin:import/warnings',
'plugin:node/recommended',
'plugin:jsdoc/recommended',
],
plugins: ['jsdoc'],
env: {
es6: true,
node: true,

View File

@ -16,7 +16,7 @@ const postgres = {
port: 5432,
host: 'localhost',
},
debug: true,
// debug: true,
};
const mysql = {

View File

@ -18,6 +18,7 @@
"eslint-config-airbnb-base": "^14.2.1",
"eslint-config-prettier": "^6.15.0",
"eslint-plugin-import": "^2.22.1",
"eslint-plugin-jsdoc": "35.4.0",
"eslint-plugin-jsx-a11y": "^6.4.1",
"eslint-plugin-node": "11.1.0",
"eslint-plugin-react": "^7.23.2",

View File

@ -46,12 +46,11 @@ module.exports = async () => {
const roleService = getService('role');
await roleService.createRolesIfNoneExist();
await roleService.resetSuperAdminPermissions();
await roleService.displayWarningIfNoSuperAdmin();
await permissionService.ensureBoundPermissionsInDatabase();
// await permissionService.cleanPermissionsInDatabase();
await permissionService.cleanPermissionsInDatabase();
await userService.displayWarningIfUsersDontHaveRole();

View File

@ -140,15 +140,13 @@ const filterPermissionsToRemove = async permissions => {
const cleanPermissionsInDatabase = async () => {
const pageSize = 200;
const contentTypeService = getService('content-type');
const total = await strapi.query('strapi::permission').count();
const pageCount = Math.ceil(total / pageSize);
console.log('cleanPermissionsInDatabase', { total, pageCount });
// let total = Infinity;
for (let page = 1; page < pageCount; page++) {
// 1. Find invalid permissions and collect their ID to delete them later
const results = await strapi
.query('strapi::permission')
.findMany({ limit: pageSize, offset: page * pageSize });
@ -161,7 +159,8 @@ const cleanPermissionsInDatabase = async () => {
const remainingPermissions = permissions.filter(
permission => !permissionsIdToRemove.includes(permission.id)
);
const permissionsWithCleanFields = getService('content-type').cleanPermissionFields(
const permissionsWithCleanFields = contentTypeService.cleanPermissionFields(
remainingPermissions
);
@ -196,9 +195,7 @@ const ensureBoundPermissionsInDatabase = async () => {
const contentTypes = Object.values(strapi.contentTypes);
const editorRole = await strapi.query('strapi::role').findOne({
where: {
code: EDITOR_CODE,
},
where: { code: EDITOR_CODE },
});
if (isNil(editorRole)) {
@ -212,9 +209,7 @@ const ensureBoundPermissionsInDatabase = async () => {
where: {
subject: contentType.uid,
action: boundActions,
role: {
id: editorRole.id,
},
role: { id: editorRole.id },
},
});

View File

@ -376,15 +376,16 @@ const resetSuperAdminPermissions = async () => {
return;
}
const allActions = getService('permission').actionProvider.values();
const permissionService = getService('permission');
const contentTypeService = getService('content-type');
const allActions = permissionService.actionProvider.values();
const contentTypesActions = allActions.filter(action => isContentTypeAction(action));
const otherActions = allActions.filter(action => !isContentTypeAction(action));
// First, get the content-types permissions
const permissions = getService('content-type').getPermissionsWithNestedFields(
contentTypesActions
);
const permissions = contentTypeService.getPermissionsWithNestedFields(contentTypesActions);
// Then add every other permission
const otherPermissions = otherActions.reduce((acc, action) => {

View File

@ -57,8 +57,6 @@ class MysqlDialect extends Dialect {
}
if (field.type == 'TINY' && field.length == 1) {
console.log('coucou');
let value = field.string();
return value ? value == '1' : null;
}

View File

@ -6,9 +6,13 @@ const types = require('../types');
const { createField } = require('../fields');
const helpers = require('./helpers');
const fromRow = (metadata, row = {}) => {
const fromRow = (metadata, row) => {
const { attributes } = metadata;
if (_.isNil(row)) {
return null;
}
const obj = {};
for (const column in row) {
@ -41,8 +45,6 @@ const fromRow = (metadata, row = {}) => {
}
}
console.log(obj);
return obj;
};
@ -286,7 +288,7 @@ const createQueryBuilder = (uid, db) => {
helpers.applyJoins(qb, state.joins);
}
console.log('Running query: ', qb.toQuery());
// console.log('Running query: ', qb.toQuery());
const rows = await qb;

View File

@ -491,8 +491,8 @@ class Strapi {
Object.freeze(this.api);
}
getModel(modelKey, plugin) {
return this.db.getModel(modelKey, plugin);
getModel(uid) {
return this.contentTypes[uid];
}
/**

View File

@ -86,6 +86,8 @@ const createCoreStore = ({ environment: defaultEnv, db }) => {
type: (typeof value).toString(),
});
await db.query('strapi::core-store').update({ where: { id: data.id }, data });
} else {
const data = Object.assign({}, where, {

View File

@ -3,7 +3,6 @@
const { getService } = require('../../utils');
module.exports = async () => {
return;
const { sendDidInitializeEvent } = getService('metrics');
const { decorator } = getService('entity-service-decorator');
const { initDefaultLocale } = getService('locales');
@ -27,7 +26,9 @@ module.exports = async () => {
engine.registerI18nPermissionsHandlers();
// Hooks & Models
registerModelsHooks();
// TODO: implement
// registerModelsHooks();
sendDidInitializeEvent();
};

View File

@ -4,7 +4,6 @@ const { getDefaultLocale } = require('../utils');
const { getService } = require('../../../../../utils');
const migrateForBookshelf = require('./migrate-for-bookshelf');
const migrateForMongoose = require('./migrate-for-mongoose');
const after = () => {};
@ -18,14 +17,7 @@ const before = async ({ model, definition, previousDefinition, ORM }, context) =
const defaultLocale = await getDefaultLocale(model, ORM);
if (model.orm === 'bookshelf') {
await migrateForBookshelf(
{ ORM, defaultLocale, definition, previousDefinition, model },
context
);
} else if (model.orm === 'mongoose') {
await migrateForMongoose({ ORM, defaultLocale, model });
}
await migrateForBookshelf({ ORM, defaultLocale, definition, previousDefinition, model }, context);
};
module.exports = {

View File

@ -1,39 +0,0 @@
'use strict';
const pmap = require('p-map');
const BATCH_SIZE = 1000;
const migrateForMongoose = async ({ model, defaultLocale }) => {
let lastId;
const findParams = { locale: { $ne: defaultLocale } };
// eslint-disable-next-line no-constant-condition
while (true) {
if (lastId) {
findParams._id = { $gt: lastId };
}
const batch = await model
.find(findParams, ['id'])
.sort({ _id: 1 })
.limit(BATCH_SIZE);
if (batch.length > 0) {
lastId = batch[batch.length - 1]._id;
}
await pmap(batch, entry => model.deleteRelations(entry), {
concurrency: 100,
stopOnError: true,
});
if (batch.length < BATCH_SIZE) {
break;
}
}
await model.deleteMany({ locale: { $ne: defaultLocale } });
await model.updateMany({}, { $unset: { locale: '' } }, { strict: false });
};
module.exports = migrateForMongoose;

View File

@ -2,7 +2,7 @@
const { difference, keys, intersection, isEmpty } = require('lodash/fp');
const { getService } = require('../../../../utils');
const migrateForMongoose = require('./migrate-for-mongoose');
const migrateForBookshelf = require('./migrate-for-bookshelf');
// Migration when i18n is disabled on a field of a content-type that have i18n enabled
@ -22,11 +22,7 @@ const after = async ({ model, definition, previousDefinition, ORM }) => {
return;
}
if (model.orm === 'bookshelf') {
await migrateForBookshelf({ ORM, model, attributesToMigrate });
} else if (model.orm === 'mongoose') {
await migrateForMongoose({ model, attributesToMigrate });
}
await migrateForBookshelf({ ORM, model, attributesToMigrate });
};
const before = () => {};

View File

@ -1,24 +0,0 @@
'use strict';
const { migrate } = require('./migrate');
const { areScalarAttributesOnly } = require('./utils');
const batchUpdate = async ({ updatesInfo, model }) => {
const updates = updatesInfo.map(({ entriesIdsToUpdate, attributesValues }) => ({
updateMany: { filter: { _id: { $in: entriesIdsToUpdate } }, update: attributesValues },
}));
await model.bulkWrite(updates);
};
const migrateForMongoose = async ({ model, attributesToMigrate }) => {
const onlyScalarAttrs = areScalarAttributesOnly({ model, attributes: attributesToMigrate });
if (onlyScalarAttrs) {
await migrate({ model, attributesToMigrate }, { migrateFn: batchUpdate });
} else {
await migrate({ model, attributesToMigrate });
}
};
module.exports = migrateForMongoose;

View File

@ -4,9 +4,9 @@ const _ = require('lodash');
const { PUBLISHED_AT_ATTRIBUTE } = require('@strapi/utils').contentTypes.constants;
const { getService } = require('../../utils');
const fieldMigration = require('./migrations/field');
const enableContentTypeMigration = require('./migrations/content-type/enable');
const disableContentTypeMigration = require('./migrations/content-type/disable');
// const fieldMigration = require('./migrations/field');
// const enableContentTypeMigration = require('./migrations/content-type/enable');
// const disableContentTypeMigration = require('./migrations/content-type/disable');
module.exports = () => {
const contentTypeService = getService('content-types');
@ -40,7 +40,7 @@ module.exports = () => {
}
});
// FIXME: to implement
// TODO: to implement
// strapi.db.migrations.register(fieldMigration);
// strapi.db.migrations.register(enableContentTypeMigration);
// strapi.db.migrations.register(disableContentTypeMigration);

View File

@ -7,7 +7,7 @@ const { validateCreateLocaleInput, validateUpdateLocaleInput } = require('../val
const { formatLocale } = require('../domain/locale');
const sanitizeLocale = locale => {
const model = strapi.getModel('locale', 'i18n');
const model = strapi.contentTypes['plugins::i18n.locale'];
return sanitizeEntity(locale, { model });
};

View File

@ -71,7 +71,7 @@ describe('Locales', () => {
const params = { name_contains: 'en' };
const localesFound = await localesService.find(params);
expect(query).toHaveBeenCalledWith('locale', 'i18n');
expect(query).toHaveBeenCalledWith('plugins::i18n.locale');
expect(find).toHaveBeenCalledWith(params);
expect(localesFound).toMatchObject(locales);
});
@ -83,7 +83,7 @@ describe('Locales', () => {
global.strapi = { query };
const localeFound = await localesService.findById(1);
expect(query).toHaveBeenCalledWith('locale', 'i18n');
expect(query).toHaveBeenCalledWith('plugins::i18n.locale');
expect(findOne).toHaveBeenCalledWith({ id: 1 });
expect(localeFound).toMatchObject(locale);
});
@ -95,7 +95,7 @@ describe('Locales', () => {
global.strapi = { query };
const localeFound = await localesService.findByCode('fr');
expect(query).toHaveBeenCalledWith('locale', 'i18n');
expect(query).toHaveBeenCalledWith('plugins::i18n.locale');
expect(findOne).toHaveBeenCalledWith({ code: 'fr' });
expect(localeFound).toMatchObject(locale);
});
@ -114,7 +114,7 @@ describe('Locales', () => {
};
const createdLocale = await localesService.create(locale);
expect(query).toHaveBeenCalledWith('locale', 'i18n');
expect(query).toHaveBeenCalledWith('plugins::i18n.locale');
expect(create).toHaveBeenCalledWith(locale);
expect(createdLocale).toMatchObject(locale);
});
@ -133,7 +133,7 @@ describe('Locales', () => {
};
const updatedLocale = await localesService.update({ code: 'fr' }, { name: 'French' });
expect(query).toHaveBeenCalledWith('locale', 'i18n');
expect(query).toHaveBeenCalledWith('plugins::i18n.locale');
expect(update).toHaveBeenCalledWith({ code: 'fr' }, { name: 'French' });
expect(updatedLocale).toMatchObject(locale);
});
@ -155,7 +155,7 @@ describe('Locales', () => {
};
const deletedLocale = await localesService.delete({ id: 1 });
expect(query).toHaveBeenCalledWith('locale', 'i18n');
expect(query).toHaveBeenCalledWith('plugins::i18n.locale');
expect(deleteFn).toHaveBeenCalledWith({ id: 1 });
expect(deletedLocale).toMatchObject(locale);
});
@ -175,7 +175,7 @@ describe('Locales', () => {
};
const deletedLocale = await localesService.delete({ id: 1 });
expect(query).toHaveBeenCalledWith('locale', 'i18n');
expect(query).toHaveBeenCalledWith('plugins::i18n.locale');
expect(deleteFn).not.toHaveBeenCalled();
expect(deletedLocale).toBeUndefined();
});

View File

@ -6,16 +6,16 @@ const { getService } = require('../utils');
const { getCoreStore } = require('../utils');
const find = (...args) => strapi.query('locale', 'i18n').find(...args);
const find = params => strapi.query('plugins::i18n.locale').findMany({ where: params });
const findById = id => strapi.query('locale', 'i18n').findOne({ id });
const findById = id => strapi.query('plugins::i18n.locale').findOne({ where: { id } });
const findByCode = code => strapi.query('locale', 'i18n').findOne({ code });
const findByCode = code => strapi.query('plugins::i18n.locale').findOne({ where: { code } });
const count = params => strapi.query('locale', 'i18n').count(params);
const count = params => strapi.query('plugins::i18n.locale').count({ where: params });
const create = async locale => {
const result = await strapi.query('locale', 'i18n').create(locale);
const result = await strapi.query('plugins::i18n.locale').create({ data: locale });
getService('metrics').sendDidUpdateI18nLocalesEvent();
@ -23,7 +23,9 @@ const create = async locale => {
};
const update = async (params, updates) => {
const result = await strapi.query('locale', 'i18n').update(params, updates);
const result = await strapi
.query('plugins::i18n.locale')
.update({ where: params, data: updates });
getService('metrics').sendDidUpdateI18nLocalesEvent();
@ -31,11 +33,11 @@ const update = async (params, updates) => {
};
const deleteFn = async ({ id }) => {
const localeToDelete = await strapi.query('locale', 'i18n').findOne({ id });
const localeToDelete = await findById(id);
if (localeToDelete) {
await deleteAllLocalizedEntriesFor({ locale: localeToDelete.code });
const result = await strapi.query('locale', 'i18n').delete({ id });
const result = await strapi.query('plugins::i18n.locale').delete({ where: { id } });
getService('metrics').sendDidUpdateI18nLocalesEvent();

View File

@ -42,16 +42,15 @@ describe('Test Graphql API create localization', () => {
});
};
const locale = await strapi.query('locale', 'i18n').create({
code: 'fr',
name: 'French',
const locale = await strapi.query('plugins::i18n.locale').create({
data: { code: 'fr', name: 'French' },
});
localeId = locale.id;
});
afterAll(async () => {
await strapi.query('locale', 'i18n').delete({ id: localeId });
await strapi.query('plugins::i18n.locale').delete({ where: { id: localeId } });
await strapi.query('recipes').delete();
await strapi.destroy();
await builder.cleanup();

21
packages/plugins/i18n/utils/index.d.ts vendored Normal file
View File

@ -0,0 +1,21 @@
import * as locales from '../services/locales';
import * as permissions from '../services/permissions';
import * as contentTypes from '../services/content-types';
import * as metrics from '../services/metrics';
import * as entityServiceDecorator from '../services/entity-service-decorator';
import * as coreAPI from '../services/core-api';
import * as ISOLocales from '../services/iso-locales';
import * as localizations from '../services/localizations';
type S = {
permissions: typeof permissions;
metrics: typeof metrics;
locales: typeof locales;
localizations: typeof localizations;
['iso-locales']: typeof ISOLocales;
['content-types']: typeof contentTypes;
['entity-service-decorator']: typeof entityServiceDecorator;
['core-api']: typeof coreAPI;
};
export function getService<T extends keyof S>(name: T): S[T];

View File

@ -2,12 +2,13 @@
const { prop } = require('lodash/fp');
const getCoreStore = () =>
strapi.store({
const getCoreStore = () => {
return strapi.store({
environment: '',
type: 'plugin',
name: 'i18n',
});
};
// retrieve a local service
const getService = name => {

View File

@ -1330,6 +1330,15 @@
resolved "https://registry.yarnpkg.com/@emotion/weak-memoize/-/weak-memoize-0.2.5.tgz#8eed982e2ee6f7f4e44c253e12962980791efd46"
integrity sha512-6U71C2Wp7r5XtFtQzYrW5iKFT67OixrSxjI4MptCHzdSVlgabczzqLe0ZSgnub/5Kp4hSbpDB1tMytZY9pwxxA==
"@es-joy/jsdoccomment@^0.8.0-alpha.2":
version "0.8.0"
resolved "https://registry.yarnpkg.com/@es-joy/jsdoccomment/-/jsdoccomment-0.8.0.tgz#1add451f50f57597676ab85ee7bd0a273d7b7c43"
integrity sha512-Xd3GzYsL2sz2pcdtYt5Q0Wz1ol/o9Nt2UQL4nFPDcaEomvPmwjJsbjkKx1SKhl2h3TgwazNBLdcNr2m0UiGiFA==
dependencies:
comment-parser "^1.1.5"
esquery "^1.4.0"
jsdoc-type-pratt-parser "1.0.4"
"@eslint/eslintrc@^0.4.0":
version "0.4.0"
resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-0.4.0.tgz#99cc0a0584d72f1df38b900fb062ba995f395547"
@ -6587,6 +6596,11 @@ commander@^7.0.0, commander@^7.1.0:
resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7"
integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==
comment-parser@1.1.5, comment-parser@^1.1.5:
version "1.1.5"
resolved "https://registry.yarnpkg.com/comment-parser/-/comment-parser-1.1.5.tgz#453627ef8f67dbcec44e79a9bd5baa37f0bce9b2"
integrity sha512-RePCE4leIhBlmrqiYTvaqEeGYg7qpSl4etaIabKtdOQVi+mSTIBBklGUwIr79GXYnl3LpMwmDw4KeR2stNc6FA==
commondir@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b"
@ -8507,6 +8521,21 @@ eslint-plugin-import@^2.22.1:
resolve "^1.17.0"
tsconfig-paths "^3.9.0"
eslint-plugin-jsdoc@35.4.0:
version "35.4.0"
resolved "https://registry.yarnpkg.com/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-35.4.0.tgz#4f4809ffa9430070aed10087fe691b9ededa2b05"
integrity sha512-0cr+NkPTxpTiMCtYOd8W5fd2IyC/CmaTHKb+0bzkpP9p8HfmJ3B2/M6FWj97rQJOLwLMkx+g2MIEZsrttpbFmQ==
dependencies:
"@es-joy/jsdoccomment" "^0.8.0-alpha.2"
comment-parser "1.1.5"
debug "^4.3.1"
esquery "^1.4.0"
jsdoc-type-pratt-parser "^1.0.4"
lodash "^4.17.21"
regextras "^0.8.0"
semver "^7.3.5"
spdx-expression-parse "^3.0.1"
eslint-plugin-jsx-a11y@^6.4.1:
version "6.4.1"
resolved "https://registry.yarnpkg.com/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.4.1.tgz#a2d84caa49756942f42f1ffab9002436391718fd"
@ -12036,6 +12065,11 @@ jsbn@~0.1.0:
resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513"
integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM=
jsdoc-type-pratt-parser@1.0.4, jsdoc-type-pratt-parser@^1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-1.0.4.tgz#5750d2d32ffb001866537d3baaedea7cf84c7036"
integrity sha512-jzmW9gokeq9+bHPDR1nCeidMyFUikdZlbOhKzh9+/nJqB75XhpNKec1/UuxW5c4+O+Pi31Gc/dCboyfSm/pSpQ==
jsdom@^16.4.0:
version "16.5.3"
resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-16.5.3.tgz#13a755b3950eb938b4482c407238ddf16f0d2136"
@ -17270,6 +17304,11 @@ regexpu-core@^4.7.1:
unicode-match-property-ecmascript "^1.0.4"
unicode-match-property-value-ecmascript "^1.2.0"
regextras@^0.8.0:
version "0.8.0"
resolved "https://registry.yarnpkg.com/regextras/-/regextras-0.8.0.tgz#ec0f99853d4912839321172f608b544814b02217"
integrity sha512-k519uI04Z3SaY0fLX843MRXnDeG2+vHOFsyhiPZvNLe7r8rD2YNRjq4BQLZZ0oAr2NrtvZlICsXysGNFPGa3CQ==
registry-auth-token@^4.0.0:
version "4.2.1"
resolved "https://registry.yarnpkg.com/registry-auth-token/-/registry-auth-token-4.2.1.tgz#6d7b4006441918972ccd5fedcd41dc322c79b250"
@ -18778,7 +18817,7 @@ spdx-exceptions@^2.1.0:
resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz#3f28ce1a77a00372683eade4a433183527a2163d"
integrity sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==
spdx-expression-parse@^3.0.0:
spdx-expression-parse@^3.0.0, spdx-expression-parse@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz#cf70f50482eefdc98e3ce0a6833e4a53ceeba679"
integrity sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==