From ccccf948540771daed31748b826c81f595f5ef9f Mon Sep 17 00:00:00 2001 From: Michele Santoro <10807610+michelu89@users.noreply.github.com> Date: Mon, 8 Apr 2024 14:39:05 +0200 Subject: [PATCH] Fix cannot change version of Aspect Model (#185) * Fixing changing namespace and version of models * Fix caching problem on current loaded model * Fix Github findings * Fix update title information * Fix cypress tests * Add testing for changing namespace and version * Fix dependencies should not update on changed file * Update sidebar-state.service.ts --- .../src/fixtures/anonymous-elements.txt | 2 +- .../change-namespace/aspect-workspace-one.txt | 30 +++ .../aspect-workspace-three.txt | 30 +++ .../change-namespace/aspect-workspace-two.txt | 30 +++ .../external-property-reference.cy.ts | 2 - ...nal-property-reference-with-children.cy.ts | 2 - .../external-property-reference.cy.ts | 2 - .../entity-that-extends-external-entity.cy.ts | 8 +- .../editor/load-anonymous-elements.cy.ts | 5 +- .../integration/settings/auto-validate.cy.ts | 2 +- .../settings/change-model-namespace.cy.ts | 176 ++++++++++++++++++ .../change-referenced-model-namespace.cy.ts | 25 --- core/apps/ame-e2e/src/support/commands.ts | 2 +- core/apps/ame-e2e/src/support/constants.ts | 9 +- core/apps/ame-e2e/src/support/helpers.ts | 14 -- core/apps/ame/src/app/app.component.ts | 2 +- .../components/loading/loading.component.ts | 6 +- core/apps/ame/src/assets/i18n/en.json | 23 ++- core/libs/api/src/lib/migrator-api.service.ts | 2 +- core/libs/api/src/lib/model-api.service.ts | 17 +- .../cache/src/lib/namespaces-cache.service.ts | 28 +-- .../confirm-dialog.component.html | 11 +- .../confirm-dialog.component.ts | 5 +- .../confirm-dialog/confirm-dialog.service.ts | 5 +- .../entity-value-modal.component.ts | 1 - .../entity-value-table.component.ts | 1 - .../entity-value-view/entity-value.service.ts | 13 +- .../entity-value/utils/EntityValueUtil.ts | 2 - .../validators/editor-dialog-validators.ts | 15 +- .../services/file-handling.service.ts | 166 ++++++++--------- core/libs/editor/src/lib/editor.service.ts | 25 +-- .../src/lib/models/confirm-dialog.enum.ts} | 8 +- .../element-service/aspect-model.service.ts | 2 +- .../lib/element-service/base-model-service.ts | 1 + .../rdf/src/lib/services/model.service.ts | 2 +- core/libs/rdf/src/lib/services/rdf.service.ts | 2 +- core/libs/rdf/src/lib/utils/rdf-model.ts | 33 ++-- .../src/lib/components/index.ts | 1 - ...amespace-confirmation-modal.component.html | 33 ---- .../namespace-confirmation-modal.component.ts | 86 --------- .../setting-dialog.component.ts | 79 +++++--- .../settings-dialog/src/lib/model/index.ts | 2 +- .../namespace-configuration.ts} | 12 +- .../src/lib/services/settings-form.service.ts | 32 ++-- .../src/lib/setting-dialog.module.ts | 2 - .../services/model-saving-tracker.service.ts | 4 +- .../sidebar/src/lib/sidebar-state.service.ts | 17 +- .../workspace-file-list.component.ts | 7 +- .../src/lib/models/language.interface.ts | 12 +- .../elements-search.component.ts | 5 +- .../files-search.component.spec.ts | 7 +- 51 files changed, 576 insertions(+), 432 deletions(-) create mode 100644 core/apps/ame-e2e/src/fixtures/change-namespace/aspect-workspace-one.txt create mode 100644 core/apps/ame-e2e/src/fixtures/change-namespace/aspect-workspace-three.txt create mode 100644 core/apps/ame-e2e/src/fixtures/change-namespace/aspect-workspace-two.txt create mode 100644 core/apps/ame-e2e/src/integration/settings/change-model-namespace.cy.ts delete mode 100644 core/apps/ame-e2e/src/integration/settings/change-referenced-model-namespace.cy.ts rename core/libs/{settings-dialog/src/lib/model/namespace.ts => editor/src/lib/models/confirm-dialog.enum.ts} (83%) delete mode 100644 core/libs/settings-dialog/src/lib/components/model-configuration/namespace-confirmation-modal/namespace-confirmation-modal.component.html delete mode 100644 core/libs/settings-dialog/src/lib/components/model-configuration/namespace-confirmation-modal/namespace-confirmation-modal.component.ts rename core/libs/settings-dialog/src/lib/{components/model-configuration/namespace-confirmation-modal/namespace-confirmation-modal.component.scss => model/namespace-configuration.ts} (67%) diff --git a/core/apps/ame-e2e/src/fixtures/anonymous-elements.txt b/core/apps/ame-e2e/src/fixtures/anonymous-elements.txt index b1681d29..671d3182 100644 --- a/core/apps/ame-e2e/src/fixtures/anonymous-elements.txt +++ b/core/apps/ame-e2e/src/fixtures/anonymous-elements.txt @@ -31,7 +31,7 @@ :property3 a samm:Property ; samm:characteristic [ a samm-c:Trait ; - samm-c:baseCharacteristic [ a samm:Characteristic ] ; + samm-c:baseCharacteristic [ a samm:Characteristic; samm:dataType xsd:string ] ; samm-c:constraint [ a samm:Constraint ] ; samm-c:constraint [ a samm:Constraint ] ] . diff --git a/core/apps/ame-e2e/src/fixtures/change-namespace/aspect-workspace-one.txt b/core/apps/ame-e2e/src/fixtures/change-namespace/aspect-workspace-one.txt new file mode 100644 index 00000000..c503bb63 --- /dev/null +++ b/core/apps/ame-e2e/src/fixtures/change-namespace/aspect-workspace-one.txt @@ -0,0 +1,30 @@ +# Copyright (c) 2024 Robert Bosch Manufacturing Solutions GmbH +# +# See the AUTHORS file(s) distributed with this work for +# additional information regarding authorship. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at https://mozilla.org/MPL/2.0/. +# +# SPDX-License-Identifier: MPL-2.0 + +@prefix samm: . +@prefix samm-c: . +@prefix samm-e: . +@prefix unit: . +@prefix rdf: . +@prefix rdfs: . +@prefix xsd: . +@prefix : . + +:AspectDefault a samm:Aspect ; + samm:properties (:property1) ; + samm:operations (); + samm:events () . + +:property1 a samm:Property; + samm:characteristic :Characteristic1 . + +:Characteristic1 a samm:Characteristic ; + samm:dataType xsd:string . diff --git a/core/apps/ame-e2e/src/fixtures/change-namespace/aspect-workspace-three.txt b/core/apps/ame-e2e/src/fixtures/change-namespace/aspect-workspace-three.txt new file mode 100644 index 00000000..fd44ca46 --- /dev/null +++ b/core/apps/ame-e2e/src/fixtures/change-namespace/aspect-workspace-three.txt @@ -0,0 +1,30 @@ +# Copyright (c) 2024 Robert Bosch Manufacturing Solutions GmbH +# +# See the AUTHORS file(s) distributed with this work for +# additional information regarding authorship. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at https://mozilla.org/MPL/2.0/. +# +# SPDX-License-Identifier: MPL-2.0 + +@prefix samm: . +@prefix samm-c: . +@prefix samm-e: . +@prefix unit: . +@prefix rdf: . +@prefix rdfs: . +@prefix xsd: . +@prefix : . + +:NewName a samm:Aspect ; + samm:properties (:property1) ; + samm:operations (); + samm:events () . + +:property1 a samm:Property; + samm:characteristic :Characteristic1 . + +:Characteristic1 a samm:Characteristic ; + samm:dataType xsd:string . diff --git a/core/apps/ame-e2e/src/fixtures/change-namespace/aspect-workspace-two.txt b/core/apps/ame-e2e/src/fixtures/change-namespace/aspect-workspace-two.txt new file mode 100644 index 00000000..513fc89b --- /dev/null +++ b/core/apps/ame-e2e/src/fixtures/change-namespace/aspect-workspace-two.txt @@ -0,0 +1,30 @@ +# Copyright (c) 2024 Robert Bosch Manufacturing Solutions GmbH +# +# See the AUTHORS file(s) distributed with this work for +# additional information regarding authorship. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at https://mozilla.org/MPL/2.0/. +# +# SPDX-License-Identifier: MPL-2.0 + +@prefix samm: . +@prefix samm-c: . +@prefix samm-e: . +@prefix unit: . +@prefix rdf: . +@prefix rdfs: . +@prefix xsd: . +@prefix : . + +:AspectDefault a samm:Aspect ; + samm:properties (:property1) ; + samm:operations (); + samm:events () . + +:property1 a samm:Property; + samm:characteristic :Characteristic1 . + +:Characteristic1 a samm:Characteristic ; + samm:dataType xsd:string . diff --git a/core/apps/ame-e2e/src/integration/drag-and-drop/different-namespace/external-property-reference.cy.ts b/core/apps/ame-e2e/src/integration/drag-and-drop/different-namespace/external-property-reference.cy.ts index 1e7aebbc..6346f00d 100644 --- a/core/apps/ame-e2e/src/integration/drag-and-drop/different-namespace/external-property-reference.cy.ts +++ b/core/apps/ame-e2e/src/integration/drag-and-drop/different-namespace/external-property-reference.cy.ts @@ -16,8 +16,6 @@ import {cyHelp} from '../../../support/helpers'; import { SELECTOR_ecProperty, - SELECTOR_fileMenuFindElements, - SELECTOR_namespaceFileMenuButton, SELECTOR_openNamespacesButton, SELECTOR_searchElementsInp, SELECTOR_workspaceBtn, diff --git a/core/apps/ame-e2e/src/integration/drag-and-drop/same-namespace/external-property-reference-with-children.cy.ts b/core/apps/ame-e2e/src/integration/drag-and-drop/same-namespace/external-property-reference-with-children.cy.ts index b13ef909..e2d5f855 100644 --- a/core/apps/ame-e2e/src/integration/drag-and-drop/same-namespace/external-property-reference-with-children.cy.ts +++ b/core/apps/ame-e2e/src/integration/drag-and-drop/same-namespace/external-property-reference-with-children.cy.ts @@ -16,8 +16,6 @@ import {cyHelp} from '../../../support/helpers'; import { SELECTOR_ecProperty, - SELECTOR_fileMenuFindElements, - SELECTOR_namespaceFileMenuButton, SELECTOR_openNamespacesButton, SELECTOR_searchElementsInp, SELECTOR_workspaceBtn, diff --git a/core/apps/ame-e2e/src/integration/drag-and-drop/same-namespace/external-property-reference.cy.ts b/core/apps/ame-e2e/src/integration/drag-and-drop/same-namespace/external-property-reference.cy.ts index 2c88959e..08d84845 100644 --- a/core/apps/ame-e2e/src/integration/drag-and-drop/same-namespace/external-property-reference.cy.ts +++ b/core/apps/ame-e2e/src/integration/drag-and-drop/same-namespace/external-property-reference.cy.ts @@ -16,8 +16,6 @@ import {cyHelp} from '../../../support/helpers'; import { SELECTOR_ecProperty, - SELECTOR_fileMenuFindElements, - SELECTOR_namespaceFileMenuButton, SELECTOR_openNamespacesButton, SELECTOR_searchElementsInp, SELECTOR_workspaceBtn, diff --git a/core/apps/ame-e2e/src/integration/editor/entity-that-extends-external-entity.cy.ts b/core/apps/ame-e2e/src/integration/editor/entity-that-extends-external-entity.cy.ts index a5a18b06..de658b05 100644 --- a/core/apps/ame-e2e/src/integration/editor/entity-that-extends-external-entity.cy.ts +++ b/core/apps/ame-e2e/src/integration/editor/entity-that-extends-external-entity.cy.ts @@ -29,9 +29,9 @@ describe('Test loading aspect with extended external Entity', () => { .wait(500) .get('.mat-mdc-cell') .contains( - ' The Aspect model contains an external reference that is not included in the namespace file structure or is invalid' + ' The Aspect model contains an external reference that is not included in the namespace file structure or is invalid', ) - .should('exist') + .should('exist'), ) .then(() => cy.wait(500).get(SELECTOR_notificationsDialogCloseButton).click({force: true})); }); @@ -54,7 +54,7 @@ describe('Test loading aspect with extended external Entity', () => { }, { fixture: '/external-reference/same-namespace/model-with-entity.txt', - } + }, ); cy.visitDefault(); @@ -94,7 +94,7 @@ describe('Test loading aspect with extended external Entity', () => { }, { fixture: '/external-reference/different-namespace/model-with-entity.txt', - } + }, ); cy.visitDefault(); diff --git a/core/apps/ame-e2e/src/integration/editor/load-anonymous-elements.cy.ts b/core/apps/ame-e2e/src/integration/editor/load-anonymous-elements.cy.ts index 30c86432..8484e1ab 100644 --- a/core/apps/ame-e2e/src/integration/editor/load-anonymous-elements.cy.ts +++ b/core/apps/ame-e2e/src/integration/editor/load-anonymous-elements.cy.ts @@ -56,9 +56,10 @@ describe('Test load aspect model with anonymous elements', () => { ':Trait1 a samm-c:Trait;\n' + ' samm-c:baseCharacteristic :Characteristic3;\n' + ' samm-c:constraint :Constraint1, :Constraint2.\n' + - ':Characteristic3 a samm:Characteristic.\n' + + ':Characteristic3 a samm:Characteristic;\n' + + ' samm:dataType xsd:string.\n' + ':Constraint1 a samm:Constraint.\n' + - ':Constraint2 a samm:Constraint.\n', + ':Constraint2 a samm:Constraint.', ); }); }); diff --git a/core/apps/ame-e2e/src/integration/settings/auto-validate.cy.ts b/core/apps/ame-e2e/src/integration/settings/auto-validate.cy.ts index 8abde39d..ebf37296 100644 --- a/core/apps/ame-e2e/src/integration/settings/auto-validate.cy.ts +++ b/core/apps/ame-e2e/src/integration/settings/auto-validate.cy.ts @@ -14,7 +14,7 @@ import {SELECTOR_settingsButton, SettingsDialogSelectors} from '../../support/constants'; describe('Auto Validate', () => { - it('should open settings dialog', () => { + it('should open editor settings dialog', () => { cy.visitDefault() .then(() => cy.startModelling()) .then(() => { diff --git a/core/apps/ame-e2e/src/integration/settings/change-model-namespace.cy.ts b/core/apps/ame-e2e/src/integration/settings/change-model-namespace.cy.ts new file mode 100644 index 00000000..26a52ab3 --- /dev/null +++ b/core/apps/ame-e2e/src/integration/settings/change-model-namespace.cy.ts @@ -0,0 +1,176 @@ +/* eslint-disable cypress/no-unnecessary-waiting */ +/* + * Copyright (c) 2024 Robert Bosch Manufacturing Solutions GmbH + * + * See the AUTHORS file(s) distributed with this work for + * additional information regarding authorship. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * SPDX-License-Identifier: MPL-2.0 + */ + +/// + +import 'cypress-real-events'; +import { + ACTION_dialogButton, + FIELD_name, + SELECTOR_namespaceTabValueInput, + SELECTOR_namespaceTabVersionInput, + SELECTOR_settingsButton, + SELECTOR_workspaceBtn, + SettingsDialogSelectors, +} from '../../support/constants'; +import {cyHelp} from '../../support/helpers'; + +describe('Test namespace settings dialog', () => { + describe('Test changing model namespace version', () => { + it('should open namespace settings', () => { + cy.visitDefault(); + cy.startModelling(); + openNamespaceSettings().then(() => verifyNamespaceSettings('org.eclipse.examples.aspect', '1.0.0')); + }); + + it('change namespace and version on settings', () => { + changeNamespaceSettings('org.eclipse.examples.test', '2.0.0') + .then(() => openNamespaceSettings()) + .then(() => verifyNamespaceSettings('org.eclipse.examples.test', '2.0.0')) + .then(() => cy.get(SettingsDialogSelectors.settingsDialogCancelButton).click()); + }); + }); + + describe('Test changing model namespace version', () => { + it('should change namespace and version and save aspect model', () => { + cy.intercept('GET', 'http://localhost:9091/ame/api/models/namespaces?shouldRefresh=true', {}); + + cy.visitDefault(); + cy.fixture('/change-namespace/aspect-workspace-one') + .as('rdfString') + .then(rdfString => cy.loadModel(rdfString)); + + cy.intercept('GET', 'http://localhost:9091/ame/api/models/namespaces?shouldRefresh=true', { + 'org.eclipse.examples.one:1.0.0': ['AspectDefault.ttl'], + }); + + cy.intercept( + { + method: 'POST', + url: 'http://localhost:9091/ame/api/models', + headers: {namespace: 'org.eclipse.examples.one:1.0.0', 'file-name': 'AspectDefault.ttl'}, + }, + { + fixture: '/change-namespace/aspect-workspace-one.txt', + }, + ); + + cy.saveAspectModelToWorkspace().then(() => cy.get(SELECTOR_workspaceBtn).click()); + + cy.intercept('GET', 'http://localhost:9091/ame/api/models/namespaces?shouldRefresh=true', { + 'org.eclipse.examples.one:1.0.0': ['AspectDefault.ttl'], + 'org.eclipse.examples.two:2.0.0': ['AspectDefault.ttl'], + }); + + cy.intercept( + { + method: 'POST', + url: 'http://localhost:9091/ame/api/models', + headers: {namespace: 'org.eclipse.examples.two:2.0.0', 'file-name': 'AspectDefault.ttl'}, + }, + { + fixture: '/change-namespace/aspect-workspace-two.txt', + }, + ); + + openNamespaceSettings() + .then(() => changeNamespaceSettings('org.eclipse.examples.two', '2.0.0')) + .then(() => cy.saveAspectModelToWorkspace()) + .then(() => cy.get(ACTION_dialogButton).click()) + .then(() => openNamespaceSettings()) + .then(() => verifyNamespaceSettings('org.eclipse.examples.two', '2.0.0')) + .then(() => cy.get(SettingsDialogSelectors.settingsDialogCancelButton).click()) + .then(() => cy.get('.namespaces').invoke('html')) + .then(html => { + expect(html).contain('org.eclipse.examples.two:2.0.0'); + expect((html.match(/AspectDefault.ttl/g) || []).length).to.equal(2); + }) + .then(() => cy.get('[ng-reflect-message="This file is currently opened "]').invoke('html')) + .then(html => expect(html).contain('AspectDefault.ttl')); + }); + + it('should change name of aspect element and save to workspace', () => { + cy.intercept('GET', 'http://localhost:9091/ame/api/models/namespaces?shouldRefresh=true', { + 'org.eclipse.examples.one:1.0.0': ['AspectDefault.ttl'], + 'org.eclipse.examples.two:2.0.0': ['AspectDefault.ttl', 'NewName.ttl'], + }); + + cy.intercept( + { + method: 'POST', + url: 'http://localhost:9091/ame/api/models', + headers: {namespace: 'org.eclipse.examples.one:1.0.0', 'file-name': 'AspectDefault.ttl'}, + }, + { + fixture: '/change-namespace/aspect-workspace-one.txt', + }, + ); + + cy.intercept( + { + method: 'POST', + url: 'http://localhost:9091/ame/api/models', + headers: {namespace: 'org.eclipse.examples.two:2.0.0', 'file-name': 'AspectDefault.ttl'}, + }, + { + fixture: '/change-namespace/aspect-workspace-two.txt', + }, + ); + + cy.intercept( + { + method: 'POST', + url: 'http://localhost:9091/ame/api/models', + headers: {namespace: 'org.eclipse.examples.two:2.0.0', 'file-name': 'NewName.ttl'}, + }, + { + fixture: '/change-namespace/aspect-workspace-three.txt', + }, + ); + + cy.dbClickShape('AspectDefault') + .then(() => cy.get(FIELD_name).clear({force: true}).type('NewName')) + .then(() => cyHelp.clickSaveButton()) + .then(() => cy.saveAspectModelToWorkspace()) + .then(() => openNamespaceSettings()) + .then(() => verifyNamespaceSettings('org.eclipse.examples.two', '2.0.0')) + .then(() => cy.get(SettingsDialogSelectors.settingsDialogCancelButton).click()) + .then(() => cy.get('.namespaces').invoke('html')) + .then(html => { + expect(html).contain('org.eclipse.examples.two:2.0.0'); + expect((html.match(/AspectDefault.ttl/g) || []).length).to.equal(2); + }) + .then(() => cy.get('[ng-reflect-message="This file is currently opened "]').invoke('html')) + .then(html => expect(html).contain('NewName.ttl')); + }); + }); + + function openNamespaceSettings() { + return cy.get(SELECTOR_settingsButton).click().wait(1000).get(':nth-child(6) > .settings__node').click(); + } + + function verifyNamespaceSettings(namespace: string, version: string): void { + cy.get(SELECTOR_namespaceTabValueInput).should('have.value', namespace); + cy.get(SELECTOR_namespaceTabVersionInput).should('have.value', version); + } + + function changeNamespaceSettings(namespace: string, version: string) { + return cy + .get(SELECTOR_namespaceTabValueInput) + .clear() + .type(namespace) + .then(() => cy.get(SELECTOR_namespaceTabVersionInput).clear().type(version)) + .then(() => cy.get(SettingsDialogSelectors.settingsDialogOkButton).click()); + } +}); diff --git a/core/apps/ame-e2e/src/integration/settings/change-referenced-model-namespace.cy.ts b/core/apps/ame-e2e/src/integration/settings/change-referenced-model-namespace.cy.ts deleted file mode 100644 index 15c6448c..00000000 --- a/core/apps/ame-e2e/src/integration/settings/change-referenced-model-namespace.cy.ts +++ /dev/null @@ -1,25 +0,0 @@ -/* eslint-disable cypress/no-unnecessary-waiting */ -/* - * Copyright (c) 2024 Robert Bosch Manufacturing Solutions GmbH - * - * See the AUTHORS file(s) distributed with this work for - * additional information regarding authorship. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * SPDX-License-Identifier: MPL-2.0 - */ - -/// - -import 'cypress-real-events'; - -/** - * Take into consideration that these tests do not use real backend and verify - * only the behavior of a client-side part in isolation, backend responses are mocked - */ - -// TODO - add tests for the following scenarios: -describe('Test modifying referenced model', () => {}); diff --git a/core/apps/ame-e2e/src/support/commands.ts b/core/apps/ame-e2e/src/support/commands.ts index 5f87be2b..857cf708 100644 --- a/core/apps/ame-e2e/src/support/commands.ts +++ b/core/apps/ame-e2e/src/support/commands.ts @@ -517,7 +517,7 @@ Cypress.Commands.add('loadModel', (rdfString: string) => { Cypress.Commands.add('saveAspectModelToWorkspace', () => { cy.intercept('POST', 'http://localhost:9091/ame/api/models/validate', {fixture: 'model-validation-response.json'}); - cy.intercept('POST', 'http://localhost:9091/ame/api/models/format', () => {}); + cy.intercept('POST', 'http://localhost:9091/ame/api/models/format', {fixture: '/default-models/aspect-default.txt'}); return cy.window().then(win => { const fileHandlingService: FileHandlingService = win['angular.fileHandlingService']; diff --git a/core/apps/ame-e2e/src/support/constants.ts b/core/apps/ame-e2e/src/support/constants.ts index 6640eb99..911a6ac8 100644 --- a/core/apps/ame-e2e/src/support/constants.ts +++ b/core/apps/ame-e2e/src/support/constants.ts @@ -22,8 +22,6 @@ export const SIDEBAR_CLOSE_BUTTON = '[data-cy="sidebar-close"]'; export const SELECTOR_elementBtn = '[data-cy="elementsBtn"]'; export const SELECTOR_workspaceBtn = '[data-cy="workspaceBtn"]'; export const SELECTOR_searchElementsInp = '[data-cy="searchElements"]'; -export const SELECTOR_namespaceFileMenuButton = '[data-cy="namespaceFileMenuButton"]'; -export const SELECTOR_fileMenuFindElements = '[data-cy="fileMenuFindElements"]'; // Settings -> Namespace export const SELECTOR_namespaceTabValueInput = '[data-cy="namespaceTabValueInput"]'; @@ -31,7 +29,6 @@ export const SELECTOR_namespaceTabVersionInput = '[data-cy="namespaceTabVersionI export const SELECTOR_addEntityValue = '[data-cy="addNewEntityValueButton"]'; export const SELECTOR_clearEntityValueButton = '[data-cy="clear-entityValue-button"]'; export const SELECTOR_clearLanguageButton = '[data-cy="clear-language-button"]'; -export const SELECTOR_clearElementCharacteristicButton = '[data-cy="clear-element-characteristic-button"]'; export const SELECTOR_clearLeftCharacteristicButton = '[data-cy="clear-left-button"]'; export const SELECTOR_entitySaveButton = '[data-cy="entitySaveButton"]'; export const SELECTOR_openNamespacesButton = '.content > span'; @@ -106,6 +103,7 @@ export const SELECTOR_searchInputField = '[data-cy="searchInputField"]'; export const SELECTOR_searchEntityValueInputField = '[data-cy="searchEntityValueInputField"]'; // Editor canvas +export const SELECTOR_ecAspect = '[data-type="aspect"]'; export const SELECTOR_ecProperty = '[data-type="property"]'; export const SELECTOR_ecAbstractProperty = '[data-type="abstract-property"]'; export const SELECTOR_ecOperation = '[data-type="operation"]'; @@ -144,3 +142,8 @@ export const GENERATION_tbOutputButton = '[data-cy="tbOutputButton"]'; export const GENERATION_tbOutputButton_YAML = '[data-cy="tbOutputButton-yaml"]'; export const GENERATION_tbDownloadDoc = '[data-cy="tbDownloadDoc"]'; export const GENERATION_downloadFileButton = '[data-cy="downloadFileButton"]'; + +// Confirmation dialog +export const CANCEL_dialogButton = '[data-cy="cancelBtn"]'; +export const ACTION_dialogButton = '[data-cy="actionBtn"]'; +export const OK_dialogButton = '[data-cy="okBtn"]'; diff --git a/core/apps/ame-e2e/src/support/helpers.ts b/core/apps/ame-e2e/src/support/helpers.ts index 1f7cd044..52471e89 100644 --- a/core/apps/ame-e2e/src/support/helpers.ts +++ b/core/apps/ame-e2e/src/support/helpers.ts @@ -379,20 +379,6 @@ export class cyHelp { assert.isNotNull(modelElement.getPreferredName(langTag)); } - /** - * Updates the namespace and version of the current model. - * @param {string} name - The new namespace to set. - * @param {string} version - The new version to set. - */ - static updateNamespace(name: string, version: string): void { - cy.get(SELECTOR_settingsButton).click().wait(500); - cy.get(':nth-child(6) > .settings__node').click(); - cy.get(SELECTOR_namespaceTabValueInput).clear().type(name); - cy.get(SELECTOR_namespaceTabVersionInput).clear().type(version); - cy.get(SettingsDialogSelectors.settingsDialogOkButton).click(); - cy.get(SELECTOR_overrideNamespace).click(); - } - /** * Loads a model from a given RDF string. * @param {string} rdfString - The RDF string representing the model to load. diff --git a/core/apps/ame/src/app/app.component.ts b/core/apps/ame/src/app/app.component.ts index 1e834584..96f51b7f 100644 --- a/core/apps/ame/src/app/app.component.ts +++ b/core/apps/ame/src/app/app.component.ts @@ -64,7 +64,7 @@ export class AppComponent implements OnInit { this.themeService.setCssVars(this.configurationService.getSettings()?.useSaturatedColors ? 'dark' : 'light'); - if (window.location.search.includes('e2e=true')) { + if (window.location.search.includes('?e2e=true')) { return; } diff --git a/core/apps/ame/src/app/components/loading/loading.component.ts b/core/apps/ame/src/app/components/loading/loading.component.ts index 730608c0..6f9071e8 100644 --- a/core/apps/ame/src/app/components/loading/loading.component.ts +++ b/core/apps/ame/src/app/components/loading/loading.component.ts @@ -36,7 +36,8 @@ export class LoadingComponent implements AfterViewInit, OnDestroy { if (!this.electronTunnel.ipcRenderer) { this.modelApiService.getPredefinedModel(PREDEFINED_MODELS.SIMPLE_ASPECT).subscribe(model => { this.electronTunnel.startUpData$.next({isFirstWindow: true, model}); - this.router.navigate(['/editor']); + const queryParams = Object.fromEntries(new URLSearchParams(window.location.search)); + this.ngZone.run(() => this.router.navigate(['/editor'], {queryParams})); }); return; } @@ -54,7 +55,8 @@ export class LoadingComponent implements AfterViewInit, OnDestroy { // Because complete is called in electron callback, // router.navigate is called outside ngZone // and needs to be called in ngZone to function - this.ngZone.run(() => this.router.navigate(['/editor'])); + const queryParams = Object.fromEntries(new URLSearchParams(window.location.search)); + this.ngZone.run(() => this.router.navigate(['/editor'], {queryParams})); }, }); diff --git a/core/apps/ame/src/assets/i18n/en.json b/core/apps/ame/src/assets/i18n/en.json index 3b456f7b..2243b3d8 100644 --- a/core/apps/ame/src/assets/i18n/en.json +++ b/core/apps/ame/src/assets/i18n/en.json @@ -467,19 +467,18 @@ "CLOSE_BUTTON": "Keep current", "OK_BUTTON": "Reload" }, - "UPDATE_ASPECT_MODEL": { - "PHRASE1": "The Aspect model \"{{fileName}}\" is already defined in your file structure.", - "PHRASE2": "Are you sure you want to overwrite it?", - "TITLE": "Update Aspect Model", - "OK_BUTTON": "Overwrite" - }, "NAMESPACE_CHANGE": { - "PHRASE1": "The namespace for \"{{originalModelName}}\" model has been changed. The process will make the following changes:", - "PHRASE2": "• move \"{{originalModelName}}\" from \"{{originalModelNamespace}}\" to \"{{newNamespace}}\" namespace", - "PHRASE3": "• update Aspect models which depend on \"{{originalModelName}}\" model", - "PHRASE4": "Are you sure you want to continue?", - "TITLE": "Model's namespace has been changed", - "OK_BUTTON": "Continue" + "TITLE": "Update Model namespace", + "PHRASE1": "The namespace for the model \"{{originalModelName}}\" has been updated.", + "PHRASE2": "Old namespace: \"{{originalModelNamespace}}\"", + "PHRASE3": "New namespace: \"{{newNamespace}}\"", + "PHRASE4": "This update will:", + "PHRASE5": "• Relocate \"{{originalModelName}}\" from the old namespace to the new namespace.", + "PHRASE6": "• Refresh any Aspect models that are linked to \"{{originalModelName}}\" to reflect this change.", + "PHRASE7": "Do you want to apply these changes now?", + "OK_BUTTON": "Save and Update Dependencies", + "ACTION_BUTTON": "Save Changes Only", + "CANCEL_BUTTON": "Cancel" }, "SAVE_BEFORE_LOAD": { "PHRASE1": "You are about to load {{fileName}}.", diff --git a/core/libs/api/src/lib/migrator-api.service.ts b/core/libs/api/src/lib/migrator-api.service.ts index 28850147..37c8349a 100644 --- a/core/libs/api/src/lib/migrator-api.service.ts +++ b/core/libs/api/src/lib/migrator-api.service.ts @@ -46,7 +46,7 @@ export class MigratorApiService { private modelApiService: ModelApiService, private editorService: EditorService, ) { - if (this.browserService.isStartedAsElectronApp() && !window.location.search.includes('e2e=true')) { + if (this.browserService.isStartedAsElectronApp() && !window.location.search.includes('?e2e=true')) { const remote = window.require('@electron/remote'); this.serviceUrl = this.serviceUrl.replace(this.defaultPort, remote.getGlobal('backendPort')); } diff --git a/core/libs/api/src/lib/model-api.service.ts b/core/libs/api/src/lib/model-api.service.ts index c1d88958..99cd9bad 100644 --- a/core/libs/api/src/lib/model-api.service.ts +++ b/core/libs/api/src/lib/model-api.service.ts @@ -13,7 +13,7 @@ import {inject, Injectable} from '@angular/core'; import {HttpClient, HttpErrorResponse, HttpHeaders} from '@angular/common/http'; -import {catchError, map, mergeMap, tap, timeout, retry} from 'rxjs/operators'; +import {catchError, map, mergeMap, retry, tap, timeout} from 'rxjs/operators'; import {forkJoin, Observable, of, throwError} from 'rxjs'; import {APP_CONFIG, AppConfig, BrowserService, FileContentModel, HttpHeaderBuilder, LogService} from '@ame/shared'; import {ModelValidatorService} from './model-validator.service'; @@ -42,7 +42,7 @@ export class ModelApiService { private browserService: BrowserService, private modelValidatorService: ModelValidatorService, ) { - if (this.browserService.isStartedAsElectronApp() && !window.location.search.includes('e2e=true')) { + if (this.browserService.isStartedAsElectronApp() && !window.location.search.includes('?e2e=true')) { const remote = window.require('@electron/remote'); this.serviceUrl = this.serviceUrl.replace(this.defaultPort, remote.getGlobal('backendPort')); } @@ -94,10 +94,15 @@ export class ModelApiService { const [namespace, version, file] = absoluteModelName.split(':'); headers = new HttpHeaderBuilder().withNamespace(`${namespace}:${version}`).withFileName(file).build(); } - return this.http.post(`${this.serviceUrl}${this.api.models}`, rdfContent, {headers}).pipe( - timeout(this.requestTimeout), - catchError(res => throwError(() => res)), - ); + return this.http + .post(`${this.serviceUrl}${this.api.models}`, rdfContent, { + headers, + responseType: 'text', + }) + .pipe( + timeout(this.requestTimeout), + catchError(res => throwError(() => res)), + ); } formatModel(rdfContent: string): Observable { diff --git a/core/libs/cache/src/lib/namespaces-cache.service.ts b/core/libs/cache/src/lib/namespaces-cache.service.ts index 708bfd38..2a6a5d58 100644 --- a/core/libs/cache/src/lib/namespaces-cache.service.ts +++ b/core/libs/cache/src/lib/namespaces-cache.service.ts @@ -130,23 +130,6 @@ export class NamespacesCacheService { this.#namespaces = new Map>(); } - /** - * Updates the association of a file from an old namespace key to a new one. - * It handles the removal of the file from the old namespace and adds it under the new namespace, - * managing multiple file associations gracefully. - * - * @param {string} oldUrn - The current namespace key of the file. - * @param {string} newUrn - The new namespace key to associate the file with. - * @param {string} fileName - The name of the file to update the namespace for. - */ - updateNamespaceKey(oldUrn, newUrn, fileName) { - const oldUrnValues = this.#namespaces.get(oldUrn); - oldUrnValues?.size > 1 ? oldUrnValues.delete(fileName) : this.#namespaces.delete(oldUrn); - - const newUrnValues = this.#namespaces.get(newUrn); - newUrnValues ? newUrnValues.set(fileName, this.#currentCachedFile) : this.addFile(newUrn, fileName); - } - /** * This method will resolve the element when it´s not defined external. * @@ -163,12 +146,15 @@ export class NamespacesCacheService { /** * Retrieves a BaseMetaModelElement from a specified namespace using the provided aspectModelUrn. * - * @param {string} namespace - The namespace from which the element should be retrieved. + * @param {string} namespaceKey - The namespaceKey from which the element should be retrieved. * @param {string} aspectModelUrn - The URN of the aspect model element to find. - * @returns {BaseMetaModelElement | null} The found BaseMetaModelElement or null if not found. + * @returns {BaseMetaModelElement} The found BaseMetaModelElement or null if not found. */ - getElementFromNamespace(namespace: string, aspectModelUrn: string): BaseMetaModelElement { - for (const value of this.getNamespace(namespace).values()) { + getElementFromNamespace(namespaceKey: string, aspectModelUrn: string): BaseMetaModelElement { + const namespace = this.getNamespace(namespaceKey); + const source = namespace ? Array.from(namespace.values()) : [this.currentCachedFile]; + + for (const value of source) { const element = value.getElement(aspectModelUrn); if (element) { return element; diff --git a/core/libs/editor/src/lib/confirm-dialog/confirm-dialog.component.html b/core/libs/editor/src/lib/confirm-dialog/confirm-dialog.component.html index cdcfaf9f..440ac093 100644 --- a/core/libs/editor/src/lib/confirm-dialog/confirm-dialog.component.html +++ b/core/libs/editor/src/lib/confirm-dialog/confirm-dialog.component.html @@ -12,7 +12,7 @@ -->

{{ data.title }}

- @@ -21,6 +21,11 @@

{{ data.title }}

- - + + + diff --git a/core/libs/editor/src/lib/confirm-dialog/confirm-dialog.component.ts b/core/libs/editor/src/lib/confirm-dialog/confirm-dialog.component.ts index f27dba08..827cbe14 100644 --- a/core/libs/editor/src/lib/confirm-dialog/confirm-dialog.component.ts +++ b/core/libs/editor/src/lib/confirm-dialog/confirm-dialog.component.ts @@ -14,18 +14,21 @@ import {Component, Inject} from '@angular/core'; import {MAT_DIALOG_DATA, MatDialogRef} from '@angular/material/dialog'; import {DialogOptions} from './confirm-dialog.service'; +import {ConfirmDialogEnum} from '../models/confirm-dialog.enum'; @Component({ templateUrl: './confirm-dialog.component.html', styles: ['.dialog-title { font-size: 24px !important; }'], }) export class ConfirmDialogComponent { + protected readonly confirmDialogEnum = ConfirmDialogEnum; + constructor( @Inject(MAT_DIALOG_DATA) public data: DialogOptions, private dialogRef: MatDialogRef, ) {} - closeAndGiveResult(result: boolean) { + closeAndGiveResult(result: ConfirmDialogEnum) { this.dialogRef.close(result); } } diff --git a/core/libs/editor/src/lib/confirm-dialog/confirm-dialog.service.ts b/core/libs/editor/src/lib/confirm-dialog/confirm-dialog.service.ts index 5ec87015..ff8bb048 100644 --- a/core/libs/editor/src/lib/confirm-dialog/confirm-dialog.service.ts +++ b/core/libs/editor/src/lib/confirm-dialog/confirm-dialog.service.ts @@ -16,25 +16,28 @@ import {MatDialog} from '@angular/material/dialog'; import {first} from 'rxjs/operators'; import {ConfirmDialogComponent} from './confirm-dialog.component'; import {Observable} from 'rxjs'; +import {ConfirmDialogEnum} from '../models/confirm-dialog.enum'; export interface DialogOptions { phrases: string[]; title: string; closeButtonText?: string; okButtonText?: string; + actionButtonText?: string; } @Injectable({providedIn: 'root'}) export class ConfirmDialogService { constructor(private matDialog: MatDialog) {} - open({phrases, title, closeButtonText, okButtonText}: DialogOptions): Observable { + open({phrases, title, closeButtonText, okButtonText, actionButtonText}: DialogOptions): Observable { return this.matDialog .open(ConfirmDialogComponent, { data: { phrases, title, closeButtonText: closeButtonText || 'Close', + actionButtonText: actionButtonText || undefined, okButtonText: okButtonText || 'Continue', }, maxWidth: 650, diff --git a/core/libs/editor/src/lib/editor-dialog/components/entity-value/entity-value-modal/entity-value-modal.component.ts b/core/libs/editor/src/lib/editor-dialog/components/entity-value/entity-value-modal/entity-value-modal.component.ts index b0b5e2a9..857d63d9 100644 --- a/core/libs/editor/src/lib/editor-dialog/components/entity-value/entity-value-modal/entity-value-modal.component.ts +++ b/core/libs/editor/src/lib/editor-dialog/components/entity-value/entity-value-modal/entity-value-modal.component.ts @@ -25,7 +25,6 @@ import { OverWrittenProperty, } from '@ame/meta-model'; import {NamespacesCacheService} from '@ame/cache'; -import {isDataTypeLangString} from '@ame/shared'; import {EntityValueUtil} from '../utils/EntityValueUtil'; export interface NewEntityValueDialogOptions { diff --git a/core/libs/editor/src/lib/editor-dialog/components/entity-value/entity-value-table/entity-value-table.component.ts b/core/libs/editor/src/lib/editor-dialog/components/entity-value/entity-value-table/entity-value-table.component.ts index 2fe4c344..16b55876 100644 --- a/core/libs/editor/src/lib/editor-dialog/components/entity-value/entity-value-table/entity-value-table.component.ts +++ b/core/libs/editor/src/lib/editor-dialog/components/entity-value/entity-value-table/entity-value-table.component.ts @@ -37,7 +37,6 @@ import {InputFieldComponent} from '../../fields'; import {map, Observable, of, startWith, Subscription} from 'rxjs'; import * as locale from 'locale-codes'; import {MatAutocompleteTrigger} from '@angular/material/autocomplete'; -import {LanguageTranslationService} from '@ame/translation'; @Component({ selector: 'ame-entity-value-table', diff --git a/core/libs/editor/src/lib/editor-dialog/components/entity-value/entity-value-view/entity-value.service.ts b/core/libs/editor/src/lib/editor-dialog/components/entity-value/entity-value-view/entity-value.service.ts index 26a3a6d8..a8792dbf 100644 --- a/core/libs/editor/src/lib/editor-dialog/components/entity-value/entity-value-view/entity-value.service.ts +++ b/core/libs/editor/src/lib/editor-dialog/components/entity-value/entity-value-view/entity-value.service.ts @@ -17,6 +17,7 @@ import {ConfirmDialogService} from '@ame/editor'; import {NamespacesCacheService} from '@ame/cache'; import {NotificationsService} from '@ame/shared'; import {MxGraphHelper} from '@ame/mx-graph'; +import {ConfirmDialogEnum} from '../../../../models/confirm-dialog.enum'; @Injectable({ providedIn: 'root', @@ -49,8 +50,8 @@ export class EntityValueService { 'Do you want to continue?', ]; - this.confirmDialogService.open({title, phrases, closeButtonText: 'No', okButtonText: 'Yes'}).subscribe(result => { - if (result) { + this.confirmDialogService.open({title, phrases, closeButtonText: 'No', okButtonText: 'Yes'}).subscribe(confirm => { + if (confirm !== ConfirmDialogEnum.cancel) { for (const entityValue of entityValues) { entityValue.removeProperty(property); } @@ -90,8 +91,8 @@ export class EntityValueService { 'Do you want to continue?', ]; - this.confirmDialogService.open({title, phrases, closeButtonText: 'No', okButtonText: 'Yes'}).subscribe(result => { - if (result) { + this.confirmDialogService.open({title, phrases, closeButtonText: 'No', okButtonText: 'Yes'}).subscribe(confirm => { + if (confirm !== ConfirmDialogEnum.cancel) { for (const entityValue of entityValues) { this.currentCachedFile.removeElement(entityValue.aspectModelUrn); } @@ -118,8 +119,8 @@ export class EntityValueService { 'Do you want to continue?', ]; - this.confirmDialogService.open({title, phrases, closeButtonText: 'No', okButtonText: 'Yes'}).subscribe(result => { - if (!result) { + this.confirmDialogService.open({title, phrases, closeButtonText: 'No', okButtonText: 'Yes'}).subscribe(confirm => { + if (confirm === ConfirmDialogEnum.cancel) { return; } diff --git a/core/libs/editor/src/lib/editor-dialog/components/entity-value/utils/EntityValueUtil.ts b/core/libs/editor/src/lib/editor-dialog/components/entity-value/utils/EntityValueUtil.ts index 40213b22..8dc717ef 100644 --- a/core/libs/editor/src/lib/editor-dialog/components/entity-value/utils/EntityValueUtil.ts +++ b/core/libs/editor/src/lib/editor-dialog/components/entity-value/utils/EntityValueUtil.ts @@ -21,10 +21,8 @@ import { Entity, EntityValueProperty, OverWrittenProperty, - Property, } from '@ame/meta-model'; import {CachedFile} from '@ame/cache'; -import {Observable} from 'rxjs'; import {extractNamespace} from '@ame/utils'; import {isDataTypeLangString} from '@ame/shared'; diff --git a/core/libs/editor/src/lib/editor-dialog/validators/editor-dialog-validators.ts b/core/libs/editor/src/lib/editor-dialog/validators/editor-dialog-validators.ts index 6a16cb8f..cbb5c7c8 100644 --- a/core/libs/editor/src/lib/editor-dialog/validators/editor-dialog-validators.ts +++ b/core/libs/editor/src/lib/editor-dialog/validators/editor-dialog-validators.ts @@ -16,7 +16,6 @@ import {AbstractControl, ValidatorFn} from '@angular/forms'; import {NamespacesCacheService} from '@ame/cache'; import {BaseMetaModelElement} from '@ame/meta-model'; import {RFC2141} from 'urn-lib'; -import {DataFactory} from 'n3'; import {RdfService} from '@ame/rdf/services'; @Injectable({providedIn: 'root'}) @@ -93,14 +92,14 @@ export class EditorDialogValidators { continue; } - const element = rdfModel.store.getQuads(DataFactory.namedNode(aspectModelUrn), null, null, null)?.[0]?.subject; + const [namespace] = aspectModelUrn.split('#'); + const files = this.namespaceCacheService + .getFiles(namespace) + .filter(file => file.fileName !== this.namespaceCacheService.currentCachedFile.fileName); + + for (const file of files) { + foundExternalElement = file.getEitherElement(aspectModelUrn); - // @TODO: load elements efficiently - if (element) { - const [namespace] = element.value.split('#'); - // @TODO: Solve this // namespaceCacheService.sidebarService.loadNamespaceFiles(namespace, namespaceCacheService.currentCachedFile); - const files = this.namespaceCacheService.getFiles(namespace); - foundExternalElement = files.reduce((acc, file) => acc || file.getEitherElement(element.value), null); if (foundExternalElement) { break; } diff --git a/core/libs/editor/src/lib/editor-toolbar/services/file-handling.service.ts b/core/libs/editor/src/lib/editor-toolbar/services/file-handling.service.ts index a16992d3..eccbbe46 100644 --- a/core/libs/editor/src/lib/editor-toolbar/services/file-handling.service.ts +++ b/core/libs/editor/src/lib/editor-toolbar/services/file-handling.service.ts @@ -21,7 +21,7 @@ import { FileUploadService, ShapeSettingsStateService, } from '@ame/editor'; -import {catchError, filter, finalize, first, map, switchMap, tap} from 'rxjs/operators'; +import {catchError, finalize, first, map, switchMap, tap} from 'rxjs/operators'; import {forkJoin, from, Observable, of, throwError} from 'rxjs'; import { ElectronSignalsService, @@ -46,6 +46,7 @@ import {environment} from '../../../../../../environments/environment'; import {SidebarStateService} from '@ame/sidebar'; import {decodeText, readFile} from '@ame/utils'; import {MxGraphService} from '@ame/mx-graph'; +import {ConfirmDialogEnum} from '../../models/confirm-dialog.enum'; export interface FileInfo { content: BufferSource; @@ -311,51 +312,14 @@ export class FileHandlingService { saveAspectModelToWorkspace(): Observable { let modelState: ModelLoaderState; - let namespaces: string[]; - let isOverwrite: boolean; - let isChangeFileName: boolean; - let isChangeNamespace: boolean; return this.modelService.synchronizeModelToRdf().pipe( - switchMap(() => this.modelApiService.getNamespacesAppendWithFiles()), - tap(targetNamespaces => (namespaces = targetNamespaces)), switchMap(() => this.getModelLoaderState()), tap(state => (modelState = state)), - switchMap(() => this.openConfirmDialog(namespaces)), - tap(overwrite => (isOverwrite = overwrite)), - filter(overwrite => overwrite !== false), - switchMap(() => (modelState.isNamespaceChanged ? this.handleNamespaceChange(modelState) : of(null))), - map(() => { - isChangeFileName = modelState.loadedFromWorkspace && modelState.isNameChanged && isOverwrite === null; - isChangeNamespace = modelState.loadedFromWorkspace && modelState.isNamespaceChanged; - return isChangeFileName || isChangeNamespace; - }), - switchMap(isWorkspaceChange => (isWorkspaceChange ? this.deleteModel(modelState.originalModelName) : of(null))), - switchMap(() => this.editorService.saveModel()), - tap(rdfModel => { - if (rdfModel instanceof RdfModel) { - this.namespaceCacheService.currentCachedFile.fileName = rdfModel.aspectModelFileName; - } - - if (isChangeFileName) { - this.notificationsService.info({ - title: this.translate.language.NOTIFICATION_SERVICE.RENAMED_FILE_TITLE, - message: this.translate.translateService.instant('NOTIFICATION_SERVICE.RENAMED_FILE_MESSAGE', { - oldFile: modelState.oldFileName, - newFile: modelState.newFileName, - }), - }); - } - - this.rdfService.currentRdfModel.originalAbsoluteFileName = null; - this.rdfService.currentRdfModel.loadedFromWorkspace = true; - - this.electronSignalsService.call('updateWindowInfo', { - namespace: RdfModelUtil.getNamespaceFromRdf(modelState.newModelName), - fromWorkspace: true, - file: modelState.newFileName, - }); - }), + switchMap(() => this.handleNamespaceChange(modelState)), + switchMap(confirm => (confirm !== ConfirmDialogEnum.cancel ? this.editorService.saveModel() : of(null))), + tap(rdfModel => this.handleRdfModel(rdfModel, modelState)), + switchMap(() => this.editorService.loadExternalModels()), finalize(() => { this.modelSaveTracker.updateSavedModel(); this.loadingScreenService.close(); @@ -363,39 +327,6 @@ export class FileHandlingService { ); } - private getModelLoaderState(): Observable { - const rdfModel = this.rdfService.currentRdfModel; - const response: ModelLoaderState = { - originalModelName: rdfModel.originalAbsoluteFileName, - newModelName: rdfModel.absoluteAspectModelFileName, - oldFileName: rdfModel.originalAbsoluteFileName?.split(':')?.pop(), - newFileName: rdfModel.absoluteAspectModelFileName?.split(':')?.pop(), - loadedFromWorkspace: rdfModel.loadedFromWorkspace, - isNameChanged: rdfModel.originalAbsoluteFileName && rdfModel.originalAbsoluteFileName !== rdfModel.absoluteAspectModelFileName, - isNamespaceChanged: rdfModel.isNamespaceChanged, - }; - return of(response); - } - - private openConfirmDialog(namespaces: string[]) { - const rdfModel = this.modelService.currentRdfModel; - - if (namespaces.some(namespace => namespace === rdfModel.absoluteAspectModelFileName)) { - return this.confirmDialogService.open({ - phrases: [ - this.translate.translateService.instant('CONFIRM_DIALOG.UPDATE_ASPECT_MODEL.PHRASE1', { - fileName: rdfModel.absoluteAspectModelFileName, - }), - this.translate.language.CONFIRM_DIALOG.UPDATE_ASPECT_MODEL.PHRASE2, - ], - title: this.translate.language.CONFIRM_DIALOG.UPDATE_ASPECT_MODEL.TITLE, - okButtonText: this.translate.language.CONFIRM_DIALOG.UPDATE_ASPECT_MODEL.OK_BUTTON, - }); - } - - return of(null); - } - onAddFileToNamespace(fileInfo?: FileInfo): void { this.resolveModelFileContent(fileInfo) .pipe( @@ -597,24 +528,49 @@ export class FileHandlingService { })); } - private handleNamespaceChange(modelState: ModelLoaderState): Observable { + private getModelLoaderState(): Observable { + const rdfModel = this.rdfService.currentRdfModel; + const response: ModelLoaderState = { + originalModelName: rdfModel.originalAbsoluteFileName, + newModelName: rdfModel.absoluteAspectModelFileName, + oldFileName: rdfModel.originalAbsoluteFileName?.split(':')?.pop(), + newFileName: rdfModel.absoluteAspectModelFileName?.split(':')?.pop(), + loadedFromWorkspace: rdfModel.loadedFromWorkspace, + isNameChanged: rdfModel.originalAbsoluteFileName && rdfModel.originalAbsoluteFileName !== rdfModel.absoluteAspectModelFileName, + isNamespaceChanged: rdfModel.namespaceHasChanged, + }; + return of(response); + } + + private handleNamespaceChange(modelState: ModelLoaderState): Observable { + if (!modelState.isNamespaceChanged) { + return of(ConfirmDialogEnum.action); + } + const confirmationDialogConfig: DialogOptions = { phrases: [ this.translate.translateService.instant('CONFIRM_DIALOG.NAMESPACE_CHANGE.PHRASE1', { - originalModelName: modelState.originalModelName, + originalModelName: modelState.newFileName, }), this.translate.translateService.instant('CONFIRM_DIALOG.NAMESPACE_CHANGE.PHRASE2', { - originalModelName: RdfModelUtil.getFileNameFromRdf(modelState.originalModelName), originalModelNamespace: RdfModelUtil.getNamespaceFromRdf(modelState.originalModelName), - newNamespace: RdfModelUtil.getNamespaceFromRdf(modelState.newModelName), }), this.translate.translateService.instant('CONFIRM_DIALOG.NAMESPACE_CHANGE.PHRASE3', { - originalModelName: RdfModelUtil.getFileNameFromRdf(modelState.originalModelName), + newNamespace: RdfModelUtil.getNamespaceFromRdf(modelState.newModelName), }), this.translate.language.CONFIRM_DIALOG.NAMESPACE_CHANGE.PHRASE4, + this.translate.translateService.instant('CONFIRM_DIALOG.NAMESPACE_CHANGE.PHRASE5', { + originalModelName: modelState.newFileName, + }), + this.translate.translateService.instant('CONFIRM_DIALOG.NAMESPACE_CHANGE.PHRASE6', { + originalModelName: modelState.newFileName, + }), + this.translate.language.CONFIRM_DIALOG.NAMESPACE_CHANGE.PHRASE7, ], title: this.translate.language.CONFIRM_DIALOG.NAMESPACE_CHANGE.TITLE, okButtonText: this.translate.language.CONFIRM_DIALOG.NAMESPACE_CHANGE.OK_BUTTON, + actionButtonText: this.translate.language.CONFIRM_DIALOG.NAMESPACE_CHANGE.ACTION_BUTTON, + closeButtonText: this.translate.language.CONFIRM_DIALOG.NAMESPACE_CHANGE.CANCEL_BUTTON, }; const loadingDialogConfig: LoadingScreenOptions = { @@ -624,22 +580,27 @@ export class FileHandlingService { }; return this.confirmDialogService.open(confirmationDialogConfig).pipe( - filter(overwrite => overwrite), tap(() => this.loadingScreenService.open(loadingDialogConfig)), - switchMap(() => this.migrateAffectedModels(modelState.originalModelName, modelState.newModelName)), + switchMap(confirm => { + if (confirm === ConfirmDialogEnum.ok) { + return this.migrateAffectedModels(modelState.originalModelName, modelState.newModelName).pipe(map(() => confirm)); + } + + return of(confirm); + }), ); } private migrateAffectedModels(originalModelName: string, newModelName: string): Observable { const originalNamespace = RdfModelUtil.getUrnFromFileName(originalModelName); const newNamespace = RdfModelUtil.getUrnFromFileName(newModelName); - const affectedModels = this.updateAffectedQuads(originalNamespace, newNamespace); + const affectedModels = this.updateAffectedQuads(originalModelName, originalNamespace, newNamespace); return this.updateAffectedModels(affectedModels, originalNamespace, newNamespace); } - public updateAffectedQuads(originalNamespace: string, newNamespace: string): RdfModel[] { + public updateAffectedQuads(originalModelName: string, originalNamespace: string, newNamespace: string): RdfModel[] { const subjects = this.rdfService.currentRdfModel.store.getSubjects(null, null, null); - const models = this.rdfService.externalRdfModels; + const models = this.rdfService.externalRdfModels.filter(model => model.absoluteAspectModelFileName !== originalModelName); const affectedModels: RdfModel[] = []; subjects.forEach(subject => { @@ -668,7 +629,38 @@ export class FileHandlingService { return requests.length ? forkJoin(requests) : of([]); } - private deleteModel(modelName: string): Observable { - return this.modelApiService.deleteFile(modelName).pipe(tap(() => this.rdfService.removeExternalRdfModel(modelName))); + private handleRdfModel(rdfModel: object | RdfModel, modelState: ModelLoaderState): void { + if (!rdfModel) { + return; + } + + if (rdfModel instanceof RdfModel) { + this.rdfService.currentRdfModel.namespaceHasChanged = false; + this.namespaceCacheService.currentCachedFile.fileName = rdfModel.aspectModelFileName; + } + + if (modelState.isNameChanged) { + this.showFileNameChangeNotification(modelState); + } + + this.rdfService.currentRdfModel.originalAbsoluteFileName = null; + this.rdfService.currentRdfModel.loadedFromWorkspace = true; + + const namespace = RdfModelUtil.getNamespaceFromRdf(modelState.newModelName); + this.electronSignalsService.call('updateWindowInfo', { + namespace, + fromWorkspace: true, + file: modelState.newFileName, + }); + } + + private showFileNameChangeNotification(modelState: ModelLoaderState): void { + const title = this.translate.language.NOTIFICATION_SERVICE.RENAMED_FILE_TITLE; + const message = this.translate.translateService.instant('NOTIFICATION_SERVICE.RENAMED_FILE_MESSAGE', { + oldFile: modelState.oldFileName, + newFile: modelState.newFileName, + }); + + this.notificationsService.info({title, message}); } } diff --git a/core/libs/editor/src/lib/editor.service.ts b/core/libs/editor/src/lib/editor.service.ts index 90534a37..ca0eeaa8 100644 --- a/core/libs/editor/src/lib/editor.service.ts +++ b/core/libs/editor/src/lib/editor.service.ts @@ -75,6 +75,7 @@ import {LargeFileWarningService} from './large-file-warning-dialog/large-file-wa import {LoadModelPayload} from './models/load-model-payload.interface'; import {LanguageTranslationService} from '@ame/translation'; import {SidebarStateService} from '@ame/sidebar'; +import {ConfirmDialogEnum} from './models/confirm-dialog.enum'; @Injectable({ providedIn: 'root', @@ -223,15 +224,17 @@ export class EditorService { } openReloadConfirmationDialog(fileName: string): Observable { - return this.confirmDialogService.open({ - phrases: [ - `${this.translate.language.CONFIRM_DIALOG.RELOAD_CONFIRMATION.VERSION_CHANGE_NOTICE} ${fileName} ${this.translate.language.CONFIRM_DIALOG.RELOAD_CONFIRMATION.WORKSPACE_LOAD_NOTICE}`, - this.translate.language.CONFIRM_DIALOG.RELOAD_CONFIRMATION.RELOAD_WARNING, - ], - title: this.translate.language.CONFIRM_DIALOG.RELOAD_CONFIRMATION.TITLE, - closeButtonText: this.translate.language.CONFIRM_DIALOG.RELOAD_CONFIRMATION.CLOSE_BUTTON, - okButtonText: this.translate.language.CONFIRM_DIALOG.RELOAD_CONFIRMATION.OK_BUTTON, - }); + return this.confirmDialogService + .open({ + phrases: [ + `${this.translate.language.CONFIRM_DIALOG.RELOAD_CONFIRMATION.VERSION_CHANGE_NOTICE} ${fileName} ${this.translate.language.CONFIRM_DIALOG.RELOAD_CONFIRMATION.WORKSPACE_LOAD_NOTICE}`, + this.translate.language.CONFIRM_DIALOG.RELOAD_CONFIRMATION.RELOAD_WARNING, + ], + title: this.translate.language.CONFIRM_DIALOG.RELOAD_CONFIRMATION.TITLE, + closeButtonText: this.translate.language.CONFIRM_DIALOG.RELOAD_CONFIRMATION.CLOSE_BUTTON, + okButtonText: this.translate.language.CONFIRM_DIALOG.RELOAD_CONFIRMATION.OK_BUTTON, + }) + .pipe(map(confirm => confirm === ConfirmDialogEnum.ok)); } loadNewAspectModel(payload: LoadModelPayload): Observable> { @@ -519,8 +522,8 @@ export class EditorService { closeButtonText: this.translate.language.CONFIRM_DIALOG.CREATE_ASPECT.CLOSE_BUTTON, okButtonText: this.translate.language.CONFIRM_DIALOG.CREATE_ASPECT.OK_BUTTON, }) - .subscribe(confirmed => { - if (!confirmed) { + .subscribe(confirm => { + if (confirm === ConfirmDialogEnum.cancel) { return; } const rdfModel = this.rdfService.currentRdfModel; diff --git a/core/libs/settings-dialog/src/lib/model/namespace.ts b/core/libs/editor/src/lib/models/confirm-dialog.enum.ts similarity index 83% rename from core/libs/settings-dialog/src/lib/model/namespace.ts rename to core/libs/editor/src/lib/models/confirm-dialog.enum.ts index 8ef1bd9a..3e23ddb7 100644 --- a/core/libs/settings-dialog/src/lib/model/namespace.ts +++ b/core/libs/editor/src/lib/models/confirm-dialog.enum.ts @@ -11,8 +11,8 @@ * SPDX-License-Identifier: MPL-2.0 */ -export interface Namespace { - name: string; - value: string; - version: string; +export enum ConfirmDialogEnum { + ok = 'ok', + action = 'action', + cancel = 'cancel', } diff --git a/core/libs/meta-model/src/lib/element-service/aspect-model.service.ts b/core/libs/meta-model/src/lib/element-service/aspect-model.service.ts index 905adcd1..fd2df378 100644 --- a/core/libs/meta-model/src/lib/element-service/aspect-model.service.ts +++ b/core/libs/meta-model/src/lib/element-service/aspect-model.service.ts @@ -55,7 +55,7 @@ export class AspectModelService extends BaseModelService { this.rdfService.currentRdfModel.absoluteAspectModelFileName = `${metaModelElement.aspectModelUrn}.ttl`; this.aspectRenderer.update({cell}); - this.titleService.updateTitle(metaModelElement?.aspectModelUrn.replace('urn:samm:', '') + 'ttl', 'Aspect'); + this.titleService.updateTitle(`${metaModelElement?.aspectModelUrn.replace('urn:samm:', '')}.ttl`, 'Aspect'); this.sidebarStateService.workspace.refresh(); } diff --git a/core/libs/meta-model/src/lib/element-service/base-model-service.ts b/core/libs/meta-model/src/lib/element-service/base-model-service.ts index 7dd610ec..14e27770 100644 --- a/core/libs/meta-model/src/lib/element-service/base-model-service.ts +++ b/core/libs/meta-model/src/lib/element-service/base-model-service.ts @@ -57,6 +57,7 @@ export abstract class BaseModelService { const aspectModelUrn = this.modelService.currentRdfModel.getAspectModelUrn(); this.currentCachedFile.updateCachedElementKey(`${aspectModelUrn}${modelElement.name}`, `${aspectModelUrn}${form.name}`); + modelElement.name = form.name; modelElement.aspectModelUrn = `${aspectModelUrn}${form.name}`; diff --git a/core/libs/rdf/src/lib/services/model.service.ts b/core/libs/rdf/src/lib/services/model.service.ts index b873e080..2707e66d 100644 --- a/core/libs/rdf/src/lib/services/model.service.ts +++ b/core/libs/rdf/src/lib/services/model.service.ts @@ -149,7 +149,7 @@ export class ModelService { [, , fileName] = namespaceFileName.split(':'); } - this.namespaceCacheService.currentCachedFile = this.namespaceCacheService.addFile(this.rdfModel.getAspectModelUrn(), fileName); + this.namespaceCacheService.currentCachedFile = new CachedFile(fileName, this.rdfModel.getAspectModelUrn()); } private instantiateFile(namespaceFileName: string) { diff --git a/core/libs/rdf/src/lib/services/rdf.service.ts b/core/libs/rdf/src/lib/services/rdf.service.ts index 098eb7d4..ac389fff 100644 --- a/core/libs/rdf/src/lib/services/rdf.service.ts +++ b/core/libs/rdf/src/lib/services/rdf.service.ts @@ -166,7 +166,7 @@ export class RdfService { } parseFileName(fileName: string, urn: string): string { - if (this.browserService.isStartedAsElectronApp()) { + if (this.browserService.isStartedAsElectronApp() && window.require) { const path = window.require('path'); fileName = fileName.includes(path.sep) ? `${urn.replace('#', ':')}${path.basename(fileName)}` : fileName; } diff --git a/core/libs/rdf/src/lib/utils/rdf-model.ts b/core/libs/rdf/src/lib/utils/rdf-model.ts index b4455e04..3ebd8d74 100644 --- a/core/libs/rdf/src/lib/utils/rdf-model.ts +++ b/core/libs/rdf/src/lib/utils/rdf-model.ts @@ -30,6 +30,7 @@ export class RdfModel { private _prefixes: Prefixes; private _isExternalRef = false; private _absoluteAspectModelFileName: string = null; + private _namespaceHasChanged = false; private _metaModelVersion: string; private _defaultAspectModelAlias = ''; private _loadedRdfModel: boolean; @@ -73,6 +74,10 @@ export class RdfModel { } set absoluteAspectModelFileName(absoluteFileName: string) { + if (!this.originalAbsoluteFileName || this.originalAbsoluteFileName !== this.absoluteAspectModelFileName) { + this.originalAbsoluteFileName = this.absoluteAspectModelFileName; + } + this._absoluteAspectModelFileName = absoluteFileName.replace('urn:samm:', '').replace('#', ':'); } @@ -88,28 +93,16 @@ export class RdfModel { return null; } - get aspectModelFileName(): string { - return this._absoluteAspectModelFileName.split(':')[2]; - } - - get isNamespaceChanged(): boolean { - return this.isNamespaceNameChanged || this.isNamespaceVersionChanged; + set namespaceHasChanged(namespaceHasChanged: boolean) { + this._namespaceHasChanged = namespaceHasChanged; } - get isNamespaceNameChanged(): boolean { - if (!this.originalAbsoluteFileName || !this.absoluteAspectModelFileName) return false; - return ( - RdfModelUtil.getNamespaceNameFromRdf(this.originalAbsoluteFileName) !== - RdfModelUtil.getNamespaceNameFromRdf(this.absoluteAspectModelFileName) - ); + get namespaceHasChanged(): boolean { + return this._namespaceHasChanged; } - get isNamespaceVersionChanged(): boolean { - if (!this.originalAbsoluteFileName || !this.absoluteAspectModelFileName) return false; - return ( - RdfModelUtil.getNamespaceVersionFromRdf(this.originalAbsoluteFileName) !== - RdfModelUtil.getNamespaceVersionFromRdf(this.absoluteAspectModelFileName) - ); + get aspectModelFileName(): string { + return this._absoluteAspectModelFileName.split(':')[2]; } constructor(loadedRdfModel = false) { @@ -372,9 +365,9 @@ export class RdfModel { } updateAbsoluteFileName(newNamespace: string, newVersion: string): void { - if (!this.originalAbsoluteFileName) this.originalAbsoluteFileName = this.absoluteAspectModelFileName; - if (this.originalAbsoluteFileName !== this.absoluteAspectModelFileName) + if (!this.originalAbsoluteFileName || this.originalAbsoluteFileName !== this.absoluteAspectModelFileName) { this.originalAbsoluteFileName = this.absoluteAspectModelFileName; + } const fileName = RdfModelUtil.getFileNameFromRdf(this.absoluteAspectModelFileName); this.absoluteAspectModelFileName = RdfModelUtil.buildAbsoluteFileName(newNamespace, newVersion, fileName); diff --git a/core/libs/settings-dialog/src/lib/components/index.ts b/core/libs/settings-dialog/src/lib/components/index.ts index 28331903..e595786e 100644 --- a/core/libs/settings-dialog/src/lib/components/index.ts +++ b/core/libs/settings-dialog/src/lib/components/index.ts @@ -16,5 +16,4 @@ export * from './system-configuration/editor-configuration/editor-configuration. export * from './system-configuration/header-copyright/header-copyright.component'; export * from './model-configuration/language-settings/language-settings.component'; export * from './model-configuration/namespace-settings/namespace-settings.component'; -export * from './model-configuration/namespace-confirmation-modal/namespace-confirmation-modal.component'; export * from './settings-dialog/setting-dialog.component'; diff --git a/core/libs/settings-dialog/src/lib/components/model-configuration/namespace-confirmation-modal/namespace-confirmation-modal.component.html b/core/libs/settings-dialog/src/lib/components/model-configuration/namespace-confirmation-modal/namespace-confirmation-modal.component.html deleted file mode 100644 index 99aa8f71..00000000 --- a/core/libs/settings-dialog/src/lib/components/model-configuration/namespace-confirmation-modal/namespace-confirmation-modal.component.html +++ /dev/null @@ -1,33 +0,0 @@ - - -

{{ 'SETTINGS_DIALOG.SUB_NODE.NAMESPACES' | translate }}

- - - Are you sure you want to change the current - namespace to {{ newNamespace }} - and - version to - {{ newVersion }} - - ? - - - - - diff --git a/core/libs/settings-dialog/src/lib/components/model-configuration/namespace-confirmation-modal/namespace-confirmation-modal.component.ts b/core/libs/settings-dialog/src/lib/components/model-configuration/namespace-confirmation-modal/namespace-confirmation-modal.component.ts deleted file mode 100644 index 3ac89c9c..00000000 --- a/core/libs/settings-dialog/src/lib/components/model-configuration/namespace-confirmation-modal/namespace-confirmation-modal.component.ts +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright (c) 2024 Robert Bosch Manufacturing Solutions GmbH - * - * See the AUTHORS file(s) distributed with this work for - * additional information regarding authorship. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - * - * SPDX-License-Identifier: MPL-2.0 - */ - -import {Component, Inject} from '@angular/core'; -import {MAT_DIALOG_DATA, MatDialogRef} from '@angular/material/dialog'; -import {NamespacesCacheService} from '@ame/cache'; -import {RdfModel} from '@ame/rdf/utils'; - -export interface NewNamespaceDialogOptions { - rdfModel: RdfModel; - oldNamespace: string; - newNamespace: string; - oldVersion: string; - newVersion: string; -} - -@Component({ - templateUrl: './namespace-confirmation-modal.component.html', - styleUrls: ['./namespace-confirmation-modal.component.scss'], -}) -export class NamespaceConfirmationModalComponent { - oldNamespace: string; - newNamespace: string; - oldVersion: string; - newVersion: string; - rdfModel: RdfModel; - - constructor( - @Inject(MAT_DIALOG_DATA) data: NewNamespaceDialogOptions, - private namespaceCacheService: NamespacesCacheService, - private namespaceConfirmationDialogRef: MatDialogRef, - ) { - this.rdfModel = data.rdfModel; - this.oldNamespace = data.oldNamespace; - this.newNamespace = data.newNamespace; - this.oldVersion = data.oldVersion; - this.newVersion = data.newVersion; - } - - onClose() { - this.namespaceConfirmationDialogRef.close(false); - } - - onSave() { - this.rdfModel.updateAbsoluteFileName(this.newNamespace, this.newVersion); - - if (this.oldNamespace !== this.newNamespace) { - this.updateAllNamespacesFromCurrentCachedFile(this.oldNamespace, this.newNamespace); - } - - if (this.oldVersion !== this.newVersion) { - this.updateAllNamespacesFromCurrentCachedFile(this.oldVersion, this.newVersion); - } - - this.updateNamespaceKey(); - this.namespaceConfirmationDialogRef.close(true); - } - - private updateNamespaceKey(): void { - const newUrn = 'urn:samm:' + this.newNamespace + ':' + this.newVersion + '#'; - const oldUrn = 'urn:samm:' + this.oldNamespace + ':' + this.oldVersion + '#'; - - this.namespaceCacheService.updateNamespaceKey(oldUrn, newUrn, this.rdfModel.aspectModelFileName); - if (this.rdfModel.aspect) { - const [, aspectName] = this.rdfModel.aspect.value.split('#'); - this.rdfModel.setAspect(`${newUrn}${aspectName}`); - } - } - - private updateAllNamespacesFromCurrentCachedFile(oldValue: string, newValue: string): void { - const currentCachedFile = this.namespaceCacheService.currentCachedFile; - - currentCachedFile.updateCachedElementsNamespace(oldValue, newValue); - this.rdfModel.updatePrefix('', oldValue, newValue); - } -} diff --git a/core/libs/settings-dialog/src/lib/components/settings-dialog/setting-dialog.component.ts b/core/libs/settings-dialog/src/lib/components/settings-dialog/setting-dialog.component.ts index 5bb09373..8461ba11 100644 --- a/core/libs/settings-dialog/src/lib/components/settings-dialog/setting-dialog.component.ts +++ b/core/libs/settings-dialog/src/lib/components/settings-dialog/setting-dialog.component.ts @@ -11,7 +11,7 @@ * SPDX-License-Identifier: MPL-2.0 */ import {Component} from '@angular/core'; -import {MatDialog, MatDialogRef} from '@angular/material/dialog'; +import {MatDialogRef} from '@angular/material/dialog'; import {FlatTreeControl} from '@angular/cdk/tree'; import {MatTreeFlatDataSource, MatTreeFlattener} from '@angular/material/tree'; import {SammLanguageSettingsService, SettingsFormService} from '../../services'; @@ -19,9 +19,10 @@ import * as locale from 'locale-codes'; import {AlertService, LoadingScreenService, LogService, TitleService} from '@ame/shared'; import {MxGraphAttributeService, MxGraphService, MxGraphShapeSelectorService, ShapeLanguageRemover} from '@ame/mx-graph'; import {ModelService} from '@ame/rdf/services'; -import {NamespaceConfirmationModalComponent} from '../model-configuration/namespace-confirmation-modal/namespace-confirmation-modal.component'; -import {finalize} from 'rxjs'; import {FormGroup} from '@angular/forms'; +import {NamespacesCacheService} from '@ame/cache'; +import {RdfModel} from '@ame/rdf/utils'; +import {NamespaceConfiguration} from '../../model'; enum NodeNames { CONFIGURATION = 'System Configuration', @@ -129,7 +130,6 @@ export class SettingDialogComponent { } constructor( - private matDialog: MatDialog, private settingDialogComponentMatDialogRef: MatDialogRef, private formService: SettingsFormService, private alertService: AlertService, @@ -141,6 +141,7 @@ export class SettingDialogComponent { private mxGraphShapeSelectorService: MxGraphShapeSelectorService, private loadingScreen: LoadingScreenService, private titleService: TitleService, + private namespaceCacheService: NamespacesCacheService, ) { this.initializeComponent(); } @@ -194,23 +195,59 @@ export class SettingDialogComponent { if (!this.formService.hasNamespaceChanged()) return; const namespaceConfig = this.formService.getNamespaceConfiguration(); - this.matDialog - .open(NamespaceConfirmationModalComponent, namespaceConfig) - .afterClosed() - .pipe( - finalize(() => { - this.formService.setNamespace(namespaceConfig.data.newNamespace); - this.formService.setVersion(namespaceConfig.data.newVersion); - - const title = this.titleService.getTitle().split(' | '); - - if (title.length > 1) { - const type = title[1].includes('Aspect') ? 'Aspect' : 'Shared'; - this.titleService.updateTitle(this.modelService.getLoadedAspectModel().rdfModel.absoluteAspectModelFileName, type); - } - }), - ) - .subscribe(); + + this.updateNamespacesIfNeeded(namespaceConfig); + this.updateNamespaceAndVersion(namespaceConfig); + this.updateTitleIfNeeded(); + } + + private updateNamespacesIfNeeded(namespaceConfig: NamespaceConfiguration): void { + const {oldNamespace, newNamespace, rdfModel, oldVersion, newVersion} = namespaceConfig; + + if (oldNamespace !== newNamespace) { + this.updateAllNamespacesFromCurrentCachedFile(oldNamespace, newNamespace, rdfModel); + } + + if (oldVersion !== newVersion) { + this.updateAllNamespacesFromCurrentCachedFile(oldVersion, newVersion, rdfModel); + } + } + + private updateAllNamespacesFromCurrentCachedFile(oldValue: string, newValue: string, rdfModel: RdfModel): void { + const currentCachedFile = this.namespaceCacheService.currentCachedFile; + + currentCachedFile.updateCachedElementsNamespace(oldValue, newValue); + rdfModel.updatePrefix('', oldValue, newValue); + rdfModel.aspectModelFileName; + } + + private updateNamespaceAndVersion(namespaceConfig: NamespaceConfiguration): void { + const {newNamespace, newVersion, rdfModel} = namespaceConfig; + + this.updateNamespaceKey(newNamespace, newVersion, rdfModel); + this.formService.setNamespace(newNamespace); + this.formService.setVersion(newVersion); + } + + private updateNamespaceKey(newNamespace: string, newVersion: string, rdfModel: RdfModel): void { + const newUrn = `urn:samm:${newNamespace}:${newVersion}#`; + + this.namespaceCacheService.addFile(newUrn, rdfModel.aspectModelFileName); + if (rdfModel.aspect) { + const [, aspectName] = rdfModel.aspect.value.split('#'); + rdfModel.namespaceHasChanged = true; + rdfModel.setAspect(`${newUrn}${aspectName}`); + rdfModel.absoluteAspectModelFileName = `${newUrn.replace('#', ':')}${aspectName}.ttl`; + } + } + + private updateTitleIfNeeded(): void { + const title = this.titleService.getTitle().split(' | '); + + if (title.length > 1) { + const type = title[1].includes('Aspect') ? 'Aspect' : 'Shared'; + this.titleService.updateTitle(this.modelService.getLoadedAspectModel().rdfModel.absoluteAspectModelFileName, type); + } } openConfirmBox(): void { diff --git a/core/libs/settings-dialog/src/lib/model/index.ts b/core/libs/settings-dialog/src/lib/model/index.ts index d1b3cd50..93e6bf48 100644 --- a/core/libs/settings-dialog/src/lib/model/index.ts +++ b/core/libs/settings-dialog/src/lib/model/index.ts @@ -11,6 +11,6 @@ * SPDX-License-Identifier: MPL-2.0 */ -export * from './namespace'; +export * from './namespace-configuration'; export * from './settings'; export * from './langcode'; diff --git a/core/libs/settings-dialog/src/lib/components/model-configuration/namespace-confirmation-modal/namespace-confirmation-modal.component.scss b/core/libs/settings-dialog/src/lib/model/namespace-configuration.ts similarity index 67% rename from core/libs/settings-dialog/src/lib/components/model-configuration/namespace-confirmation-modal/namespace-confirmation-modal.component.scss rename to core/libs/settings-dialog/src/lib/model/namespace-configuration.ts index 7558c577..492b025d 100644 --- a/core/libs/settings-dialog/src/lib/components/model-configuration/namespace-confirmation-modal/namespace-confirmation-modal.component.scss +++ b/core/libs/settings-dialog/src/lib/model/namespace-configuration.ts @@ -1,4 +1,4 @@ -/*! +/* * Copyright (c) 2024 Robert Bosch Manufacturing Solutions GmbH * * See the AUTHORS file(s) distributed with this work for @@ -11,6 +11,12 @@ * SPDX-License-Identifier: MPL-2.0 */ -.bold-text { - font-weight: bold; +import {RdfModel} from '@ame/rdf/utils'; + +export interface NamespaceConfiguration { + oldNamespace: string; + oldVersion: string; + rdfModel: RdfModel; + newNamespace: string; + newVersion: string; } diff --git a/core/libs/settings-dialog/src/lib/services/settings-form.service.ts b/core/libs/settings-dialog/src/lib/services/settings-form.service.ts index ab040321..d2a7b611 100644 --- a/core/libs/settings-dialog/src/lib/services/settings-form.service.ts +++ b/core/libs/settings-dialog/src/lib/services/settings-form.service.ts @@ -12,7 +12,7 @@ */ import {Injectable} from '@angular/core'; import {AbstractControl, FormArray, FormBuilder, FormControl, FormGroup, ValidationErrors, ValidatorFn, Validators} from '@angular/forms'; -import {ConfigurationService, SammLanguageSettingsService} from '@ame/settings-dialog'; +import {ConfigurationService, NamespaceConfiguration, SammLanguageSettingsService} from '@ame/settings-dialog'; import {LanguageTranslationService} from '@ame/translation'; import * as locale from 'locale-codes'; import {GeneralConfig} from '@ame/shared'; @@ -102,7 +102,13 @@ export class SettingsFormService { this.form = this.formBuilder.group({ automatedWorkflow: this.formBuilder.group({ autoSaveEnabled: [settings.autoSaveEnabled], - saveTimerSeconds: [{value: settings.saveTimerSeconds, disabled: !settings.autoSaveEnabled}, [Validators.min(60)]], + saveTimerSeconds: [ + { + value: settings.saveTimerSeconds, + disabled: !settings.autoSaveEnabled, + }, + [Validators.min(60)], + ], autoValidationEnabled: [settings.autoValidationEnabled], validationTimerSeconds: [ { @@ -165,26 +171,20 @@ export class SettingsFormService { } hasNamespaceChanged(): boolean { - const namespaceConfiguration = this.getNamespaceConfiguration(); - return ( - namespaceConfiguration && - (namespaceConfiguration.data.oldNamespace !== namespaceConfiguration.data.newNamespace || - namespaceConfiguration.data.oldVersion !== namespaceConfiguration.data.newVersion) - ); + const {oldNamespace, newNamespace, oldVersion, newVersion} = this.getNamespaceConfiguration(); + return oldNamespace !== newNamespace || oldVersion !== newVersion; } getNamespaceConfiguration(): any { const settings = this.configurationService.getSettings(); return { - data: { - oldNamespace: this.namespace, - oldVersion: this.version, - rdfModel: this.loadedRdfModel, - newNamespace: settings.namespace, - newVersion: settings.version, - }, - }; + oldNamespace: this.namespace, + oldVersion: this.version, + rdfModel: this.loadedRdfModel, + newNamespace: settings.namespace, + newVersion: settings.version, + } as NamespaceConfiguration; } getForm(): FormGroup { diff --git a/core/libs/settings-dialog/src/lib/setting-dialog.module.ts b/core/libs/settings-dialog/src/lib/setting-dialog.module.ts index d7acd869..4bf5b2dc 100644 --- a/core/libs/settings-dialog/src/lib/setting-dialog.module.ts +++ b/core/libs/settings-dialog/src/lib/setting-dialog.module.ts @@ -18,7 +18,6 @@ import { EditorConfigurationComponent, HeaderCopyrightComponent, LanguageSettingsComponent, - NamespaceConfirmationModalComponent, NamespaceSettingsComponent, SettingDialogComponent, } from './components'; @@ -48,7 +47,6 @@ import {MatExpansionModule} from '@angular/material/expansion'; AutomatedWorkflowComponent, EditorConfigurationComponent, HeaderCopyrightComponent, - NamespaceConfirmationModalComponent, ], imports: [ CommonModule, diff --git a/core/libs/shared/src/lib/services/model-saving-tracker.service.ts b/core/libs/shared/src/lib/services/model-saving-tracker.service.ts index 5ebc63ec..89aa04bd 100644 --- a/core/libs/shared/src/lib/services/model-saving-tracker.service.ts +++ b/core/libs/shared/src/lib/services/model-saving-tracker.service.ts @@ -13,8 +13,8 @@ import {ModelService, RdfService} from '@ame/rdf/services'; import {MxGraphService} from '@ame/mx-graph'; -import {Injectable, inject} from '@angular/core'; -import {map, take, Timestamp} from 'rxjs'; +import {inject, Injectable} from '@angular/core'; +import {map, take} from 'rxjs'; @Injectable({providedIn: 'root'}) export class ModelSavingTrackerService { diff --git a/core/libs/sidebar/src/lib/sidebar-state.service.ts b/core/libs/sidebar/src/lib/sidebar-state.service.ts index e5945809..6e56e763 100644 --- a/core/libs/sidebar/src/lib/sidebar-state.service.ts +++ b/core/libs/sidebar/src/lib/sidebar-state.service.ts @@ -18,6 +18,7 @@ import {ModelApiService} from '@ame/api'; import {APP_CONFIG, AppConfig, BrowserService, ElectronSignals, ElectronSignalsService, NotificationsService} from '@ame/shared'; import {ExporterHelper} from '@ame/migrator'; import {environment} from '../../../../environments/environment'; +import {NamespacesCacheService} from '@ame/cache'; class SidebarState { private opened$ = new BehaviorSubject(false); @@ -84,6 +85,8 @@ export class FileStatus { } class NamespacesManager { + public namespacesCacheService: NamespacesCacheService = inject(NamespacesCacheService); + public namespaces: {[key: string]: FileStatus[]} = {}; public hasOutdatedFiles = false; @@ -109,7 +112,9 @@ class NamespacesManager { lockFiles(files: {namespace: string; file: string}[]) { for (const namespace of this.namespacesKeys) { for (const fileStatus of this.namespaces[namespace]) { - fileStatus.locked = files.some(file => file.namespace === namespace && file.file === fileStatus.name); + if (fileStatus.name !== this.namespacesCacheService.currentCachedFile?.fileName) { + fileStatus.locked = files.some(file => file.namespace === namespace && file.file === fileStatus.name); + } } } } @@ -122,7 +127,9 @@ class NamespacesManager { @Injectable({providedIn: 'root'}) export class SidebarStateService { private electronSignalsService: ElectronSignals = inject(ElectronSignalsService); + private namespacesCacheService: NamespacesCacheService = inject(NamespacesCacheService); private config: AppConfig = inject(APP_CONFIG); + public sammElements = new SidebarState(); public workspace = new SidebarStateWithRefresh(); public fileElements = new SidebarState(); @@ -146,11 +153,11 @@ export class SidebarStateService { return Boolean(currentRdfModel?.originalAbsoluteFileName || currentRdfModel?.absoluteAspectModelFileName); } - public isCurrentFile(namespace: string, namespaceFile: string): boolean { - const currentRdfModel = this.rdfService.currentRdfModel; + public isCurrentFile(namespace: string, fileName: string): boolean { + const currentFileName = this.namespacesCacheService.currentCachedFile?.fileName; + const currentNamespace = this.rdfService.currentRdfModel.getAspectModelUrn().replace('urn:samm:', '').replace('#', ''); - const fileName = currentRdfModel?.originalAbsoluteFileName || currentRdfModel?.absoluteAspectModelFileName; - return fileName === `${namespace}:${namespaceFile}`; + return `${currentNamespace}:${currentFileName}` === `${namespace}:${fileName}`; } public requestGetNamespaces() { diff --git a/core/libs/sidebar/src/lib/workspace/workspace-file-list/workspace-file-list.component.ts b/core/libs/sidebar/src/lib/workspace/workspace-file-list/workspace-file-list.component.ts index 0e3abce9..b613fd74 100644 --- a/core/libs/sidebar/src/lib/workspace/workspace-file-list/workspace-file-list.component.ts +++ b/core/libs/sidebar/src/lib/workspace/workspace-file-list/workspace-file-list.component.ts @@ -19,6 +19,7 @@ import {RdfService} from '@ame/rdf/services'; import {ModelApiService} from '@ame/api'; import {Subscription, switchMap} from 'rxjs'; import {LanguageTranslationService} from '@ame/translation'; +import {ConfirmDialogEnum} from '../../../../../editor/src/lib/models/confirm-dialog.enum'; @Component({ selector: 'ame-workspace-file-list', @@ -173,7 +174,7 @@ export class WorkspaceFileListComponent implements OnInit, OnDestroy { okButtonText: this.translate.language.CONFIRM_DIALOG.SAVE_BEFORE_LOAD.OK_BUTTON, }) .subscribe(confirmed => { - if (confirmed) { + if (confirmed !== ConfirmDialogEnum.cancel) { this.editorService.saveModel().subscribe(); } // TODO improve this functionality @@ -192,8 +193,8 @@ export class WorkspaceFileListComponent implements OnInit, OnDestroy { ], title: this.translate.language.CONFIRM_DIALOG.DELETE_FILE.TITLE, }) - .subscribe(confirmed => { - if (confirmed) { + .subscribe(confirm => { + if (confirm !== ConfirmDialogEnum.cancel) { this.modelApiService.deleteFile(aspectModelFileName).subscribe(() => { this.sidebarService.workspace.refresh(); this.electronSignalsService.call('requestRefreshWorkspaces'); diff --git a/core/libs/translation/src/lib/models/language.interface.ts b/core/libs/translation/src/lib/models/language.interface.ts index 05bde46f..6eaa522d 100644 --- a/core/libs/translation/src/lib/models/language.interface.ts +++ b/core/libs/translation/src/lib/models/language.interface.ts @@ -110,7 +110,6 @@ export interface LoadingScreenDialog { export interface ConfirmDialog { CREATE_ASPECT: CreateAspect; RELOAD_CONFIRMATION: ReloadConfirmation; - UPDATE_ASPECT_MODEL: UpdateAspectModel; NAMESPACE_CHANGE: NamespaceChange; SAVE_BEFORE_LOAD: SaveBeforeLoad; DELETE_FILE: DeleteFile; @@ -139,14 +138,13 @@ export interface NamespaceChange { PHRASE2: string; PHRASE3: string; PHRASE4: string; + PHRASE5: string; + PHRASE6: string; + PHRASE7: string; TITLE: string; OK_BUTTON: string; -} -export interface UpdateAspectModel { - PHRASE1: string; - PHRASE2: string; - TITLE: string; - OK_BUTTON: string; + ACTION_BUTTON: string; + CANCEL_BUTTON: string; } export interface ReloadConfirmation { TITLE: string; diff --git a/core/libs/utils/src/lib/components/elements-search/elements-search.component.ts b/core/libs/utils/src/lib/components/elements-search/elements-search.component.ts index 7e8db8f1..be163ef1 100644 --- a/core/libs/utils/src/lib/components/elements-search/elements-search.component.ts +++ b/core/libs/utils/src/lib/components/elements-search/elements-search.component.ts @@ -28,6 +28,7 @@ import {ConfirmDialogService, EditorDialogModule, ShapeSettingsService} from '@a import {SearchesStateService} from '../../search-state.service'; import {mxgraph} from 'mxgraph-factory'; import {LanguageTranslateModule, LanguageTranslationService} from '@ame/translation'; +import {ConfirmDialogEnum} from '../../../../../editor/src/lib/models/confirm-dialog.enum'; @Component({ standalone: true, @@ -76,8 +77,8 @@ export class ElementsSearchComponent { closeButtonText: this.translate.language.CONFIRM_DIALOG.NEW_WINDOW_ELEMENT.CANCEL_BUTTON, okButtonText: this.translate.language.CONFIRM_DIALOG.NEW_WINDOW_ELEMENT.OK_BUTTON, }) - .subscribe(result => { - result + .subscribe(confirm => { + confirm !== ConfirmDialogEnum.cancel ? this.electronSignalsService.call('openWindow', { file: element.fileName, namespace: element.aspectModelUrn.replace('urn:samm:', '').split('#')[0], diff --git a/core/libs/utils/src/lib/components/files-search/files-search.component.spec.ts b/core/libs/utils/src/lib/components/files-search/files-search.component.spec.ts index cf67568d..3187fa3d 100644 --- a/core/libs/utils/src/lib/components/files-search/files-search.component.spec.ts +++ b/core/libs/utils/src/lib/components/files-search/files-search.component.spec.ts @@ -3,7 +3,7 @@ import {FormsModule} from '@angular/forms'; import {MatFormFieldModule} from '@angular/material/form-field'; import {MatInputModule} from '@angular/material/input'; import {MatDialog, MatDialogModule, MatDialogRef} from '@angular/material/dialog'; -import {MockProviders} from 'ng-mocks'; +import {MockProvider} from 'ng-mocks'; import {TranslateModule, TranslateService} from '@ngx-translate/core'; import {BrowserAnimationsModule} from '@angular/platform-browser/animations'; import {FilesSearchComponent} from './files-search.component'; @@ -32,7 +32,10 @@ describe('Files search', () => { HttpClientModule, ], providers: [ - MockProviders(MatDialogRef, MxGraphService!, NotificationsService, FileHandlingService), + MockProvider(MatDialogRef), + MockProvider(MxGraphService), + MockProvider(NotificationsService), + MockProvider(FileHandlingService), TranslateService, SearchesStateService, SidebarStateService,