Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 26 additions & 4 deletions admin/src/components/ConfigDiff/index.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import React from 'react';
import { useDispatch } from 'react-redux';
import RDV, { DiffMethod } from 'react-diff-viewer-continued';
import { useIntl } from 'react-intl';
import { getFetchClient, useNotification } from '@strapi/strapi/admin';

/**
* An issue with the diff-viewer library causes a difference in the way the library is exported.
Expand All @@ -19,13 +21,20 @@ if (typeof RDV.default !== 'undefined') {
}

import {
Button,
Modal,
Grid,
Typography,
} from '@strapi/design-system';
import ConfirmModal from '../ConfirmModal';
import { exportAllConfig, importAllConfig } from '../../state/actions/Config';

const ConfigDiff = ({ oldValue, newValue, configName, trigger }) => {
const { formatMessage } = useIntl();
const dispatch = useDispatch();
const { toggleNotification } = useNotification();
const { post, get } = getFetchClient();
const commonHeadingStyle = { paddingRight: '2rem' };

return (
<Modal.Root>
Expand All @@ -40,11 +49,24 @@ const ConfigDiff = ({ oldValue, newValue, configName, trigger }) => {
</Modal.Header>
<Modal.Body>
<Grid.Root paddingBottom={4} style={{ textAlign: 'center' }}>
<Grid.Item col={6}>
<Typography variant="delta" style={{ width: '100%' }}>{formatMessage({ id: 'config-sync.ConfigDiff.SyncDirectory' })}</Typography>
<Grid.Item col={6} style={{ justifyContent: 'center' }}>
<Typography variant="delta" style={commonHeadingStyle}>{formatMessage({ id: 'config-sync.ConfigDiff.SyncDirectory' })}
</Typography>
<ConfirmModal
type="import"
trigger={<Button title="Import config into DB for this file only">Import</Button>}
onSubmit={(force) => dispatch(importAllConfig([configName], force, toggleNotification, formatMessage, post, get))}
/>
</Grid.Item>
<Grid.Item col={6}>
<Typography variant="delta" style={{ width: '100%' }}>{formatMessage({ id: 'config-sync.ConfigDiff.Database' })}</Typography>
<Grid.Item col={6} style={{ justifyContent: 'center' }}>
<Typography variant="delta" style={commonHeadingStyle}>
{formatMessage({ id: 'config-sync.ConfigDiff.Database' })}
</Typography>
<ConfirmModal
type="export"
trigger={<Button title="Export DB config for this file only">Export</Button>}
onSubmit={() => dispatch(exportAllConfig([configName], toggleNotification, formatMessage, post, get))}
/>
</Grid.Item>
</Grid.Root>
<Typography variant="pi">
Expand Down
46 changes: 46 additions & 0 deletions playground/__tests__/cli.test.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
'use strict';

const fs = require('fs');
const util = require('util');
const exec = util.promisify(require('child_process').exec);

jest.setTimeout(20000);

const selectedConfig = 'admin-role.strapi-editor';
const otherConfig = 'admin-role.strapi-author';
const selectedConfigPath = `config/sync/${selectedConfig}.json`;
const otherConfigPath = `config/sync/${otherConfig}.json`;

describe('Test the config-sync CLI', () => {
afterAll(async () => {
// Remove the generated files and the DB.
Expand All @@ -19,6 +25,46 @@ describe('Test the config-sync CLI', () => {
expect(diffOutput).toContain('No differences between DB and sync directory');
});

test('Partial export only exports the selected config', async () => {
const selectedConfigBefore = fs.readFileSync(selectedConfigPath, 'utf8');
const otherConfigBefore = fs.readFileSync(otherConfigPath, 'utf8');
const selectedConfigData = JSON.parse(selectedConfigBefore);
selectedConfigData.description = `${selectedConfigData.description} (partial export test)`;
fs.writeFileSync(selectedConfigPath, JSON.stringify(selectedConfigData));

try {
const { stdout: exportOutput } = await exec(`yarn cs export --partial ${selectedConfig} -y`);

expect(exportOutput).toContain(`Exported ${selectedConfig}`);
expect(exportOutput).not.toContain(`Exported ${otherConfig}`);
expect(JSON.parse(fs.readFileSync(selectedConfigPath, 'utf8'))).toEqual(JSON.parse(selectedConfigBefore));
expect(fs.readFileSync(otherConfigPath, 'utf8')).toBe(otherConfigBefore);
} finally {
fs.writeFileSync(selectedConfigPath, selectedConfigBefore);
}
});

test('Partial import only imports the selected config', async () => {
const selectedConfigBefore = fs.readFileSync(selectedConfigPath, 'utf8');
const otherConfigBefore = fs.readFileSync(otherConfigPath, 'utf8');
const selectedConfigData = JSON.parse(selectedConfigBefore);
selectedConfigData.description = `${selectedConfigData.description} (partial import test)`;
fs.writeFileSync(selectedConfigPath, JSON.stringify(selectedConfigData));

try {
const { stdout: importOutput } = await exec(`yarn cs import --partial ${selectedConfig} -y`);
const { stdout: diffOutput } = await exec('yarn cs diff');

expect(importOutput).toContain(`Imported ${selectedConfig}`);
expect(importOutput).not.toContain(`Imported ${otherConfig}`);
expect(diffOutput).toContain('No differences between DB and sync directory');
expect(fs.readFileSync(otherConfigPath, 'utf8')).toBe(otherConfigBefore);
} finally {
fs.writeFileSync(selectedConfigPath, selectedConfigBefore);
await exec(`yarn cs import --partial ${selectedConfig} -y`);
}
});

test('Import (delete)', async () => {
// Remove a file to trigger a delete.
await exec('mv config/sync/admin-role.strapi-editor.json .tmp');
Expand Down
Loading