From dd8213ef521160908552904878a955d781096dd5 Mon Sep 17 00:00:00 2001 From: Dominik Riemer Date: Fri, 16 Aug 2024 17:01:37 +0200 Subject: [PATCH] feat(#3141): Add option to configure production sites (#3142) * feat(#3141): Add option to configure production sites * Add headers * Fix import * feat: Show asset location on map * Remove scss * Fix formatting --- .../model/configuration/LocationConfig.java | 26 ++++ .../configuration/SpCoreConfiguration.java | 10 ++ .../streampipes/rest/ResetManagement.java | 42 ++++-- .../admin/LocationConfigurationResource.java | 53 +++++++ .../utils/configuration/ConfigurationUtils.ts | 31 +++++ .../support/utils/configuration/SiteUtils.ts | 65 +++++++++ .../tests/assetManagement/createAsset.spec.ts | 2 +- .../sites/sites-geo-features.spec.ts | 41 ++++++ .../tests/configuration/sites/sites.spec.ts | 64 +++++++++ .../src/lib/apis/datalake-rest.service.ts | 32 +---- .../src/lib/apis/location-config.service.ts | 48 +++++++ .../src/lib/model/assets/asset.model.ts | 29 +++- .../src/lib/model/gen/streampipes-model.ts | 71 +++------- .../platform-services/src/public-api.ts | 1 + .../basic-field-description.component.html | 3 +- ui/src/app/assets/assets.module.ts | 4 + .../asset-details-basics.component.html | 19 ++- .../asset-details-basics.component.ts | 38 ++++-- .../asset-details-labels.component.ts | 2 +- .../asset-details-site.component.html | 46 +++++++ .../asset-details-site.component.ts | 63 +++++++++ .../asset-location.component.html | 34 +++++ .../asset-location.component.ts | 53 +++++++ .../asset-details-links.component.html | 4 +- .../asset-details-links.component.ts | 2 +- .../asset-link-item.component.ts | 2 +- .../asset-link-section.component.html | 4 +- .../asset-link-section.component.ts | 2 +- .../asset-details-panel.component.html | 10 +- .../asset-details-panel.component.ts | 10 +- .../asset-details.component.html | 12 +- .../asset-details/asset-details.component.ts | 47 ++++--- .../asset-selection-panel.component.html | 4 +- .../asset-selection-panel.component.ts | 10 +- .../asset-overview.component.ts | 2 +- .../app/assets/constants/asset.constants.ts | 1 + .../app/configuration/configuration-tabs.ts | 5 + .../app/configuration/configuration.module.ts | 18 +++ .../app/configuration/configuration.routes.ts | 1 - .../edit-location-area.component.html | 68 +++++++++ .../edit-location-area.component.scss} | 15 +- .../edit-location-area.component.ts | 40 ++++++ .../edit-location.component.html | 59 ++++++++ .../edit-location/edit-location.component.ts | 32 +++++ .../manage-site-dialog.component.html | 52 +++++++ .../manage-site-dialog.component.scss | 35 +++++ .../manage-site-dialog.component.ts | 83 +++++++++++ ...tion-features-configuration.component.html | 83 +++++++++++ ...cation-features-configuration.component.ts | 107 +++++++++++++++ .../site-area-configuration.component.html | 80 +++++++++++ .../site-area-configuration.component.ts | 88 ++++++++++++ .../sites-configuration.component.html | 27 ++++ .../sites-configuration.component.ts | 42 ++++++ ui/src/app/core-ui/core-ui.module.ts | 5 + .../single-marker-map.component.html | 29 ++++ .../single-marker-map.component.ts | 129 ++++++++++++++++++ 56 files changed, 1729 insertions(+), 156 deletions(-) create mode 100644 streampipes-model/src/main/java/org/apache/streampipes/model/configuration/LocationConfig.java create mode 100644 streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/admin/LocationConfigurationResource.java create mode 100644 ui/cypress/support/utils/configuration/ConfigurationUtils.ts create mode 100644 ui/cypress/support/utils/configuration/SiteUtils.ts create mode 100644 ui/cypress/tests/configuration/sites/sites-geo-features.spec.ts create mode 100644 ui/cypress/tests/configuration/sites/sites.spec.ts create mode 100644 ui/projects/streampipes/platform-services/src/lib/apis/location-config.service.ts create mode 100644 ui/src/app/assets/components/asset-details/asset-details-panel/asset-details-basics/asset-details-site/asset-details-site.component.html create mode 100644 ui/src/app/assets/components/asset-details/asset-details-panel/asset-details-basics/asset-details-site/asset-details-site.component.ts create mode 100644 ui/src/app/assets/components/asset-details/asset-details-panel/asset-details-basics/asset-details-site/asset-location/asset-location.component.html create mode 100644 ui/src/app/assets/components/asset-details/asset-details-panel/asset-details-basics/asset-details-site/asset-location/asset-location.component.ts create mode 100644 ui/src/app/configuration/dialog/manage-site/edit-location/edit-location-area/edit-location-area.component.html rename ui/{cypress/support/utils/configuration/ConfigutationUtils.ts => src/app/configuration/dialog/manage-site/edit-location/edit-location-area/edit-location-area.component.scss} (84%) create mode 100644 ui/src/app/configuration/dialog/manage-site/edit-location/edit-location-area/edit-location-area.component.ts create mode 100644 ui/src/app/configuration/dialog/manage-site/edit-location/edit-location.component.html create mode 100644 ui/src/app/configuration/dialog/manage-site/edit-location/edit-location.component.ts create mode 100644 ui/src/app/configuration/dialog/manage-site/manage-site-dialog.component.html create mode 100644 ui/src/app/configuration/dialog/manage-site/manage-site-dialog.component.scss create mode 100644 ui/src/app/configuration/dialog/manage-site/manage-site-dialog.component.ts create mode 100644 ui/src/app/configuration/sites-configuration/location-features-configuration/location-features-configuration.component.html create mode 100644 ui/src/app/configuration/sites-configuration/location-features-configuration/location-features-configuration.component.ts create mode 100644 ui/src/app/configuration/sites-configuration/site-area-configuration/site-area-configuration.component.html create mode 100644 ui/src/app/configuration/sites-configuration/site-area-configuration/site-area-configuration.component.ts create mode 100644 ui/src/app/configuration/sites-configuration/sites-configuration.component.html create mode 100644 ui/src/app/configuration/sites-configuration/sites-configuration.component.ts create mode 100644 ui/src/app/core-ui/single-marker-map/single-marker-map.component.html create mode 100644 ui/src/app/core-ui/single-marker-map/single-marker-map.component.ts diff --git a/streampipes-model/src/main/java/org/apache/streampipes/model/configuration/LocationConfig.java b/streampipes-model/src/main/java/org/apache/streampipes/model/configuration/LocationConfig.java new file mode 100644 index 0000000000..573e9abe70 --- /dev/null +++ b/streampipes-model/src/main/java/org/apache/streampipes/model/configuration/LocationConfig.java @@ -0,0 +1,26 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.streampipes.model.configuration; + +import org.apache.streampipes.model.shared.annotation.TsModel; + +@TsModel +public record LocationConfig(boolean locationEnabled, + String tileServerUrl, + String attributionText) {} diff --git a/streampipes-model/src/main/java/org/apache/streampipes/model/configuration/SpCoreConfiguration.java b/streampipes-model/src/main/java/org/apache/streampipes/model/configuration/SpCoreConfiguration.java index 6aad62a172..b5766bd3f6 100644 --- a/streampipes-model/src/main/java/org/apache/streampipes/model/configuration/SpCoreConfiguration.java +++ b/streampipes-model/src/main/java/org/apache/streampipes/model/configuration/SpCoreConfiguration.java @@ -32,6 +32,7 @@ public class SpCoreConfiguration { private EmailConfig emailConfig; private EmailTemplateConfig emailTemplateConfig; private GeneralConfig generalConfig; + private LocationConfig locationConfig; private boolean isConfigured; private SpCoreConfigurationStatus serviceStatus; @@ -40,6 +41,7 @@ public class SpCoreConfiguration { private String filesDir; public SpCoreConfiguration() { + this.locationConfig = new LocationConfig(false, "", ""); } public String getRev() { @@ -129,4 +131,12 @@ public SpCoreConfigurationStatus getServiceStatus() { public void setServiceStatus(SpCoreConfigurationStatus serviceStatus) { this.serviceStatus = serviceStatus; } + + public LocationConfig getLocationConfig() { + return locationConfig; + } + + public void setLocationConfig(LocationConfig locationConfig) { + this.locationConfig = locationConfig; + } } diff --git a/streampipes-rest/src/main/java/org/apache/streampipes/rest/ResetManagement.java b/streampipes-rest/src/main/java/org/apache/streampipes/rest/ResetManagement.java index 5bce889136..caca0dac6c 100644 --- a/streampipes-rest/src/main/java/org/apache/streampipes/rest/ResetManagement.java +++ b/streampipes-rest/src/main/java/org/apache/streampipes/rest/ResetManagement.java @@ -83,6 +83,8 @@ public static void reset(String username) { removeAllPipelineTemplates(); + clearGenericStorage(); + logger.info("Resetting the system was completed"); } @@ -106,7 +108,7 @@ private static void stopAndDeleteAllPipelines() { private static void stopAndDeleteAllAdapters() { AdapterMasterManagement adapterMasterManagement = new AdapterMasterManagement( StorageDispatcher.INSTANCE.getNoSqlStore() - .getAdapterInstanceStorage(), + .getAdapterInstanceStorage(), new SpResourceManager().manageAdapters(), new SpResourceManager().manageDataStreams(), AdapterMetricsManager.INSTANCE.getAdapterMetrics() @@ -152,37 +154,37 @@ private static void removeAllDataInDataLake() { private static void removeAllDataViewWidgets() { var widgetStorage = StorageDispatcher.INSTANCE.getNoSqlStore() - .getDataExplorerWidgetStorage(); + .getDataExplorerWidgetStorage(); widgetStorage.findAll() - .forEach(widget -> widgetStorage.deleteElementById(widget.getElementId())); + .forEach(widget -> widgetStorage.deleteElementById(widget.getElementId())); } private static void removeAllDataViews() { var dataLakeDashboardStorage = StorageDispatcher.INSTANCE.getNoSqlStore() - .getDataExplorerDashboardStorage(); + .getDataExplorerDashboardStorage(); dataLakeDashboardStorage.findAll() - .forEach(dashboard -> dataLakeDashboardStorage.deleteElementById(dashboard.getElementId())); + .forEach(dashboard -> dataLakeDashboardStorage.deleteElementById(dashboard.getElementId())); } private static void removeAllDashboardWidgets() { var dashboardWidgetStorage = StorageDispatcher.INSTANCE.getNoSqlStore() - .getDashboardWidgetStorage(); + .getDashboardWidgetStorage(); dashboardWidgetStorage.findAll() - .forEach(widget -> dashboardWidgetStorage.deleteElementById(widget.getElementId())); + .forEach(widget -> dashboardWidgetStorage.deleteElementById(widget.getElementId())); } private static void removeAllDashboards() { var dashboardStorage = StorageDispatcher.INSTANCE.getNoSqlStore() - .getDashboardStorage(); + .getDashboardStorage(); dashboardStorage.findAll() - .forEach(dashboard -> dashboardStorage.deleteElementById(dashboard.getElementId())); + .forEach(dashboard -> dashboardStorage.deleteElementById(dashboard.getElementId())); } private static void removeAllAssets(String username) { IGenericStorage genericStorage = StorageDispatcher.INSTANCE.getNoSqlStore() - .getGenericStorage(); + .getGenericStorage(); try { for (Map asset : genericStorage.findAll("asset-management")) { genericStorage.delete((String) asset.get("_id"), (String) asset.get("_rev")); @@ -203,4 +205,24 @@ private static void removeAllPipelineTemplates() { .forEach(pipelineElementTemplateStorage::deleteElement); } + + private static void clearGenericStorage() { + var appDocTypesToDelete = List.of( + "asset-management", + "asset-sites" + ); + var genericStorage = StorageDispatcher.INSTANCE.getNoSqlStore().getGenericStorage(); + + appDocTypesToDelete.forEach(docType -> { + try { + var allDocs = genericStorage.findAll(docType); + for (var doc : allDocs) { + genericStorage.delete(doc.get("_id").toString(), doc.get("_rev").toString()); + } + } catch (IOException e) { + throw new RuntimeException(e); + } + }); + + } } diff --git a/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/admin/LocationConfigurationResource.java b/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/admin/LocationConfigurationResource.java new file mode 100644 index 0000000000..7872efed0b --- /dev/null +++ b/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/admin/LocationConfigurationResource.java @@ -0,0 +1,53 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.streampipes.rest.impl.admin; + +import org.apache.streampipes.model.configuration.LocationConfig; +import org.apache.streampipes.rest.core.base.impl.AbstractAuthGuardedRestResource; +import org.apache.streampipes.rest.security.AuthConstants; + +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/api/v2/admin/location-config") +public class LocationConfigurationResource extends AbstractAuthGuardedRestResource { + + @GetMapping(produces = MediaType.APPLICATION_JSON_VALUE) + public LocationConfig getLocationConfig() { + return getSpCoreConfigurationStorage().get().getLocationConfig(); + } + + @PutMapping(consumes = MediaType.APPLICATION_JSON_VALUE) + @PreAuthorize(AuthConstants.IS_ADMIN_ROLE) + public ResponseEntity updateGeneralConfiguration(@RequestBody LocationConfig config) { + var storage = getSpCoreConfigurationStorage(); + var cfg = storage.get(); + cfg.setLocationConfig(config); + storage.updateElement(cfg); + + return ok(); + } +} diff --git a/ui/cypress/support/utils/configuration/ConfigurationUtils.ts b/ui/cypress/support/utils/configuration/ConfigurationUtils.ts new file mode 100644 index 0000000000..f8148033e5 --- /dev/null +++ b/ui/cypress/support/utils/configuration/ConfigurationUtils.ts @@ -0,0 +1,31 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +export class ConfigurationUtils { + public static goToConfigurationExport() { + cy.visit('#/configuration/export'); + } + + public static goToSitesConfiguration() { + cy.visit('#/configuration/sites'); + } + + public static goToGeneralConfiguration() { + cy.visit('#/configuration/general'); + } +} diff --git a/ui/cypress/support/utils/configuration/SiteUtils.ts b/ui/cypress/support/utils/configuration/SiteUtils.ts new file mode 100644 index 0000000000..59bbf0900e --- /dev/null +++ b/ui/cypress/support/utils/configuration/SiteUtils.ts @@ -0,0 +1,65 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +export class SiteUtils { + public static CHECKBOX_ENABLE_LOCATION_FEATURES = + 'sites-enable-location-features-checkbox'; + public static INPUT_TILE_SERVER_URL = 'sites-location-config-tile-server'; + + public static BUTTON_MANAGE_SITES = 'sites-manage-sites-button'; + public static BUTTON_EDIT_SITE = 'sites-edit-button'; + public static BUTTON_DELETE_SITE = 'sites-delete-button'; + public static BUTTON_SITE_DIALOG_SAVE = 'sites-dialog-save-button'; + public static BUTTON_SITE_DIALOG_CANCEL = 'sites-dialog-cancel-button'; + public static BUTTON_SITE_DIALOG_REMOVE_AREA = + 'sites-dialog-remove-area-button'; + public static BUTTON_SITE_DIALOG_ADD_AREA = 'sites-dialog-add-area-button'; + + public static INPUT_SITE_DIALOG_SITE_INPUT = 'sites-dialog-site-input'; + public static INPUT_SITE_DIALOG_NEW_AREA_INPUT = + 'sites-dialog-new-area-input'; + + public static LABEL_TABLE_NAME = 'site-table-row-label'; + public static LABEL_TABLE_AREA = 'site-table-row-areas'; + + public static enableGeoFeatures(tileServerUrl: string): void { + cy.dataCy(SiteUtils.CHECKBOX_ENABLE_LOCATION_FEATURES).click(); + cy.dataCy(SiteUtils.INPUT_TILE_SERVER_URL).clear().type(tileServerUrl); + cy.dataCy('sites-location-features-button').click(); + } + + public static createNewSite(name: string, areas: string[]): void { + cy.dataCy(SiteUtils.BUTTON_MANAGE_SITES).click(); + cy.dataCy(SiteUtils.INPUT_SITE_DIALOG_SITE_INPUT, { timeout: 2000 }) + .clear() + .type(name); + + areas.forEach(area => { + cy.dataCy(SiteUtils.INPUT_SITE_DIALOG_NEW_AREA_INPUT) + .clear() + .type(area); + cy.dataCy(SiteUtils.BUTTON_SITE_DIALOG_ADD_AREA).click(); + }); + + cy.dataCy(this.BUTTON_SITE_DIALOG_SAVE).click(); + } + + public static openEditSiteDialog() { + cy.dataCy(SiteUtils.BUTTON_EDIT_SITE).first().click(); + } +} diff --git a/ui/cypress/tests/assetManagement/createAsset.spec.ts b/ui/cypress/tests/assetManagement/createAsset.spec.ts index e52d7b500c..7f5171631a 100644 --- a/ui/cypress/tests/assetManagement/createAsset.spec.ts +++ b/ui/cypress/tests/assetManagement/createAsset.spec.ts @@ -20,7 +20,7 @@ import { AdapterBuilder } from '../../support/builder/AdapterBuilder'; import { ConnectUtils } from '../../support/utils/connect/ConnectUtils'; import { AssetUtils } from '../../support/utils/asset/AssetUtils'; import { DashboardUtils } from '../../support/utils/DashboardUtils'; -import { ConfigurationUtils } from '../../support/utils/configuration/ConfigutationUtils'; +import { ConfigurationUtils } from '../../support/utils/configuration/ConfigurationUtils'; describe('Creates a new adapter, add to assets and export assets', () => { beforeEach('Setup Test', () => { diff --git a/ui/cypress/tests/configuration/sites/sites-geo-features.spec.ts b/ui/cypress/tests/configuration/sites/sites-geo-features.spec.ts new file mode 100644 index 0000000000..4f8649f8e0 --- /dev/null +++ b/ui/cypress/tests/configuration/sites/sites-geo-features.spec.ts @@ -0,0 +1,41 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +import { SiteUtils } from '../../../support/utils/configuration/SiteUtils'; +import { ConfigurationUtils } from '../../../support/utils/configuration/ConfigurationUtils'; + +describe('Test geo features settings', () => { + beforeEach('Setup Test', () => { + cy.initStreamPipesTest(); + }); + + it('Perform Test', () => { + // enable geo features + ConfigurationUtils.goToSitesConfiguration(); + SiteUtils.enableGeoFeatures('url'); + + ConfigurationUtils.goToGeneralConfiguration(); + ConfigurationUtils.goToSitesConfiguration(); + + cy.dataCy(SiteUtils.CHECKBOX_ENABLE_LOCATION_FEATURES) + .find('input') + .should('be.checked'); + + cy.dataCy(SiteUtils.INPUT_TILE_SERVER_URL).should('have.value', 'url'); + }); +}); diff --git a/ui/cypress/tests/configuration/sites/sites.spec.ts b/ui/cypress/tests/configuration/sites/sites.spec.ts new file mode 100644 index 0000000000..5b0fb8b861 --- /dev/null +++ b/ui/cypress/tests/configuration/sites/sites.spec.ts @@ -0,0 +1,64 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +import { SiteUtils } from '../../../support/utils/configuration/SiteUtils'; +import { ConfigurationUtils } from '../../../support/utils/configuration/ConfigurationUtils'; + +describe('Test configuration of sites', () => { + beforeEach('Setup Test', () => { + cy.initStreamPipesTest(); + }); + + it('Perform Test', () => { + const site = 'My Site'; + const newSite = 'My modified Site'; + const areas = ['Area A', 'Area B']; + ConfigurationUtils.goToSitesConfiguration(); + SiteUtils.createNewSite(site, areas); + + cy.dataCy(SiteUtils.LABEL_TABLE_NAME).should('have.length', 1); + + cy.dataCy(SiteUtils.LABEL_TABLE_NAME) + .first() + .should('contain.text', site); + cy.dataCy(SiteUtils.LABEL_TABLE_AREA) + .first() + .should('contains.text', areas.toString()); + + SiteUtils.openEditSiteDialog(); + + cy.dataCy(SiteUtils.INPUT_SITE_DIALOG_SITE_INPUT, { timeout: 2000 }) + .clear() + .type(newSite); + + cy.dataCy(SiteUtils.BUTTON_SITE_DIALOG_REMOVE_AREA + '_Area_A').click(); + cy.dataCy(SiteUtils.BUTTON_SITE_DIALOG_SAVE).click(); + + cy.dataCy(SiteUtils.LABEL_TABLE_NAME).should('have.length', 1); + + cy.dataCy(SiteUtils.LABEL_TABLE_NAME) + .first() + .should('contain.text', newSite); + cy.dataCy(SiteUtils.LABEL_TABLE_AREA) + .first() + .should('have.text', ' Area B '); + + cy.dataCy(SiteUtils.BUTTON_DELETE_SITE + '-My_modified_Site').click(); + cy.dataCy(SiteUtils.LABEL_TABLE_NAME).should('have.length', 0); + }); +}); diff --git a/ui/projects/streampipes/platform-services/src/lib/apis/datalake-rest.service.ts b/ui/projects/streampipes/platform-services/src/lib/apis/datalake-rest.service.ts index a8e8c8928f..a2cb61594b 100644 --- a/ui/projects/streampipes/platform-services/src/lib/apis/datalake-rest.service.ts +++ b/ui/projects/streampipes/platform-services/src/lib/apis/datalake-rest.service.ts @@ -19,11 +19,7 @@ import { Injectable } from '@angular/core'; import { HttpClient, HttpParams, HttpRequest } from '@angular/common/http'; import { Observable, of } from 'rxjs'; -import { - DataLakeMeasure, - PageResult, - SpQueryResult, -} from '../model/gen/streampipes-model'; +import { DataLakeMeasure, SpQueryResult } from '../model/gen/streampipes-model'; import { map } from 'rxjs/operators'; import { DatalakeQueryParameters } from '../model/datalake/DatalakeQueryParameters'; @@ -103,32 +99,6 @@ export class DatalakeRestService { } } - getPagedData( - index: string, - itemsPerPage: number, - page: number, - columns?: string, - order?: string, - ): Observable { - const url = this.dataLakeUrl + '/measurements/' + index; - - const queryParams: DatalakeQueryParameters = this.getQueryParameters( - columns, - undefined, - undefined, - page, - itemsPerPage, - undefined, - undefined, - order, - undefined, - undefined, - ); - - // @ts-ignore - return this.http.get(url, { params: queryParams }); - } - getTagValues( index: string, fieldNames: string[], diff --git a/ui/projects/streampipes/platform-services/src/lib/apis/location-config.service.ts b/ui/projects/streampipes/platform-services/src/lib/apis/location-config.service.ts new file mode 100644 index 0000000000..d07e8b8e13 --- /dev/null +++ b/ui/projects/streampipes/platform-services/src/lib/apis/location-config.service.ts @@ -0,0 +1,48 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +import { Injectable } from '@angular/core'; +import { HttpClient } from '@angular/common/http'; +import { Observable } from 'rxjs'; +import { map } from 'rxjs/operators'; +import { PlatformServicesCommons } from './commons.service'; +import { LocationConfig } from '../model/gen/streampipes-model'; + +@Injectable({ + providedIn: 'root', +}) +export class LocationConfigService { + constructor( + private http: HttpClient, + private platformServicesCommons: PlatformServicesCommons, + ) {} + + getLocationConfig(): Observable { + return this.http + .get(this.locationConfigPath) + .pipe(map(response => response as LocationConfig)); + } + + updateLocationConfig(config: LocationConfig): Observable { + return this.http.put(this.locationConfigPath, config); + } + + private get locationConfigPath() { + return `${this.platformServicesCommons.apiBasePath}/admin/location-config`; + } +} diff --git a/ui/projects/streampipes/platform-services/src/lib/model/assets/asset.model.ts b/ui/projects/streampipes/platform-services/src/lib/model/assets/asset.model.ts index c1872f5f31..084b874214 100644 --- a/ui/projects/streampipes/platform-services/src/lib/model/assets/asset.model.ts +++ b/ui/projects/streampipes/platform-services/src/lib/model/assets/asset.model.ts @@ -48,15 +48,40 @@ export interface Isa95TypeDesc { type: Isa95Type; } +export interface AssetLocation { + coordinates: LatLng; + zoom?: number; +} + +export interface AssetSiteDesc { + _id: string; + _rev?: string; + appDocType: string; + label: string; + location?: AssetLocation; + areas: string[]; +} + +export interface LatLng { + latitude: number; + longitude: number; +} + +export interface AssetSite { + siteId: string; + area: string; + hasExactLocation: boolean; + location?: AssetLocation; +} + export interface SpAsset { assetId: string; assetName: string; assetDescription: string; - assetType: AssetType; labelIds?: string[]; assetLinks: AssetLink[]; - + assetSite?: AssetSite; assets: SpAsset[]; } diff --git a/ui/projects/streampipes/platform-services/src/lib/model/gen/streampipes-model.ts b/ui/projects/streampipes/platform-services/src/lib/model/gen/streampipes-model.ts index 29b7a7f837..59568c24a9 100644 --- a/ui/projects/streampipes/platform-services/src/lib/model/gen/streampipes-model.ts +++ b/ui/projects/streampipes/platform-services/src/lib/model/gen/streampipes-model.ts @@ -20,11 +20,10 @@ /* tslint:disable */ /* eslint-disable */ // @ts-nocheck -// Generated using typescript-generator version 3.2.1263 on 2024-07-29 21:03:44. +// Generated using typescript-generator version 3.2.1263 on 2024-08-15 17:36:05. export class NamedStreamPipesEntity implements Storable { '@class': - | 'org.apache.streampipes.model.connect.grounding.ProtocolDescription' | 'org.apache.streampipes.model.template.PipelineTemplateDescription' | 'org.apache.streampipes.model.SpDataStream' | 'org.apache.streampipes.model.base.VersionedNamedStreamPipesEntity' @@ -2137,6 +2136,26 @@ export class ListOutputStrategy extends OutputStrategy { } } +export class LocationConfig { + attributionText: string; + locationEnabled: boolean; + tileServerUrl: string; + + static fromData( + data: LocationConfig, + target?: LocationConfig, + ): LocationConfig { + if (!data) { + return data; + } + const instance = target || new LocationConfig(); + instance.attributionText = data.attributionText; + instance.locationEnabled = data.locationEnabled; + instance.tileServerUrl = data.tileServerUrl; + return instance; + } +} + export class MappingProperty extends StaticProperty { '@class': | 'org.apache.streampipes.model.staticproperty.MappingProperty' @@ -2430,25 +2449,6 @@ export class Option { } } -/** - * @deprecated since 0.92.0, for removal - */ -export class PageResult extends DataSeries { - page: number; - pageSum: number; - - static fromData(data: PageResult, target?: PageResult): PageResult { - if (!data) { - return data; - } - const instance = target || new PageResult(); - super.fromData(data, instance); - instance.page = data.page; - instance.pageSum = data.pageSum; - return instance; - } -} - export class Pipeline implements Storable { _id: string; _rev: string; @@ -3054,35 +3054,6 @@ export class PropertyValueSpecification { } } -/** - * @deprecated since 0.93.0, for removal - */ -export class ProtocolDescription extends NamedStreamPipesEntity { - '@class': 'org.apache.streampipes.model.connect.grounding.ProtocolDescription'; - 'category': string[]; - 'config': StaticPropertyUnion[]; - 'sourceType': string; - - static 'fromData'( - data: ProtocolDescription, - target?: ProtocolDescription, - ): ProtocolDescription { - if (!data) { - return data; - } - const instance = target || new ProtocolDescription(); - super.fromData(data, instance); - instance.category = __getCopyArrayFn(__identity())( - data.category, - ); - instance.config = __getCopyArrayFn(StaticProperty.fromDataUnion)( - data.config, - ); - instance.sourceType = data.sourceType; - return instance; - } -} - export class PulsarTransportProtocol extends TransportProtocol { '@class': 'org.apache.streampipes.model.grounding.PulsarTransportProtocol'; diff --git a/ui/projects/streampipes/platform-services/src/public-api.ts b/ui/projects/streampipes/platform-services/src/public-api.ts index ce377827b3..8e0d2d428c 100644 --- a/ui/projects/streampipes/platform-services/src/public-api.ts +++ b/ui/projects/streampipes/platform-services/src/public-api.ts @@ -34,6 +34,7 @@ export * from './lib/apis/functions.service'; export * from './lib/apis/general-config.service'; export * from './lib/apis/generic-storage.service'; export * from './lib/apis/labels.service'; +export * from './lib/apis/location-config.service'; export * from './lib/apis/mail-config.service'; export * from './lib/apis/measurement-units.service'; export * from './lib/apis/permissions.service'; diff --git a/ui/projects/streampipes/shared-ui/src/lib/components/basic-field-description/basic-field-description.component.html b/ui/projects/streampipes/shared-ui/src/lib/components/basic-field-description/basic-field-description.component.html index 83115501f2..a33844fb7d 100644 --- a/ui/projects/streampipes/shared-ui/src/lib/components/basic-field-description/basic-field-description.component.html +++ b/ui/projects/streampipes/shared-ui/src/lib/components/basic-field-description/basic-field-description.component.html @@ -19,7 +19,8 @@
diff --git a/ui/src/app/assets/assets.module.ts b/ui/src/app/assets/assets.module.ts index c8adc3fa06..fc81b953f7 100644 --- a/ui/src/app/assets/assets.module.ts +++ b/ui/src/app/assets/assets.module.ts @@ -57,6 +57,8 @@ import { MatButtonToggleModule } from '@angular/material/button-toggle'; import { AssetDetailsLabelsComponent } from './components/asset-details/asset-details-panel/asset-details-basics/asset-details-labels/asset-details-labels.component'; import { MatChipGrid, MatChipsModule } from '@angular/material/chips'; import { MatAutocompleteModule } from '@angular/material/autocomplete'; +import { AssetDetailsSiteComponent } from './components/asset-details/asset-details-panel/asset-details-basics/asset-details-site/asset-details-site.component'; +import { AssetLocationComponent } from './components/asset-details/asset-details-panel/asset-details-basics/asset-details-site/asset-location/asset-location.component'; @NgModule({ imports: [ @@ -112,7 +114,9 @@ import { MatAutocompleteModule } from '@angular/material/autocomplete'; AssetDetailsBasicsComponent, AssetDetailsLabelsComponent, AssetDetailsLinksComponent, + AssetDetailsSiteComponent, AssetLinkSectionComponent, + AssetLocationComponent, AssetUploadDialogComponent, EditAssetLinkDialogComponent, SpAssetDetailsComponent, diff --git a/ui/src/app/assets/components/asset-details/asset-details-panel/asset-details-basics/asset-details-basics.component.html b/ui/src/app/assets/components/asset-details/asset-details-panel/asset-details-basics/asset-details-basics.component.html index 71f9b00b9c..0cc81d4e70 100644 --- a/ui/src/app/assets/components/asset-details/asset-details-panel/asset-details-basics/asset-details-basics.component.html +++ b/ui/src/app/assets/components/asset-details/asset-details-panel/asset-details-basics/asset-details-basics.component.html @@ -100,12 +100,27 @@ label="Labels" description="Assign additional labels to better discover your assets" > - - + + + + +
diff --git a/ui/src/app/assets/components/asset-details/asset-details-panel/asset-details-basics/asset-details-basics.component.ts b/ui/src/app/assets/components/asset-details/asset-details-panel/asset-details-basics/asset-details-basics.component.ts index f84ef1fd3b..7315d5e4c4 100644 --- a/ui/src/app/assets/components/asset-details/asset-details-panel/asset-details-basics/asset-details-basics.component.ts +++ b/ui/src/app/assets/components/asset-details/asset-details-panel/asset-details-basics/asset-details-basics.component.ts @@ -16,37 +16,55 @@ * */ -import { Component, Input, OnInit } from '@angular/core'; import { + Component, + Input, + OnChanges, + OnInit, + SimpleChanges, +} from '@angular/core'; +import { + AssetSiteDesc, Isa95TypeDesc, Isa95TypeService, SpAsset, } from '@streampipes/platform-services'; @Component({ - selector: 'sp-asset-details-basics-component', + selector: 'sp-asset-details-basics', templateUrl: './asset-details-basics.component.html', styleUrls: ['./asset-details-basics.component.scss'], }) -export class AssetDetailsBasicsComponent implements OnInit { +export class AssetDetailsBasicsComponent implements OnInit, OnChanges { @Input() asset: SpAsset; @Input() editMode: boolean; + @Input() + rootNode: boolean; + + @Input() + sites: AssetSiteDesc[]; + isa95Types: Isa95TypeDesc[] = []; constructor(private isa95TypeService: Isa95TypeService) {} ngOnInit() { - this.asset.assetType ??= { - assetIcon: undefined, - assetIconColor: undefined, - assetTypeCategory: undefined, - assetTypeLabel: undefined, - isa95AssetType: 'OTHER', - }; this.isa95Types = this.isa95TypeService.getTypeDescriptions(); } + + ngOnChanges(changes: SimpleChanges) { + if (changes['asset']) { + this.asset.assetType ??= { + assetIcon: undefined, + assetIconColor: undefined, + assetTypeCategory: undefined, + assetTypeLabel: undefined, + isa95AssetType: 'OTHER', + }; + } + } } diff --git a/ui/src/app/assets/components/asset-details/asset-details-panel/asset-details-basics/asset-details-labels/asset-details-labels.component.ts b/ui/src/app/assets/components/asset-details/asset-details-panel/asset-details-basics/asset-details-labels/asset-details-labels.component.ts index 445072cf5d..b26fbcde6a 100644 --- a/ui/src/app/assets/components/asset-details/asset-details-panel/asset-details-basics/asset-details-labels/asset-details-labels.component.ts +++ b/ui/src/app/assets/components/asset-details/asset-details-panel/asset-details-basics/asset-details-labels/asset-details-labels.component.ts @@ -39,7 +39,7 @@ import { map, startWith } from 'rxjs/operators'; import { SpColorizationService } from '@streampipes/shared-ui'; @Component({ - selector: 'sp-asset-details-labels-component', + selector: 'sp-asset-details-labels', templateUrl: './asset-details-labels.component.html', }) export class AssetDetailsLabelsComponent implements OnInit, OnChanges { diff --git a/ui/src/app/assets/components/asset-details/asset-details-panel/asset-details-basics/asset-details-site/asset-details-site.component.html b/ui/src/app/assets/components/asset-details/asset-details-panel/asset-details-basics/asset-details-site/asset-details-site.component.html new file mode 100644 index 0000000000..5b0c819560 --- /dev/null +++ b/ui/src/app/assets/components/asset-details/asset-details-panel/asset-details-basics/asset-details-site/asset-details-site.component.html @@ -0,0 +1,46 @@ + + +
+ + Site + + + {{ site.label }} + + + + + Area + + + {{ area }} + + + + + +
diff --git a/ui/src/app/assets/components/asset-details/asset-details-panel/asset-details-basics/asset-details-site/asset-details-site.component.ts b/ui/src/app/assets/components/asset-details/asset-details-panel/asset-details-basics/asset-details-site/asset-details-site.component.ts new file mode 100644 index 0000000000..50988ff75a --- /dev/null +++ b/ui/src/app/assets/components/asset-details/asset-details-panel/asset-details-basics/asset-details-site/asset-details-site.component.ts @@ -0,0 +1,63 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +import { Component, Input, OnChanges, SimpleChanges } from '@angular/core'; +import { AssetSiteDesc, SpAsset } from '@streampipes/platform-services'; +import { MatSelectChange } from '@angular/material/select'; + +@Component({ + selector: 'sp-asset-details-site', + templateUrl: './asset-details-site.component.html', +}) +export class AssetDetailsSiteComponent implements OnChanges { + @Input() + asset: SpAsset; + + @Input() + editMode: boolean; + + @Input() + sites: AssetSiteDesc[]; + + currentSite: AssetSiteDesc; + + constructor() {} + + ngOnChanges(changes: SimpleChanges) { + if (changes['asset']) { + this.asset.assetSite ??= { + area: undefined, + siteId: undefined, + hasExactLocation: false, + }; + if (this.sites.length > 0) { + if (this.asset.assetSite.siteId !== undefined) { + this.selectCurrentSite(this.asset.assetSite.siteId); + } + } + } + } + + handleLocationChange(event: MatSelectChange) { + this.selectCurrentSite(event.value); + } + + selectCurrentSite(siteId: string): void { + this.currentSite = this.sites.find(loc => loc._id === siteId); + } +} diff --git a/ui/src/app/assets/components/asset-details/asset-details-panel/asset-details-basics/asset-details-site/asset-location/asset-location.component.html b/ui/src/app/assets/components/asset-details/asset-details-panel/asset-details-basics/asset-details-site/asset-location/asset-location.component.html new file mode 100644 index 0000000000..64443f8483 --- /dev/null +++ b/ui/src/app/assets/components/asset-details/asset-details-panel/asset-details-basics/asset-details-site/asset-location/asset-location.component.html @@ -0,0 +1,34 @@ + + +
+ + Add exact location + +
+ + +
+
diff --git a/ui/src/app/assets/components/asset-details/asset-details-panel/asset-details-basics/asset-details-site/asset-location/asset-location.component.ts b/ui/src/app/assets/components/asset-details/asset-details-panel/asset-details-basics/asset-details-site/asset-location/asset-location.component.ts new file mode 100644 index 0000000000..f0a7f7efe3 --- /dev/null +++ b/ui/src/app/assets/components/asset-details/asset-details-panel/asset-details-basics/asset-details-site/asset-location/asset-location.component.ts @@ -0,0 +1,53 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +import { Component, Input, OnInit } from '@angular/core'; +import { + LocationConfig, + LocationConfigService, + SpAsset, +} from '@streampipes/platform-services'; + +@Component({ + selector: 'sp-asset-location', + templateUrl: './asset-location.component.html', +}) +export class AssetLocationComponent implements OnInit { + @Input() + asset: SpAsset; + + @Input() + editMode: boolean; + + locationConfig: LocationConfig; + + constructor(private locationConfigService: LocationConfigService) {} + + ngOnInit() { + this.asset.assetSite.location ??= { + coordinates: { + latitude: 0, + longitude: 0, + }, + zoom: 1, + }; + this.locationConfigService + .getLocationConfig() + .subscribe(res => (this.locationConfig = res)); + } +} diff --git a/ui/src/app/assets/components/asset-details/asset-details-panel/asset-details-links/asset-details-links.component.html b/ui/src/app/assets/components/asset-details/asset-details-panel/asset-details-links/asset-details-links.component.html index 10795c7ac6..bb5d58a2bc 100644 --- a/ui/src/app/assets/components/asset-details/asset-details-panel/asset-details-links/asset-details-links.component.html +++ b/ui/src/app/assets/components/asset-details/asset-details-panel/asset-details-links/asset-details-links.component.html @@ -50,7 +50,7 @@ class="mt-10" >
- - +
diff --git a/ui/src/app/assets/components/asset-details/asset-details-panel/asset-details-links/asset-details-links.component.ts b/ui/src/app/assets/components/asset-details/asset-details-panel/asset-details-links/asset-details-links.component.ts index c56cf74786..b9baa36975 100644 --- a/ui/src/app/assets/components/asset-details/asset-details-panel/asset-details-links/asset-details-links.component.ts +++ b/ui/src/app/assets/components/asset-details/asset-details-panel/asset-details-links/asset-details-links.component.ts @@ -29,7 +29,7 @@ import { EditAssetLinkDialogComponent } from '../../../../dialog/edit-asset-link import { AssetConstants } from '../../../../constants/asset.constants'; @Component({ - selector: 'sp-asset-details-links-component', + selector: 'sp-asset-details-links', templateUrl: './asset-details-links.component.html', }) export class AssetDetailsLinksComponent implements OnInit { diff --git a/ui/src/app/assets/components/asset-details/asset-details-panel/asset-details-links/asset-link-section/asset-link-item/asset-link-item.component.ts b/ui/src/app/assets/components/asset-details/asset-details-panel/asset-details-links/asset-link-section/asset-link-item/asset-link-item.component.ts index 34825aba31..46eda1e238 100644 --- a/ui/src/app/assets/components/asset-details/asset-details-panel/asset-details-links/asset-link-section/asset-link-item/asset-link-item.component.ts +++ b/ui/src/app/assets/components/asset-details/asset-details-panel/asset-details-links/asset-link-section/asset-link-item/asset-link-item.component.ts @@ -21,7 +21,7 @@ import { AssetLink, AssetLinkType } from '@streampipes/platform-services'; import { Router } from '@angular/router'; @Component({ - selector: 'sp-asset-link-item-component', + selector: 'sp-asset-link-item', templateUrl: './asset-link-item.component.html', styleUrls: ['./asset-link-item.component.scss'], }) diff --git a/ui/src/app/assets/components/asset-details/asset-details-panel/asset-details-links/asset-link-section/asset-link-section.component.html b/ui/src/app/assets/components/asset-details/asset-details-panel/asset-details-links/asset-link-section/asset-link-section.component.html index d5e20ef909..8d936f3f62 100644 --- a/ui/src/app/assets/components/asset-details/asset-details-panel/asset-details-links/asset-link-section/asset-link-section.component.html +++ b/ui/src/app/assets/components/asset-details/asset-details-panel/asset-details-links/asset-link-section/asset-link-section.component.html @@ -27,7 +27,7 @@ | assetTypeFilter: assetLinkType.linkType " > - - + diff --git a/ui/src/app/assets/components/asset-details/asset-details-panel/asset-details-links/asset-link-section/asset-link-section.component.ts b/ui/src/app/assets/components/asset-details/asset-details-panel/asset-details-links/asset-link-section/asset-link-section.component.ts index ebfe1055ff..f92f3a6287 100644 --- a/ui/src/app/assets/components/asset-details/asset-details-panel/asset-details-links/asset-link-section/asset-link-section.component.ts +++ b/ui/src/app/assets/components/asset-details/asset-details-panel/asset-details-links/asset-link-section/asset-link-section.component.ts @@ -24,7 +24,7 @@ import { } from '@streampipes/platform-services'; @Component({ - selector: 'sp-asset-link-section-component', + selector: 'sp-asset-link-section', templateUrl: './asset-link-section.component.html', styleUrls: ['./asset-link-section.component.scss'], }) diff --git a/ui/src/app/assets/components/asset-details/asset-details-panel/asset-details-panel.component.html b/ui/src/app/assets/components/asset-details/asset-details-panel/asset-details-panel.component.html index 9b9157f43c..0e95be38a9 100644 --- a/ui/src/app/assets/components/asset-details/asset-details-panel/asset-details-panel.component.html +++ b/ui/src/app/assets/components/asset-details/asset-details-panel/asset-details-panel.component.html @@ -19,19 +19,21 @@
- - + - - +
diff --git a/ui/src/app/assets/components/asset-details/asset-details-panel/asset-details-panel.component.ts b/ui/src/app/assets/components/asset-details/asset-details-panel/asset-details-panel.component.ts index e61b6d379b..c4a7a55fc2 100644 --- a/ui/src/app/assets/components/asset-details/asset-details-panel/asset-details-panel.component.ts +++ b/ui/src/app/assets/components/asset-details/asset-details-panel/asset-details-panel.component.ts @@ -17,10 +17,10 @@ */ import { Component, EventEmitter, Input, Output } from '@angular/core'; -import { SpAsset } from '@streampipes/platform-services'; +import { AssetSiteDesc, SpAsset } from '@streampipes/platform-services'; @Component({ - selector: 'sp-asset-details-panel-component', + selector: 'sp-asset-details-panel', templateUrl: './asset-details-panel.component.html', styleUrls: ['./asset-details-panel.component.scss'], }) @@ -31,6 +31,12 @@ export class SpAssetDetailsPanelComponent { @Input() editMode: boolean; + @Input() + rootNode: boolean; + + @Input() + sites: AssetSiteDesc[]; + @Output() updateAssetEmitter: EventEmitter = new EventEmitter(); } diff --git a/ui/src/app/assets/components/asset-details/asset-details.component.html b/ui/src/app/assets/components/asset-details/asset-details.component.html index e99f362281..2e0928fd96 100644 --- a/ui/src/app/assets/components/asset-details/asset-details.component.html +++ b/ui/src/app/assets/components/asset-details/asset-details.component.html @@ -51,25 +51,27 @@ class="asset-tree-panel" >
- - +
- - + diff --git a/ui/src/app/assets/components/asset-details/asset-details.component.ts b/ui/src/app/assets/components/asset-details/asset-details.component.ts index 7956dae908..cbb53dc398 100644 --- a/ui/src/app/assets/components/asset-details/asset-details.component.ts +++ b/ui/src/app/assets/components/asset-details/asset-details.component.ts @@ -21,21 +21,25 @@ import { SpBreadcrumbService } from '@streampipes/shared-ui'; import { ActivatedRoute } from '@angular/router'; import { AssetConstants } from '../../constants/asset.constants'; import { + AssetSiteDesc, GenericStorageService, SpAsset, SpAssetModel, } from '@streampipes/platform-services'; import { SpAssetRoutes } from '../../assets.routes'; +import { zip } from 'rxjs'; @Component({ - selector: 'sp-asset-details-component', + selector: 'sp-asset-details', templateUrl: './asset-details.component.html', styleUrls: ['./asset-details.component.scss'], }) export class SpAssetDetailsComponent implements OnInit { asset: SpAssetModel; + sites: AssetSiteDesc[] = []; selectedAsset: SpAsset; + rootNode = true; editMode: boolean; @@ -50,22 +54,33 @@ export class SpAssetDetailsComponent implements OnInit { ngOnInit(): void { this.assetModelId = this.route.snapshot.params.assetId; this.editMode = this.route.snapshot.queryParams.editMode; - this.loadAsset(); + this.loadResources(); } - loadAsset(): void { - this.genericStorageService - .getDocument(AssetConstants.ASSET_APP_DOC_NAME, this.assetModelId) - .subscribe(asset => { - this.asset = asset; - if (!this.selectedAsset) { - this.selectedAsset = this.asset; - } - this.breadcrumbService.updateBreadcrumb([ - SpAssetRoutes.BASE, - { label: this.asset.assetName }, - ]); - }); + loadResources(): void { + const assetReq = this.genericStorageService.getDocument( + AssetConstants.ASSET_APP_DOC_NAME, + this.assetModelId, + ); + const locationsReq = this.genericStorageService.getAllDocuments( + AssetConstants.ASSET_SITES_APP_DOC_NAME, + ); + zip([assetReq, locationsReq]).subscribe(res => { + this.asset = res[0]; + this.sites = res[1]; + if (!this.selectedAsset) { + this.selectedAsset = this.asset; + } + this.breadcrumbService.updateBreadcrumb([ + SpAssetRoutes.BASE, + { label: this.asset.assetName }, + ]); + }); + } + + applySelectedAsset(event: { asset: SpAsset; rootNode: boolean }): void { + this.selectedAsset = event.asset; + this.rootNode = event.rootNode; } updateAsset() { @@ -76,7 +91,7 @@ export class SpAssetDetailsComponent implements OnInit { this.genericStorageService .updateDocument(AssetConstants.ASSET_APP_DOC_NAME, this.asset) .subscribe(res => { - this.loadAsset(); + this.loadResources(); this.editMode = false; }); } diff --git a/ui/src/app/assets/components/asset-details/asset-selection-panel/asset-selection-panel.component.html b/ui/src/app/assets/components/asset-details/asset-selection-panel/asset-selection-panel.component.html index 851c39ef78..bf93eed73a 100644 --- a/ui/src/app/assets/components/asset-details/asset-selection-panel/asset-selection-panel.component.html +++ b/ui/src/app/assets/components/asset-details/asset-selection-panel/asset-selection-panel.component.html @@ -45,7 +45,7 @@

Asset Browser

" fxLayout="row" fxFlex="100" - (click)="selectNode(node)" + (click)="selectNode(node, false)" > {{ node.assetName @@ -94,7 +94,7 @@

Asset Browser

" fxLayout="row" fxFlex="100" - (click)="selectNode(node)" + (click)="selectNode(node, true)" > {{ node.assetName diff --git a/ui/src/app/assets/components/asset-details/asset-selection-panel/asset-selection-panel.component.ts b/ui/src/app/assets/components/asset-details/asset-selection-panel/asset-selection-panel.component.ts index b69e8566aa..80674a89ed 100644 --- a/ui/src/app/assets/components/asset-details/asset-selection-panel/asset-selection-panel.component.ts +++ b/ui/src/app/assets/components/asset-details/asset-selection-panel/asset-selection-panel.component.ts @@ -17,7 +17,6 @@ */ import { - AfterViewInit, Component, EventEmitter, Input, @@ -30,7 +29,7 @@ import { NestedTreeControl } from '@angular/cdk/tree'; import { MatTreeNestedDataSource } from '@angular/material/tree'; @Component({ - selector: 'sp-asset-selection-panel-component', + selector: 'sp-asset-selection-panel', templateUrl: './asset-selection-panel.component.html', styleUrls: ['./asset-selection-panel.component.scss'], }) @@ -45,7 +44,8 @@ export class SpAssetSelectionPanelComponent implements OnInit { editMode: boolean; @Output() - selectedAssetEmitter: EventEmitter = new EventEmitter(); + selectedAssetEmitter: EventEmitter<{ asset: SpAsset; rootNode: boolean }> = + new EventEmitter<{ asset: SpAsset; rootNode: boolean }>(); treeControl = new NestedTreeControl(node => node.assets); dataSource = new MatTreeNestedDataSource(); @@ -63,8 +63,8 @@ export class SpAssetSelectionPanelComponent implements OnInit { this.treeControl.expandAll(); } - selectNode(asset: SpAsset) { - this.selectedAssetEmitter.emit(asset); + selectNode(asset: SpAsset, rootNode: boolean) { + this.selectedAssetEmitter.emit({ asset, rootNode }); } addAsset(node: SpAsset) { diff --git a/ui/src/app/assets/components/asset-overview/asset-overview.component.ts b/ui/src/app/assets/components/asset-overview/asset-overview.component.ts index ba13abcf04..956c37fb0a 100644 --- a/ui/src/app/assets/components/asset-overview/asset-overview.component.ts +++ b/ui/src/app/assets/components/asset-overview/asset-overview.component.ts @@ -38,7 +38,7 @@ import { saveAs } from 'file-saver'; import { IdGeneratorService } from '../../../core-services/id-generator/id-generator.service'; @Component({ - selector: 'sp-asset-overview-component', + selector: 'sp-asset-overview', templateUrl: './asset-overview.component.html', styleUrls: ['./asset-overview.component.scss'], }) diff --git a/ui/src/app/assets/constants/asset.constants.ts b/ui/src/app/assets/constants/asset.constants.ts index 9d99800ca1..879e1a1243 100644 --- a/ui/src/app/assets/constants/asset.constants.ts +++ b/ui/src/app/assets/constants/asset.constants.ts @@ -18,5 +18,6 @@ export class AssetConstants { public static ASSET_APP_DOC_NAME = 'asset-management'; + public static ASSET_SITES_APP_DOC_NAME = 'asset-sites'; public static ASSET_LINK_TYPES_DOC_NAME = 'asset-link-type'; } diff --git a/ui/src/app/configuration/configuration-tabs.ts b/ui/src/app/configuration/configuration-tabs.ts index f897f212a5..e3359dd72f 100644 --- a/ui/src/app/configuration/configuration-tabs.ts +++ b/ui/src/app/configuration/configuration-tabs.ts @@ -66,6 +66,11 @@ export class SpConfigurationTabs { itemTitle: 'Security', itemLink: ['configuration', 'security'], }, + { + itemId: 'sites', + itemTitle: 'Sites', + itemLink: ['configuration', 'sites'], + }, ]; } } diff --git a/ui/src/app/configuration/configuration.module.ts b/ui/src/app/configuration/configuration.module.ts index 305a667d6d..3d781332a1 100644 --- a/ui/src/app/configuration/configuration.module.ts +++ b/ui/src/app/configuration/configuration.module.ts @@ -79,6 +79,13 @@ import { EndpointItemComponent } from './extensions-installation/endpoint-item/e import { SpExtensionsInstallationComponent } from './extensions-installation/extensions-installation.component'; import { MatMenuModule } from '@angular/material/menu'; import { SpConfigurationLinkSettingsComponent } from './general-configuration/link-settings/link-settings.component'; +import { SitesConfigurationComponent } from './sites-configuration/sites-configuration.component'; +import { LocationFeaturesConfigurationComponent } from './sites-configuration/location-features-configuration/location-features-configuration.component'; +import { SiteAreaConfigurationComponent } from './sites-configuration/site-area-configuration/site-area-configuration.component'; +import { MatSort } from '@angular/material/sort'; +import { ManageSiteDialogComponent } from './dialog/manage-site/manage-site-dialog.component'; +import { EditAssetLocationComponent } from './dialog/manage-site/edit-location/edit-location.component'; +import { EditAssetLocationAreaComponent } from './dialog/manage-site/edit-location/edit-location-area/edit-location-area.component'; @NgModule({ imports: [ @@ -148,12 +155,17 @@ import { SpConfigurationLinkSettingsComponent } from './general-configuration/li path: 'security', component: SecurityConfigurationComponent, }, + { + path: 'sites', + component: SitesConfigurationComponent, + }, ], }, ]), SharedUiModule, ColorPickerModule, CodemirrorModule, + MatSort, ], declarations: [ ServiceConfigsComponent, @@ -162,16 +174,22 @@ import { SpConfigurationLinkSettingsComponent } from './general-configuration/li ServiceConfigsBooleanComponent, ServiceConfigsNumberComponent, DeleteDatalakeIndexComponent, + EditAssetLocationComponent, + EditAssetLocationAreaComponent, EditUserDialogComponent, EditGroupDialogComponent, EmailConfigurationComponent, GeneralConfigurationComponent, ExtensionsServiceManagementComponent, + LocationFeaturesConfigurationComponent, + ManageSiteDialogComponent, + SitesConfigurationComponent, SecurityAuthenticationConfigurationComponent, SecurityConfigurationComponent, SecurityUserConfigComponent, SecurityUserGroupConfigComponent, SecurityServiceConfigComponent, + SiteAreaConfigurationComponent, MessagingConfigurationComponent, DatalakeConfigurationComponent, SpConfigurationLinkSettingsComponent, diff --git a/ui/src/app/configuration/configuration.routes.ts b/ui/src/app/configuration/configuration.routes.ts index 14ac644c49..83500ea14d 100644 --- a/ui/src/app/configuration/configuration.routes.ts +++ b/ui/src/app/configuration/configuration.routes.ts @@ -19,6 +19,5 @@ import { SpBreadcrumbItem } from '@streampipes/shared-ui'; export class SpConfigurationRoutes { - static CONFIGURATION_BASE_LINK = 'configuration'; static BASE: SpBreadcrumbItem = { label: 'Configuration' }; } diff --git a/ui/src/app/configuration/dialog/manage-site/edit-location/edit-location-area/edit-location-area.component.html b/ui/src/app/configuration/dialog/manage-site/edit-location/edit-location-area/edit-location-area.component.html new file mode 100644 index 0000000000..ad0392c3e0 --- /dev/null +++ b/ui/src/app/configuration/dialog/manage-site/edit-location/edit-location-area/edit-location-area.component.html @@ -0,0 +1,68 @@ + + +
+
+
{{ area }}
+
+ +
+
+
+
No areas defined yet.
+
+
+
+ + + +
+
+ +
+
+
diff --git a/ui/cypress/support/utils/configuration/ConfigutationUtils.ts b/ui/src/app/configuration/dialog/manage-site/edit-location/edit-location-area/edit-location-area.component.scss similarity index 84% rename from ui/cypress/support/utils/configuration/ConfigutationUtils.ts rename to ui/src/app/configuration/dialog/manage-site/edit-location/edit-location-area/edit-location-area.component.scss index 44731e6e4f..d1743a813a 100644 --- a/ui/cypress/support/utils/configuration/ConfigutationUtils.ts +++ b/ui/src/app/configuration/dialog/manage-site/edit-location/edit-location-area/edit-location-area.component.scss @@ -1,4 +1,4 @@ -/* +/*! * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. @@ -16,8 +16,13 @@ * */ -export class ConfigurationUtils { - public static goToConfigurationExport() { - cy.visit('#/configuration/export'); - } +.no-areas-defined { + margin-top: 5px; + margin-bottom: 5px; + font-size: smaller; +} + +.area { + padding: 5px; + font-weight: bold; } diff --git a/ui/src/app/configuration/dialog/manage-site/edit-location/edit-location-area/edit-location-area.component.ts b/ui/src/app/configuration/dialog/manage-site/edit-location/edit-location-area/edit-location-area.component.ts new file mode 100644 index 0000000000..ddd7baab40 --- /dev/null +++ b/ui/src/app/configuration/dialog/manage-site/edit-location/edit-location-area/edit-location-area.component.ts @@ -0,0 +1,40 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +import { Component, Input } from '@angular/core'; +import { AssetSiteDesc } from '@streampipes/platform-services'; + +@Component({ + selector: 'sp-edit-asset-location-area-component', + templateUrl: './edit-location-area.component.html', + styleUrls: ['./edit-location-area.component.scss'], +}) +export class EditAssetLocationAreaComponent { + @Input() + site: AssetSiteDesc; + + newArea: string = ''; + + addNewArea(): void { + this.site.areas.push(this.newArea); + } + + removeArea(area: string): void { + this.site.areas.splice(this.site.areas.indexOf(area), 1); + } +} diff --git a/ui/src/app/configuration/dialog/manage-site/edit-location/edit-location.component.html b/ui/src/app/configuration/dialog/manage-site/edit-location/edit-location.component.html new file mode 100644 index 0000000000..0c24fba58f --- /dev/null +++ b/ui/src/app/configuration/dialog/manage-site/edit-location/edit-location.component.html @@ -0,0 +1,59 @@ + + +
+ + + + + + + + + + + + + +
diff --git a/ui/src/app/configuration/dialog/manage-site/edit-location/edit-location.component.ts b/ui/src/app/configuration/dialog/manage-site/edit-location/edit-location.component.ts new file mode 100644 index 0000000000..9b1bc554c9 --- /dev/null +++ b/ui/src/app/configuration/dialog/manage-site/edit-location/edit-location.component.ts @@ -0,0 +1,32 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +import { Component, Input } from '@angular/core'; +import { AssetSiteDesc, LocationConfig } from '@streampipes/platform-services'; + +@Component({ + selector: 'sp-edit-asset-location-component', + templateUrl: './edit-location.component.html', +}) +export class EditAssetLocationComponent { + @Input() + site: AssetSiteDesc; + + @Input() + locationConfig: LocationConfig; +} diff --git a/ui/src/app/configuration/dialog/manage-site/manage-site-dialog.component.html b/ui/src/app/configuration/dialog/manage-site/manage-site-dialog.component.html new file mode 100644 index 0000000000..5cff55e084 --- /dev/null +++ b/ui/src/app/configuration/dialog/manage-site/manage-site-dialog.component.html @@ -0,0 +1,52 @@ + + +
+
+
+ + +
+
+ +
+ + +
+
diff --git a/ui/src/app/configuration/dialog/manage-site/manage-site-dialog.component.scss b/ui/src/app/configuration/dialog/manage-site/manage-site-dialog.component.scss new file mode 100644 index 0000000000..18577c0d5e --- /dev/null +++ b/ui/src/app/configuration/dialog/manage-site/manage-site-dialog.component.scss @@ -0,0 +1,35 @@ +/*! + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +.location-selection-panel { + border-right: 1px solid var(--color-bg-2); +} + +.location { + padding: 15px; + border-bottom: 1px solid var(--color-bg-2); +} + +.location:hover { + background: var(--color-bg-2); + cursor: pointer; +} + +.add-location-button { + border-bottom: 1px solid var(--color-bg-3); +} diff --git a/ui/src/app/configuration/dialog/manage-site/manage-site-dialog.component.ts b/ui/src/app/configuration/dialog/manage-site/manage-site-dialog.component.ts new file mode 100644 index 0000000000..0c69f44770 --- /dev/null +++ b/ui/src/app/configuration/dialog/manage-site/manage-site-dialog.component.ts @@ -0,0 +1,83 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +import { Component, Input, OnInit } from '@angular/core'; +import { DialogRef } from '@streampipes/shared-ui'; +import { + AssetSiteDesc, + GenericStorageService, + LocationConfig, +} from '@streampipes/platform-services'; +import { AssetConstants } from '../../../assets/constants/asset.constants'; + +@Component({ + selector: 'sp-manage-site-dialog-component', + templateUrl: './manage-site-dialog.component.html', + styleUrls: ['./manage-site-dialog.component.scss'], +}) +export class ManageSiteDialogComponent implements OnInit { + @Input() + site: AssetSiteDesc; + + @Input() + locationConfig: LocationConfig; + + clonedSite: AssetSiteDesc; + createMode = false; + + constructor( + private dialogRef: DialogRef, + private genericStorageService: GenericStorageService, + ) {} + + ngOnInit(): void { + if (this.site !== undefined) { + this.clonedSite = JSON.parse(JSON.stringify(this.site)); + } else { + this.initializeNewSite(); + } + } + + close(emitReload = false): void { + this.dialogRef.close(emitReload); + } + + initializeNewSite(): void { + this.clonedSite = { + appDocType: AssetConstants.ASSET_SITES_APP_DOC_NAME, + _id: undefined, + label: 'New site', + location: { coordinates: { latitude: 0, longitude: 0 } }, + areas: [], + }; + this.createMode = true; + } + + store(): void { + const observable = this.createMode + ? this.genericStorageService.createDocument( + AssetConstants.ASSET_SITES_APP_DOC_NAME, + this.clonedSite, + ) + : this.genericStorageService.updateDocument( + AssetConstants.ASSET_SITES_APP_DOC_NAME, + this.clonedSite, + ); + observable.subscribe(res => this.close(true)); + } +} diff --git a/ui/src/app/configuration/sites-configuration/location-features-configuration/location-features-configuration.component.html b/ui/src/app/configuration/sites-configuration/location-features-configuration/location-features-configuration.component.html new file mode 100644 index 0000000000..eb6e28df23 --- /dev/null +++ b/ui/src/app/configuration/sites-configuration/location-features-configuration/location-features-configuration.component.html @@ -0,0 +1,83 @@ + + +
+
+ + Enable geo features + +
+
+ Tile server URL (use placeholders for x, y and z + coordinates) +
+ + + +
+ Attribution text if required by the tile server +
+ + + +
+
+ +
+ +
+
+
+
diff --git a/ui/src/app/configuration/sites-configuration/location-features-configuration/location-features-configuration.component.ts b/ui/src/app/configuration/sites-configuration/location-features-configuration/location-features-configuration.component.ts new file mode 100644 index 0000000000..9e878f9a30 --- /dev/null +++ b/ui/src/app/configuration/sites-configuration/location-features-configuration/location-features-configuration.component.ts @@ -0,0 +1,107 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +import { Component, Input, OnDestroy, OnInit } from '@angular/core'; +import { + UntypedFormBuilder, + UntypedFormControl, + UntypedFormGroup, + Validators, +} from '@angular/forms'; +import { + LocationConfig, + LocationConfigService, +} from '@streampipes/platform-services'; +import { Subscription } from 'rxjs'; +import { MatSnackBar } from '@angular/material/snack-bar'; + +@Component({ + selector: 'sp-location-features-configuration', + templateUrl: './location-features-configuration.component.html', +}) +export class LocationFeaturesConfigurationComponent + implements OnInit, OnDestroy +{ + @Input() + locationConfig: LocationConfig; + + locationForm: UntypedFormGroup; + formSubscription: Subscription; + showLocationDetails = false; + + constructor( + private fb: UntypedFormBuilder, + private snackBar: MatSnackBar, + private locationConfigService: LocationConfigService, + ) {} + + ngOnInit(): void { + this.locationForm = this.fb.group({}); + this.showLocationDetails = this.locationConfig.locationEnabled; + this.locationForm.addControl( + 'locationFeaturesEnabled', + new UntypedFormControl(this.locationConfig.locationEnabled), + ); + this.locationForm.addControl( + 'tileServerUrl', + new UntypedFormControl( + this.locationConfig.tileServerUrl, + this.showLocationDetails ? Validators.required : [], + ), + ); + this.locationForm.addControl( + 'attributionText', + new UntypedFormControl(this.locationConfig.attributionText || ''), + ); + this.formSubscription = this.locationForm + .get('locationFeaturesEnabled') + .valueChanges.subscribe(checked => { + this.showLocationDetails = checked; + if (checked) { + this.locationForm.controls.tileServerUrl.setValidators( + Validators.required, + ); + } else { + this.locationForm.controls.tileServerUrl.setValidators([]); + } + }); + } + + save(): void { + this.locationConfig.locationEnabled = this.locationForm.get( + 'locationFeaturesEnabled', + ).value; + if (this.locationConfig.locationEnabled) { + this.locationConfig.tileServerUrl = + this.locationForm.get('tileServerUrl').value; + this.locationConfig.attributionText = + this.locationForm.get('attributionText').value; + } + this.locationConfigService + .updateLocationConfig(this.locationConfig) + .subscribe(() => { + this.snackBar.open('Location configuration updated', 'Ok', { + duration: 3000, + }); + }); + } + + ngOnDestroy() { + this.formSubscription?.unsubscribe(); + } +} diff --git a/ui/src/app/configuration/sites-configuration/site-area-configuration/site-area-configuration.component.html b/ui/src/app/configuration/sites-configuration/site-area-configuration/site-area-configuration.component.html new file mode 100644 index 0000000000..c7705c3c93 --- /dev/null +++ b/ui/src/app/configuration/sites-configuration/site-area-configuration/site-area-configuration.component.html @@ -0,0 +1,80 @@ + + + +
+ +
+ + + Site + + {{ site.label }} + + + + Areas + + {{ site.areas.toString() }} + + + + + +
+ + +
+ +
+
+
diff --git a/ui/src/app/configuration/sites-configuration/site-area-configuration/site-area-configuration.component.ts b/ui/src/app/configuration/sites-configuration/site-area-configuration/site-area-configuration.component.ts new file mode 100644 index 0000000000..d0de6e25e9 --- /dev/null +++ b/ui/src/app/configuration/sites-configuration/site-area-configuration/site-area-configuration.component.ts @@ -0,0 +1,88 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +import { Component, Input, OnInit } from '@angular/core'; +import { + AssetSiteDesc, + GenericStorageService, + LocationConfig, +} from '@streampipes/platform-services'; +import { AssetConstants } from '../../../assets/constants/asset.constants'; +import { MatTableDataSource } from '@angular/material/table'; +import { ManageSiteDialogComponent } from '../../dialog/manage-site/manage-site-dialog.component'; +import { DialogService, PanelType } from '@streampipes/shared-ui'; + +@Component({ + selector: 'sp-site-area-configuration', + templateUrl: './site-area-configuration.component.html', +}) +export class SiteAreaConfigurationComponent implements OnInit { + @Input() + locationConfig: LocationConfig; + + allSites: AssetSiteDesc[] = []; + dataSource: MatTableDataSource = + new MatTableDataSource(); + displayedColumns = ['name', 'areas', 'actions']; + + constructor( + private genericStorageService: GenericStorageService, + private dialogService: DialogService, + ) {} + + ngOnInit() { + this.loadSites(); + } + + loadSites(): void { + this.genericStorageService + .getAllDocuments(AssetConstants.ASSET_SITES_APP_DOC_NAME) + .subscribe(res => { + this.allSites = res; + this.dataSource.data = this.allSites; + }); + } + + deleteSite(site: AssetSiteDesc): void { + this.genericStorageService + .deleteDocument( + AssetConstants.ASSET_SITES_APP_DOC_NAME, + site._id, + site._rev, + ) + .subscribe(() => this.loadSites()); + } + + openManageSitesDialog(site: AssetSiteDesc): void { + const dialogRef = this.dialogService.open(ManageSiteDialogComponent, { + panelType: PanelType.SLIDE_IN_PANEL, + title: 'Manage site', + width: '50vw', + data: { + site, + locationConfig: this.locationConfig, + }, + }); + + dialogRef.afterClosed().subscribe(reload => { + if (reload) { + this.loadSites(); + } + }); + } +} diff --git a/ui/src/app/configuration/sites-configuration/sites-configuration.component.html b/ui/src/app/configuration/sites-configuration/sites-configuration.component.html new file mode 100644 index 0000000000..939208cfb5 --- /dev/null +++ b/ui/src/app/configuration/sites-configuration/sites-configuration.component.html @@ -0,0 +1,27 @@ + + + +
+ + + + + +
+
diff --git a/ui/src/app/configuration/sites-configuration/sites-configuration.component.ts b/ui/src/app/configuration/sites-configuration/sites-configuration.component.ts new file mode 100644 index 0000000000..2f26fe2b32 --- /dev/null +++ b/ui/src/app/configuration/sites-configuration/sites-configuration.component.ts @@ -0,0 +1,42 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +import { Component, OnInit } from '@angular/core'; +import { SpConfigurationTabs } from '../configuration-tabs'; +import { + LocationConfig, + LocationConfigService, +} from '@streampipes/platform-services'; + +@Component({ + selector: 'sp-sites-configuration', + templateUrl: './sites-configuration.component.html', +}) +export class SitesConfigurationComponent implements OnInit { + tabs = SpConfigurationTabs.getTabs(); + + locationConfig: LocationConfig; + + constructor(private locationConfigService: LocationConfigService) {} + + ngOnInit() { + this.locationConfigService.getLocationConfig().subscribe(res => { + this.locationConfig = res; + }); + } +} diff --git a/ui/src/app/core-ui/core-ui.module.ts b/ui/src/app/core-ui/core-ui.module.ts index 971206876f..bf9a91ff30 100644 --- a/ui/src/app/core-ui/core-ui.module.ts +++ b/ui/src/app/core-ui/core-ui.module.ts @@ -113,6 +113,8 @@ import { StaticTreeInputButtonMenuComponent } from './static-properties/static-r import { StaticTreeInputSelectedNodesComponent } from './static-properties/static-runtime-resolvable-tree-input/static-tree-input-selected-nodes/static-tree-input-selected-nodes.component'; import { StaticTreeInputBrowseNodesComponent } from './static-properties/static-runtime-resolvable-tree-input/static-tree-input-browse-nodes/static-tree-input-browse-nodes.component'; import { StaticTreeInputNodeDetailsComponent } from './static-properties/static-runtime-resolvable-tree-input/static-tree-input-node-details/static-tree-input-node-details.component'; +import { SingleMarkerMapComponent } from './single-marker-map/single-marker-map.component'; +import { LeafletModule } from '@asymmetrik/ngx-leaflet'; import { StaticTreeInputTextEditorComponent } from './static-properties/static-runtime-resolvable-tree-input/static-tree-input-text-editor/static-tree-input-text-editor.component'; @NgModule({ @@ -163,6 +165,7 @@ import { StaticTreeInputTextEditorComponent } from './static-properties/static-r QuillModule.forRoot(), MatTreeModule, MarkdownModule.forRoot(), + LeafletModule, ], declarations: [ DataDownloadDialogComponent, @@ -197,6 +200,7 @@ import { StaticTreeInputTextEditorComponent } from './static-properties/static-r StaticTreeInputNodeDetailsComponent, StaticTreeInputTextEditorComponent, StaticSlideToggleComponent, + SingleMarkerMapComponent, ErrorHintComponent, AddToCollectionComponent, PipelineStartedStatusComponent, @@ -250,6 +254,7 @@ import { StaticTreeInputTextEditorComponent } from './static-properties/static-r StatusIndicatorComponent, MultiStepStatusIndicatorComponent, PipelineOperationStatusComponent, + SingleMarkerMapComponent, ], }) export class CoreUiModule {} diff --git a/ui/src/app/core-ui/single-marker-map/single-marker-map.component.html b/ui/src/app/core-ui/single-marker-map/single-marker-map.component.html new file mode 100644 index 0000000000..921d108a40 --- /dev/null +++ b/ui/src/app/core-ui/single-marker-map/single-marker-map.component.html @@ -0,0 +1,29 @@ + + +
+
+
diff --git a/ui/src/app/core-ui/single-marker-map/single-marker-map.component.ts b/ui/src/app/core-ui/single-marker-map/single-marker-map.component.ts new file mode 100644 index 0000000000..ffeb12657b --- /dev/null +++ b/ui/src/app/core-ui/single-marker-map/single-marker-map.component.ts @@ -0,0 +1,129 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +import { Component, Input, OnInit } from '@angular/core'; +import { + icon, + Layer, + LeafletMouseEvent, + Map, + MapOptions, + marker, + Marker, + tileLayer, +} from 'leaflet'; +import { + AssetLocation, + LatLng, + LocationConfig, +} from '@streampipes/platform-services'; + +@Component({ + selector: 'sp-single-marker-map', + templateUrl: './single-marker-map.component.html', +}) +export class SingleMarkerMapComponent implements OnInit { + @Input() + locationConfig: LocationConfig; + + @Input() + assetLocation: AssetLocation; + + @Input() + mapHeight: number = 400; + + @Input() + readonly = false; + + map: Map; + mapOptions: MapOptions; + layers: Layer[]; + marker: Marker; + + ngOnInit() { + this.assetLocation ??= { + coordinates: { + latitude: 0, + longitude: 0, + }, + zoom: 1, + }; + this.mapOptions = { + layers: [ + tileLayer(this.locationConfig.tileServerUrl, { + maxZoom: 18, + attribution: this.locationConfig.attributionText, + }), + ], + zoom: this.assetLocation.zoom || 1, + center: { + lat: this.assetLocation.coordinates.latitude, + lng: this.assetLocation.coordinates.longitude, + }, + }; + } + + makeMarker(location: LatLng): Marker { + return marker( + { lat: location.latitude, lng: location.longitude }, + { + icon: icon({ + iconSize: [25, 41], + iconAnchor: [13, 41], + iconUrl: 'assets/img/marker-icon.png', + shadowUrl: 'assets/img/marker-shadow.png', + }), + }, + ); + } + + onMapReady(map: Map) { + this.map = map; + this.map.attributionControl.setPrefix(''); + this.map.invalidateSize(); + this.addMarker(this.assetLocation.coordinates); + } + + onZoomChange(zoom: number): void { + this.assetLocation.zoom = zoom; + } + + onMarkerAdded(e: LeafletMouseEvent) { + if (!this.readonly) { + this.addMarker({ + latitude: e.latlng.lat, + longitude: e.latlng.lng, + }); + } + } + + addMarker(location: LatLng): void { + if (location) { + if (!this.marker) { + this.marker = this.makeMarker(location); + this.marker.addTo(this.map); + } else { + this.marker.setLatLng({ + lat: location.latitude, + lng: location.longitude, + }); + } + this.assetLocation.coordinates = location; + } + } +}