[ACA-1597] allow extensions overwrite existing settings (#523)

* merge objects within the arrays

* metadata properties

* code cleanup
This commit is contained in:
Denys Vuika 2018-07-20 14:46:43 +01:00 committed by GitHub
parent 8c9ffc1160
commit 3ab9cee163
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 136 additions and 38 deletions

View File

@ -214,20 +214,20 @@
},
"type": "object",
"required": ["name", "version"],
"required": ["$name", "$version"],
"properties": {
"name": {
"$name": {
"description": "Extension name",
"type": "string"
},
"version": {
"$version": {
"description": "Extension version",
"type": "string"
},
"description": {
"$description": {
"description": "Brief description on what the extension does"
},
"references": {
"$references": {
"description": "References to external files",
"type": "array",
"items": {

View File

@ -29,8 +29,10 @@ import { RuleRef } from './rule.extensions';
import { ActionRef, ContentActionRef } from './action.extensions';
export interface ExtensionConfig {
version: string;
references?: Array<string>;
$name: string;
$version: string;
$description?: string;
$references?: Array<string>;
rules?: Array<RuleRef>;
routes?: Array<RouteRef>;
actions?: Array<ActionRef>;

View File

@ -43,10 +43,59 @@ describe('ExtensionService', () => {
extensions = TestBed.get(ExtensionService);
});
describe('configs', () => {
it('should merge two arrays based on [id] keys', () => {
const left = [
{
name: 'item0'
},
{
id: '#1',
name: 'item1'
},
{
id: '#2',
name: 'item2'
}
];
const right = [
{
name: 'extra-1'
},
{
id: '#2',
name: 'custom2',
tag: 'extra tag'
}
];
const result = extensions.mergeArrays(left, right);
expect(result).toEqual([
{
id: '#1',
name: 'item1'
},
{
id: '#2',
name: 'custom2',
tag: 'extra tag'
},
{
name: 'item0'
},
{
name: 'extra-1'
},
]);
});
});
describe('actions', () => {
beforeEach(() => {
extensions.setup({
version: '1.0.0',
$name: 'test',
$version: '1.0.0',
actions: [
{
id: 'aca:actions/create-folder',
@ -179,7 +228,8 @@ describe('ExtensionService', () => {
beforeEach(() => {
extensions.setup({
version: '1.0.0',
$name: 'test',
$version: '1.0.0',
routes: [
{
id: 'aca:routes/about',
@ -237,7 +287,8 @@ describe('ExtensionService', () => {
describe('content actions', () => {
it('should load content actions from the config', () => {
extensions.setup({
version: '1.0.0',
$name: 'test',
$version: '1.0.0',
features: {
content: {
actions: [
@ -263,7 +314,8 @@ describe('ExtensionService', () => {
it('should sort content actions by order', () => {
extensions.setup({
version: '1.0.0',
$name: 'test',
$version: '1.0.0',
features: {
content: {
actions: [
@ -297,7 +349,8 @@ describe('ExtensionService', () => {
describe('open with', () => {
it('should load [open with] actions for the viewer', () => {
extensions.setup({
version: '1.0.0',
$name: 'test',
$version: '1.0.0',
features: {
viewer: {
openWith: [
@ -322,7 +375,8 @@ describe('ExtensionService', () => {
it('should load only enabled [open with] actions for the viewer', () => {
extensions.setup({
version: '1.0.0',
$name: 'test',
$version: '1.0.0',
features: {
viewer: {
openWith: [
@ -358,7 +412,8 @@ describe('ExtensionService', () => {
it('should sort [open with] actions by order', () => {
extensions.setup({
version: '1.0.0',
$name: 'test',
$version: '1.0.0',
features: {
viewer: {
openWith: [
@ -396,7 +451,8 @@ describe('ExtensionService', () => {
describe('create', () => {
it('should load [create] actions from config', () => {
extensions.setup({
version: '1.0.0',
$name: 'test',
$version: '1.0.0',
features: {
create: [
{
@ -415,7 +471,8 @@ describe('ExtensionService', () => {
it('should sort [create] actions by order', () => {
extensions.setup({
version: '1.0.0',
$name: 'test',
$version: '1.0.0',
features: {
create: [
{

View File

@ -74,8 +74,8 @@ export class ExtensionService implements RuleContext {
this.loadConfig(this.configPath, 0).then(result => {
let config = result.config;
if (config.references && config.references.length > 0) {
const plugins = config.references.map(
if (config.$references && config.$references.length > 0) {
const plugins = config.$references.map(
(name, idx) => this.loadConfig(`${this.pluginsPath}/${name}`, idx)
);
@ -86,7 +86,7 @@ export class ExtensionService implements RuleContext {
.map(entry => entry.config);
if (configs.length > 0) {
config = this.mergeConfigs(config, ...configs);
config = this.mergeObjects(config, ...configs);
}
this.setup(config);
@ -411,23 +411,50 @@ export class ExtensionService implements RuleContext {
return false;
}
// todo: requires overwrite support for array entries
// todo: overwrite only particular areas, don't touch version or other top-level props
protected mergeConfigs(...objects): any {
mergeObjects(...objects): any {
const result = {};
objects.forEach(source => {
Object.keys(source).forEach(prop => {
if (prop in result && Array.isArray(result[prop])) {
result[prop] = result[prop].concat(source[prop]);
} else if (prop in result && typeof result[prop] === 'object') {
result[prop] = this.mergeConfigs(result[prop], source[prop]);
} else {
result[prop] = source[prop];
if (!prop.startsWith('$')) {
if (prop in result && Array.isArray(result[prop])) {
// result[prop] = result[prop].concat(source[prop]);
result[prop] = this.mergeArrays(result[prop], source[prop]);
} else if (prop in result && typeof result[prop] === 'object') {
result[prop] = this.mergeObjects(result[prop], source[prop]);
} else {
result[prop] = source[prop];
}
}
});
});
return result;
}
mergeArrays(left: any[], right: any[]): any[] {
const result = [];
const map = {};
(left || []).forEach(entry => {
const element = entry;
if (element && element.hasOwnProperty('id')) {
map[element.id] = element;
} else {
result.push(element);
}
});
(right || []).forEach(entry => {
const element = entry;
if (element && element.hasOwnProperty('id') && map[element.id]) {
const merged = this.mergeObjects(map[element.id], element);
map[element.id] = merged;
} else {
result.push(element);
}
});
return Object.values(map).concat(result);
}
}

View File

@ -1,9 +1,8 @@
{
"$schema": "../../extension.schema.json",
"name": "app",
"version": "1.0.0",
"references": [
"$name": "app",
"$version": "1.0.0",
"$references": [
"plugin1.json",
"plugin2.json"
],

View File

@ -1,8 +1,8 @@
{
"$schema": "../../../extension.schema.json",
"version": "1.0.0",
"name": "plugin1",
"description": "demo plugin",
"$version": "1.0.0",
"$name": "plugin1",
"$description": "demo plugin",
"actions": [
{
@ -36,6 +36,19 @@
}
]
},
"navbar": [
{
"id": "app.navbar.primary",
"items": [
{
"id": "app.navbar.personalFiles",
"icon": "extension",
"title": "APP.BROWSE.PERSONAL.SIDENAV_LINK.LABEL",
"route": "personal-files"
}
]
}
],
"content": {
"actions": [
{

View File

@ -1,8 +1,8 @@
{
"$schema": "../../../extension.schema.json",
"version": "1.0.0",
"name": "plugin2",
"description": "demo plugin",
"$version": "1.0.0",
"$name": "plugin2",
"$description": "demo plugin",
"routes": [
{