diff options
34 files changed, 814 insertions, 55 deletions
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/CommonImportManager.java b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/CommonImportManager.java index 474df3f9e5..bfbebf398a 100644 --- a/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/CommonImportManager.java +++ b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/CommonImportManager.java @@ -127,7 +127,10 @@ public class CommonImportManager { private Map<String, Object> convertToFieldMap(String elementTypesYml) { Map<String, Object> toscaJson = null; try { - toscaJson = (Map<String, Object>) new Yaml().load(elementTypesYml); + toscaJson = new Yaml().load(elementTypesYml); + if (toscaJson.containsKey("data_types")){ + toscaJson = (Map<String, Object>) toscaJson.get("data_types"); + } } catch (Exception e) { log.debug("Failed to yaml file {}", elementTypesYml, e); } @@ -276,11 +279,9 @@ public class CommonImportManager { eitherResult = handleType(elementType, validator, elementInfoGetter, elementFetcher, elementAdder, elementUpgrader) .left() .map(elem -> append(createdElementTypes, elem)); - if (eitherResult.isRight()) { break; } - if (!elementTypeItr.hasNext()) { log.info("all {} were created successfully!!!", elementType); } @@ -295,7 +296,6 @@ public class CommonImportManager { propertyOperation.getJanusGraphGenericDao().rollback(); } } - return eitherResult; } diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/DataTypeServlet.java b/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/DataTypeServlet.java index 8139237e38..a7327bf184 100644 --- a/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/DataTypeServlet.java +++ b/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/DataTypeServlet.java @@ -163,4 +163,19 @@ public class DataTypeServlet extends BeGenericServlet { return Response.status(Status.CREATED).entity(property).build(); } + @GET + @Path("{dataTypeName}/models") + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + @Operation(description = "Get models for type", method = "GET", summary = "Returns list of models for type", responses = { + @ApiResponse(content = @Content(array = @ArraySchema(schema = @Schema(implementation = Response.class)))), + @ApiResponse(responseCode = "200", description = "dataTypeModels"), @ApiResponse(responseCode = "403", description = "Restricted operation"), + @ApiResponse(responseCode = "400", description = "Invalid content / Missing content"), + @ApiResponse(responseCode = "403", description = "Restricted operation"), + @ApiResponse(responseCode = "404", description = "Data type not found")}) + @PermissionAllowed(AafPermission.PermNames.INTERNAL_ALL_VALUE) + public Response getDataTypeModels(@PathParam("dataTypeName") String dataTypeName) { + return buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.OK), + gson.toJson(dataTypeOperation.getAllDataTypeModels(dataTypeName))); + } } diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/TypesUploadServlet.java b/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/TypesUploadServlet.java index 786c0338c4..4fcc3c0842 100644 --- a/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/TypesUploadServlet.java +++ b/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/TypesUploadServlet.java @@ -31,7 +31,9 @@ import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.servers.Server; import io.swagger.v3.oas.annotations.tags.Tag; import java.io.File; +import java.io.FileInputStream; import java.io.IOException; +import java.nio.charset.StandardCharsets; import java.util.List; import java.util.Map; import java.util.Set; @@ -49,6 +51,7 @@ import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import org.apache.commons.lang3.tuple.ImmutablePair; import org.glassfish.jersey.media.multipart.FormDataParam; +import org.jetbrains.annotations.NotNull; import org.openecomp.sdc.be.components.impl.ArtifactTypeImportManager; import org.openecomp.sdc.be.components.impl.CapabilityTypeImportManager; import org.openecomp.sdc.be.components.impl.CategoriesImportManager; @@ -218,7 +221,7 @@ public class TypesUploadServlet extends AbstractValidationsServlet { @POST @Path("/datatypes") - @Operation(description = "Create Categories from yaml", method = "POST", summary = "Returns created data types", responses = { + @Operation(description = "Create Data Types from zip", method = "POST", summary = "Returns created data types", responses = { @ApiResponse(content = @Content(array = @ArraySchema(schema = @Schema(implementation = Response.class)))), @ApiResponse(responseCode = "201", description = "Data types created"), @ApiResponse(responseCode = "403", description = "Restricted operation"), @@ -234,6 +237,23 @@ public class TypesUploadServlet extends AbstractValidationsServlet { } @POST + @Path("/datatypesyaml") + @Operation(description = "Create Data Types from yaml", method = "POST", summary = "Returns created data types", responses = { + @ApiResponse(content = @Content(array = @ArraySchema(schema = @Schema(implementation = Response.class)))), + @ApiResponse(responseCode = "201", description = "Data types created"), + @ApiResponse(responseCode = "403", description = "Restricted operation"), + @ApiResponse(responseCode = "400", description = "Invalid content / Missing content"), + @ApiResponse(responseCode = "409", description = "Data types already exist")}) + @PermissionAllowed(AafPermission.PermNames.INTERNAL_ALL_VALUE) + public Response uploadDataTypesYaml(@Parameter(description = "FileInputStream") @FormDataParam("dataTypesYaml") File file, + @Context final HttpServletRequest request, @HeaderParam("USER_ID") String creator, + @Parameter(description = "model") @FormDataParam("model") String modelName, + @Parameter(description = "includeToModelImport") @FormDataParam("includeToModelImport") boolean includeToModelDefaultImports) { + return uploadElementTypeServletLogicYaml(this::createDataTypes, file, request, creator, NodeTypeEnum.DataType.getName(), modelName, + includeToModelDefaultImports); + } + + @POST @Path("/grouptypes") @Operation(description = "Create GroupTypes from yaml", method = "POST", summary = "Returns created group types", responses = { @ApiResponse(content = @Content(array = @ArraySchema(schema = @Schema(implementation = Response.class)))), @@ -323,6 +343,42 @@ public class TypesUploadServlet extends AbstractValidationsServlet { } } + private Response uploadElementTypeServletLogicYaml(final ConsumerFourParam<Wrapper<Response>, String, String, Boolean> createElementsMethod, + final File file, final HttpServletRequest request, final String creator, + final String elementTypeName, final String modelName, final boolean includeToModelDefaultImports) { + init(); + final String userId = initHeaderParam(creator, request, Constants.USER_ID_HEADER); + try { + final Wrapper<String> yamlStringWrapper = new Wrapper<>(); + final String url = request.getMethod() + " " + request.getRequestURI(); + log.debug(START_HANDLE_REQUEST_OF, url); + final Wrapper<Response> responseWrapper = doUploadTypeValidations(request, userId, file); + if (responseWrapper.isEmpty()) { + final String yamlAsString = getFileAsString(file); + log.debug("received yaml: {}", yamlAsString); + yamlStringWrapper.setInnerElement(yamlAsString); + } + if (responseWrapper.isEmpty()) { + createElementsMethod.accept(responseWrapper, yamlStringWrapper.getInnerElement(), modelName, includeToModelDefaultImports); + } + return responseWrapper.getInnerElement(); + } catch (final Exception e) { + log.debug(CREATE_FAILED_WITH_EXCEPTION, elementTypeName, e); + BeEcompErrorManager.getInstance().logBeRestApiGeneralError(CREATE + elementTypeName); + return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR)); + } + } + + @NotNull + private String getFileAsString(File file) throws IOException { + FileInputStream fl = new FileInputStream(file); + byte[] arr = new byte[(int) file.length()]; + fl.read(arr); + fl.close(); + final String yamlAsString = new String(arr, StandardCharsets.UTF_8); + return yamlAsString; + } + private Wrapper<Response> doUploadTypeValidations(final HttpServletRequest request, String userId, File file) { Wrapper<Response> responseWrapper = new Wrapper<>(); Wrapper<User> userWrapper = new Wrapper<>(); diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/DataTypeOperation.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/DataTypeOperation.java index 36dcaf44ba..7d01f3f273 100644 --- a/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/DataTypeOperation.java +++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/DataTypeOperation.java @@ -29,6 +29,7 @@ import java.util.Optional; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections4.MapUtils; import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.tuple.ImmutableTriple; import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource; import org.apache.tinkerpop.gremlin.structure.Vertex; import org.janusgraph.core.JanusGraph; @@ -37,6 +38,7 @@ import org.openecomp.sdc.be.config.BeEcompErrorManager.ErrorSeverity; import org.openecomp.sdc.be.dao.api.ActionStatus; import org.openecomp.sdc.be.dao.janusgraph.HealingJanusGraphGenericDao; import org.openecomp.sdc.be.dao.janusgraph.JanusGraphOperationStatus; +import org.openecomp.sdc.be.dao.janusgraph.QueryType; import org.openecomp.sdc.be.dao.neo4j.GraphEdgeLabels; import org.openecomp.sdc.be.dao.neo4j.GraphPropertiesDictionary; import org.openecomp.sdc.be.datatypes.elements.DataTypeDataDefinition; @@ -124,6 +126,20 @@ public class DataTypeOperation extends AbstractOperation { return dataTypesFound; } + public List<String> getAllDataTypeModels(final String dataTypeName) { + final List<String> models = new ArrayList<>(); + ImmutableTriple<QueryType, String, Object> criteria = + new ImmutableTriple<>(QueryType.HAS, GraphPropertiesDictionary.NAME.getProperty(), dataTypeName); + + final Either<List<DataTypeData>, JanusGraphOperationStatus> getAllDataTypesForModel = + janusGraphGenericDao.getByCriteria(NodeTypeEnum.DataType, DataTypeData.class, List.of(criteria)); + final var dataTypesValidated = validateDataType(getAllDataTypesForModel, null); + for (DataTypeData dataType : dataTypesValidated) { + models.add(dataType.getDataTypeDataDefinition().getModel()); + } + return models; + } + private List<DataTypeData> getAllDataTypesWithModel(final String modelName) { final Either<List<DataTypeData>, JanusGraphOperationStatus> getAllDataTypesByModel = janusGraphGenericDao .getByCriteriaForModel(NodeTypeEnum.DataType, null, modelName, DataTypeData.class); diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/ModelOperation.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/ModelOperation.java index 0d462c9874..8baa9a73c0 100644 --- a/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/ModelOperation.java +++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/ModelOperation.java @@ -296,7 +296,7 @@ public class ModelOperation { rebuiltModelImportList = new ArrayList<>(modelImportList); } - final Map<String, Object> typesYamlMap = new Yaml().load(typesYaml); + final Map<String, Object> typesYamlMap = new Yaml().loadAs(typesYaml, Map.class); removeExistingTypesFromDefaultImports(elementTypeEnum, typesYamlMap, rebuiltModelImportList); final Map<String, Object> originalContent = new Yaml().load(additionalTypeDefinitionsImport.getContent()); diff --git a/catalog-ui/src/app/app.ts b/catalog-ui/src/app/app.ts index 76c1d28489..296b63bca7 100644 --- a/catalog-ui/src/app/app.ts +++ b/catalog-ui/src/app/app.ts @@ -38,6 +38,7 @@ import {ComponentFactory} from "./utils/component-factory"; import {Component} from "./models/components/component"; import {IUserProperties} from "./models/user"; import {WorkspaceService} from "./ng2/pages/workspace/workspace.service"; +import {TypeWorkspaceGeneralComponent} from "./ng2/pages/type-workspace/type-workspace-general/type-workspace-general.component"; let moduleName: string = 'sdcApp'; let viewModelsModuleName: string = 'Sdc.ViewModels'; @@ -213,8 +214,11 @@ ng1appModule.config([ $stateProvider.state( States.TYPE_WORKSPACE, { url: '/:previousState/type-workspace/:type/:id/:subPage', - template: '<app-type-workspace></app-type-workspace>', - } + params: { + 'importedFile': null + }, + template: '<app-type-workspace></app-type-workspace>' + } ); $stateProvider.state( diff --git a/catalog-ui/src/app/ng2/components/ui/modal-import-type/modal-import-type.component.html b/catalog-ui/src/app/ng2/components/ui/modal-import-type/modal-import-type.component.html new file mode 100644 index 0000000000..37439f8d70 --- /dev/null +++ b/catalog-ui/src/app/ng2/components/ui/modal-import-type/modal-import-type.component.html @@ -0,0 +1,51 @@ +<!-- + ~ - + ~ ============LICENSE_START======================================================= + ~ Copyright (C) 2023 Nordix Foundation. + ~ ================================================================================ + ~ Licensed 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. + ~ + ~ SPDX-License-Identifier: Apache-2.0 + ~ ============LICENSE_END========================================================= + --> +<div class="import-type-container"> + + <form novalidate class="w-sdc-form" name="importForm" validation-on-load form-to-validate="importForm"> + + <div class="w-sdc-form-section-container"> + + <div class="i-sdc-form-item"> + <label class="i-sdc-form-label required">Type</label> + <select class="i-sdc-form-select" + data-ng-class="{'view-mode': true}" + data-ng-disabled="false" + + data-tests-id="selectType" + data-ng-required="true" + + > + <option value="" selected>Data Type</option> + </select> + </div> + + <div class="i-sdc-form-item"> + <label class="i-sdc-form-label required">Select File to Import + <input (change)="onFileChange($event)" type="file" class="i-sdc-dashboard-item-upload-input"/> + </label> + </div> + + </div> + + </form> + +</div>
\ No newline at end of file diff --git a/catalog-ui/src/app/ng2/components/ui/modal-import-type/modal-import-type.component.less b/catalog-ui/src/app/ng2/components/ui/modal-import-type/modal-import-type.component.less new file mode 100644 index 0000000000..8c391c52c8 --- /dev/null +++ b/catalog-ui/src/app/ng2/components/ui/modal-import-type/modal-import-type.component.less @@ -0,0 +1,60 @@ +/* + * - + * ============LICENSE_START======================================================= + * Copyright (C) 2023 Nordix Foundation. + * ================================================================================ + * Licensed 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. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +.import-type-container { + max-width: 100%; +} + +.default-value-container { + overflow: scroll; + max-height: 300px; + max-width: 100%; + + ul { + margin: 0 0 0 20px; + list-style: none; + line-height: 2em; + } + + &::-webkit-scrollbar-track { + border: 0; + } +} + +.input-label { + margin: 0; + font-weight: bold; +} + +.input-value { + display: flex; + flex-flow: row nowrap; + gap: 7px; + + input { + min-width: 150px; + max-width: 250px; + } +} + +.empty-value { + color: #aaaaaa; +}
\ No newline at end of file diff --git a/catalog-ui/src/app/ng2/components/ui/modal-import-type/modal-import-type.component.spec.ts b/catalog-ui/src/app/ng2/components/ui/modal-import-type/modal-import-type.component.spec.ts new file mode 100644 index 0000000000..9e785638d8 --- /dev/null +++ b/catalog-ui/src/app/ng2/components/ui/modal-import-type/modal-import-type.component.spec.ts @@ -0,0 +1,45 @@ +/* + * - + * ============LICENSE_START======================================================= + * Copyright (C) 2023 Nordix Foundation. + * ================================================================================ + * Licensed 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. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ModalImportTypeComponent } from './modal-import-type.component'; + +describe('ModalImportTypeComponent', () => { + let component: ModalImportTypeComponent; + let fixture: ComponentFixture<ModalImportTypeComponent>; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ ModalImportTypeComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(ModalImportTypeComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/catalog-ui/src/app/ng2/components/ui/modal-import-type/modal-import-type.component.ts b/catalog-ui/src/app/ng2/components/ui/modal-import-type/modal-import-type.component.ts new file mode 100644 index 0000000000..2c56b2ef7f --- /dev/null +++ b/catalog-ui/src/app/ng2/components/ui/modal-import-type/modal-import-type.component.ts @@ -0,0 +1,47 @@ +/* + * - + * ============LICENSE_START======================================================= + * Copyright (C) 2023 Nordix Foundation. + * ================================================================================ + * Licensed 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. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ +import {Component, Inject, OnInit} from '@angular/core'; + +@Component({ + selector: 'app-modal-import-type', + templateUrl: './modal-import-type.component.html', + styleUrls: ['./modal-import-type.component.less'] +}) +export class ModalImportTypeComponent implements OnInit { + + file:File = null; + + constructor() {} + + ngOnInit() { + } + + onFileChange(event: any) { + this.file = event.target.files[0]; + } + + public onImportDataType(file: any): void { + if (file && file.filename) { + console.log("file: " + file.filename); + } + } + +} diff --git a/catalog-ui/src/app/ng2/components/ui/ui-elements.module.ts b/catalog-ui/src/app/ng2/components/ui/ui-elements.module.ts index cdb173cb12..8e0a66c8f5 100644 --- a/catalog-ui/src/app/ng2/components/ui/ui-elements.module.ts +++ b/catalog-ui/src/app/ng2/components/ui/ui-elements.module.ts @@ -44,6 +44,7 @@ import {FileOpenerComponent} from "./file-opener/file-opener.component"; import {DownloadArtifactComponent} from "app/ng2/components/ui/download-artifact/download-artifact.component"; import {SdcElementIconComponent} from "./sdc-element-icon/sdc-element-icon.component"; import {PanelWrapperComponent} from "./panel-wrapper/panel-wrapper.component"; +import { ModalImportTypeComponent } from './modal-import-type/modal-import-type.component'; @NgModule({ declarations: [ @@ -56,7 +57,8 @@ import {PanelWrapperComponent} from "./panel-wrapper/panel-wrapper.component"; FileOpenerComponent, SdcElementIconComponent, DownloadArtifactComponent, - PanelWrapperComponent + PanelWrapperComponent, + ModalImportTypeComponent ], imports: [ @@ -97,7 +99,8 @@ import {PanelWrapperComponent} from "./panel-wrapper/panel-wrapper.component"; SdcElementIconComponent, FileOpenerComponent, DownloadArtifactComponent, - PanelWrapperComponent + PanelWrapperComponent, + ModalImportTypeComponent ], entryComponents: [SearchWithAutoCompleteComponent, SdcElementIconComponent, PaletteAnimationComponent] }) diff --git a/catalog-ui/src/app/ng2/pages/home/__snapshots__/home.component.spec.ts.snap b/catalog-ui/src/app/ng2/pages/home/__snapshots__/home.component.spec.ts.snap index 42686c1567..dd864d48f7 100644 --- a/catalog-ui/src/app/ng2/pages/home/__snapshots__/home.component.spec.ts.snap +++ b/catalog-ui/src/app/ng2/pages/home/__snapshots__/home.component.spec.ts.snap @@ -12,7 +12,9 @@ exports[`home component should match current snapshot 1`] = ` isDefaultFilter={[Function Function]} loaderService={[Function Object]} modalService={[Function Object]} + modalServiceSdc="undefined" modalsHandler={[Function Object]} + openModalImportType={[Function Function]} resourceService={[Function Object]} sdcConfig={[Function Object]} sdcMenu={[Function Object]} diff --git a/catalog-ui/src/app/ng2/pages/home/home.component.html b/catalog-ui/src/app/ng2/pages/home/home.component.html index 0c2e41eb11..b16b30b2b2 100644 --- a/catalog-ui/src/app/ng2/pages/home/home.component.html +++ b/catalog-ui/src/app/ng2/pages/home/home.component.html @@ -58,6 +58,7 @@ (fileUpload)="onImportService($event)" [convertToBase64]="true" ></sdc-button-file-opener> + <sdc-button *ngIf="roles[user.role].dashboard.showCreateNew" testId="importTypebutton" size="medium" type="secondary" text="Import Type" (click)="openModalImportType()"></sdc-button> </div> </div> </div> diff --git a/catalog-ui/src/app/ng2/pages/home/home.component.spec.ts b/catalog-ui/src/app/ng2/pages/home/home.component.spec.ts index 1c03790e04..2642b9e41b 100644 --- a/catalog-ui/src/app/ng2/pages/home/home.component.spec.ts +++ b/catalog-ui/src/app/ng2/pages/home/home.component.spec.ts @@ -7,7 +7,7 @@ import {HomeComponent} from "./home.component"; import {ConfigureFn, configureTests} from "../../../../jest/test-config.helper"; import {NO_ERRORS_SCHEMA} from "@angular/core"; import {TranslateService} from "../../shared/translator/translate.service"; -import {AuthenticationService, CacheService, HomeService, ImportVSPService, ResourceServiceNg2} from '../../../../app/services-ng2'; +import {AuthenticationService, CacheService, HomeService, ImportVSPService, ModalService, ResourceServiceNg2} from '../../../../app/services-ng2'; import {ModalsHandler} from "../../../../app/utils"; import {SdcUiServices} from "onap-ui-angular"; import {ComponentType, ResourceType} from "../../../utils/constants"; @@ -23,6 +23,7 @@ describe('home component', () => { let importVspService: Partial<ImportVSPService>; let mockStateService; let modalServiceMock :Partial<SdcUiServices.ModalService>; + let modalServiceMock_ :Partial<ModalService>; let translateServiceMock : Partial<TranslateService>; let foldersItemsMenuMock; let homeFilterMock :Partial<HomeFilter>; @@ -79,6 +80,7 @@ describe('home component', () => { {provide: TranslateService, useValue: translateServiceMock}, {provide: ModalsHandler, useValue: {}}, {provide: SdcUiServices.ModalService, useValue: modalServiceMock}, + {provide: ModalService, useValue: modalServiceMock_}, {provide: SdcUiServices.LoaderService, useValue: loaderServiceMock}, {provide: ImportVSPService, useValue: {}}, {provide: ResourceServiceNg2, useValue: resourceServiceNg2Mock} diff --git a/catalog-ui/src/app/ng2/pages/home/home.component.ts b/catalog-ui/src/app/ng2/pages/home/home.component.ts index 784823eacd..666b36eb8e 100644 --- a/catalog-ui/src/app/ng2/pages/home/home.component.ts +++ b/catalog-ui/src/app/ng2/pages/home/home.component.ts @@ -19,10 +19,20 @@ */ 'use strict'; import {Component as NgComponent, Inject, OnInit} from '@angular/core'; -import {Component, ComponentMetadata, IConfigRoles, IUserProperties, Resource, Service} from 'app/models'; +import { + ButtonModel, + Component, + ComponentMetadata, + IConfigRoles, + IUserProperties, + ModalModel, + Resource, + Service +} from 'app/models'; +import {ModalService} from "../../services/modal.service"; import {HomeFilter} from 'app/models/home-filter'; import {AuthenticationService, CacheService, HomeService, ResourceServiceNg2} from 'app/services-ng2'; -import {ComponentState, ModalsHandler} from 'app/utils'; +import {ComponentState, ModalsHandler, States} from 'app/utils'; import {SdcUiServices} from 'onap-ui-angular'; import {CHANGE_COMPONENT_CSAR_VERSION_FLAG, ComponentType, ResourceType} from '../../../utils/constants'; import {ImportVSPService} from '../../components/modals/onboarding-modal/import-vsp.service'; @@ -33,6 +43,7 @@ import {TranslateService} from '../../shared/translator/translate.service'; import {FoldersItemsMenu, FoldersItemsMenuGroup, FoldersMenu} from './folders'; import {ImportVSPdata} from "../../components/modals/onboarding-modal/onboarding-modal.component"; import {DataTypeCatalogComponent} from "../../../models/data-type-catalog-component"; +import {ModalImportTypeComponent} from "../../components/ui/modal-import-type/modal-import-type.component"; @NgComponent({ selector: 'home-page', @@ -64,6 +75,7 @@ export class HomeComponent implements OnInit { private translateService: TranslateService, private modalsHandler: ModalsHandler, private modalService: SdcUiServices.ModalService, + private modalServiceSdc: ModalService, private loaderService: SdcUiServices.LoaderService, private importVSPService: ImportVSPService, private resourceService: ResourceServiceNg2 @@ -398,4 +410,41 @@ export class HomeComponent implements OnInit { this.notificationIconCallback = this.notificationIconCallback.bind(this); } + openModalImportType = () => { + let modalTitle = 'Import Type'; + let modal = this.modalServiceSdc.createCustomModal(new ModalModel( + 'sm', + modalTitle, + null, + [ + new ButtonModel('Import', 'blue', () => + { this.uploadDataTypeFile(modal.instance.dynamicContent.instance.file); modal.instance.close();}, () => false), + new ButtonModel('Cancel', 'outline grey', () => { + modal.instance.close(); + }), + ], + null + )); + this.modalServiceSdc.addDynamicContentToModal(modal, ModalImportTypeComponent, {}); + modal.instance.open(); + } + + private uploadDataTypeFile(file: any): void { + if (file && file.name) { + // Check that the file has valid extension. + const fileExtension: string = file.name.split('.').pop(); + if (this.sdcConfig.toscaFileExtension.indexOf(fileExtension.toLowerCase()) !== -1) { + this.$state.go(States.TYPE_WORKSPACE, { + type: "datatype", + subPage: "general", + id: "import", + importedFile: file + }); + } else { + const title: string = this.translateService.translate('NEW_SERVICE_RESOURCE_ERROR_VALID_TOSCA_EXTENSIONS_TITLE'); + const message: string = this.translateService.translate('NEW_SERVICE_RESOURCE_ERROR_VALID_TOSCA_EXTENSIONS', {extensions: this.sdcConfig.toscaFileExtension}); + this.modalService.openWarningModal(title, message, 'error-invalid-tosca-ext'); + } + } + } } diff --git a/catalog-ui/src/app/ng2/pages/home/home.module.ts b/catalog-ui/src/app/ng2/pages/home/home.module.ts index 1a397b44be..acfc299a31 100644 --- a/catalog-ui/src/app/ng2/pages/home/home.module.ts +++ b/catalog-ui/src/app/ng2/pages/home/home.module.ts @@ -7,6 +7,7 @@ import { GlobalPipesModule } from "../../pipes/global-pipes.module"; import { TranslateModule } from "../../shared/translator/translate.module"; import { SdcUiComponentsModule } from "onap-ui-angular"; import { ResourceServiceNg2 } from "../../services/component-services/resource.service"; +import {ModalImportTypeComponent} from "../../components/ui/modal-import-type/modal-import-type.component"; @NgModule({ declarations: [ @@ -19,12 +20,14 @@ import { ResourceServiceNg2 } from "../../services/component-services/resource.s UiElementsModule, GlobalPipesModule, TranslateModule + ], exports: [ HomeComponent ], entryComponents: [ - HomeComponent + HomeComponent, + ModalImportTypeComponent ], providers: [ResourceServiceNg2] }) diff --git a/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace-general/type-workspace-general.component.html b/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace-general/type-workspace-general.component.html index 877c58bd58..04c334eee1 100644 --- a/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace-general/type-workspace-general.component.html +++ b/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace-general/type-workspace-general.component.html @@ -33,9 +33,14 @@ <div class="i-sdc-form-item"> <label class="i-sdc-form-label" [ngClass]="{'required': !isViewOnly}">{{'MODEL_LABEL' | translate}}:</label> - <span>{{dataType.model ? dataType.model : DEFAULT_MODEL_NAME}}</span> + <span *ngIf="isViewOnly">{{dataType.model ? dataType.model : DEFAULT_MODEL_NAME}}</span> + <select *ngIf="!isViewOnly" formControlName="model" (change)="onModelChange()"> + <option *ngFor="let model of models" + [value]="model.name">{{model.name}}</option> + </select> </div> + <div class="i-sdc-form-item"> <label class="i-sdc-form-label" [ngClass]="{'required': !isViewOnly}">{{'DERIVED_FROM_LABEL' | translate}}:</label> <span>{{dataType.derivedFromName}}</span> @@ -44,12 +49,12 @@ </div> <div class="i-sdc-form-item description-field"> - <label class="i-sdc-form-label" [ngClass]="{'required': !isViewOnly}">{{'DESCRIPTION_LABEL' | translate}}:</label> - <textarea class="description" + <label class="i-sdc-form-label">{{'DESCRIPTION_LABEL' | translate}}:</label> + <textarea class="description" #description formControlName="description" - [ngClass]="{'view-mode': isViewOnly}" - [required]="true" + [ngClass]="{'view-mode': true}" [attr.test-id]="description" + [(ngModel)]="dataType.description" [value]="dataType.description"></textarea> </div> </div> diff --git a/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace-general/type-workspace-general.component.spec.ts b/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace-general/type-workspace-general.component.spec.ts index fe7b070354..1484954e4b 100644 --- a/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace-general/type-workspace-general.component.spec.ts +++ b/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace-general/type-workspace-general.component.spec.ts @@ -25,22 +25,53 @@ import {TypeWorkspaceGeneralComponent} from './type-workspace-general.component' import {ReactiveFormsModule} from "@angular/forms"; import {TranslateModule} from "../../../shared/translator/translate.module"; import {TranslateService} from "../../../shared/translator/translate.service"; +import {SdcUiComponentsModule} from "onap-ui-angular/dist"; +import {Observable} from "rxjs/Observable"; +import {DataTypeModel} from "../../../../models/data-types"; +import {DataTypeService} from "../../../services/data-type.service"; +import {ModelService} from "../../../services/model.service"; +import {IWorkspaceViewModelScope} from "../../../../view-models/workspace/workspace-view-model"; +import {IScope} from "angular"; +import {States} from "../../../../utils/constants"; describe('TypeWorkspaceGeneralComponent', () => { let component: TypeWorkspaceGeneralComponent; let fixture: ComponentFixture<TypeWorkspaceGeneralComponent>; + let dataTypeServiceMock: Partial<DataTypeService>; + let modelServiceMock: Partial<ModelService>; let translateServiceMock: Partial<TranslateService> = { 'translate': jest.fn() }; + let importedFileMock: File = null; + let stateParamsMock: Partial<ng.ui.IStateParamsService> = { + 'importedFile': importedFileMock + }; + let resolveMock = {"$stateParams": stateParamsMock}; + let parentScopeMock: Partial<IScope> = { + '$resolve': resolveMock + }; + let scopeMock_: Partial<IWorkspaceViewModelScope> = { + '$parent': parentScopeMock, + 'current': { + 'name': States.TYPE_WORKSPACE + } + } + beforeEach(async(() => { TestBed.configureTestingModule({ declarations: [ TypeWorkspaceGeneralComponent ], imports: [ ReactiveFormsModule, + SdcUiComponentsModule, TranslateModule ], providers: [ + {provide: TranslateService, useValue: translateServiceMock}, + {provide: "$scope", useValue: scopeMock_ }, + {provide: "$state", useValue: {}}, + {provide: DataTypeService, useValue: dataTypeServiceMock}, + {provide: ModelService, useValue: modelServiceMock}, {provide: TranslateService, useValue: translateServiceMock} ] }) @@ -50,6 +81,7 @@ describe('TypeWorkspaceGeneralComponent', () => { beforeEach(() => { fixture = TestBed.createComponent(TypeWorkspaceGeneralComponent); component = fixture.componentInstance; + component.dataTypeMap$ = new Observable<Map<string, DataTypeModel>>(); fixture.detectChanges(); }); diff --git a/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace-general/type-workspace-general.component.ts b/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace-general/type-workspace-general.component.ts index 8728c3020e..a6e4d1efeb 100644 --- a/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace-general/type-workspace-general.component.ts +++ b/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace-general/type-workspace-general.component.ts @@ -19,10 +19,20 @@ * ============LICENSE_END========================================================= */ -import {Component, Input, OnInit} from '@angular/core'; +import {Component, EventEmitter, Inject, Input, OnInit, Output} from '@angular/core'; import {FormControl, FormGroup, Validators} from "@angular/forms"; import {DataTypeModel} from "../../../../models/data-types"; -import { DEFAULT_MODEL_NAME } from "app/utils/constants"; +import {DEFAULT_MODEL_NAME} from "app/utils/constants"; +import {IWorkspaceViewModelScope} from "../../../../view-models/workspace/workspace-view-model"; +import {ServiceDataTypeReader} from "../../../../utils/service-data-type-reader"; +import {TranslateService} from "../../../shared/translator/translate.service"; +import {SdcUiServices} from "onap-ui-angular/dist"; +import {ModelService} from "../../../services/model.service"; +import {Model} from "../../../../models/model"; +import {DataTypesMap} from "../../../../models/data-types-map"; +import {DataTypeService} from "../../../services/data-type.service"; +import {Observable} from "rxjs/Observable"; +import {IDropDownOption} from "onap-ui-angular/dist/form-elements/dropdown/dropdown-models"; @Component({ selector: 'app-type-workspace-general', @@ -30,9 +40,18 @@ import { DEFAULT_MODEL_NAME } from "app/utils/constants"; styleUrls: ['./type-workspace-general.component.less'] }) export class TypeWorkspaceGeneralComponent implements OnInit { + @Input() isViewOnly = true; @Input() dataType: DataTypeModel = new DataTypeModel(); - + @Output() onImportedType = new EventEmitter<any>(); + importedFile: File; + models: Array<Model>; + selectedModelName: string; + dataTypes: DataTypesMap; + derivedFromName: string; + dataTypeMap$: Observable<Map<string, DataTypeModel>>; + dataTypeMap: Map<string, DataTypeModel>; + typeOptions: Array<IDropDownOption>; DEFAULT_MODEL_NAME = DEFAULT_MODEL_NAME; type: FormControl = new FormControl(undefined, [Validators.required, Validators.minLength(1), Validators.maxLength(300)]); @@ -46,17 +65,92 @@ export class TypeWorkspaceGeneralComponent implements OnInit { 'derivedFrom': this.derivedFrom }); + constructor(@Inject('$scope') private $scope: IWorkspaceViewModelScope, + @Inject('$state') private $state: ng.ui.IStateService, + protected dataTypeService: DataTypeService, + private modalServiceSdcUI: SdcUiServices.ModalService, + private modelService: ModelService, + private translateService: TranslateService) { + this.typeOptions = []; + } + ngOnInit(): void { + this.getImportedFile(); + if (!this.isViewOnly) { + console.log("file size: " + this.importedFile.size); + console.log("file type: " + this.importedFile.type); + console.log("file lastModifiedDate: " + this.importedFile.lastModifiedDate); + + new ServiceDataTypeReader().read(this.importedFile).then( + (serviceType) => { + this.dataType = serviceType; + this.dataType.modificationTime = this.importedFile.lastModifiedDate; + this.dataType.creationTime = this.importedFile.lastModifiedDate; + this.derivedFromName = serviceType.derivedFromName; + this.dataType.uniqueId = this.dataType.model ? this.dataType.model + "." + this.dataType.name : this.dataType.name + ".datatype"; + this.$scope.dataType = this.dataType; + this.onImportedType.emit(this.dataType); + + this.models = []; + this.modelService.getDataTypeModels(this.derivedFromName).subscribe((modelsFound: any) => { + modelsFound.sort().forEach(modelName => { + let model:Model; + if (modelName === null || "" === modelName) { + model = new Model({"name": DEFAULT_MODEL_NAME, "derivedFrom": "", "modelType": "normative"}); + } + else { + model = new Model({"name": modelName, "derivedFrom": "", "modelType": "normative"}); + } + this.models.push(model); + }); + this.onModelChange(); + this.$scope.dataType = this.dataType; + }); + + }, + (error) => { + const errorMsg = this.translateService.translate('IMPORT_DATA_TYPE_FAILURE_MESSAGE_TEXT'); + console.error(errorMsg, error); + const errorDetails = { + 'Error': error.reason, + 'Details': error.message + }; + console.error(error.reason); + this.modalServiceSdcUI.openErrorDetailModal('Error', errorMsg, + 'error-modal', errorDetails); + this.$state.go('dashboard'); + }); + } this.initForm(); } + onModelChange(): void { + this.selectedModelName = this.models.filter(x => x.name == this.model.value).pop().name; + console.log("selected model: " + this.selectedModelName); + this.dataType.model = new Model({"name": this.selectedModelName, "derivedFrom": "", "modelType": "normative"}); + this.dataType.uniqueId = this.dataType.model.name === DEFAULT_MODEL_NAME ? + this.dataType.name + ".datatype" : this.dataType.model.name + "." + this.dataType.name + ".datatype"; + this.$scope.dataType.derivedFromName = this.derivedFromName; + this.$scope.dataType = this.dataType; + this.$scope.dataType.model = this.dataType.model; + } + + private getImportedFile(): void { + let importedFile = this.$scope["$parent"]["$resolve"]["$stateParams"]["importedFile"]; + this.importedFile = <File>importedFile; + this.$scope.importFile = this.importedFile; + if (this.importedFile) { + this.isViewOnly = false; + } + } + private initForm(): void { if (!this.dataType) { return; } this.type.setValue(this.dataType.name); this.description.setValue(this.dataType.description); - this.model.setValue(this.dataType.model); - this.derivedFrom.setValue(this.dataType.derivedFrom); + this.model.setValue(this.dataType.model ? this.dataType.model : this.$scope.dataType && this.$scope.dataType.model ? this.$scope.dataType.model : DEFAULT_MODEL_NAME); + this.derivedFrom.setValue(this.dataType.derivedFromName); } } diff --git a/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace-properties/type-workspace-properties.component.spec.ts b/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace-properties/type-workspace-properties.component.spec.ts index e6e9c12d14..f89be56359 100644 --- a/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace-properties/type-workspace-properties.component.spec.ts +++ b/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace-properties/type-workspace-properties.component.spec.ts @@ -31,6 +31,10 @@ import {DataTypeModel} from "../../../../models/data-types"; import {Component, ViewChild} from "@angular/core"; import {PropertyBEModel} from "../../../../models/properties-inputs/property-be-model"; import {ModalService} from "../../../services/modal.service"; +import {IScope} from "../../../../../typings/angularjs/angular"; +import {IWorkspaceViewModelScope} from "../../../../view-models/workspace/workspace-view-model"; +import {States} from "../../../../utils/constants"; +import {SdcUiServices} from "onap-ui-angular/dist"; describe('TypeWorkspacePropertiesComponent', () => { const messages = require("../../../../../assets/languages/en_US.json"); @@ -54,6 +58,28 @@ describe('TypeWorkspacePropertiesComponent', () => { return messages[translateKey]; }) }; + let importedFileMock: File = null; + let stateParamsMock: Partial<ng.ui.IStateParamsService> = { + 'importedFile': importedFileMock + }; + let resolveMock = {"$stateParams": stateParamsMock}; + let parentScopeMock: Partial<IScope> = { + '$resolve': resolveMock + }; + let scopeMock_: Partial<IWorkspaceViewModelScope> = { + '$parent': parentScopeMock, + 'current': { + 'name': States.TYPE_WORKSPACE + } + } + let stateMock: Partial<ng.ui.IStateService> = { + 'current': { + 'name': States.TYPE_WORKSPACE + } + }; + + let modalServiceSdcUIMock: Partial<SdcUiServices.ModalService>; + let modalServiceMock: Partial<ModalService>; beforeEach(async(() => { TestBed.configureTestingModule({ @@ -65,6 +91,10 @@ describe('TypeWorkspacePropertiesComponent', () => { providers: [ {provide: DataTypeService, useValue: dataTypeServiceMock}, {provide: TranslateService, useValue: translateServiceMock}, + {provide: SdcUiServices.ModalService, useValue: modalServiceSdcUIMock}, + {provide: ModalService, useValue: modalServiceMock}, + {provide: "$scope", useValue: scopeMock_}, + {provide: '$state', useValue: stateMock}, {provide: ModalService, useValue: modalService} ] }) diff --git a/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace-properties/type-workspace-properties.component.ts b/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace-properties/type-workspace-properties.component.ts index f53ad5b376..bcc5fe9c28 100644 --- a/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace-properties/type-workspace-properties.component.ts +++ b/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace-properties/type-workspace-properties.component.ts @@ -19,7 +19,7 @@ * ============LICENSE_END========================================================= */ -import {Component, Input, OnInit} from '@angular/core'; +import {Component, Inject, Input, OnInit} from '@angular/core'; import {DataTypeModel} from "../../../../models/data-types"; import {DataTypeService} from "../../../services/data-type.service"; import {PropertyBEModel} from "../../../../models/properties-inputs/property-be-model"; @@ -30,6 +30,8 @@ import {ModalModel} from "../../../../models/modal"; import {ButtonModel} from "../../../../models/button"; import {TranslateService} from "../../../shared/translator/translate.service"; import {AddPropertyComponent, PropertyValidationEvent} from "./add-property/add-property.component"; +import {IWorkspaceViewModelScope} from "../../../../view-models/workspace/workspace-view-model"; +import {SdcUiServices} from "onap-ui-angular/dist"; @Component({ selector: 'app-type-workspace-properties', @@ -40,6 +42,8 @@ export class TypeWorkspacePropertiesComponent implements OnInit { @Input() isViewOnly = true; @Input() dataType: DataTypeModel = new DataTypeModel(); + importedFile: File; + derivedFromName: string; properties: Array<PropertyBEModel> = []; filteredProperties: Array<PropertyBEModel> = []; tableHeadersList: Array<TableHeader> = []; @@ -48,7 +52,12 @@ export class TypeWorkspacePropertiesComponent implements OnInit { tableFilterTerm: string = undefined; tableSearchTermUpdate = new Subject<string>(); - constructor(private dataTypeService: DataTypeService, private modalService: ModalService, private translateService: TranslateService) { + constructor(@Inject('$scope') private $scope: IWorkspaceViewModelScope, + @Inject('$state') private $state: ng.ui.IStateService, + protected dataTypeService: DataTypeService, + private modalServiceSdcUI: SdcUiServices.ModalService, + private modalService: ModalService, + private translateService: TranslateService) { } ngOnInit(): void { @@ -70,22 +79,12 @@ export class TypeWorkspacePropertiesComponent implements OnInit { {title: 'Required', property: 'required'}, {title: 'Description', property: 'description'}, ]; - this.tableSortBy = this.tableHeadersList[0].property; } private initProperties(): void { this.dataTypeService.findAllProperties(this.dataType.uniqueId).subscribe(properties => { - this.properties = properties.map(value => { - const property = new PropertyBEModel(value); - if (property.defaultValue) { - property.defaultValue = JSON.parse(property.defaultValue); - } - - return property; - }); - this.filteredProperties = Array.from(this.properties); - this.sort(); + this.showPropertiesMap(properties); }); } @@ -188,6 +187,19 @@ export class TypeWorkspacePropertiesComponent implements OnInit { onRowClick(property: PropertyBEModel) { this.openAddPropertyModal(property, true); } + + private showPropertiesMap(properties: Array<PropertyBEModel>): void { + this.properties = properties.map(value => { + const property = new PropertyBEModel(value); + if (property.defaultValue) { + property.defaultValue = JSON.parse(property.defaultValue); + } + + return property; + }); + this.filteredProperties = Array.from(this.properties); + this.sort(); + } } interface TableHeader { diff --git a/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace.component.html b/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace.component.html index 105c89d7a4..cdd8e41503 100644 --- a/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace.component.html +++ b/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace.component.html @@ -32,6 +32,8 @@ <div class="progress-container"> </div> <div class="sdc-workspace-top-bar-buttons"> + <button *ngIf="!isViewOnly" data-ng-disabled="!isValidForm || isDisableMode() || isLoading || unsavedChanges" (click)="createImportType()" class="tlv-btn outline green" data-tests-id="create/save">Create</button> + <span *ngIf="!isViewOnly" class="delimiter"></span> <span class="sprite-new x-btn" (click)="goToBreadcrumbHome()" sdc-smart-tooltip="Close" [title]="'CLOSE_LABEL' | translate"></span> </div> </div> @@ -43,7 +45,7 @@ </div> </div> <div class="w-sdc-main-container-body-content" *ngIf="dataType"> - <app-type-workspace-general *ngIf="currentMenu.state === 'general'" [dataType]="dataType"></app-type-workspace-general> + <app-type-workspace-general *ngIf="currentMenu.state === 'general'" [dataType]="dataType" (onImportedType)="onImportedType($event)"></app-type-workspace-general> <app-type-workspace-properties *ngIf="currentMenu.state === 'properties'" [dataType]="dataType" [isViewOnly]="dataType.normative"></app-type-workspace-properties> <app-type-workspace-tosca-artifact *ngIf="currentMenu.state === 'tosca_artifacts'" [dataType]="dataType"></app-type-workspace-tosca-artifact> </div> diff --git a/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace.component.spec.ts b/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace.component.spec.ts index 3db2504564..a5cf20f4e9 100644 --- a/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace.component.spec.ts +++ b/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace.component.spec.ts @@ -41,6 +41,10 @@ import {TypeWorkspacePropertiesComponent} from "./type-workspace-properties/type import {TypeWorkspaceToscaArtifactPageComponent} from "./type-workspace-tosca-artifacts/type-workspace-tosca-artifact-page.component"; import {NgxDatatableModule} from "@swimlane/ngx-datatable"; import {SvgIconModule} from "onap-ui-angular/dist/svg-icon/svg-icon.module"; +import {NO_ERRORS_SCHEMA} from "@angular/core"; +import {IScope} from "angular"; +import {IWorkspaceViewModelScope} from "../../../view-models/workspace/workspace-view-model"; +import {ModelService} from "../../services/model.service"; describe('TypeWorkspaceComponent', () => { let component: TypeWorkspaceComponent; @@ -50,6 +54,7 @@ describe('TypeWorkspaceComponent', () => { 'translate': jest.fn() }; let dataTypeServiceMock: Partial<DataTypeService>; + let notificationMock: Partial<any>; let cacheService: Partial<CacheService> = { 'get': jest.fn(param => { if (param === 'version') { @@ -65,7 +70,7 @@ describe('TypeWorkspaceComponent', () => { 'name': States.TYPE_WORKSPACE } }; - let stateParamsMock: Partial<ng.ui.IStateParamsService> = {}; + let injectorMock: Partial<ng.auto.IInjectorService> = { 'get': jest.fn(param => { if (param === '$state') { @@ -89,6 +94,21 @@ describe('TypeWorkspaceComponent', () => { return user; }) }; + let importedFileMock: File = null; + let stateParamsMock: Partial<ng.ui.IStateParamsService> = { + 'importedFile': importedFileMock + }; + let resolveMock = {"$stateParams": stateParamsMock}; + let parentScopeMock: Partial<IScope> = { + '$resolve': resolveMock + }; + let scopeMock_: Partial<IWorkspaceViewModelScope> = { + '$parent': parentScopeMock, + 'current': { + 'name': States.TYPE_WORKSPACE + } + } + let modelServiceMock: Partial<ModelService>; beforeEach(async(() => { TestBed.configureTestingModule({ @@ -102,10 +122,14 @@ describe('TypeWorkspaceComponent', () => { NgxDatatableModule, SvgIconModule ], + schemas: [NO_ERRORS_SCHEMA], providers: [ {provide: DataTypeService, useValue: dataTypeServiceMock}, {provide: TranslateService, useValue: translateServiceMock}, {provide: CacheService, useValue: cacheService}, + {provide: "Notification", useValue: notificationMock }, + {provide: ModelService, useValue: modelServiceMock}, + {provide: "$scope", useValue: scopeMock_ }, {provide: '$state', useValue: stateMock}, {provide: '$stateParams', useValue: stateParamsMock}, {provide: '$injector', useValue: injectorMock}, diff --git a/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace.component.ts b/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace.component.ts index d0d5ae8c69..e0f7ac77a0 100644 --- a/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace.component.ts +++ b/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace.component.ts @@ -19,11 +19,17 @@ * ============LICENSE_END========================================================= */ -import {Component, Inject, OnInit} from '@angular/core'; +import {Component, Inject, Injector, OnInit} from '@angular/core'; import {MenuItem, MenuItemGroup} from "../../../utils/menu-handler"; import {CacheService} from "../../services/cache.service"; import {DataTypeModel} from "../../../models/data-types"; import {DataTypeService} from "../../services/data-type.service"; +import {IWorkspaceViewModelScope} from "../../../view-models/workspace/workspace-view-model"; +import {TranslateService} from "../../shared/translator/translate.service"; +import {HttpErrorResponse} from "@angular/common/http"; +import {ServerErrorResponse} from "../../../models/server-error-response"; +import {Observable} from "rxjs/Observable"; +import {SdcUiServices} from "onap-ui-angular/dist"; @Component({ selector: 'app-type-workspace', @@ -33,27 +39,32 @@ import {DataTypeService} from "../../services/data-type.service"; export class TypeWorkspaceComponent implements OnInit { private typeMenuItemGroup: MenuItemGroup; - isLoading: boolean; disabled: boolean; isViewOnly: boolean = true; sdcVersion: string; breadcrumbsModel: Array<MenuItemGroup> = []; dataType: DataTypeModel = new DataTypeModel(); + importedDataType: DataTypeModel = new DataTypeModel(); currentMenu: MenuItem; - constructor(private dataTypeService: DataTypeService, private cacheService: CacheService, + constructor(@Inject('$scope') private $scope: IWorkspaceViewModelScope, + private dataTypeService: DataTypeService, private cacheService: CacheService, + @Inject('Notification') private Notification: any, + private translateService: TranslateService, @Inject('$state') private $state: ng.ui.IStateService, - @Inject('$stateParams') private stateParams) { } + @Inject('$stateParams') private stateParams, + private injector: Injector) { } ngOnInit(): void { this.sdcVersion = this.cacheService.get('version'); this.typeMenuItemGroup = this.createTypeBreadcrumb(); + this.loadDataType(); } private loadDataType(): void { - if (this.stateParams.id) { + if (this.stateParams.id && this.stateParams.id != "import") { this.dataTypeService.findById(this.stateParams.id).subscribe(dataType => { this.dataType = dataType; this.updateTypeBreadcrumb(); @@ -61,11 +72,49 @@ export class TypeWorkspaceComponent implements OnInit { console.debug('Could not find data type %s', this.stateParams.id, error); this.goToBreadcrumbHome(); }); + this.isViewOnly = true; } else { + + this.isViewOnly = false; this.dataType = new DataTypeModel(); } } + onImportedType(dataType) { + this.typeMenuItemGroup.updateSelectedMenuItemText(`Data Type: ${dataType.name}`); + } + + private createImportType() { + if (this.$scope.dataType.derivedFromName != undefined && this.$scope.dataType.model != undefined) { + this.dataTypeService.createImportedType(this.$scope.dataType.model.name, this.$scope.importFile) + .subscribe(response => { + this.importedDataType = new DataTypeModel(response); + this.Notification.success({ + message: this.$scope.dataType.name + ' ' + this.translateService.translate('IMPORT_DATA_TYPE_SUCCESS_MESSAGE_TEXT'), + title: this.translateService.translate('IMPORT_DATA_TYPE_TITLE_TEXT') + }); + this.$state.go(this.$state.current.name, {importedFile: null, id: this.$scope.dataType.uniqueId, isViewOnly: true}, {reload: true}); + }, error => {//because overriding http interceptor + if (error instanceof HttpErrorResponse) { + const errorResponse: ServerErrorResponse = new ServerErrorResponse(error); + const modalService = this.injector.get(SdcUiServices.ModalService); + const errorDetails = { + 'Error Code': errorResponse.status != 409 ? errorResponse.messageId : "Data Type already exists", + 'Status Code': errorResponse.status + }; + modalService.openErrorDetailModal('Error', errorResponse.status != 409 ? errorResponse.message : "Data Type already exists", 'error-modal', errorDetails); + return Observable.throwError(error); + } + }); + } + else { + this.Notification.error({ + message: this.$scope.dataType.name + ' ' + "Derived from is invalid in file", + title: this.translateService.translate('IMPORT_DATA_TYPE_TITLE_TEXT') + }); + } + } + private updateTypeBreadcrumb(): void { this.typeMenuItemGroup.updateSelectedMenuItemText(`Data Type: ${this.dataType.name}`); } @@ -82,10 +131,15 @@ export class TypeWorkspaceComponent implements OnInit { onMenuUpdate(menuItemGroup: MenuItemGroup): void { this.breadcrumbsModel.push(...[this.typeMenuItemGroup, menuItemGroup]); + if (!this.isViewOnly) { + this.$scope.leftBarTabs.menuItems.forEach((item: MenuItem) => { + item.isDisabled = ('general' !== item.state); + item.disabledCategory = ('general' !== item.state); + }); + } } onMenuClick(menuItem: MenuItem): void { this.currentMenu = menuItem; } - } diff --git a/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace.module.ts b/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace.module.ts index 87b29b615d..e7ddb46602 100644 --- a/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace.module.ts +++ b/catalog-ui/src/app/ng2/pages/type-workspace/type-workspace.module.ts @@ -38,6 +38,7 @@ import {TypeWorkspaceToscaArtifactPageComponent} from "./type-workspace-tosca-ar import {ModalService} from "../../services/modal.service"; import {AddPropertyComponent} from './type-workspace-properties/add-property/add-property.component'; import {InterfaceOperationHandlerModule} from "../composition/interface-operatons/operation-creator/interface-operation-handler.module"; +import {AutoCompleteModule} from "onap-ui-angular/dist/autocomplete/autocomplete.module"; @NgModule({ imports: [ @@ -51,6 +52,7 @@ import {InterfaceOperationHandlerModule} from "../composition/interface-operaton InterfaceOperationHandlerModule, NgxDatatableModule, SvgIconModule, + AutoCompleteModule ], declarations: [ TypeWorkspaceComponent, @@ -64,7 +66,8 @@ import {InterfaceOperationHandlerModule} from "../composition/interface-operaton CacheService, WorkspaceMenuComponent, DataTypeService, - ModalService + ModalService, + FileReader ], entryComponents: [TypeWorkspaceComponent, AddPropertyComponent], exports: [TypeWorkspaceComponent] diff --git a/catalog-ui/src/app/ng2/pages/type-workspace/workspace-menu/workspace-menu.component.html b/catalog-ui/src/app/ng2/pages/type-workspace/workspace-menu/workspace-menu.component.html index 18c6949b46..02d2683c9d 100644 --- a/catalog-ui/src/app/ng2/pages/type-workspace/workspace-menu/workspace-menu.component.html +++ b/catalog-ui/src/app/ng2/pages/type-workspace/workspace-menu/workspace-menu.component.html @@ -25,8 +25,8 @@ </div> <div class="i-sdc-designer-sidebar-section-content-item" [ngClass]="{'selected': isSelected(menuItem)}" - *ngFor="let menuItem of leftBarTabs.menuItems"> - <div class="expand-collapse-menu-box-item-text" ng-class="{'disabled': menuItem.isDisabled }"> + *ngFor="let menuItem of $scope.leftBarTabs.menuItems"> + <div class="expand-collapse-menu-box-item-text" [ngClass]="{ 'disabled': menuItem.isDisabled }"> <button [attr.data-tests-id]="menuItem.text + 'LeftSideMenu'" type="button" class="i-sdc-designer-sidebar-section-content-item-service-cat" (click)="menuItem.callback()" [disabled]="menuItem.disabledCategory">{{menuItem.text}}</button> </div> diff --git a/catalog-ui/src/app/ng2/pages/type-workspace/workspace-menu/workspace-menu.component.spec.ts b/catalog-ui/src/app/ng2/pages/type-workspace/workspace-menu/workspace-menu.component.spec.ts index a91258c00e..d9bb8833cd 100644 --- a/catalog-ui/src/app/ng2/pages/type-workspace/workspace-menu/workspace-menu.component.spec.ts +++ b/catalog-ui/src/app/ng2/pages/type-workspace/workspace-menu/workspace-menu.component.spec.ts @@ -26,6 +26,8 @@ import {CacheService} from "../../../services/cache.service"; import {States} from "../../../../utils/constants"; import {IAppMenu} from "../../../../models/app-config"; import {SdcMenuToken} from "../../../config/sdc-menu.config"; +import {IScope} from "../../../../../typings/angularjs/angular"; +import {IWorkspaceViewModelScope} from "../../../../view-models/workspace/workspace-view-model"; describe('WorkspaceMenuComponent', () => { let component: WorkspaceMenuComponent; @@ -59,6 +61,20 @@ describe('WorkspaceMenuComponent', () => { } }) }; + let importedFileMock: File = null; + let stateParamsMock: Partial<ng.ui.IStateParamsService> = { + 'importedFile': importedFileMock + }; + let resolveMock = {"$stateParams": stateParamsMock}; + let parentScopeMock: Partial<IScope> = { + '$resolve': resolveMock + }; + let scopeMock_: Partial<IWorkspaceViewModelScope> = { + '$parent': parentScopeMock, + 'current': { + 'name': States.TYPE_WORKSPACE + } + } beforeEach(async(() => { TestBed.configureTestingModule({ @@ -66,6 +82,7 @@ describe('WorkspaceMenuComponent', () => { providers: [ {provide: CacheService, useValue: cacheService}, {provide: '$injector', useValue: injectorMock}, + {provide: "$scope", useValue: scopeMock_ }, {provide: SdcMenuToken, useValue: sdcMenuMock} ] }) diff --git a/catalog-ui/src/app/ng2/pages/type-workspace/workspace-menu/workspace-menu.component.ts b/catalog-ui/src/app/ng2/pages/type-workspace/workspace-menu/workspace-menu.component.ts index c5e49d4d7d..ee4f3a84bd 100644 --- a/catalog-ui/src/app/ng2/pages/type-workspace/workspace-menu/workspace-menu.component.ts +++ b/catalog-ui/src/app/ng2/pages/type-workspace/workspace-menu/workspace-menu.component.ts @@ -25,6 +25,7 @@ import {CacheService} from "../../../services/cache.service"; import {IAppMenu} from "../../../../models/app-config"; import {IUserProperties} from "../../../../models/user"; import {SdcMenuToken} from "../../../config/sdc-menu.config"; +import {IWorkspaceViewModelScope} from "../../../../view-models/workspace/workspace-view-model"; @Component({ selector: 'app-workspace-menu', @@ -44,7 +45,10 @@ export class WorkspaceMenuComponent implements OnInit { leftBarTabs: MenuItemGroup = new MenuItemGroup(); - constructor(private cacheService: CacheService, @Inject(SdcMenuToken) private sdcMenu: IAppMenu, @Inject('$injector') $injector) { + constructor(private cacheService: CacheService, + @Inject('$scope') private $scope: IWorkspaceViewModelScope, + @Inject(SdcMenuToken) private sdcMenu: IAppMenu, + @Inject('$injector') $injector) { this.$state = $injector.get('$state'); this.$q = $injector.get('$q'); } @@ -70,6 +74,7 @@ export class WorkspaceMenuComponent implements OnInit { return menuItem; }); this.updateSelectedMenuItem(); + this.$scope.leftBarTabs = this.leftBarTabs; this.onMenuUpdate.emit(this.leftBarTabs); } diff --git a/catalog-ui/src/app/ng2/services/data-type.service.ts b/catalog-ui/src/app/ng2/services/data-type.service.ts index 298ba90b31..3a27801315 100644 --- a/catalog-ui/src/app/ng2/services/data-type.service.ts +++ b/catalog-ui/src/app/ng2/services/data-type.service.ts @@ -26,8 +26,9 @@ import { PROPERTY_DATA } from "app/utils"; import {DerivedFEAttribute} from "../../models/attributes-outputs/derived-fe-attribute"; import {ISdcConfig} from "../config/sdc-config.config.factory"; import {SdcConfigToken} from "../config/sdc-config.config"; -import {HttpClient} from "@angular/common/http"; +import {HttpBackend, HttpClient, HttpHeaders} from "@angular/common/http"; import {Observable} from "rxjs/Observable"; +import {AuthenticationService} from "./authentication.service"; /** This is a new service for NG2, to eventually replace app/services/data-types-service.ts * @@ -40,14 +41,17 @@ export class DataTypeService { public dataTypes: DataTypesMap; private readonly baseUrl: string; private readonly dataTypeUrl: string; + private readonly dataTypeUploadUrl: string; - constructor(private dataTypeService: DataTypesService, private httpClient: HttpClient, @Inject(SdcConfigToken) sdcConfig: ISdcConfig) { + + constructor(private dataTypeService: DataTypesService, private authService: AuthenticationService, private handler: HttpBackend, private httpClient: HttpClient, @Inject(SdcConfigToken) sdcConfig: ISdcConfig) { this.dataTypes = dataTypeService.getAllDataTypes(); //This should eventually be replaced by an NG2 call to the backend instead of utilizing Angular1 downgraded component. this.baseUrl = sdcConfig.api.root + sdcConfig.api.component_api_root; - this.dataTypeUrl = `${this.baseUrl}data-types` + this.dataTypeUrl = `${this.baseUrl}data-types`; + this.dataTypeUploadUrl = `${this.baseUrl}uploadType`; + this.httpClient = new HttpClient(handler); } - public getDataTypeByModelAndTypeName(modelName: string, typeName: string): DataTypeModel { this.dataTypes = this.dataTypeService.getAllDataTypesFromModel(modelName); let dataTypeFound = this.dataTypes[typeName]; @@ -88,6 +92,18 @@ export class DataTypeService { return this.httpClient.post<PropertyBEModel>(url, property); } + public createImportedType(model: string, importingFile: File): Observable<any> { + const url = `${this.dataTypeUploadUrl}/datatypesyaml`; + const formData = new FormData(); + formData.append('dataTypesYaml', importingFile); + formData.append('model', model != 'SDC AID' ? model : "") + formData.append('includeToModelImport', "true"); + let headers = new HttpHeaders({'USER_ID': this.authService.getLoggedinUser().userId}); + let options = {headers: headers}; + + return this.httpClient.post<any>(url, formData, options); + } + public getConstraintsByParentTypeAndUniqueID(rootPropertyType, propertyName){ // const property = this.dataTypes[rootPropertyType].properties.filter(property => // property.name == propertyName); @@ -149,6 +165,5 @@ export class DataTypeService { if (prop.name == 'instance_name') prop.hidden = generatedNamingVal; }); } - } diff --git a/catalog-ui/src/app/ng2/services/model.service.ts b/catalog-ui/src/app/ng2/services/model.service.ts index 83e16b12c7..60b7ac902d 100644 --- a/catalog-ui/src/app/ng2/services/model.service.ts +++ b/catalog-ui/src/app/ng2/services/model.service.ts @@ -39,4 +39,7 @@ export class ModelService { return this.http.get<Model[]>(this.baseUrl + "/v1/catalog/model?modelType=" + type); } + getDataTypeModels(typeName: string):Observable<any> { + return this.http.get<Array<string>>(this.baseUrl + "/v1/catalog/data-types/" + typeName + "/models"); + } } diff --git a/catalog-ui/src/app/services/data-types-service.ts b/catalog-ui/src/app/services/data-types-service.ts index 09ece87907..5d247a14d9 100644 --- a/catalog-ui/src/app/services/data-types-service.ts +++ b/catalog-ui/src/app/services/data-types-service.ts @@ -134,6 +134,21 @@ export class DataTypesService implements IDataTypesService { }); } + public findAllDataTypesByModelIncludingRoot = (modelName: string): Promise<Map<string, DataTypeModel>> => { + return new Promise<Map<string, DataTypeModel>>((resolve, reject) => { + this.fetchDataTypesByModel(modelName).then(response => { + const dataTypes = response.data; + const dataTypeMap = new Map<string, DataTypeModel>(); + for(const dataTypeKey of Object.keys(dataTypes)) { + dataTypeMap.set(dataTypeKey, new DataTypeModel(dataTypes[dataTypeKey])) + } + resolve(dataTypeMap); + }).catch(reason => { + reject(reason); + }); + }); + } + public getAllDataTypes = ():DataTypesMap => { return this.dataTypes; }; diff --git a/catalog-ui/src/app/utils/service-data-type-reader.ts b/catalog-ui/src/app/utils/service-data-type-reader.ts new file mode 100644 index 0000000000..fd5ee2fc19 --- /dev/null +++ b/catalog-ui/src/app/utils/service-data-type-reader.ts @@ -0,0 +1,88 @@ +/* + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2022 Nordix Foundation. All rights reserved. + * ================================================================================ + * Licensed 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. + * ============LICENSE_END========================================================= + */ + +import {DataTypeModel, PropertyBEModel} from "../models"; +import {load} from 'js-yaml'; + +export class ServiceDataTypeReader { + + private serviceDataType = new DataTypeModel(); + + public read(dataTypeFile: File): Promise<DataTypeModel> { + + return new Promise<DataTypeModel>((resolve, reject) => { + const reader = new FileReader(); + reader.onloadend = () => { + try { + const result = <String>reader.result; + const loadedContent = load(result); + console.log("Readed content: " + loadedContent); + this.readName(loadedContent); + this.readDerivedFrom(this.getDataType(loadedContent)); + this.readDescription(this.getDataType(loadedContent)); + this.readProperties(this.getDataType(loadedContent)); + resolve(this.serviceDataType); + } catch (error) { + reject(error); + } + } + reader.readAsText(dataTypeFile); + }); + } + + private getDataType(fileContent:any) { + const index = Object.keys(fileContent).indexOf("data_types",0) + if (index == -1){ + return fileContent; + } + return fileContent["data_types"]; + } + + private readName(fileContent: any) { + this.serviceDataType.name = Object.keys(fileContent).values().next().value; + } + + private readDerivedFrom(fileContent: any) { + let dataType = Object.keys(fileContent).values().next().value; + this.serviceDataType.derivedFromName = fileContent[dataType]["derived_from"]; + } + + private readDescription(fileContent: any) { + let dataType = Object.keys(fileContent).values().next().value; + this.serviceDataType.description = fileContent[dataType]["description"]; + } + + private readProperties(fileContent: any) { + this.serviceDataType.properties = new Array<PropertyBEModel>(); + let dataType = Object.keys(fileContent).values().next().value; + const properties = fileContent[dataType]["properties"]; + Object.keys(properties).forEach((key )=> + { + let property = new PropertyBEModel(); + property.name = key; + property.description = properties[key]["description"]; + property.type = properties[key]["type"]; + property.schemaType = properties[key]["schema"]; + property.required = properties[key]["required"]; + this.serviceDataType.properties.push(property); + } + ); + } +}
\ No newline at end of file diff --git a/catalog-ui/src/app/view-models/workspace/workspace-view-model.ts b/catalog-ui/src/app/view-models/workspace/workspace-view-model.ts index 0778b2d803..accc0437fb 100644 --- a/catalog-ui/src/app/view-models/workspace/workspace-view-model.ts +++ b/catalog-ui/src/app/view-models/workspace/workspace-view-model.ts @@ -23,7 +23,7 @@ */ 'use strict'; import * as _ from 'lodash'; -import {Component, IAppMenu, IUserProperties, Plugin, PluginsConfiguration, Resource, Service} from 'app/models'; +import {Component, DataTypeModel, IAppMenu, IUserProperties, Plugin, PluginsConfiguration, Resource, Service} from 'app/models'; import { CHANGE_COMPONENT_CSAR_VERSION_FLAG, ChangeLifecycleStateHandler, @@ -57,6 +57,7 @@ export interface IWorkspaceViewModelScope extends ng.IScope { isLoading: boolean; isCreateProgress: boolean; component: Component; + dataType: DataTypeModel; originComponent: Component; componentType: string; importFile: any; diff --git a/catalog-ui/src/assets/languages/en_US.json b/catalog-ui/src/assets/languages/en_US.json index 5dec922980..bb2ebd596d 100644 --- a/catalog-ui/src/assets/languages/en_US.json +++ b/catalog-ui/src/assets/languages/en_US.json @@ -585,6 +585,11 @@ "UNIQUE_ID_LABEL": "Unique Id", "=========== SERVICE IMPORT ===========": "", "IMPORT_FAILURE_MESSAGE_TEXT": "Import Failure - error reading CSAR", + "=========== DATA TYPE IMPORT ===========": "", + "IMPORT_DATA_TYPE_FAILURE_MESSAGE_TEXT": "Import Failure - error reading data type file", + "IMPORT_DATA_TYPE_FAILURE_PROCESSING_MESSAGE_TEXT": "Import Failure - error importing data type", + "IMPORT_DATA_TYPE_SUCCESS_MESSAGE_TEXT": "Successfully imported", + "IMPORT_DATA_TYPE_TITLE_TEXT": "Import Data Type", "=========== PROPERTIES ===========": "", "PROPERTY_LIST_EMPTY_MESSAGE": "There are no properties to display", "PROPERTY_SHOWING_LABEL": "Showing Properties", |