diff --git a/org.eclipse.winery.frontends/app/tosca-management/src/app/instance/instance.service.ts b/org.eclipse.winery.frontends/app/tosca-management/src/app/instance/instance.service.ts index dd65e17fe7..274fb21d0f 100644 --- a/org.eclipse.winery.frontends/app/tosca-management/src/app/instance/instance.service.ts +++ b/org.eclipse.winery.frontends/app/tosca-management/src/app/instance/instance.service.ts @@ -50,7 +50,8 @@ export class InstanceService { break; case ToscaTypes.ServiceTemplate: subMenu = [SubMenuItems.readme, SubMenuItems.license, SubMenuItems.topologyTemplate, SubMenuItems.plans, SubMenuItems.selfServicePortal, - SubMenuItems.boundaryDefinitions, SubMenuItems.tags, SubMenuItems.constraintChecking, SubMenuItems.documentation, SubMenuItems.xml]; + SubMenuItems.boundaryDefinitions, SubMenuItems.tags, SubMenuItems.propertiesDefinition, SubMenuItems.constraintChecking, + SubMenuItems.documentation, SubMenuItems.xml]; if (this.configurationService.configuration.features.nfv) { subMenu.push(SubMenuItems.threatModeling); } diff --git a/org.eclipse.winery.frontends/app/tosca-management/src/app/instance/serviceTemplates/boundaryDefinitions/boundaryDefinitions.module.ts b/org.eclipse.winery.frontends/app/tosca-management/src/app/instance/serviceTemplates/boundaryDefinitions/boundaryDefinitions.module.ts index 34b4afd683..685a446f68 100644 --- a/org.eclipse.winery.frontends/app/tosca-management/src/app/instance/serviceTemplates/boundaryDefinitions/boundaryDefinitions.module.ts +++ b/org.eclipse.winery.frontends/app/tosca-management/src/app/instance/serviceTemplates/boundaryDefinitions/boundaryDefinitions.module.ts @@ -35,9 +35,10 @@ import { InterfacesComponent } from '../../sharedComponents/interfaces/interface import { InterfacesModule } from '../../sharedComponents/interfaces/interfaces.module'; import { WineryQNameSelectorModule } from '../../../wineryQNameSelector/wineryQNameSelector.module'; import { WineryEditorModule } from '../../../wineryEditorModule/wineryEditor.module'; +import { PropertiesComponent } from '../../sharedComponents/properties/properties.component'; export const boundaryDefinitionsRoutes: Routes = [ - { path: 'properties', component: EditXMLComponent }, + { path: 'properties', component: PropertiesComponent}, { path: 'propertymappings', component: PropertyMappingsComponent }, { path: 'propertyconstraints', component: PropertyConstraintsComponent }, { path: 'requirements', component: RequirementsComponent }, diff --git a/org.eclipse.winery.frontends/app/tosca-management/src/app/instance/serviceTemplates/boundaryDefinitions/propertyMappings/propertyMappings.component.html b/org.eclipse.winery.frontends/app/tosca-management/src/app/instance/serviceTemplates/boundaryDefinitions/propertyMappings/propertyMappings.component.html index 7c73f423d2..a1b194b671 100644 --- a/org.eclipse.winery.frontends/app/tosca-management/src/app/instance/serviceTemplates/boundaryDefinitions/propertyMappings/propertyMappings.component.html +++ b/org.eclipse.winery.frontends/app/tosca-management/src/app/instance/serviceTemplates/boundaryDefinitions/propertyMappings/propertyMappings.component.html @@ -32,7 +32,15 @@
Service Template Property
-
+ + + +
-
-
-
- Property name is required! +
+
+ Property name is required! +
@@ -145,8 +153,9 @@ + [disableOkButton]="!(propertyMappingForm?.form.valid && currentSelectedItem.serviceTemplatePropertyRef && currentSelectedItem.serviceTemplatePropertyRef.length > 0 + && currentSelectedItem.targetObjectRef && currentSelectedItem.targetObjectRef.length > 0 + && currentSelectedItem.targetPropertyRef && currentSelectedItem.targetPropertyRef.length > 0)"> @@ -164,7 +173,7 @@ + [okButtonLabel]="'Delete'"> diff --git a/org.eclipse.winery.frontends/app/tosca-management/src/app/instance/serviceTemplates/boundaryDefinitions/propertyMappings/propertyMappings.component.ts b/org.eclipse.winery.frontends/app/tosca-management/src/app/instance/serviceTemplates/boundaryDefinitions/propertyMappings/propertyMappings.component.ts index 8b77858892..c1c2bc40bb 100644 --- a/org.eclipse.winery.frontends/app/tosca-management/src/app/instance/serviceTemplates/boundaryDefinitions/propertyMappings/propertyMappings.component.ts +++ b/org.eclipse.winery.frontends/app/tosca-management/src/app/instance/serviceTemplates/boundaryDefinitions/propertyMappings/propertyMappings.component.ts @@ -38,7 +38,7 @@ export class PropertyMappingsComponent implements OnInit { loading = true; targetTypeSelected = false; - apiData: PropertyMappingsApiData; + apiData: PropertyMappingsApiData = { propertyMappings: { propertyMapping: [] } }; columns: Array = [ { title: 'Service Template Property', name: 'serviceTemplatePropertyRef', sort: true }, { title: 'Target', name: 'targetObjectRef', sort: true }, @@ -53,6 +53,8 @@ export class PropertyMappingsComponent implements OnInit { currentSelectedItem: Property = new Property(); addOrUpdate = 'Add'; properties: { name: string, property: string } = { name: '', property: '' }; + propertiesList: Array = []; + propertiesAreKeyValuePairs = false; xmlData: any; selectedProperty = ''; templateList: Array = []; @@ -71,13 +73,14 @@ export class PropertyMappingsComponent implements OnInit { ngOnInit() { this.getMappings(); - this.getProperties(); - this.getTopologyTemplate(); } getMappings() { this.service.getPropertyMappings().subscribe( - data => this.handleData(data), + data => { + this.handleData(data); + this.getProperties(); + }, error => this.notify.error(error.toString()) ); } @@ -91,26 +94,42 @@ export class PropertyMappingsComponent implements OnInit { getProperties() { this.service.getPropertiesOfServiceTemplate().subscribe( - data => this.handleProperties(data), + data => { + this.handleProperties(data); + this.getTopologyTemplate(); + }, error => this.handleError(error) ); } handleTopologyTemplateData(data: WineryTopologyTemplate) { this.topologyTemplate = data; - if (!isNullOrUndefined(this.xmlData) && !isNullOrUndefined(this.apiData)) { - this.loading = false; - } + this.loading = false; } - handleProperties(props: string) { - const parser = new DOMParser(); - this.xmlData = parser.parseFromString(props, 'application/xml'); - this.properties.name = this.xmlData.firstChild.localName; - this.properties.property = '/*[local-name()=\'' + this.properties.name + '\']'; + handleProperties(props: any) { + if (props.isXML) { + if (props.properties) { + const parser = new DOMParser(); + this.xmlData = parser.parseFromString(props.properties, 'application/xml'); + this.properties.name = this.xmlData.firstChild.localName; + this.properties.property = '/*[local-name()=\'' + this.properties.name + '\']'; + } + } else { + this.propertiesAreKeyValuePairs = true; + if (props.properties) { + for (const p in props.properties) { + if (props.properties.hasOwnProperty(p)) { + this.propertiesList.push({ 'id': p, 'text': p }); + } + } + } + } + } - if (!isNullOrUndefined(this.topologyTemplate) && !isNullOrUndefined(this.apiData)) { - this.loading = false; + onPropertySelected(property: any) { + if (property.text && property.text.length > 0) { + this.currentSelectedItem.serviceTemplatePropertyRef = property.text; } } @@ -200,9 +219,6 @@ export class PropertyMappingsComponent implements OnInit { handleData(data: PropertyMappingsApiData) { this.apiData = data; - if (!isNullOrUndefined(this.xmlData) && !isNullOrUndefined(this.topologyTemplate)) { - this.loading = false; - } } onCellSelected(selectedItem: WineryRowData) { diff --git a/org.eclipse.winery.frontends/app/tosca-management/src/app/instance/serviceTemplates/boundaryDefinitions/propertyMappings/propertyMappings.service.ts b/org.eclipse.winery.frontends/app/tosca-management/src/app/instance/serviceTemplates/boundaryDefinitions/propertyMappings/propertyMappings.service.ts index e2ed5f835d..3f25d5ceda 100644 --- a/org.eclipse.winery.frontends/app/tosca-management/src/app/instance/serviceTemplates/boundaryDefinitions/propertyMappings/propertyMappings.service.ts +++ b/org.eclipse.winery.frontends/app/tosca-management/src/app/instance/serviceTemplates/boundaryDefinitions/propertyMappings/propertyMappings.service.ts @@ -18,6 +18,7 @@ import { backendBaseURL } from '../../../../configuration'; import { ModalDirective } from 'ngx-bootstrap'; import { PropertiesDefinitionsResourceApiData } from '../../../sharedComponents/propertiesDefinition/propertiesDefinitionsResourceApiData'; import { HttpClient, HttpHeaders, HttpResponse } from '@angular/common/http'; +import { map } from 'rxjs/operators'; export class Property { serviceTemplatePropertyRef: string; @@ -66,14 +67,18 @@ export class PropertyMappingService { ); } - getPropertiesOfServiceTemplate(): Observable { - const headers = new HttpHeaders({ 'Accept': 'application/xml' }); + getPropertiesOfServiceTemplate(): Observable { const newPath: string = this.path.replace('propertymappings', 'properties'); - - return this.http - .get(newPath + '/', - { headers: headers, responseType: 'text' } - ); + return this.http.get(newPath, { observe: 'response', responseType: 'text' }) + .pipe(map(res => { + if (res.headers.get('Content-Type') === 'application/json') { + return { + isXML: false, properties: JSON.parse(res.body) + }; + } else { + return { isXML: true, properties: res.body }; + } + })); } getTargetObjKVProperties(targetPath: string): Observable { diff --git a/org.eclipse.winery.frontends/app/tosca-management/src/app/instance/sharedComponents/properties/properties.service.ts b/org.eclipse.winery.frontends/app/tosca-management/src/app/instance/sharedComponents/properties/properties.service.ts index 7b95914ed8..2e76df1a85 100644 --- a/org.eclipse.winery.frontends/app/tosca-management/src/app/instance/sharedComponents/properties/properties.service.ts +++ b/org.eclipse.winery.frontends/app/tosca-management/src/app/instance/sharedComponents/properties/properties.service.ts @@ -17,6 +17,7 @@ import { Observable } from 'rxjs'; import { backendBaseURL } from '../../../configuration'; import { HttpClient, HttpHeaders, HttpResponse } from '@angular/common/http'; import { map } from 'rxjs/operators'; +import { ToscaTypes } from '../../../model/enums'; @Injectable() export class PropertiesService { @@ -26,6 +27,9 @@ export class PropertiesService { constructor(private http: HttpClient, private sharedData: InstanceService) { this.path = backendBaseURL + this.sharedData.path + '/properties/'; + if (this.sharedData.toscaComponent.toscaType === ToscaTypes.ServiceTemplate ) { + this.path = this.path.replace('/properties/', '/boundarydefinitions/properties/'); + } } /** @@ -46,8 +50,7 @@ export class PropertiesService { } public saveProperties(properties: any, isXML: boolean): Observable> { - const headers = new HttpHeaders(); - headers.set('Content-Type', isXML ? 'application/xml' : 'application/json'); + const headers = new HttpHeaders({ 'Content-Type' : isXML ? 'application/xml' : 'application/json'}); return this.http .put( this.path, diff --git a/org.eclipse.winery.frontends/app/tosca-management/src/app/wineryMainModules/serviceTemplates/serviceTemplate.module.ts b/org.eclipse.winery.frontends/app/tosca-management/src/app/wineryMainModules/serviceTemplates/serviceTemplate.module.ts index 8d644f740b..9160fadc80 100644 --- a/org.eclipse.winery.frontends/app/tosca-management/src/app/wineryMainModules/serviceTemplates/serviceTemplate.module.ts +++ b/org.eclipse.winery.frontends/app/tosca-management/src/app/wineryMainModules/serviceTemplates/serviceTemplate.module.ts @@ -15,7 +15,6 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { ServiceTemplateRouterModule } from './serviceTemplateRouter.module'; -import { TopologyTemplateComponent } from '../../instance/sharedComponents/topologyTemplate/topologyTemplate.component'; import { PlansComponent } from '../../instance/serviceTemplates/plans/plans.component'; import { WineryLoaderModule } from '../../wineryLoader/wineryLoader.module'; import { WineryModalModule } from '../../wineryModalModule/winery.modal.module'; @@ -34,7 +33,8 @@ import { WineryLicenseModule } from '../../wineryLicenseModule/wineryLicense.mod import { WinerySourceModule } from '../../instance/sharedComponents/artifactSource/source.module'; import { ConstraintCheckingComponent } from '../../instance/serviceTemplates/constraintChecking/constraintChecking.component'; import { WineryEditorModule } from '../../wineryEditorModule/wineryEditor.module'; -import { TopologyTemplateModule } from '../../instance/sharedComponents/topologyTemplate/topologyTemplate.module'; +import { TopologyTemplateModule } from '../../instance/serviceTemplates/topologyTemplate/topologyTemplate.module'; +import { PropertiesDefinitionModule } from '../../instance/sharedComponents/propertiesDefinition/propertiesDefinition.module'; import { ThreatAssessmentComponent } from '../../instance/serviceTemplates/threatAssessment/threatAssessment.component'; @NgModule({ @@ -57,7 +57,8 @@ import { ThreatAssessmentComponent } from '../../instance/serviceTemplates/threa WineryTableModule, ServiceTemplateRouterModule, WineryReadmeModule, - WineryLicenseModule + WineryLicenseModule, + PropertiesDefinitionModule ], declarations: [ PlansComponent, diff --git a/org.eclipse.winery.frontends/app/tosca-management/src/app/wineryMainModules/serviceTemplates/serviceTemplateRouter.module.ts b/org.eclipse.winery.frontends/app/tosca-management/src/app/wineryMainModules/serviceTemplates/serviceTemplateRouter.module.ts index 0f2e9a89b6..3fc5be68d7 100644 --- a/org.eclipse.winery.frontends/app/tosca-management/src/app/wineryMainModules/serviceTemplates/serviceTemplateRouter.module.ts +++ b/org.eclipse.winery.frontends/app/tosca-management/src/app/wineryMainModules/serviceTemplates/serviceTemplateRouter.module.ts @@ -31,6 +31,7 @@ import { WineryReadmeComponent } from '../../wineryReadmeModule/wineryReadme.com import { WineryLicenseComponent } from '../../wineryLicenseModule/wineryLicense.component'; import { ConstraintCheckingComponent } from '../../instance/serviceTemplates/constraintChecking/constraintChecking.component'; import { ThreatAssessmentComponent } from '../../instance/serviceTemplates/threatAssessment/threatAssessment.component'; +import { PropertiesDefinitionComponent } from '../../instance/sharedComponents/propertiesDefinition/propertiesDefinition.component'; const toscaType = ToscaTypes.ServiceTemplate; @@ -60,6 +61,7 @@ const serviceTemplateRoutes: Routes = [ // 'app/instance/serviceTemplates/boundaryDefinitions/boundaryDefinitions.module#BoundaryDefinitionsModule' children: boundaryDefinitionsRoutes }, + { path: 'propertiesdefinition', component: PropertiesDefinitionComponent}, { path: 'constraintchecking', component: ConstraintCheckingComponent }, { path: 'tags', component: TagComponent }, { path: 'documentation', component: DocumentationComponent }, diff --git a/org.eclipse.winery.model.tosca/src/main/java/org/eclipse/winery/model/tosca/TBoundaryDefinitions.java b/org.eclipse.winery.model.tosca/src/main/java/org/eclipse/winery/model/tosca/TBoundaryDefinitions.java index 94a222bf2d..f0098e798c 100644 --- a/org.eclipse.winery.model.tosca/src/main/java/org/eclipse/winery/model/tosca/TBoundaryDefinitions.java +++ b/org.eclipse.winery.model.tosca/src/main/java/org/eclipse/winery/model/tosca/TBoundaryDefinitions.java @@ -21,7 +21,6 @@ import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; -import javax.xml.bind.annotation.XmlAnyElement; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlType; @@ -174,26 +173,11 @@ public List getInterface() { } @XmlAccessorType(XmlAccessType.FIELD) - @XmlType(name = "", propOrder = { - "any", - "propertyMappings" - }) - public static class Properties implements Serializable { + public static class Properties extends TEntityTemplate.Properties { - @XmlAnyElement(lax = true) - protected Object any; @XmlElement(name = "PropertyMappings") protected TBoundaryDefinitions.Properties.PropertyMappings propertyMappings; - @Nullable - public Object getAny() { - return any; - } - - public void setAny(@Nullable Object value) { - this.any = value; - } - public TBoundaryDefinitions.Properties.@Nullable PropertyMappings getPropertyMappings() { return propertyMappings; } diff --git a/org.eclipse.winery.model.tosca/src/main/java/org/eclipse/winery/model/tosca/TEntityType.java b/org.eclipse.winery.model.tosca/src/main/java/org/eclipse/winery/model/tosca/TEntityType.java index 36a1ff86b7..3a37b345d7 100644 --- a/org.eclipse.winery.model.tosca/src/main/java/org/eclipse/winery/model/tosca/TEntityType.java +++ b/org.eclipse.winery.model.tosca/src/main/java/org/eclipse/winery/model/tosca/TEntityType.java @@ -23,13 +23,11 @@ import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlSchemaType; import javax.xml.bind.annotation.XmlSeeAlso; -import javax.xml.bind.annotation.XmlTransient; import javax.xml.bind.annotation.XmlType; import javax.xml.bind.annotation.adapters.CollapsedStringAdapter; import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; import javax.xml.namespace.QName; -import org.eclipse.winery.model.tosca.kvproperties.WinerysPropertiesDefinition; import org.eclipse.winery.model.tosca.visitor.Visitor; import com.fasterxml.jackson.annotation.JsonIgnore; @@ -52,7 +50,6 @@ TPolicyType.class }) public abstract class TEntityType extends TExtensibleElements implements HasName, HasInheritance, HasTargetNamespace { - public static final String NS_SUFFIX_PROPERTIESDEFINITION_WINERY = "propertiesdefinition/winery"; @XmlElement(name = "Tags") protected TTags tags; @@ -184,47 +181,6 @@ public void setTargetNamespace(@Nullable String value) { this.targetNamespace = value; } - /** - * This is a special method for Winery. Winery allows to define a property definition by specifying name/type - * values. Instead of parsing the extensible elements returned TDefinitions, this method is a convenience method to - * access this information - * - * @return a WinerysPropertiesDefinition object, which includes a map of name/type-pairs denoting the associated - * property definitions. A default element name and namespace is added if it is not defined in the underlying XML. - * null if no Winery specific KV properties are defined for the given entity type - */ - @XmlTransient - @JsonIgnore - public WinerysPropertiesDefinition getWinerysPropertiesDefinition() { - // similar implementation as org.eclipse.winery.repository.resources.entitytypes.properties.PropertiesDefinitionResource.getListFromEntityType(TEntityType) - WinerysPropertiesDefinition res = null; - for (Object o : this.getAny()) { - if (o instanceof WinerysPropertiesDefinition) { - res = (WinerysPropertiesDefinition) o; - } - } - - if (res != null) { - // we put defaults if elementname and namespace have not been set - - if (res.getElementName() == null) { - res.setElementName("Properties"); - } - - if (res.getNamespace() == null) { - // we use the targetnamespace of the original element - String ns = this.getTargetNamespace(); - if (!ns.endsWith("/")) { - ns += "/"; - } - ns += NS_SUFFIX_PROPERTIESDEFINITION_WINERY; - res.setNamespace(ns); - } - } - - return res; - } - @XmlAccessorType(XmlAccessType.FIELD) @XmlType(name = "") public static class DerivedFrom implements HasType { diff --git a/org.eclipse.winery.model.tosca/src/main/java/org/eclipse/winery/model/tosca/TExtensibleElements.java b/org.eclipse.winery.model.tosca/src/main/java/org/eclipse/winery/model/tosca/TExtensibleElements.java index 5591230cd0..8d3a9d5bc2 100644 --- a/org.eclipse.winery.model.tosca/src/main/java/org/eclipse/winery/model/tosca/TExtensibleElements.java +++ b/org.eclipse.winery.model.tosca/src/main/java/org/eclipse/winery/model/tosca/TExtensibleElements.java @@ -27,11 +27,14 @@ import javax.xml.bind.annotation.XmlAnyAttribute; import javax.xml.bind.annotation.XmlAnyElement; import javax.xml.bind.annotation.XmlSeeAlso; +import javax.xml.bind.annotation.XmlTransient; import javax.xml.bind.annotation.XmlType; import javax.xml.namespace.QName; +import org.eclipse.winery.model.tosca.kvproperties.WinerysPropertiesDefinition; import org.eclipse.winery.model.tosca.visitor.Visitor; +import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonInclude; import io.github.adr.embedded.ADR; import org.eclipse.jdt.annotation.NonNull; @@ -62,6 +65,8 @@ @JsonInclude(JsonInclude.Include.NON_NULL) public abstract class TExtensibleElements implements Serializable { + public static final String NS_SUFFIX_PROPERTIESDEFINITION_WINERY = "propertiesdefinition/winery"; + protected List documentation; @XmlAnyElement(lax = true) @@ -69,7 +74,7 @@ public abstract class TExtensibleElements implements Serializable { @XmlAnyAttribute @NonNull - private Map otherAttributes = new HashMap(); + private Map otherAttributes = new HashMap<>(); public TExtensibleElements() { } @@ -102,7 +107,7 @@ public int hashCode() { @NonNull public List getDocumentation() { if (documentation == null) { - documentation = new ArrayList(); + documentation = new ArrayList<>(); } return this.documentation; } @@ -110,11 +115,60 @@ public List getDocumentation() { @NonNull public List getAny() { if (any == null) { - any = new ArrayList(); + any = new ArrayList<>(); } return this.any; } + /** + * This is a special method for Winery. Winery allows to define a property definition by specifying name/type + * values. Instead of parsing the extensible elements returned TDefinitions, this method is a convenience method to + * access this information + * + * @return a WinerysPropertiesDefinition object, which includes a map of name/type-pairs denoting the associated + * property definitions. A default element name and namespace is added if it is not defined in the underlying XML. + * null if no Winery specific KV properties are defined for the given entity type + */ + @XmlTransient + @JsonIgnore + public WinerysPropertiesDefinition getWinerysPropertiesDefinition() { + // similar implementation as org.eclipse.winery.repository.resources.entitytypes.properties.PropertiesDefinitionResource.getListFromEntityType(TEntityType) + WinerysPropertiesDefinition res = null; + for (Object o : this.getAny()) { + if (o instanceof WinerysPropertiesDefinition) { + res = (WinerysPropertiesDefinition) o; + } + } + + if (res != null) { + // we put defaults if elementname and namespace have not been set + setWPDElement(res); + setWPDNamespace(res); + } + + return res; + } + + private void setWPDElement(WinerysPropertiesDefinition res) { + if (res.getElementName() == null) { + res.setElementName("Properties"); + } + } + + private void setWPDNamespace(WinerysPropertiesDefinition res) { + if (Objects.isNull(res.getNamespace())) { + if (this instanceof HasTargetNamespace) { + // we use the targetnamespace of the original element + String ns = ((HasTargetNamespace) this).getTargetNamespace(); + if (!ns.endsWith("/")) { + ns += "/"; + } + ns += NS_SUFFIX_PROPERTIESDEFINITION_WINERY; + res.setNamespace(ns); + } + } + } + @NonNull public Map getOtherAttributes() { return otherAttributes; diff --git a/org.eclipse.winery.model.tosca/src/main/java/org/eclipse/winery/model/tosca/TServiceTemplate.java b/org.eclipse.winery.model.tosca/src/main/java/org/eclipse/winery/model/tosca/TServiceTemplate.java index 85345b58be..a0043e576c 100644 --- a/org.eclipse.winery.model.tosca/src/main/java/org/eclipse/winery/model/tosca/TServiceTemplate.java +++ b/org.eclipse.winery.model.tosca/src/main/java/org/eclipse/winery/model/tosca/TServiceTemplate.java @@ -25,6 +25,8 @@ import javax.xml.bind.annotation.XmlType; import javax.xml.namespace.QName; +import org.eclipse.winery.model.tosca.constants.Namespaces; +import org.eclipse.winery.model.tosca.kvproperties.WinerysPropertiesDefinition; import org.eclipse.winery.model.tosca.utils.RemoveEmptyLists; import org.eclipse.winery.model.tosca.visitor.Visitor; @@ -32,12 +34,16 @@ @XmlAccessorType(XmlAccessType.FIELD) @XmlType(name = "tServiceTemplate", propOrder = { + "propertiesDefinition", "tags", "boundaryDefinitions", "topologyTemplate", "plans" }) public class TServiceTemplate extends HasId implements HasName, HasTargetNamespace { + + @XmlElement(name = "BoundaryDefPropertiesDefinition", namespace = Namespaces.TOSCA_WINERY_EXTENSIONS_NAMESPACE) + protected TServiceTemplate.PropertiesDefinition propertiesDefinition; @XmlElement(name = "Tags") protected TTags tags; @@ -66,6 +72,7 @@ public TServiceTemplate() { public TServiceTemplate(Builder builder) { super(builder); + this.propertiesDefinition = builder.propertiesDefinition; this.tags = builder.tags; this.boundaryDefinitions = builder.boundaryDefinitions; this.topologyTemplate = builder.topologyTemplate; @@ -81,7 +88,8 @@ public boolean equals(Object o) { if (!(o instanceof TServiceTemplate)) return false; if (!super.equals(o)) return false; TServiceTemplate that = (TServiceTemplate) o; - return Objects.equals(tags, that.tags) && + return Objects.equals(propertiesDefinition, that.propertiesDefinition) && + Objects.equals(tags, that.tags) && Objects.equals(boundaryDefinitions, that.boundaryDefinitions) && Objects.equals(topologyTemplate, that.topologyTemplate) && Objects.equals(plans, that.plans) && @@ -92,7 +100,7 @@ public boolean equals(Object o) { @Override public int hashCode() { - return Objects.hash(super.hashCode(), tags, boundaryDefinitions, topologyTemplate, plans, name, targetNamespace, substitutableNodeType); + return Objects.hash(super.hashCode(), propertiesDefinition, tags, boundaryDefinitions, topologyTemplate, plans, name, targetNamespace, substitutableNodeType); } @Nullable @@ -169,11 +177,41 @@ public void accept(Visitor visitor) { visitor.visit(this); } + public TServiceTemplate.@Nullable PropertiesDefinition getPropertiesDefinition() { + return propertiesDefinition; + } + + public void setPropertiesDefinition(TServiceTemplate.@Nullable PropertiesDefinition value) { + this.propertiesDefinition = value; + } + + /** + *

Java class for anonymous complex type. + *

+ *

The following schema fragment specifies the expected content contained within this class. + *

+ *

+     * <complexType>
+     *   <complexContent>
+     *     <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+     *       <attribute name="element" type="{http://www.w3.org/2001/XMLSchema}QName" />
+     *       <attribute name="type" type="{http://www.w3.org/2001/XMLSchema}QName" />
+     *     </restriction>
+     *   </complexContent>
+     * </complexType>
+     * 
+ */ + @XmlAccessorType(XmlAccessType.FIELD) + @XmlType(name = "") + public static class PropertiesDefinition extends TEntityType.PropertiesDefinition { + } + public static class Builder extends HasId.Builder { private final TTopologyTemplate topologyTemplate; private TTags tags; private TBoundaryDefinitions boundaryDefinitions; + private TServiceTemplate.PropertiesDefinition propertiesDefinition; private TPlans plans; private String name; private String targetNamespace; @@ -255,5 +293,10 @@ public Builder self() { public TServiceTemplate build() { return new TServiceTemplate(this); } + + public Builder setPropertiesDefinition(PropertiesDefinition propertiesDefinition) { + this.propertiesDefinition = propertiesDefinition; + return this; + } } } diff --git a/org.eclipse.winery.model.tosca/src/main/java/org/eclipse/winery/model/tosca/utils/ModelUtilities.java b/org.eclipse.winery.model.tosca/src/main/java/org/eclipse/winery/model/tosca/utils/ModelUtilities.java index df3fa2f864..11717c1275 100644 --- a/org.eclipse.winery.model.tosca/src/main/java/org/eclipse/winery/model/tosca/utils/ModelUtilities.java +++ b/org.eclipse.winery.model.tosca/src/main/java/org/eclipse/winery/model/tosca/utils/ModelUtilities.java @@ -181,7 +181,7 @@ public static Document getWinerysPropertiesDefinitionXsdAsDocument(WinerysProper * Removes an existing Winery's Properties definition. If no such definition exists, the TEntityType is not * modified */ - public static void removeWinerysPropertiesDefinition(TEntityType et) { + public static void removeWinerysPropertiesDefinition(TExtensibleElements et) { for (Iterator iterator = et.getAny().iterator(); iterator.hasNext(); ) { Object o = iterator.next(); if (o instanceof WinerysPropertiesDefinition) { @@ -191,7 +191,7 @@ public static void removeWinerysPropertiesDefinition(TEntityType et) { } } - public static void replaceWinerysPropertiesDefinition(TEntityType et, WinerysPropertiesDefinition wpd) { + public static void replaceWinerysPropertiesDefinition(TExtensibleElements et, WinerysPropertiesDefinition wpd) { ModelUtilities.removeWinerysPropertiesDefinition(et); et.getAny().add(wpd); } diff --git a/org.eclipse.winery.repository.rest/src/main/java/org/eclipse/winery/repository/rest/resources/entitytemplates/PropertiesResource.java b/org.eclipse.winery.repository.rest/src/main/java/org/eclipse/winery/repository/rest/resources/entitytemplates/PropertiesResource.java index e9cda41249..4a2322cdeb 100644 --- a/org.eclipse.winery.repository.rest/src/main/java/org/eclipse/winery/repository/rest/resources/entitytemplates/PropertiesResource.java +++ b/org.eclipse.winery.repository.rest/src/main/java/org/eclipse/winery/repository/rest/resources/entitytemplates/PropertiesResource.java @@ -13,9 +13,16 @@ *******************************************************************************/ package org.eclipse.winery.repository.rest.resources.entitytemplates; -import io.github.adr.embedded.ADR; -import org.eclipse.jdt.annotation.NonNull; -import org.eclipse.jdt.annotation.Nullable; +import java.util.Map; + +import javax.ws.rs.Consumes; +import javax.ws.rs.GET; +import javax.ws.rs.PUT; +import javax.ws.rs.Produces; +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; + import org.eclipse.winery.model.tosca.TEntityTemplate; import org.eclipse.winery.model.tosca.TEntityType; import org.eclipse.winery.model.tosca.kvproperties.WinerysPropertiesDefinition; @@ -23,14 +30,13 @@ import org.eclipse.winery.repository.backend.RepositoryFactory; import org.eclipse.winery.repository.rest.RestUtils; import org.eclipse.winery.repository.rest.resources._support.AbstractComponentInstanceResource; + +import io.github.adr.embedded.ADR; +import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import javax.ws.rs.*; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; -import java.util.Map; - public class PropertiesResource { private static final Logger LOGGER = LoggerFactory.getLogger(PropertiesResource.class); diff --git a/org.eclipse.winery.repository.rest/src/main/java/org/eclipse/winery/repository/rest/resources/entitytypes/properties/PropertiesDefinitionResource.java b/org.eclipse.winery.repository.rest/src/main/java/org/eclipse/winery/repository/rest/resources/entitytypes/properties/PropertiesDefinitionResource.java index 7583dce42d..2370d94a97 100644 --- a/org.eclipse.winery.repository.rest/src/main/java/org/eclipse/winery/repository/rest/resources/entitytypes/properties/PropertiesDefinitionResource.java +++ b/org.eclipse.winery.repository.rest/src/main/java/org/eclipse/winery/repository/rest/resources/entitytypes/properties/PropertiesDefinitionResource.java @@ -56,20 +56,23 @@ * {@link TEntityType.PropertiesDefinition} */ public class PropertiesDefinitionResource { - private static final Logger LOGGER = LoggerFactory.getLogger(PropertiesDefinitionResource.class); - // We hold a copy of super.res as we work on the type EntityTypeResource instead of AbstractComponentInstanceResource - private final EntityTypeResource parentRes; - // we assume that this class is created at each request // therefore, we can have "wpd" final - private final WinerysPropertiesDefinition wpd; + protected final WinerysPropertiesDefinition wpd; + // We hold a copy of super.res as we work on the type EntityTypeResource instead of AbstractComponentInstanceResource + private final EntityTypeResource parentRes; public PropertiesDefinitionResource(EntityTypeResource res) { this.parentRes = res; - this.wpd = ModelUtilities.getWinerysPropertiesDefinition(res.getEntityType()); + this.wpd = res.getEntityType().getWinerysPropertiesDefinition(); + } + + protected PropertiesDefinitionResource() { + wpd = null; + parentRes = null; } @GET diff --git a/org.eclipse.winery.repository.rest/src/main/java/org/eclipse/winery/repository/rest/resources/servicetemplates/ServiceTemplateResource.java b/org.eclipse.winery.repository.rest/src/main/java/org/eclipse/winery/repository/rest/resources/servicetemplates/ServiceTemplateResource.java index 3b14852a74..d7af454faf 100644 --- a/org.eclipse.winery.repository.rest/src/main/java/org/eclipse/winery/repository/rest/resources/servicetemplates/ServiceTemplateResource.java +++ b/org.eclipse.winery.repository.rest/src/main/java/org/eclipse/winery/repository/rest/resources/servicetemplates/ServiceTemplateResource.java @@ -63,6 +63,7 @@ import org.eclipse.winery.repository.rest.resources.apiData.QNameApiData; import org.eclipse.winery.repository.rest.resources.servicetemplates.boundarydefinitions.BoundaryDefinitionsResource; import org.eclipse.winery.repository.rest.resources.servicetemplates.plans.PlansResource; +import org.eclipse.winery.repository.rest.resources.servicetemplates.propertiesdefinition.BoundaryDefsPropertiesDefinitionResource; import org.eclipse.winery.repository.rest.resources.servicetemplates.selfserviceportal.SelfServicePortalResource; import org.eclipse.winery.repository.rest.resources.servicetemplates.topologytemplates.TopologyTemplateResource; import org.eclipse.winery.repository.splitting.InjectRemoval; @@ -133,6 +134,11 @@ public BoundaryDefinitionsResource getBoundaryDefinitionsResource() { return new BoundaryDefinitionsResource(this, boundaryDefinitions); } + @Path("propertiesdefinition/") + public BoundaryDefsPropertiesDefinitionResource getJson() { + return new BoundaryDefsPropertiesDefinitionResource(this); + } + @Override public String getName() { String name = this.getServiceTemplate().getName(); diff --git a/org.eclipse.winery.repository.rest/src/main/java/org/eclipse/winery/repository/rest/resources/servicetemplates/boundarydefinitions/BoundaryDefinitionsJSPData.java b/org.eclipse.winery.repository.rest/src/main/java/org/eclipse/winery/repository/rest/resources/servicetemplates/boundarydefinitions/BoundaryDefinitionsJSPData.java index fa1aa1263f..6e665da587 100644 --- a/org.eclipse.winery.repository.rest/src/main/java/org/eclipse/winery/repository/rest/resources/servicetemplates/boundarydefinitions/BoundaryDefinitionsJSPData.java +++ b/org.eclipse.winery.repository.rest/src/main/java/org/eclipse/winery/repository/rest/resources/servicetemplates/boundarydefinitions/BoundaryDefinitionsJSPData.java @@ -23,7 +23,6 @@ import org.eclipse.winery.common.ids.definitions.PolicyTypeId; import org.eclipse.winery.model.tosca.TBoundaryDefinitions; -import org.eclipse.winery.model.tosca.TBoundaryDefinitions.Properties; import org.eclipse.winery.model.tosca.TPlan; import org.eclipse.winery.model.tosca.TPlans; import org.eclipse.winery.model.tosca.TServiceTemplate; @@ -54,7 +53,7 @@ public BoundaryDefinitionsJSPData(TServiceTemplate ste, URI baseURI) { } private String getDefinedProperties() { - Properties p = ModelUtilities.getProperties(this.defs); + TBoundaryDefinitions.Properties p = ModelUtilities.getProperties(this.defs); Object o = p.getAny(); if (o == null) { // nothing stored -> return empty string diff --git a/org.eclipse.winery.repository.rest/src/main/java/org/eclipse/winery/repository/rest/resources/servicetemplates/boundarydefinitions/BoundaryDefinitionsPropertiesResource.java b/org.eclipse.winery.repository.rest/src/main/java/org/eclipse/winery/repository/rest/resources/servicetemplates/boundarydefinitions/BoundaryDefinitionsPropertiesResource.java new file mode 100644 index 0000000000..2fb67ef7ab --- /dev/null +++ b/org.eclipse.winery.repository.rest/src/main/java/org/eclipse/winery/repository/rest/resources/servicetemplates/boundarydefinitions/BoundaryDefinitionsPropertiesResource.java @@ -0,0 +1,99 @@ +/******************************************************************************** + * Copyright (c) 2018 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, or the Apache Software License 2.0 + * which is available at https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 + *******************************************************************************/ + +package org.eclipse.winery.repository.rest.resources.servicetemplates.boundarydefinitions; + +import java.util.Map; + +import javax.ws.rs.Consumes; +import javax.ws.rs.GET; +import javax.ws.rs.PUT; +import javax.ws.rs.Produces; +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; + +import org.eclipse.winery.model.tosca.TBoundaryDefinitions; +import org.eclipse.winery.model.tosca.TServiceTemplate; +import org.eclipse.winery.model.tosca.kvproperties.WinerysPropertiesDefinition; +import org.eclipse.winery.model.tosca.utils.ModelUtilities; +import org.eclipse.winery.repository.backend.BackendUtils; +import org.eclipse.winery.repository.rest.RestUtils; +import org.eclipse.winery.repository.rest.resources._support.AbstractComponentInstanceResource; +import org.eclipse.winery.repository.rest.resources.servicetemplates.ServiceTemplateResource; + +import io.github.adr.embedded.ADR; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import org.w3c.dom.Document; + +public class BoundaryDefinitionsPropertiesResource { + + private AbstractComponentInstanceResource res; + private TServiceTemplate template; + + public BoundaryDefinitionsPropertiesResource(AbstractComponentInstanceResource res) { + this.template = ((ServiceTemplateResource) res).getServiceTemplate(); + this.res = res; + } + + @GET + @Produces( {MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON}) + public Response getProperties() { + WinerysPropertiesDefinition wpd = template.getWinerysPropertiesDefinition(); + TBoundaryDefinitions.Properties props = this.template.getBoundaryDefinitions().getProperties(); + if (wpd == null) { + // no Winery special treatment, just return the XML properties + if (props == null) { + return Response.ok().type(MediaType.APPLICATION_XML).build(); + } else { + try { + @ADR(6) + String xmlAsString = BackendUtils.getXMLAsString(TBoundaryDefinitions.Properties.class, props, true); + return Response + .ok() + .entity(xmlAsString) + .type(MediaType.APPLICATION_XML) + .build(); + } catch (Exception e) { + throw new WebApplicationException(e); + } + } + } else { + Map kvProperties = this.template.getBoundaryDefinitions().getProperties().getKVProperties(); + return Response.ok().entity(kvProperties).type(MediaType.APPLICATION_JSON).build(); + } + } + + /* + * The well-formedness of the XML element is done using the framework. If you see [Fatal Error] :1:19: The + * prefix "tosca" for element "tosca:properties" is not bound. in the console, it is an indicator that the XML element is not well-formed. + */ + @PUT + @Consumes( {MediaType.APPLICATION_XML, MediaType.TEXT_XML}) + @ApiOperation(value = "saves properties of boundary definitions", notes = "Models the user-defined properties. The property mappings go into a separate resource propertymappings.") + public Response putProperties(@ApiParam(value = "Stored properties. The XSD allows a single element only. Therefore, we go for the contained element") Document doc) { + TBoundaryDefinitions.Properties properties = ModelUtilities.getProperties(this.template.getBoundaryDefinitions()); + properties.setAny(doc.getDocumentElement()); + return RestUtils.persist(res); + } + + @PUT + @Consumes( {MediaType.APPLICATION_JSON}) + @ApiOperation(value = "saves properties of boundary definitions", notes = "Models the user-defined properties. The property mappings go into a separate resource propertymappings.") + public Response putCustomProperties(@ApiParam(value = "Stored properties. The XSD allows a single element only. Therefore, we go for the contained element") Map props) { + this.template.getBoundaryDefinitions().getProperties().setKVProperties(props); + return RestUtils.persist(res); + } +} diff --git a/org.eclipse.winery.repository.rest/src/main/java/org/eclipse/winery/repository/rest/resources/servicetemplates/boundarydefinitions/BoundaryDefinitionsResource.java b/org.eclipse.winery.repository.rest/src/main/java/org/eclipse/winery/repository/rest/resources/servicetemplates/boundarydefinitions/BoundaryDefinitionsResource.java index 28ac35f898..4be1a2416a 100644 --- a/org.eclipse.winery.repository.rest/src/main/java/org/eclipse/winery/repository/rest/resources/servicetemplates/boundarydefinitions/BoundaryDefinitionsResource.java +++ b/org.eclipse.winery.repository.rest/src/main/java/org/eclipse/winery/repository/rest/resources/servicetemplates/boundarydefinitions/BoundaryDefinitionsResource.java @@ -35,7 +35,6 @@ import org.eclipse.winery.model.tosca.TBoundaryDefinitions.Requirements; import org.eclipse.winery.model.tosca.TCapabilityRef; import org.eclipse.winery.model.tosca.TRequirementRef; -import org.eclipse.winery.model.tosca.utils.ModelUtilities; import org.eclipse.winery.repository.rest.RestUtils; import org.eclipse.winery.repository.rest.resources.servicetemplates.ServiceTemplateResource; import org.eclipse.winery.repository.rest.resources.servicetemplates.boundarydefinitions.interfaces.InterfacesResource; @@ -44,15 +43,12 @@ import org.eclipse.winery.repository.rest.resources.servicetemplates.boundarydefinitions.reqscaps.RequirementsResource; import io.swagger.annotations.ApiOperation; -import io.swagger.annotations.ApiParam; -import org.w3c.dom.Document; public class BoundaryDefinitionsResource { private final ServiceTemplateResource serviceTemplateResource; private final TBoundaryDefinitions boundaryDefinitions; - public BoundaryDefinitionsResource(ServiceTemplateResource serviceTemplateResource, TBoundaryDefinitions boundaryDefinitions) { this.serviceTemplateResource = serviceTemplateResource; this.boundaryDefinitions = boundaryDefinitions; @@ -80,24 +76,9 @@ public Response setModel(TBoundaryDefinitions boundaryDefinitions) { } @Path("properties/") - @GET - @Produces(MediaType.APPLICATION_XML) - public String getProperties(@Context UriInfo uriInfo) { - return new BoundaryDefinitionsJSPData(this.serviceTemplateResource.getServiceTemplate(), uriInfo.getBaseUri()).getPropertiesAsXMLString(); - } - - /** - * The well-formedness of the XML element is done using the framework. If you see [Fatal Error] :1:19: The - * prefix "tosca" for element "tosca:properties" is not bound. in the console, it is an indicator that the XML element is not well-formed. - */ - @Path("properties/") - @PUT - @Consumes( {MediaType.APPLICATION_XML, MediaType.TEXT_XML}) - @ApiOperation(value = "saves properties of boundary definitions", notes = "Models the user-defined properties. The property mappings go into a separate resource propertymappings.") - public Response putProperties(@ApiParam(value = "Stored properties. The XSD allows a single element only. Therefore, we go for the contained element") Document doc) { - org.eclipse.winery.model.tosca.TBoundaryDefinitions.Properties properties = ModelUtilities.getProperties(this.boundaryDefinitions); - properties.setAny(doc.getDocumentElement()); - return RestUtils.persist(this.serviceTemplateResource); + public BoundaryDefinitionsPropertiesResource getProperties(@Context UriInfo uriInfo) { + return new BoundaryDefinitionsPropertiesResource(this.serviceTemplateResource); + //return new BoundaryDefinitionsJSPData(this.serviceTemplateResource.getServiceTemplate(), uriInfo.getBaseUri()).getPropertiesAsXMLString(); } @Path("requirements/") diff --git a/org.eclipse.winery.repository.rest/src/main/java/org/eclipse/winery/repository/rest/resources/servicetemplates/propertiesdefinition/BoundaryDefsPropertiesDefinitionResource.java b/org.eclipse.winery.repository.rest/src/main/java/org/eclipse/winery/repository/rest/resources/servicetemplates/propertiesdefinition/BoundaryDefsPropertiesDefinitionResource.java new file mode 100644 index 0000000000..05a5de526c --- /dev/null +++ b/org.eclipse.winery.repository.rest/src/main/java/org/eclipse/winery/repository/rest/resources/servicetemplates/propertiesdefinition/BoundaryDefsPropertiesDefinitionResource.java @@ -0,0 +1,142 @@ +/******************************************************************************** + * Copyright (c) 2018 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, or the Apache Software License 2.0 + * which is available at https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 + *******************************************************************************/ + +package org.eclipse.winery.repository.rest.resources.servicetemplates.propertiesdefinition; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.stream.Stream; + +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; + +import org.eclipse.winery.model.tosca.TServiceTemplate; +import org.eclipse.winery.model.tosca.kvproperties.WinerysPropertiesDefinition; +import org.eclipse.winery.model.tosca.utils.ModelUtilities; +import org.eclipse.winery.repository.backend.BackendUtils; +import org.eclipse.winery.repository.backend.NamespaceManager; +import org.eclipse.winery.repository.backend.RepositoryFactory; +import org.eclipse.winery.repository.rest.RestUtils; +import org.eclipse.winery.repository.rest.resources.apiData.PropertiesDefinitionEnum; +import org.eclipse.winery.repository.rest.resources.apiData.PropertiesDefinitionResourceApiData; +import org.eclipse.winery.repository.rest.resources.entitytypes.properties.PropertiesDefinitionResource; +import org.eclipse.winery.repository.rest.resources.servicetemplates.ServiceTemplateResource; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class BoundaryDefsPropertiesDefinitionResource extends PropertiesDefinitionResource { + private static final Logger LOGGER = LoggerFactory.getLogger(BoundaryDefsPropertiesDefinitionResource.class); + + private final ServiceTemplateResource parentRes; + private final WinerysPropertiesDefinition wpd; + + public BoundaryDefsPropertiesDefinitionResource(ServiceTemplateResource res) { + this.parentRes = res; + this.wpd = res.getElement().getWinerysPropertiesDefinition(); + } + + @GET + @Produces(MediaType.APPLICATION_JSON) + public PropertiesDefinitionResourceApiData getJson() { + TServiceTemplate.PropertiesDefinition definition = this.parentRes.getServiceTemplate().getPropertiesDefinition(); + return new PropertiesDefinitionResourceApiData(definition, this.wpd); + } + + @DELETE + public Response clearPropertiesDefinition() { + TServiceTemplate st = this.parentRes.getServiceTemplate(); + st.setPropertiesDefinition(null); + if (Objects.nonNull(st.getBoundaryDefinitions())) { + if (Stream.of( + st.getBoundaryDefinitions().getPolicies(), + st.getBoundaryDefinitions().getCapabilities(), + st.getBoundaryDefinitions().getPropertyConstraints(), + st.getBoundaryDefinitions().getRequirements(), + st.getBoundaryDefinitions().getInterfaces()) + .allMatch(Objects::isNull)) { + st.setBoundaryDefinitions(null); + } else { + st.getBoundaryDefinitions().setProperties(null); + } + } + ModelUtilities.removeWinerysPropertiesDefinition(this.parentRes.getServiceTemplate()); + return RestUtils.persist(this.parentRes); + } + + @POST + @Consumes(MediaType.APPLICATION_JSON) + public Response onJsonPost(PropertiesDefinitionResourceApiData data) { + TServiceTemplate st = this.parentRes.getServiceTemplate(); + + if (data.selectedValue == PropertiesDefinitionEnum.Element || data.selectedValue == PropertiesDefinitionEnum.Type) { + if (Objects.nonNull(st.getBoundaryDefinitions())) { + st.getBoundaryDefinitions().setProperties(null); + } + // first of all, remove Winery's Properties definition (if it exists) + ModelUtilities.removeWinerysPropertiesDefinition(st); + // replace old properties definition by new one + TServiceTemplate.PropertiesDefinition def = new TServiceTemplate.PropertiesDefinition(); + + if (data.propertiesDefinition.getElement() != null) { + def.setElement(data.propertiesDefinition.getElement()); + } else if (data.propertiesDefinition.getType() != null) { + def.setType(data.propertiesDefinition.getType()); + } else { + return Response.status(Response.Status.BAD_REQUEST).entity("Wrong data submitted!").build(); + } + + st.setPropertiesDefinition(def); + List errors = new ArrayList<>(); + BackendUtils.deriveWPD(st, errors); + // currently the errors are just logged + for (String error : errors) { + LOGGER.debug(error); + } + BackendUtils.initializeProperties(RepositoryFactory.getRepository(), st); + + return RestUtils.persist(this.parentRes); + } else if (data.selectedValue == PropertiesDefinitionEnum.Custom) { + // clear current properties definition + st.setPropertiesDefinition(null); + + if (!data.winerysPropertiesDefinition.getPropertyDefinitionKVList().getPropertyDefinitionKVs().isEmpty()) { + // create winery properties definition and persist it + ModelUtilities.replaceWinerysPropertiesDefinition(st, data.winerysPropertiesDefinition); + String namespace = data.winerysPropertiesDefinition.getNamespace(); + NamespaceManager namespaceManager = RepositoryFactory.getRepository().getNamespaceManager(); + if (!namespaceManager.hasPermanentProperties(namespace)) { + namespaceManager.addPermanentNamespace(namespace); + } + + BackendUtils.initializeProperties(RepositoryFactory.getRepository(), st); + } + else { + return Response.status(Response.Status.BAD_REQUEST) + .entity("Empty KV Properties Definition is not allowed!") + .build(); + } + + return RestUtils.persist(this.parentRes); + } + + return Response.status(Response.Status.BAD_REQUEST).entity("Wrong data submitted!").build(); + } +} diff --git a/org.eclipse.winery.repository/src/main/java/org/eclipse/winery/repository/backend/BackendUtils.java b/org.eclipse.winery.repository/src/main/java/org/eclipse/winery/repository/backend/BackendUtils.java index 34a53c0c82..4d443eacee 100644 --- a/org.eclipse.winery.repository/src/main/java/org/eclipse/winery/repository/backend/BackendUtils.java +++ b/org.eclipse.winery.repository/src/main/java/org/eclipse/winery/repository/backend/BackendUtils.java @@ -100,6 +100,7 @@ import org.eclipse.winery.model.tosca.TArtifactReference; import org.eclipse.winery.model.tosca.TArtifactTemplate; import org.eclipse.winery.model.tosca.TArtifactType; +import org.eclipse.winery.model.tosca.TBoundaryDefinitions; import org.eclipse.winery.model.tosca.TCapability; import org.eclipse.winery.model.tosca.TCapabilityType; import org.eclipse.winery.model.tosca.TComplianceRule; @@ -121,6 +122,7 @@ import org.eclipse.winery.model.tosca.TPlans; import org.eclipse.winery.model.tosca.TPolicyTemplate; import org.eclipse.winery.model.tosca.TPolicyType; +import org.eclipse.winery.model.tosca.TPropertyMapping; import org.eclipse.winery.model.tosca.TRelationshipTemplate; import org.eclipse.winery.model.tosca.TRelationshipType; import org.eclipse.winery.model.tosca.TRelationshipTypeImplementation; @@ -304,7 +306,7 @@ public static String getName(DefinitionsChildId instanceId) throws RepositoryCor } /** - * Do not use this for creating URLs. Use {@link Utils#getURLforPathInsideRepo(java.lang.String)} or + * Do not use this for creating URLs. Use {@link Utils.getURLforPathInsideRepo(java.lang.String)} or * {@link Utils#getAbsoluteURL(org.eclipse.winery.common.ids.GenericId) instead. * * @return the path starting from the root element to the current element. Separated by "/", URLencoded, but @@ -664,17 +666,32 @@ public static Definitions createWrapperDefinitionsAndInitialEmptyElement(IReposi /** * Properties need to be initialized in the case of K/V Properties * - * @param repository The repository to work on - * @param entityTemplate the entity template to update + * @param repository The repository to work on + * @param extEl The entity template or service template to update */ - public static void initializeProperties(IRepository repository, TEntityTemplate entityTemplate) { + public static void initializeProperties(IRepository repository, TExtensibleElements extEl) { Objects.requireNonNull(repository); - Objects.requireNonNull(entityTemplate); + Objects.requireNonNull(extEl); - Objects.requireNonNull(entityTemplate.getType()); + if (extEl instanceof TEntityTemplate) { + Objects.requireNonNull(((TEntityTemplate) extEl).getType()); + } + + WinerysPropertiesDefinition winerysPropertiesDefinition; + Map existingKVProperties = null; + List addedPropertyNames = new ArrayList<>(); + if (extEl instanceof TEntityTemplate) { + final TEntityType entityType = repository.getTypeForTemplate(((TEntityTemplate) extEl)); + winerysPropertiesDefinition = entityType.getWinerysPropertiesDefinition(); + } else { + // serviceTemplate allows direct access to WinerysPropertiesDefinition + winerysPropertiesDefinition = extEl.getWinerysPropertiesDefinition(); + TServiceTemplate st = ((TServiceTemplate) extEl); + if (Objects.nonNull(st.getBoundaryDefinitions()) && Objects.nonNull(st.getBoundaryDefinitions().getProperties())) { + existingKVProperties = st.getBoundaryDefinitions().getProperties().getKVProperties(); + } + } - final TEntityType entityType = repository.getTypeForTemplate(entityTemplate); - final WinerysPropertiesDefinition winerysPropertiesDefinition = entityType.getWinerysPropertiesDefinition(); if (winerysPropertiesDefinition == null) { return; } @@ -694,12 +711,46 @@ public static void initializeProperties(IRepository repository, TEntityTemplate for (PropertyDefinitionKV propertyDefinitionKV : winerysPropertiesDefinition.getPropertyDefinitionKVList()) { // we always write the element tag as the XSD forces that final Element valueElement = document.createElementNS(namespace, propertyDefinitionKV.getKey()); + + if (Objects.nonNull(existingKVProperties)) { + if (existingKVProperties.containsKey(propertyDefinitionKV.getKey())) { + valueElement.setTextContent(existingKVProperties.get(propertyDefinitionKV.getKey())); + } + } wrapperElement.appendChild(valueElement); + addedPropertyNames.add(propertyDefinitionKV.getKey()); } - TEntityTemplate.Properties properties = new TEntityTemplate.Properties(); - properties.setAny(document.getDocumentElement()); - entityTemplate.setProperties(properties); + if (extEl instanceof TEntityTemplate) { + TEntityTemplate.Properties properties = new TEntityTemplate.Properties(); + properties.setAny(document.getDocumentElement()); + ((TEntityTemplate) extEl).setProperties(properties); + } else { + TServiceTemplate st = ((TServiceTemplate) extEl); + TBoundaryDefinitions bd = st.getBoundaryDefinitions(); + + TBoundaryDefinitions.Properties properties = new TBoundaryDefinitions.Properties(); + properties.setAny(document.getDocumentElement()); + + if (Objects.nonNull(bd) && Objects.nonNull(bd.getProperties()) + && Objects.nonNull(bd.getProperties().getPropertyMappings())) { + + TBoundaryDefinitions.Properties.PropertyMappings propertyMappings = new TBoundaryDefinitions.Properties.PropertyMappings(); + for (TPropertyMapping mapping : bd.getProperties().getPropertyMappings().getPropertyMapping()) { + if (addedPropertyNames.contains(mapping.getServiceTemplatePropertyRef())) { + propertyMappings.getPropertyMapping().add(mapping); + } + } + properties.setPropertyMappings(propertyMappings); + } + + if (Objects.nonNull(bd)) { + st.getBoundaryDefinitions().setProperties(properties); + } else { + st.setBoundaryDefinitions(new TBoundaryDefinitions()); + st.getBoundaryDefinitions().setProperties(properties); + } + } } /** @@ -850,72 +901,83 @@ public String getBaseURI() { * @param ci the entity type to try to modify the WPDs * @param errors the list to add errors to */ - public static void deriveWPD(TEntityType ci, List errors) { + public static void deriveWPD(TExtensibleElements ci, List errors) { BackendUtils.LOGGER.trace("deriveWPD"); - PropertiesDefinition propertiesDefinition = ci.getPropertiesDefinition(); - QName element = propertiesDefinition.getElement(); - if (element == null) { - BackendUtils.LOGGER.debug("only works for an element definition, not for types"); - } else { - BackendUtils.LOGGER.debug("Looking for the definition of {" + element.getNamespaceURI() + "}" + element.getLocalPart()); - // fetch the XSD defining the element - final XsdImportManager xsdImportManager = RepositoryFactory.getRepository().getXsdImportManager(); - Map mapFromLocalNameToXSD = xsdImportManager.getMapFromLocalNameToXSD(new Namespace(element.getNamespaceURI(), false), false); - RepositoryFileReference ref = mapFromLocalNameToXSD.get(element.getLocalPart()); - if (ref == null) { - String msg = "XSD not found for " + element.getNamespaceURI() + " / " + element.getLocalPart(); - BackendUtils.LOGGER.debug(msg); - errors.add(msg); - return; + if (ci instanceof TEntityType || ci instanceof TServiceTemplate) { + PropertiesDefinition propertiesDefinition; + if (ci instanceof TEntityType) { + propertiesDefinition = ((TEntityType) ci).getPropertiesDefinition(); + } else { + propertiesDefinition = ((TServiceTemplate) ci).getPropertiesDefinition(); } - final Optional xsModelOptional = BackendUtils.getXSModel(ref); - if (!xsModelOptional.isPresent()) { - LOGGER.error("no XSModel found"); - } - XSModel xsModel = xsModelOptional.get(); - XSElementDeclaration elementDeclaration = xsModel.getElementDeclaration(element.getLocalPart(), element.getNamespaceURI()); - if (elementDeclaration == null) { - String msg = "XSD model claimed to contain declaration for {" + element.getNamespaceURI() + "}" + element.getLocalPart() + ", but it did not."; - BackendUtils.LOGGER.debug(msg); - errors.add(msg); - return; - } + QName element = propertiesDefinition.getElement(); + if (element == null) { + BackendUtils.LOGGER.debug("only works for an element definition, not for types"); + } else { + BackendUtils.LOGGER.debug("Looking for the definition of {" + element.getNamespaceURI() + "}" + element.getLocalPart()); + // fetch the XSD defining the element + final XsdImportManager xsdImportManager = RepositoryFactory.getRepository().getXsdImportManager(); + Map mapFromLocalNameToXSD = xsdImportManager.getMapFromLocalNameToXSD(new Namespace(element.getNamespaceURI(), false), false); + RepositoryFileReference ref = mapFromLocalNameToXSD.get(element.getLocalPart()); + if (ref == null) { + String msg = "XSD not found for " + element.getNamespaceURI() + " / " + element.getLocalPart(); + BackendUtils.LOGGER.debug(msg); + errors.add(msg); + return; + } - // go through the XSD definition and - XSTypeDefinition typeDefinition = elementDeclaration.getTypeDefinition(); - if (typeDefinition instanceof XSComplexTypeDefinition) { - XSComplexTypeDefinition cTypeDefinition = (XSComplexTypeDefinition) typeDefinition; - XSParticle particle = cTypeDefinition.getParticle(); - if (particle == null) { - BackendUtils.LOGGER.debug("XSD does not follow the requirements put by winery: Complex type does not contain particles"); - } else { - XSTerm term = particle.getTerm(); - if (term instanceof XSModelGroup) { - XSModelGroup modelGroup = (XSModelGroup) term; - if (modelGroup.getCompositor() == XSModelGroup.COMPOSITOR_SEQUENCE) { - XSObjectList particles = modelGroup.getParticles(); - int len = particles.getLength(); - boolean everyThingIsASimpleType = true; - PropertyDefinitionKVList list = new PropertyDefinitionKVList(); - if (len != 0) { - for (int i = 0; i < len; i++) { - XSParticle innerParticle = (XSParticle) particles.item(i); - XSTerm innerTerm = innerParticle.getTerm(); - if (innerTerm instanceof XSElementDeclaration) { - XSElementDeclaration innerElementDeclaration = (XSElementDeclaration) innerTerm; - String name = innerElementDeclaration.getName(); - XSTypeDefinition innerTypeDefinition = innerElementDeclaration.getTypeDefinition(); - if (innerTypeDefinition instanceof XSSimpleType) { - XSSimpleType xsSimpleType = (XSSimpleType) innerTypeDefinition; - String typeNS = xsSimpleType.getNamespace(); - String typeName = xsSimpleType.getName(); - if (typeNS.equals(XMLConstants.W3C_XML_SCHEMA_NS_URI)) { - PropertyDefinitionKV def = new PropertyDefinitionKV(); - def.setKey(name); - // convention at WPD: use "xsd" as prefix for XML Schema Definition - def.setType("xsd:" + typeName); - list.add(def); + final Optional xsModelOptional = BackendUtils.getXSModel(ref); + if (!xsModelOptional.isPresent()) { + LOGGER.error("no XSModel found"); + } + XSModel xsModel = xsModelOptional.get(); + XSElementDeclaration elementDeclaration = xsModel.getElementDeclaration(element.getLocalPart(), element.getNamespaceURI()); + if (elementDeclaration == null) { + String msg = "XSD model claimed to contain declaration for {" + element.getNamespaceURI() + "}" + element.getLocalPart() + ", but it did not."; + BackendUtils.LOGGER.debug(msg); + errors.add(msg); + return; + } + + // go through the XSD definition and + XSTypeDefinition typeDefinition = elementDeclaration.getTypeDefinition(); + if (typeDefinition instanceof XSComplexTypeDefinition) { + XSComplexTypeDefinition cTypeDefinition = (XSComplexTypeDefinition) typeDefinition; + XSParticle particle = cTypeDefinition.getParticle(); + if (particle == null) { + BackendUtils.LOGGER.debug("XSD does not follow the requirements put by winery: Complex type does not contain particles"); + } else { + XSTerm term = particle.getTerm(); + if (term instanceof XSModelGroup) { + XSModelGroup modelGroup = (XSModelGroup) term; + if (modelGroup.getCompositor() == XSModelGroup.COMPOSITOR_SEQUENCE) { + XSObjectList particles = modelGroup.getParticles(); + int len = particles.getLength(); + boolean everyThingIsASimpleType = true; + PropertyDefinitionKVList list = new PropertyDefinitionKVList(); + if (len != 0) { + for (int i = 0; i < len; i++) { + XSParticle innerParticle = (XSParticle) particles.item(i); + XSTerm innerTerm = innerParticle.getTerm(); + if (innerTerm instanceof XSElementDeclaration) { + XSElementDeclaration innerElementDeclaration = (XSElementDeclaration) innerTerm; + String name = innerElementDeclaration.getName(); + XSTypeDefinition innerTypeDefinition = innerElementDeclaration.getTypeDefinition(); + if (innerTypeDefinition instanceof XSSimpleType) { + XSSimpleType xsSimpleType = (XSSimpleType) innerTypeDefinition; + String typeNS = xsSimpleType.getNamespace(); + String typeName = xsSimpleType.getName(); + if (typeNS.equals(XMLConstants.W3C_XML_SCHEMA_NS_URI)) { + PropertyDefinitionKV def = new PropertyDefinitionKV(); + def.setKey(name); + // convention at WPD: use "xsd" as prefix for XML Schema Definition + def.setType("xsd:" + typeName); + list.add(def); + } else { + everyThingIsASimpleType = false; + break; + } } else { everyThingIsASimpleType = false; break; @@ -924,33 +986,30 @@ public static void deriveWPD(TEntityType ci, List errors) { everyThingIsASimpleType = false; break; } - } else { - everyThingIsASimpleType = false; - break; } } - } - if (everyThingIsASimpleType) { - // everything went allright, we can add a WPD - WinerysPropertiesDefinition wpd = new WinerysPropertiesDefinition(); - wpd.setIsDerivedFromXSD(Boolean.TRUE); - wpd.setElementName(element.getLocalPart()); - wpd.setNamespace(element.getNamespaceURI()); - wpd.setPropertyDefinitionKVList(list); - ModelUtilities.replaceWinerysPropertiesDefinition(ci, wpd); - BackendUtils.LOGGER.debug("Successfully generated WPD"); + if (everyThingIsASimpleType) { + // everything went allright, we can add a WPD + WinerysPropertiesDefinition wpd = new WinerysPropertiesDefinition(); + wpd.setIsDerivedFromXSD(Boolean.TRUE); + wpd.setElementName(element.getLocalPart()); + wpd.setNamespace(element.getNamespaceURI()); + wpd.setPropertyDefinitionKVList(list); + ModelUtilities.replaceWinerysPropertiesDefinition(ci, wpd); + BackendUtils.LOGGER.debug("Successfully generated WPD"); + } else { + BackendUtils.LOGGER.debug("XSD does not follow the requirements put by winery: Not all types in the sequence are simple types"); + } } else { - BackendUtils.LOGGER.debug("XSD does not follow the requirements put by winery: Not all types in the sequence are simple types"); + BackendUtils.LOGGER.debug("XSD does not follow the requirements put by winery: Model group is not a sequence"); } } else { - BackendUtils.LOGGER.debug("XSD does not follow the requirements put by winery: Model group is not a sequence"); + BackendUtils.LOGGER.debug("XSD does not follow the requirements put by winery: Not a model group"); } - } else { - BackendUtils.LOGGER.debug("XSD does not follow the requirements put by winery: Not a model group"); } + } else { + BackendUtils.LOGGER.debug("XSD does not follow the requirements put by winery: No Complex Type Definition"); } - } else { - BackendUtils.LOGGER.debug("XSD does not follow the requirements put by winery: No Complex Type Definition"); } } }