Solving unmarshalling/marshalling problems...

This commit is contained in:
Maria Teresa Paratore 2024-05-02 16:49:49 +02:00
parent 1a27b8a43e
commit 1e9cc50909
37 changed files with 1453 additions and 451 deletions

View File

@ -285,6 +285,12 @@
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
<!-- GSon -->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
</dependency>
<dependency>
<groupId>org.gcube.information-system</groupId>

View File

@ -2,11 +2,14 @@ package org.gcube.informationsystem.service;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import org.apache.commons.lang3.StringUtils;
import org.gcube.com.fasterxml.jackson.databind.ObjectMapper;
import org.gcube.com.fasterxml.jackson.databind.ObjectWriter;
import org.gcube.common.authorization.utils.manager.SecretManager;
@ -17,6 +20,8 @@ import org.gcube.informationsystem.base.reference.AccessType;
import org.gcube.informationsystem.contexts.reference.entities.Context;
import org.gcube.informationsystem.model.knowledge.ModelKnowledge;
import org.gcube.informationsystem.model.reference.entities.Resource;
import org.gcube.informationsystem.resourceregistry.api.exceptions.ResourceRegistryException;
import org.gcube.informationsystem.resourceregistry.api.exceptions.types.SchemaNotFoundException;
import org.gcube.informationsystem.resourceregistry.client.ResourceRegistryClient;
import org.gcube.informationsystem.resourceregistry.client.ResourceRegistryClientFactory;
import org.gcube.informationsystem.resourceregistry.publisher.ResourceRegistryPublisher;
@ -28,11 +33,14 @@ import org.gcube.informationsystem.service.dto.FacetPropertyDTO;
import org.gcube.informationsystem.service.dto.FacetSpecDTO;
import org.gcube.informationsystem.service.dto.FacetTypeDTO;
import org.gcube.informationsystem.service.dto.ResourceTypeDTO;
import org.gcube.informationsystem.service.dto.ValidationObjDTO;
import org.gcube.informationsystem.tree.Node;
import org.gcube.informationsystem.tree.Tree;
import org.gcube.informationsystem.types.PropertyTypeName;
import org.gcube.informationsystem.types.PropertyTypeName.BaseType;
import org.gcube.informationsystem.types.impl.entities.FacetTypeImpl;
import org.gcube.informationsystem.types.impl.entities.ResourceTypeImpl;
import org.gcube.informationsystem.types.impl.properties.PropertyDefinitionImpl;
import org.gcube.informationsystem.types.knowledge.TypeInformation;
import org.gcube.informationsystem.types.reference.Type;
import org.gcube.informationsystem.types.reference.properties.LinkedEntity;
@ -116,7 +124,6 @@ public class InformationSystemService {
ObjectMapper objectMapper = new ObjectMapper();
//resourceRegistryClient returns an array of one element
FacetTypeImpl ft = objectMapper.readValue(jsonResult, FacetTypeImpl[].class)[0];
ArrayList<FacetPropertyDTO> properties = new ArrayList<FacetPropertyDTO>();
ArrayList<FacetPropGui> propsGui = new ArrayList<FacetPropGui>();
FacetSpecDTO fsdto = new FacetSpecDTO();
fsdto.setName(ft.getName());
@ -136,18 +143,86 @@ public class InformationSystemService {
//prop.setReadonly(pd.isReadonly());
prop.setRegexp(pd.getRegexp());
prop.setType(pd.getTypeName());
propsGui.add(prop.toFacetPropGui());
propsGui.add(toFacetPropGui(prop, resourceRegistryClient));
}catch(Exception e) {
e.printStackTrace();
}
}
}
//fsdto.setProperties(properties);
fsdto.setGuiProps(propsGui);
return fsdto;
}
private FacetPropGui toFacetPropGui(FacetPropertyDTO fp, ResourceRegistryClient client) {
FacetPropGui prop = new FacetPropGui();
prop.setPropDescription(fp.getDescription());
prop.setName(fp.getName());
prop.setLabel(StringUtils.capitalize(fp.getName()));
prop.setValue("");
prop.setPattern(fp.getRegexp());
String tmp = "text";
String typeForClient = fp.getPropertyType();
switch(typeForClient) {
case "Boolean": tmp="boolean";
break;
case "Date": tmp="date";
break;
case "String": tmp="text";
break;
case "Long": tmp="number";
break;
case "Integer": tmp="number";
break;
//TODO: per ora uso "typeprop" come tipo speciale (ad es. per le authorization)
case "Property": tmp="typeprop";
break;
default: try {
tmp = client.getType(typeForClient, false);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
prop.setType(tmp);
ValidationObjDTO val = new ValidationObjDTO();
val.setMessage(tmp);
val.setName(tmp);
val.setValidator(tmp);
ArrayList<ValidationObjDTO> validations = new ArrayList<ValidationObjDTO>();
if(fp.isMandatory()) {
ValidationObjDTO vv = new ValidationObjDTO();
vv.setMessage("The field is required");
vv.setName("required");
vv.setValidator("required");
validations.add(vv);
}
//check if notnull is ok here
if(!fp.isMandatory() && fp.isNotnull()) {
ValidationObjDTO vv = new ValidationObjDTO();
vv.setMessage("The field cannot be empty");
vv.setName("notnull");
vv.setValidator("required");
validations.add(vv);
}
if(StringUtils.isNotEmpty(fp.getRegexp())) {
ValidationObjDTO vv = new ValidationObjDTO();
vv.setMessage("Wrong field format, required is: "+fp.getRegexp());
vv.setName("pattern");
vv.setValidator("pattern");
validations.add(vv);
}
prop.setValidations(validations);
return prop;
}
public ResourceTypeDTO getResourceType(String typeName) throws Exception {
String currentCtx = SecretManagerProvider.instance.get().getContext();
@ -166,7 +241,6 @@ public class InformationSystemService {
List<LinkedEntity> fcts = rt.getFacets();
if(fcts!=null) {
ArrayList<FacetTypeDTO> facetTypes = new ArrayList<FacetTypeDTO>();
//TODO: RIEMPI LE FACET
for(LinkedEntity le: fcts) {
FacetTypeDTO ftDto = new FacetTypeDTO(le.getRelation(),le.getTarget(),le.getDescription(),le.getMin(),le.getMax());
facetTypes.add(new FacetTypeDTO(le.getRelation(),le.getTarget(),le.getDescription(),le.getMin(),le.getMax()));
@ -253,11 +327,12 @@ public class InformationSystemService {
}
public ArrayList<ResourceTypeDTO> getResourceTypesTree() throws Exception {
public ArrayList<ResourceTypeDTO> getResourceTypesTree(String resourceTypeName ) throws Exception {
String currentCtx = SecretManagerProvider.instance.get().getContext();
ResourceRegistryClient resourceRegistryClient= ResourceRegistryClientFactory.create(currentCtx);
ModelKnowledge <Type, TypeInformation> modelKnowledge = resourceRegistryClient.getModelKnowledge();
Type resourcesType = modelKnowledge.getTypeByName(AccessType.RESOURCE.getName());
//Type resourcesType = modelKnowledge.getTypeByName(AccessType.RESOURCE.getName());
Type resourcesType = modelKnowledge.getTypeByName(resourceTypeName);
Tree<Type> typeTree = modelKnowledge.getTree(resourcesType.getAccessType());
SimpleTreeMaker treeMaker = new SimpleTreeMaker();
typeTree.elaborate(treeMaker);
@ -404,17 +479,9 @@ public class InformationSystemService {
String currentCtx = SecretManagerProvider.instance.get().getContext();
ResourceRegistryPublisher publisher = ResourceRegistryPublisherFactory.create(currentCtx);
String res = publisher.createResource(jsonDescription);
System.out.println("*************RES*********");
System.out.println(res);
System.out.println("**********************");
}
//CRUD - 2
public void updateResourceInstance(String resourceType, String uuid) throws Exception {
String currentCtx = SecretManagerProvider.instance.get().getContext();
//TODO: uso la getResource (riga 206)? Però restituisce una String (JSON)...
//publisher.update(resourceTypeString, jsonString);
}
//CRUD - 3
public boolean deleteResourceInstance(String resourceType, String uuid) throws Exception {
@ -422,19 +489,9 @@ public class InformationSystemService {
ResourceRegistryPublisher publisher = ResourceRegistryPublisherFactory.create(currentCtx);
UUIDUtility uuidUtil = new UUIDUtility();
return publisher.deleteResource(resourceType, uuidUtil.fromString(uuid));
//TODO: GESTIRE ERRORI!
}
//CRUD - 4
//TODO: vedi se questo va passato da un REST di JHipster
public void retrieveResourceInstance(String resourceType) throws Exception {
String currentCtx = SecretManagerProvider.instance.get().getContext();
ResourceRegistryPublisher publisher = ResourceRegistryPublisherFactory.create(currentCtx);
//publisher.addResourceToContext(null, null, null)
//TODO: quale metodo usare per ottenere una risorsa partendo da UUID?
//publisher.delete(resourceType, uuidString);
}
}

View File

@ -0,0 +1,100 @@
package org.gcube.informationsystem.service;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.Set;
import org.gcube.informationsystem.service.dto.ResourceTypeDTO;
import org.gcube.informationsystem.tree.Node;
import org.gcube.informationsystem.tree.NodeElaborator;
import org.gcube.informationsystem.types.reference.Type;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class TypesTreeMaker implements NodeElaborator<Type> {
private static final Logger log = LoggerFactory.getLogger(TypesTreeMaker.class);
private ResourceTypeDTO myNode;
//private SimpleTreeNode[] children;
//ordered tree node list
private ArrayList<ResourceTypeDTO> nodeList;
Map<String, ResourceTypeDTO> nodeMap;
public TypesTreeMaker() {
this.nodeList = new ArrayList<ResourceTypeDTO>();
this.nodeMap = new HashMap<>();
}
public Map<String, ResourceTypeDTO> getNodeMap() {
return nodeMap;
}
public ResourceTypeDTO getSimpleTree() {
return myNode;
}
public ArrayList<ResourceTypeDTO> getNodeList() {
return nodeList;
}
@Override
public void elaborate(Node<Type> node, int level) throws Exception {
myNode = new ResourceTypeDTO(node.getNodeElement().getName(), node.getNodeElement().getID().toString(),
node.getNodeElement().getDescription(), node.getNodeElement().isAbstract());
if(node.getParent()!=null) {
myNode.setFatherId(node.getParent().getNodeElement().getID().toString());
}else {
myNode.setFatherId(null);
}
for(Node<Type> nt:node.getChildrenNodes()) {
String name = nt.getNodeElement().getName();
String id = nt.getNodeElement().getID().toString();
String desc = nt.getNodeElement().getDescription();
boolean abst = nt.getNodeElement().isAbstract();
ResourceTypeDTO cn = new ResourceTypeDTO(name, id, desc, abst);
cn.setFatherId(nt.getParent().getNodeElement().getID().toString());
myNode.getChildren().add(cn);
}
if(!myNode.getChildren().isEmpty()) {
nodeList.add(myNode);
}
cleanDuplicates(nodeList);
return;
}
private void cleanDuplicates(ArrayList<ResourceTypeDTO> allNodes) {
for(ResourceTypeDTO node:allNodes) {
if(nodeMap.containsKey(node.getId())) {
nodeMap.remove(node.getId());
}else {
nodeMap.put(node.getId(), node);
}
}
}
private void visitNodes(ResourceTypeDTO node) {
Queue<ResourceTypeDTO> queue = new ArrayDeque<>();
queue.add(node);
ResourceTypeDTO currentNode;
Set<ResourceTypeDTO> alreadyVisited = new HashSet<>();
log.debug("Visited nodes: ");
while (!queue.isEmpty()) {
currentNode = queue.remove();
log.debug(currentNode.getName() + " | ");
alreadyVisited.add(currentNode);
queue.addAll(currentNode.getChildren());
queue.removeAll(alreadyVisited);
}
}
}

View File

@ -0,0 +1,318 @@
[
{
"type": "PropertyType",
"name": "Property",
"description": "This is the base type for any Property",
"version": "1.0.0",
"changelog": {
"1.0.0": "First Version"
},
"abstract": false,
"final": false,
"extendedTypes": [
"PropertyElement"
],
"id": "f2b0c34a-4a9a-47da-aa0b-74e6cc417c67"
},
{
"type": "PropertyType",
"name": "Encrypted",
"description": "This type is used to properly manage values must be stored safely (e.g. encrypted) in the IS.",
"properties": [
{
"type": "PropertyDefinition",
"name": "value",
"description": "The value to store safely in the IS",
"mandatory": true,
"readonly": false,
"notnull": true,
"max": null,
"min": null,
"regexp": null,
"propertyType": "String"
}
],
"version": "1.0.0",
"changelog": {
"1.0.0": "First Version"
},
"abstract": false,
"final": false,
"extendedTypes": [
"Property"
],
"id": "319d102c-a07d-4b84-b48b-999b70747807"
},
{
"type": "PropertyType",
"name": "GCubeProperty",
"description": "Marker type for any properties extended in the gCube model.",
"version": "1.0.0",
"changelog": {
"1.0.0": "First Version"
},
"abstract": false,
"final": false,
"extendedTypes": [
"Property"
],
"id": "74d38214-cf20-48cd-aaea-d128fd9689f5"
},
{
"type": "PropertyType",
"name": "AccessPolicy",
"description": "AccessPolicy information",
"properties": [
{
"type": "PropertyDefinition",
"name": "note",
"description": "",
"mandatory": false,
"readonly": false,
"notnull": false,
"max": null,
"min": null,
"regexp": null,
"propertyType": "String"
},
{
"type": "PropertyDefinition",
"name": "policy",
"description": "",
"mandatory": false,
"readonly": false,
"notnull": false,
"max": null,
"min": null,
"regexp": null,
"propertyType": "ValueSchema"
}
],
"version": "1.0.0",
"changelog": {
"1.0.0": "First Version"
},
"abstract": false,
"final": false,
"extendedTypes": [
"GCubeProperty"
],
"id": "85ee49ec-1318-4a27-8ee6-c8c3e2d0ca67"
},
{
"type": "PropertyType",
"name": "EnumStringProperty",
"description": "Enum String Property",
"version": "1.0.0",
"changelog": {
"1.0.0": "First Version"
},
"abstract": false,
"final": false,
"extendedTypes": [
"GCubeProperty"
],
"id": "f5768739-5bdb-4e1c-ae93-50c9c55e7655"
},
{
"type": "PropertyType",
"name": "RegexProperty",
"description": "A property validated with a regular expression.",
"properties": [
{
"type": "PropertyDefinition",
"name": "value",
"description": "",
"mandatory": false,
"readonly": false,
"notnull": false,
"max": null,
"min": null,
"regexp": null,
"propertyType": "String"
},
{
"type": "PropertyDefinition",
"name": "schema",
"description": "",
"mandatory": false,
"readonly": false,
"notnull": false,
"max": null,
"min": null,
"regexp": null,
"propertyType": "String"
}
],
"version": "1.0.0",
"changelog": {
"1.0.0": "First Version"
},
"abstract": false,
"final": false,
"extendedTypes": [
"GCubeProperty"
],
"id": "545f19a5-2777-4df3-8e2c-625d6a1d560f"
},
{
"type": "PropertyType",
"name": "ValueSchema",
"description": "This type aims at exposing a value which can be automatically managed by any client with no knowledge of its format.",
"properties": [
{
"type": "PropertyDefinition",
"name": "value",
"description": "The value which schema is available at the URI provided in the schema property.",
"mandatory": true,
"readonly": false,
"notnull": true,
"max": null,
"min": null,
"regexp": null,
"propertyType": "String"
},
{
"type": "PropertyDefinition",
"name": "schema",
"description": "An URI containing a schema used to validate/interpret the content of the value. It is only an informative field. The validation is in charge of the client.",
"mandatory": false,
"readonly": false,
"notnull": false,
"max": null,
"min": null,
"regexp": null,
"propertyType": "String"
}
],
"version": "1.0.0",
"changelog": {
"1.0.0": "First Version"
},
"abstract": false,
"final": false,
"extendedTypes": [
"GCubeProperty"
],
"id": "63669c11-3dc4-484c-9ce7-1f9fa66a4568"
},
{
"type": "PropertyType",
"name": "Metadata",
"description": "This type provides metadata per every IdentifiableElement",
"properties": [
{
"type": "PropertyDefinition",
"name": "creationTime",
"description": "Creation time. It is represented in the format yyyy-MM-dd HH:mm:ss.SSS Z.",
"mandatory": true,
"readonly": true,
"notnull": true,
"max": null,
"min": null,
"regexp": null,
"propertyType": "Date"
},
{
"type": "PropertyDefinition",
"name": "lastUpdateBy",
"description": "The user that made the last update to the Entity or the Relation. At Creation Time, it assumes the same value of createdBy.",
"mandatory": true,
"readonly": false,
"notnull": true,
"max": null,
"min": null,
"regexp": null,
"propertyType": "String"
},
{
"type": "PropertyDefinition",
"name": "createdBy",
"description": "The user that created the Entity or the Relation. It is initialized at Creation Time.",
"mandatory": true,
"readonly": true,
"notnull": true,
"max": null,
"min": null,
"regexp": null,
"propertyType": "String"
},
{
"type": "PropertyDefinition",
"name": "lastUpdateTime",
"description": "Last Update time. At creation time it assumes the same value of creationTime. It is represented in the format yyyy-MM-dd HH:mm:ss.SSS Z",
"mandatory": true,
"readonly": false,
"notnull": true,
"max": null,
"min": null,
"regexp": null,
"propertyType": "Date"
}
],
"version": "1.0.0",
"changelog": {
"1.0.0": "First Version"
},
"abstract": false,
"final": true,
"extendedTypes": [
"Property"
],
"id": "b7e25547-0de3-4e5a-8b14-da97d1c2b8ec"
},
{
"type": "PropertyType",
"name": "PropagationConstraint",
"description": "This type provides propagation constraint for Relation",
"properties": [
{
"type": "PropertyDefinition",
"name": "remove",
"description": "It indicates the behaviour to implement for the target Entity when a 'remove' action is performed on the source Resource. Remove actions is the operation of removing an instance from a context.",
"mandatory": true,
"readonly": false,
"notnull": true,
"max": null,
"min": null,
"regexp": "^(cascadeWhenOrphan|cascade|keep)$",
"propertyType": "String"
},
{
"type": "PropertyDefinition",
"name": "add",
"description": "It indicates the behaviour to implement for the target Entity when an 'add' action is performed on the source Resource. Add action is the operation of adding an instance to a context.",
"mandatory": true,
"readonly": false,
"notnull": true,
"max": null,
"min": null,
"regexp": "^(propagate|unpropagate)$",
"propertyType": "String"
},
{
"type": "PropertyDefinition",
"name": "delete",
"description": "It indicates the behaviour to implement for the target Entity when a 'delete' action is performed on the source Resource. Delet action is the operation of deleting an instance (it has an impact on all contexts).",
"mandatory": true,
"readonly": false,
"notnull": true,
"max": null,
"min": null,
"regexp": "^(cascadeWhenOrphan|cascade|keep)$",
"propertyType": "String"
}
],
"version": "1.1.0",
"changelog": {
"1.0.0": "First Version",
"1.1.0": "Added 'delete' propagation constraint"
},
"abstract": false,
"final": true,
"extendedTypes": [
"Property"
],
"id": "aec65058-73a6-4790-8144-2ce026e05a9a"
}
]

View File

@ -72,84 +72,5 @@ public class FacetPropertyDTO {
this.propertyType = propertyType;
}
public FacetPropGui toFacetPropGui() {
FacetPropGui prop = new FacetPropGui();
//TODO: vedi dove mettere description nella GUI
prop.setPropDescription(this.getDescription());
prop.setName(this.getName());
prop.setLabel(StringUtils.capitalize(this.getName()));
prop.setValue("");
prop.setPattern(this.getRegexp());
String tmp = "text";
switch(this.getPropertyType()) {
case "Boolean": tmp="boolean";
break;
case "Date": tmp="date";
break;
case "String": tmp="text";
break;
case "Long": tmp="number";
break;
case "Integer": tmp="number";
break;
}
prop.setType(tmp);
ValidationObjDTO val = new ValidationObjDTO();
///////
val.setMessage(tmp);
val.setName(tmp);
val.setValidator(tmp);
ArrayList<ValidationObjDTO> validations = new ArrayList<ValidationObjDTO>();
if(this.isMandatory()) {
ValidationObjDTO vv = new ValidationObjDTO();
vv.setMessage("The field is required");
vv.setName("required");
vv.setValidator("required");
validations.add(vv);
}
//check if notnull is ok here
if(!this.isMandatory() && this.isNotnull()) {
ValidationObjDTO vv = new ValidationObjDTO();
vv.setMessage("The field cannot be empty");
vv.setName("notnull");
vv.setValidator("required");
validations.add(vv);
}
if(StringUtils.isNotEmpty(this.getRegexp())) {
ValidationObjDTO vv = new ValidationObjDTO();
vv.setMessage("Wrong field format, required is: "+this.getRegexp());
vv.setName("pattern");
vv.setValidator("pattern");
validations.add(vv);
}
prop.setValidations(validations);
return prop;
}
}
/*
"validations": [
{
"name": "required",
"validator": "required",
"message": "Email is required"
},
{
"name": "pattern",
"validator": "email",
"message": "Invalid email format"
}
]
*/
//<input name="firstName" ngModel pattern="[a-zA-Z ]*">

View File

@ -24,6 +24,8 @@ public class FacetSpecDTO {
public void setDescription(String description) {
this.description = description;
}
private String name; //nome della facet
private String description; //descrizione della facet
private ArrayList<FacetPropertyDTO> properties;

View File

@ -0,0 +1,17 @@
package org.gcube.informationsystem.service.dto;
import org.gcube.informationsystem.service.dto.UmaResponseDTO;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class FacetTarget {
String id;
String type;
}

View File

@ -7,7 +7,7 @@ import org.slf4j.LoggerFactory;
//descrive una facet che concorre a comporre un oggetto (PRESA DAL CLIENT)
public class FacetTypeDTO {
public FacetTypeDTO(String relation, String target, String description, Integer min, Integer max) {
this.relation = relation;
this.target = target;
@ -30,7 +30,8 @@ public class FacetTypeDTO {
public String getMin() {
return min;
}
public void setMin(String min) {
public void setMin(String min) {
this.min = min;
}
public String getMax() {

View File

@ -1,26 +0,0 @@
package org.gcube.informationsystem.service.dto;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class HostingNodeTypeDTO {
private String id;
private String name;
private HostingNodeTypeDTO[] children;
/*
*
type:string;
name: string;
id: string;
status: string;
lastmod: string;
memavailable: string;
hdspace: string;
*/
}

View File

@ -15,7 +15,6 @@ import org.slf4j.LoggerFactory;
public class ResourceTypeDTO {
private static final Logger log = LoggerFactory.getLogger(ResourceTypeDTO.class);
//TODO: vedi se a regime può servire ID
private String name;
private String id;
private String fatherId;

View File

@ -0,0 +1,16 @@
package org.gcube.informationsystem.service.dto.Serialization;
import org.gcube.informationsystem.service.dto.UmaResponseDTO;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ConsistsOfType {
String id;
String type; //relazione
FacetGeneric target;
}

View File

@ -0,0 +1,15 @@
package org.gcube.informationsystem.service.dto.Serialization;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ExtraProperty {
String key;
String value;
String type;
}

View File

@ -0,0 +1,14 @@
package org.gcube.informationsystem.service.dto.Serialization;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class FacetGeneric {
String id;
String type; //facet name
}

View File

@ -0,0 +1,19 @@
package org.gcube.informationsystem.service.dto.Serialization;
import java.util.ArrayList;
import java.util.Map;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class FacetSimpleProperty extends FacetGeneric{
String name;
String value;
//TODO: GSON DEVE MAPPARE I CAMPI EXTRA COME ENTITà DIVERSE (RESTA IL PROBLEMA DEI TIPI)
ArrayList<Map<String,String>> extraProps;
}

View File

@ -0,0 +1,22 @@
package org.gcube.informationsystem.service.dto.Serialization;
import java.util.ArrayList;
import java.util.Map;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class FacetSoftware extends FacetGeneric{
String optional;
String name;
String group;
String description;
String qualifier;
String version;
ArrayList<Map<String,String>> extraProps;
}

View File

@ -0,0 +1,18 @@
package org.gcube.informationsystem.service.dto.Serialization;
import java.util.ArrayList;
import java.util.Map;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class FacetState extends FacetGeneric{
String value;
ArrayList<Map<String,String>> extraProps;
}

View File

@ -0,0 +1,19 @@
package org.gcube.informationsystem.service.dto.Serialization;
import java.util.ArrayList;
import java.util.Map;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class FacetTargetCpu extends FacetGeneric{
String model;
String vendor;
String clockSpeed;
//TODO: GSON DEVE MAPPARE I CAMPI EXTRA COME ENTITà DIVERSE (RESTA IL PROBLEMA DEI TIPI)
ArrayList<Map<String,String>> extraProps;
}

View File

@ -0,0 +1,18 @@
package org.gcube.informationsystem.service.dto.Serialization;
import java.util.ArrayList;
import java.util.Map;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class FacetTargetEvent extends FacetGeneric{
String event;
String date;
//TODO: GSON DEVE MAPPARE I CAMPI EXTRA COME ENTITà DIVERSE (RESTA IL PROBLEMA DEI TIPI)
ArrayList<Map<String,String>> extraProps;
}

View File

@ -0,0 +1,22 @@
package org.gcube.informationsystem.service.dto.Serialization;
import java.util.ArrayList;
import java.util.Map;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class FacetTargetMemory extends FacetGeneric{
String broadcastAddress;
String ipAddress;
String hostName;
String domaniName;
String mask;
//TODO: GSON DEVE MAPPARE I CAMPI EXTRA COME ENTITà DIVERSE (RESTA IL PROBLEMA DEI TIPI)
ArrayList<Map<String,String>> extraProps;
}

View File

@ -0,0 +1,20 @@
package org.gcube.informationsystem.service.dto.Serialization;
import java.util.ArrayList;
import java.util.Map;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class FacetTargetNetworking extends FacetGeneric{
String used;
String unit;
String size;
//TODO: GSON DEVE MAPPARE I CAMPI EXTRA COME ENTITà DIVERSE (RESTA IL PROBLEMA DEI TIPI)
ArrayList<Map<String,String>> extraProps;
}

View File

@ -0,0 +1,17 @@
package org.gcube.informationsystem.service.dto.Serialization;
import java.util.ArrayList;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ResourceModel {
String id;
String type;
ArrayList<ConsistsOfType> consistsOf;
}

View File

@ -3,7 +3,6 @@ package org.gcube.informationsystem.web.rest;
import java.util.List;
import java.util.Map;
import org.gcube.informationsystem.service.InformationSystemService;
import org.gcube.informationsystem.service.dto.EServiceDTO;
import org.gcube.informationsystem.service.dto.HostingNodeDTO;
import org.gcube.informationsystem.service.dto.VirtualServiceDTO;
@ -14,8 +13,6 @@ import com.jayway.jsonpath.Configuration;
import com.jayway.jsonpath.JsonPath;
import com.jayway.jsonpath.Option;
import net.minidev.json.JSONArray;
import net.minidev.json.JSONObject;
/*
* Prende il JSON di un singolo nodo e lo trasforma in oggetto udando JSONPath

View File

@ -1,10 +1,12 @@
package org.gcube.informationsystem.web.rest;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
@ -13,6 +15,7 @@ import javax.annotation.Nullable;
import org.gcube.com.fasterxml.jackson.databind.ObjectMapper;
import org.gcube.com.fasterxml.jackson.databind.ObjectWriter;
import org.gcube.common.authorization.utils.manager.SecretManagerProvider;
import org.gcube.informationsystem.base.reference.AccessType;
import org.gcube.informationsystem.config.TokenManager;
import org.gcube.informationsystem.model.reference.entities.Resource;
import org.gcube.informationsystem.resourceregistry.publisher.ResourceRegistryPublisher;
@ -37,18 +40,14 @@ import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.util.UriUtils;
import com.jayway.jsonpath.JsonPath;
import io.swagger.v3.core.util.Json;
import lombok.RequiredArgsConstructor;
import net.minidev.json.JSONArray;
import tech.jhipster.web.util.HeaderUtil;
@RestController
@RequestMapping("/api/is")
@RequiredArgsConstructor
public class InformationSystemResource {
public class InformationSystemResource{
private static final Logger log = LoggerFactory.getLogger(InformationSystemResource.class);
@ -108,7 +107,7 @@ public class InformationSystemResource {
log.debug("Request resource types");
try {
informationSystemService.setUma(createUmaToken(currentContext));
ArrayList<ResourceTypeDTO> treeNode = informationSystemService.getResourceTypesTree();
ArrayList<ResourceTypeDTO> treeNode = informationSystemService.getResourceTypesTree(AccessType.RESOURCE.getName());
ObjectMapper objectMapper = new ObjectMapper();
String sc = objectMapper.writeValueAsString(treeNode);
return ResponseEntity.ok().body(sc);
@ -146,6 +145,7 @@ public class InformationSystemResource {
// ritorna l'elenco delle facet e come costruirle
@GetMapping("/facetspecifications")
public ResponseEntity<String> resourceTypeComplete(@RequestParam String typeName,@RequestParam @Nullable String currentContext) {
ArrayList<FacetSpecDTO> facetSpecs = new ArrayList<FacetSpecDTO>();
try {
informationSystemService.setUma(createUmaToken(currentContext));
@ -160,12 +160,13 @@ public class InformationSystemResource {
fs.setRelationOptions(informationSystemService.getFacetRelationsOptions(ft.getRelation()));
facetSpecs.add(fs);
}
builderDto.setFacetSpecs(facetSpecs);
ObjectMapper objectMapper = new ObjectMapper();
String sc = objectMapper.writeValueAsString(builderDto);
return ResponseEntity.ok().body(sc);
} catch (Exception e) {
e.printStackTrace();
//e.printStackTrace();
log.error("****ERROR*************");
log.error(e.getMessage(), e);
return ResponseEntity.noContent()
@ -250,10 +251,8 @@ public class InformationSystemResource {
informationSystemService.setUma(createUmaToken(currentContext));
formData = informationSystemService.createResourceInternal(resourceType, formData);
try {
//String currentCtx = SecretManagerProvider.instance.get().getContext();
ResourceRegistryPublisher publisher = ResourceRegistryPublisherFactory.create();
result = publisher.createResource(formData);
//result = publisher.create(formData);
}catch(Exception e) {
e.printStackTrace();
log.error(e.getMessage());
@ -263,14 +262,17 @@ public class InformationSystemResource {
//CRUD - 2
@PostMapping("/updateresource")
String updateresource(@RequestParam String resourceType, @RequestBody String formData) throws Exception {
String updateresource(@RequestParam String resourceType, @RequestBody String oldData) throws Exception {
//TODO: QUI VA FATTO L'UNMARSHALLING, ma rispetto a quale classe??
//ElementMapper.unmarshal(qualcosa.class, oldData);
String currentContext = SecretManagerProvider.instance.get().getContext();
String result = "";
informationSystemService.setUma(createUmaToken(currentContext));
formData = informationSystemService.createResourceInternal(resourceType, formData);
try {
ResourceRegistryPublisher publisher = ResourceRegistryPublisherFactory.create();
result = publisher.updateResource(formData);
result = publisher.updateResource(oldData);
}catch(Exception e) {
e.printStackTrace();
log.error(e.getMessage());

View File

@ -0,0 +1,122 @@
package org.gcube.informationsystem.web.rest;
import java.text.DateFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.gson.FieldNamingPolicy;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.jayway.jsonpath.Criteria;
import com.jayway.jsonpath.Filter;
import com.jayway.jsonpath.JsonPath;
import com.jayway.jsonpath.Predicate;
import io.swagger.v3.core.util.Json;
import net.minidev.json.JSONArray;
import net.minidev.json.JSONObject;
public class ProveVarie {
static ArrayList<String> facetNames = new ArrayList<>();
static ArrayList<String> facetRelations = new ArrayList<>();
static ArrayList<JSONArray> extraProps = new ArrayList<JSONArray>();
static JSONArray props = new JSONArray();
public static void main(String[] args) {
String type="VirtualService";
try {
facetNames = JsonPath.parse(formData).read("$..facetName");
facetRelations = JsonPath.parse(formData).read("$..relationFacet");
extraProps = JsonPath.parse(formData).read("$..extraProps");
props = JsonPath.parse(formData).read("$..props");
ResourceDescription rd = new ResourceDescription();
rd = buildSendingJson(type,facetNames,facetRelations,extraProps,props);
// System.out.println(Json.pretty(rd));
JsonElement je= JsonParser.parseString(formData);
System.out.println(je);
}catch(Exception e) {
e.printStackTrace();
}
}
static void createUno(String jsnStr) {
/*
Gson gson = new GsonBuilder()
//.registerTypeAdapter(null, jsnStr)
.enableComplexMapKeySerialization()
.serializeNulls()
.setDateFormat(DateFormat.LONG)
.setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE)
.setPrettyPrinting()
.setVersion(1.0)
.create();
*/
Gson gson = new Gson();
}
//create the JSON object to be sent to the service for creation
static ResourceDescription buildSendingJson(String resourceType, ArrayList<String> facetNames, ArrayList<String> relations, ArrayList<JSONArray> extraProps, JSONArray props ) {
ResourceDescription rd = new ResourceDescription();
rd.setType(resourceType);
for(int i=0; i<facetNames.size(); i++) {
////////////////////
LinkedHashMap targetMap_i = new LinkedHashMap<String, Object>();
FacetDescription facet_i = new FacetDescription();
facet_i.setType(relations.get(i));
FacetDescription facet_d = new FacetDescription();
LinkedHashMap joProp_i = (LinkedHashMap)props.get(i);
joProp_i.put("type", facetNames.get(i));
Iterator<String> keyit = (Iterator) joProp_i.keySet().iterator();
while(keyit.hasNext()) {
String key = keyit.next();
if(key.equals("optional")) {
continue;
}
targetMap_i.put(key, joProp_i.get(key).toString());
}
JSONArray extra_joProps_i = (JSONArray)extraProps.get(i);
if(extra_joProps_i.size()!=0) {
for(int j=0; j<extra_joProps_i.size(); j++) {
LinkedHashMap extraProps_ij = (LinkedHashMap)extra_joProps_i.get(j);
Iterator<String> it = (Iterator) extraProps_ij.keySet().iterator();
while(it.hasNext()) {
String key = it.next();
targetMap_i.put(extraProps_ij.get("deno").toString(),extraProps_ij.get("val").toString());//to avoid ClassCastException
}
}
}
facet_i.setTarget(targetMap_i);
if(rd.getConsistsOf()==null) {
rd.setConsistsOf(new ArrayList<FacetDescription>());
}
rd.getConsistsOf().add(facet_i);
}
return rd;
}
static String formData ="{SoftwareFacet_IsIdentifiedBy\":[{\"facetName\":\"SoftwareFacet\",\"max\":\"many\",\"min\":\"1\",\"facetDescription\": \"SoftwareFacetcapturesinformationonanysoftwareassociatedwiththereource.\",\"relationFacet\":\"IsIdentifiedBy\",\"isAdded\":false,\"extraProps\":[{\"deno\":\"namecustom1\",\"val\":\"valuecutom1\",\"tipo\":\"String\"},{\"deno\":\"namecustom2\",\"val\":\"valuecustom2\",\"tipo\":\"String\"}],\"props\":{\"optional\":true,\"name\":\"name1\",\"group\":\"group1\",\"description\":\"descr1\",\"version\":\"v1\",\"qualifier\":\"qual1\"}},{\"facetName\":\"SoftwareFacet\",\"max\":\"many\",\"min\":\"1\",\"facetDescription\":\"SoftwareFacetcapturesinformationonanysoftwareassociatedwiththeresource.\",\"relationFacet\":\"IsIdentifiedBy\",\"isAdded\":true,\"extraProps\":[{\"deno\":\"customname2\",\"val\":\"customvalue2\",\"tipo\":\"String\"}],\"props\":{\"optional\":true,\"name\":\"name2\",\"group\":\"group2\",\"description\":\"\",\"version\":\"v2\",\"qualifier\":\"\"}},{\"facetName\":\"SoftwareFacet\",\"max\":\"many\",\"min\":\"1\",\"facetDescription\":\"SoftwareFacetcapturesinformationonanysoftwareassociatedwiththeresource.\",\"relationFacet\":\"IsIdentifiedBy\",\"isAdded\":true,\"extraProps\":[],\"props\":{\"optional\":true,\"name\":\"name3\",\"group\":\"group3\",\"description\":\"decr3\",\"version\":\"v333\",\"qualifier\":\"\"}}]}";
}

View File

@ -0,0 +1,61 @@
package org.gcube.informationsystem.web.rest;
import java.io.Reader;
import java.nio.file.Files;
import java.util.Map;
import com.google.gson.Gson;
import io.swagger.v3.core.util.Json;
import io.swagger.v3.oas.models.Paths;
import net.minidev.json.JSONArray;
import net.minidev.json.JSONObject;
public class ProveVarie2 {
public static void main(String[] args) {
try {
// create Gson instance
Gson gson = new Gson();
// create a reader
Reader reader = Files.newBufferedReader(java.nio.file.Paths.get("./test.json"));
// convert JSON file to map
Map<?, ?> map = gson.fromJson(reader, Map.class);
// print map entries
for (Map.Entry<?, ?> entry : map.entrySet()) {
System.out.println(entry.getKey() + "=" + entry.getValue());
}
// close reader
reader.close();
} catch (Exception ex) {
ex.printStackTrace();
}
}
static void createUno(String jsnStr) {
/*
Gson gson = new GsonBuilder()
//.registerTypeAdapter(null, jsnStr)
.enableComplexMapKeySerialization()
.serializeNulls()
.setDateFormat(DateFormat.LONG)
.setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE)
.setPrettyPrinting()
.setVersion(1.0)
.create();
*/
}
}

View File

@ -45,11 +45,26 @@
<!-- PROPERTIES -->
<div formGroupName ="props" >
<div *ngFor="let prop of facetTemplate.value.guiProps">
<mat-form-field>
<mat-label>{{prop.label}}</mat-label>
<input matInput formControlName="{{prop.name}}" id="{{prop.name}}"
type="{{prop.type}}"/>
<div *ngIf="prop.type==='typeprop'; else singlefield" style="border: 1px solid rgb(202, 202, 202); padding: 10px; margin: 5px;">
<p style="color: darkgrey;">{{prop.label}}</p>
<mat-form-field>
<mat-label>username</mat-label>
<input matInput formControlName="username" id="username"
type="text"/>
</mat-form-field>
<mat-form-field>
<mat-label>password</mat-label>
<input matInput formControlName="password" id="password"
type="password"/>
</mat-form-field>
</div>
<ng-template #singlefield>
<mat-form-field>
<mat-label>{{prop.label}}</mat-label>
<input matInput formControlName="{{prop.name}}" id="{{prop.name}}"
type="{{prop.type}}"/>
</mat-form-field>
</ng-template>
</div>
</div>
@ -100,7 +115,8 @@
</div>
</div> <!-- ends the form -->
</mat-dialog-content>
</mat-dialog-content>
<span>{{myForm.value|json}}</span>
<mat-dialog-actions align="end">
<button mat-button type="submit" (click)="onSubmit()">Submit</button>
<button mat-button (click)="close()">Cancel</button>

View File

@ -186,7 +186,7 @@ export class FacetComposerComponent implements OnInit {
//1. creo group con le properties
//2. aggiungo formgroup delle properties ai controls per la facet
return this.addPropertyControls(facetFg,item.guiProps);
}
@ -195,6 +195,7 @@ export class FacetComposerComponent implements OnInit {
const propsFg = this.fb.group({});
let fc:FormControl;
for(let i=0; i<props.length; i++){
const prop=props[i];
if(prop.type==="date"){
@ -207,7 +208,7 @@ export class FacetComposerComponent implements OnInit {
fc = this.fb.control(true)
}
else{
fc = this.fb.control('') //text
fc = this.fb.control('') //text or typeprop
}
if (prop.validations.length>0){
for (let k=0; k<prop.validations.length; k++){
@ -219,8 +220,15 @@ export class FacetComposerComponent implements OnInit {
}
}
}
propsFg.addControl(prop.name,fc); //formGroup.addControl(prop_'controlName', formControl);
// facetFg.addControl("prop_"+prop.name,fc); //formGroup.addControl(prop_'controlName', formControl);
if(prop.type==="typeprop"){ //TODO: forse va messo il controllo anche sul tipo di facet
let fc2:FormControl;
propsFg.addControl("username",fc);
// eslint-disable-next-line prefer-const
fc2 = this.fb.control('') //text or typeprop
propsFg.addControl("password",fc2);
}else{
propsFg.addControl(prop.name,fc); //formGroup.addControl(prop_'controlName', formControl);
}
}
facetFg.addControl('props',propsFg);
return facetFg;

View File

@ -18,11 +18,6 @@ export interface ITypeSpecification{
providedIn: 'root'
})
export class FacetComposerService {
/*
httpOptions = {
headers: new HttpHeaders({ 'Content-Type': 'application/json' }),
};
*/
constructor(private http: HttpClient, private applicationConfigService: ApplicationConfigService) { }

View File

@ -76,9 +76,7 @@ export class FacetEditorComponent implements OnInit {
ngOnInit(): void {
this.dataService.getJsonDetails('',this.titleType,this.uid).subscribe(res => {
console.debug("*******RAW OBJECT*********");
this.lookoutObject(res);
console.debug("****************");
});
this.guiService.getFormStructure(this.titlePath,this.titleType).subscribe(res => {
@ -92,30 +90,29 @@ export class FacetEditorComponent implements OnInit {
lookoutObject(obj:any){
for(const key in obj){
console.debug("*********** lookoutObject *********");
console.debug("key: " + key + ", value: " + obj[key]);
console.debug("*********** *********");
if(obj[key] instanceof Object){
this.lookoutObject(obj[key]);
}
}
}
}
createAndFillForm(rawSource:string, fData:ITypeSpecification):void{
for(let i=0; i<fData.facetSpecs.length; i++){
const facetSpec = fData.facetSpecs[i];
this.createFacetArrayEntry(facetSpec);
}
}
createFacetArrayEntry(item: IFacetComposer):void{
createFacetArrayEntry(item: IFacetComposer):void{
const nameplus:string = item.name+'_'+item.relation;
const singleFacetArray: FormArray = this.fb.array([]);
singleFacetArray.push(this.createFacetGroup(item,false));
this.myForm.addControl(nameplus,singleFacetArray);
}
}
get extraProps():FormArray {
return this.fb.array([

View File

@ -1,80 +0,0 @@
/* eslint-disable @typescript-eslint/no-inferrable-types */
/* eslint-disable @typescript-eslint/no-empty-function */
/* eslint-disable @typescript-eslint/explicit-function-return-type */
/* eslint-disable no-console */
import { Injectable } from '@angular/core';
import { InMemoryDbService, RequestInfo } from 'angular-in-memory-web-api';
import { Observable } from 'rxjs';
import { HttpClient } from '@angular/common/http';
import { IDummy } from './i-dummy';
/*
import { Observable, catchError, map, throwError } from 'rxjs';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { ApplicationConfigService } from 'app/core/config/application-config.service';
import { Resource } from 'app/model/resource';
*/
@Injectable({
providedIn: 'root'
})
export class DummyService implements InMemoryDbService {
SERVER_URL: string = "dummyservice/api/";
createDb() {
const resources = [
{ uid: 1, type:'HostingNode',name: 'HostingNode One', },
{ uid: 2, type:'HostingNode',name: 'HostingNode Two', },
{ uid: 3, type:'HostingNode',name: 'HostingNode Three', }
];
return { resources };
}
// eslint-disable-next-line @typescript-eslint/member-ordering
constructor(private httpClient: HttpClient) { }
public getDummies():Observable<any>{
return this.httpClient.get(this.SERVER_URL + 'resources');
}
public getDummy(uid:string):Observable<any>{
return this.httpClient.get(`${this.SERVER_URL + 'resources'}/${uid}`);
}
public createDummy(dummy: IDummy){
return this.httpClient.post(`${this.SERVER_URL + 'resources'}`, dummy)
}
public deleteDummy(uid:string){
return this.httpClient.delete(`${this.SERVER_URL + 'resources'}/${uid}`)
}
public updateDummy(dummy: IDummy):Observable<any>{
return this.httpClient.put(`${this.SERVER_URL + 'resources'}/${dummy.uid}`, dummy)
}
}
/*
httpOptions = {
headers: new HttpHeaders({ 'Content-Type': 'application/json' })
};
constructor(private http: HttpClient) { }
getEmployeePositions(): Observable<employeePosition[]> {
return this.http.get<employeePosition[]>(this.positionsUrl).pipe(
tap(_ => this.log('fetched positions')),
catchError(this.handleError<employeePosition[]>('getEmployeePositions', []))
);
}
addEmployeePosition(position: employeePosition): Observable<employeePosition> {
return this.http.post<employeePosition>(this.positionsUrl, position, this.httpOptions).pipe(
tap((newPosition: employeePosition) => this.log(`added position with id=${newPosition}`)),
catchError(this.handleError<employeePosition>('addEmployeePosition'))
);
}
*/

View File

@ -34,11 +34,9 @@ export class ResourcesImplService {
queryParams = queryParams.append("currentContext",ctxPath).append("resourceType",type);
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
if(type==='HostingNode'){
//console.debug("******GETTING HOSTING NODE*****");
return this.http.get<IHostingNode[]>(resourceUrl,{params:queryParams});
}
if(type==='VirtualMachine'){
//console.debug("******GETTING HOSTING NODE*****");
return this.http.get<IVirtualMachine[]>(resourceUrl,{params:queryParams});
}
if(type==='EService'){

View File

@ -50,8 +50,9 @@
<div class="d-flex flex-row">
<button mat-button class="action-btn" color="primary" (click)="addTab(this.item.id)" ><mat-icon>visibility</mat-icon></button>
<button mat-button class="action-btn" color="primary" (click)="openFacetEditor(this.item.id)"><mat-icon>edit</mat-icon></button>
<button mat-button class="action-btn" color="primary"><mat-icon>delete</mat-icon></button>
<button mat-button class="action-btn" color="primary" (click)="removeItem(this.item.id)"><mat-icon>delete</mat-icon></button>
</div>
</td>
</ng-container>

View File

@ -181,8 +181,18 @@ export class TableScreenEsComponent implements OnInit, AfterViewInit, OnChanges{
dialogConfig.disableClose = true;
dialogConfig.autoFocus = true;
dialogConfig.data= {type: this.typeObject, context: this.currentCtx};
this.createDialog.open(FacetComposerComponent,dialogConfig);
}
const dialogAddRef = this.createDialog.open(FacetComposerComponent,dialogConfig);
dialogAddRef.afterClosed().subscribe( //res is a boolean
res=>{
if(res!=='error'){ //res di tipo string
this.reloadTable();
}else{
alert('ERROR');
}
}
);
}
openFacetEditor(uid: string): void {
const dialogConfig = new MatDialogConfig();
@ -197,7 +207,16 @@ export class TableScreenEsComponent implements OnInit, AfterViewInit, OnChanges{
dialogConfig.disableClose = true;
dialogConfig.autoFocus = true;
dialogConfig.data= {type: this.typeObject, context: this.currentCtx,uid};
this.createDialog.open(RemoveResourceComponent,dialogConfig);
const dialogRemoveRef = this.createDialog.open(RemoveResourceComponent,dialogConfig);
dialogRemoveRef.afterClosed().subscribe( //res is a boolean
res=>{if(res){
this.reloadTable();
}else{
alert('ERROR');
}
}
);
}
}

View File

@ -157,8 +157,6 @@ reloadTable():void{
})
}
closeTab(index: number): void {
const x = this.chosenIds.indexOf(this.tabs[index].id);

View File

@ -52,8 +52,8 @@
<!--<div class="d-flex flex-row py-4">-->
<div class="d-flex flex-row">
<button mat-button class="action-btn" color="primary" (click)="addTab(this.item.id)" ><mat-icon>visibility</mat-icon></button>
<button mat-button class="action-btn" color="primary" (click)="openFacetEditor(this.item.id)"><mat-icon>edit</mat-icon></button>
<button mat-button class="action-btn" color="primary"><mat-icon>delete</mat-icon></button>
<button mat-button class="action-btn" color="primary" (click)="openFacetEditor(this.item.id)"><mat-icon>edit</mat-icon></button>
<button mat-button class="action-btn" color="primary" (click)="removeItem(this.item.id)"><mat-icon>delete</mat-icon></button>
</div>
</td>
</ng-container>

View File

@ -23,6 +23,7 @@ import { MatTab, MatTabGroup } from '@angular/material/tabs';
import { MatDialogRef, MatDialog, MatDialogConfig } from '@angular/material/dialog';
import { ITabbedEntity } from 'app/i-tabbed-entity';
import { IHostingNode } from 'app/services/i-hosting-node';
import { IVirtualMachine } from 'app/services/i-virtual-machine';
import { ResourcesImplService } from 'app/services/resources-impl.service';
import { IResource } from 'app/services/i-resource';
import { IResourceType } from 'app/services/i-resource-type';
@ -30,6 +31,9 @@ import { MatFormFieldControl } from '@angular/material/form-field';
import { FacetComposerComponent } from 'app/facet-composer/facet-composer.component';
import { FacetEditorComponent } from 'app/facet-editor/facet-editor.component';
import { RemoveResourceComponent } from 'app/remove-resource/remove-resource.component';
import { IVirtualService } from 'app/services/i-virtual-service';
@Component({
selector: 'jhi-table-screen',
@ -44,7 +48,7 @@ import { FacetEditorComponent } from 'app/facet-editor/facet-editor.component';
export class TableScreenComponent implements OnInit, AfterViewInit, OnChanges {
//NB 'actions' CI DEVE ESSERE, altrimenti la tabella non viene visualizzata
displayedColumns: string[] = ['name', 'id', 'status', 'lastMod', 'avMemory', 'hdSpace', 'actions'];
dataFromService: IHostingNode[];
dataFromService: IHostingNode[]|IVirtualMachine[];
dataSource = new MatTableDataSource();
tableDetail: IHostingNode;
//dialogAddRef: MatDialogRef<ResourceAddComponent> |undefined;
@ -55,7 +59,7 @@ export class TableScreenComponent implements OnInit, AfterViewInit, OnChanges {
@Input() currentCtx: IContextNode; //fetching event from parent
@Input() currentCtxPath: string; //fetching event from parent
@Input() typeObject: IResource;
@Output() jsonEmitter = new EventEmitter<IHostingNode>();
@Output() jsonEmitter = new EventEmitter<IHostingNode|IVirtualMachine>();
@ViewChild(MatSort) sort: MatSort;
@ViewChild(MatPaginator) paginator: MatPaginator;
@ -76,7 +80,7 @@ export class TableScreenComponent implements OnInit, AfterViewInit, OnChanges {
constructor(private myDataService: ResourcesImplService, private createDialog: MatDialog, private editDialog: MatDialog) {
this.currentCtx = {} as IContextNode;
this.tableDetail = {} as IHostingNode;
this.tableDetail = {} as IHostingNode|IVirtualMachine;
this.dataFromService = [];
this.sort = new MatSort();
this.paginator = {} as MatPaginator;
@ -84,8 +88,6 @@ export class TableScreenComponent implements OnInit, AfterViewInit, OnChanges {
this.typeObject = {} as IResourceType;
this.currentCtxPath = '';
this.rawJson = '';
// this.dummyResource = {} as IDummy;
// this.dummies = [];
}
ngAfterViewInit(): void {
@ -165,7 +167,6 @@ export class TableScreenComponent implements OnInit, AfterViewInit, OnChanges {
}
reloadTable():void{
alert(this.typeObject.name)
this.myDataService.fetchResourceImpls(this.currentCtx.path,this.typeObject.name).subscribe(res => {
this.dataFromService = res;
this.dataSource.data = res;
@ -190,10 +191,20 @@ export class TableScreenComponent implements OnInit, AfterViewInit, OnChanges {
dialogConfig.disableClose = true;
dialogConfig.autoFocus = true;
dialogConfig.data= {type: this.typeObject, context: this.currentCtx};
this.createDialog.open(FacetComposerComponent,dialogConfig);
const dialogAddRef = this.createDialog.open(FacetComposerComponent,dialogConfig);
dialogAddRef.afterClosed().subscribe( //res is a boolean
res=>{
if(res!=='error'){ //res di tipo string
this.reloadTable();
}else{
alert('ERROR');
}
}
);
}
openFacetEditor(uid: string): void {
const dialogConfig = new MatDialogConfig();
dialogConfig.disableClose = true;
@ -202,50 +213,20 @@ export class TableScreenComponent implements OnInit, AfterViewInit, OnChanges {
this.editDialog.open(FacetEditorComponent,dialogConfig);
}
/*
//TODO: GUARDA QUESTI METODI PER GESTIRE IL DOPO CREAZIONE/AGGIORNAMENTO
dialogRef.afterClosed().subscribe(result => {
if(result.event === 'add'){
this.addNewData(result.data);
}else if(result.event ==='update'){
//TODO
//this.updateRowData(result.data);
}else if(result.event === 'delete'){
//TODO
// this.deleteRowData(result.data);
}
});
}
showNewData(data: any):void{
const rsc = {} as IDummy;
rsc.type = this.typeObject.name;
rsc.uid = data.uid;
rsc.name = data.name;
this.dummies.push(rsc);
}
addNewData(data: any):void{
const rsc = {} as IDummy;
rsc.type = this.typeObject.name;
rsc.uid = data.uid;
rsc.name = data.name;
this.dummies.push(rsc);
}
updateData(uid: string, data:any):void{
//TODO: recupera resource con uid passato come parametro e type=this.typeObject.name
//TODO: chiama il metodo delle API per fare la update
const rsc = {} as IDummy;
rsc.type = this.typeObject.name;
rsc.uid = data.uid;
rsc.name = data.name;
this.dummies.push(rsc);
}
removeData(uid: string):void{
//TODO: chiama il metodo delle API per fare la remove
}
*/
removeItem(uid:string):void{
const dialogConfig = new MatDialogConfig();
dialogConfig.disableClose = true;
dialogConfig.autoFocus = true;
dialogConfig.data= {type: this.typeObject, context: this.currentCtx,uid};
const dialogRemoveRef = this.createDialog.open(RemoveResourceComponent,dialogConfig);
dialogRemoveRef.afterClosed().subscribe( //res is a boolean
res=>{if(res){
this.reloadTable();
}else{
alert('ERROR');
}
}
);
}
}

View File

@ -1,155 +1,447 @@
{
"NetworkingFacet_IsIdentifiedBy": [
"name": "EService",
"fatherId": null,
"description": "EService is any running service that is registered in the infrastructure and made available by an access point.",
"astratto": false,
"facetSpecs": [
{
"facetName": "NetworkingFacet",
"max": "1",
"min": "1",
"facetDescription": "NetworkingFacet captures information on any (computer) network interface associated with the resource.It is mainly used to describe the network interface of a host. It should not be confused with the {@link AccessPointFacet} which instead describes the protocol and the endpoint of a web-based service.",
"relationFacet": "IsIdentifiedBy",
"isAdded": false,
"extraProps": [],
"prop_broadcastAddress": "broadcastAddress_1",
"prop_IPAddress": "12.66.86",
"prop_hostName": "host name 1",
"prop_domainName": "domain name 1",
"prop_mask": "mask 1"
}
],
"CPUFacet_ConsistsOf": [
{
"facetName": "CPUFacet",
"max": "many",
"min": "1",
"facetDescription": "CPUFacet captures information on the Central Processing Unit (CPU) of the resource it is associated with. A resource which needs to indicate a multi-processor/multi-core CPU will consist of more than one CPUFacet. Even if more than one CPUFacet is associated with a resource (i.e., an {@link HostingNode}), we did not find any reason to differentiate the CPUs.",
"relationFacet": "ConsistsOf",
"isAdded": false,
"extraProps": [
"name": "SoftwareFacet",
"description": "SoftwareFacet captures information on any software associated with the resource.",
"properties": null,
"guiProps": [
{
"prop_deno": "extra1",
"prop_val": "value1",
"prop_tipo": "String"
"type": "text",
"label": "Name",
"name": "name",
"value": "",
"validations": [
{
"name": "required",
"validator": "required",
"message": "The field is required"
}
],
"pattern": null,
"propDescription": "The name of the software artifact being described, e.g., artifactId in maven coordinates, the software name for retail software such as 'Office' (in Microsoft\u2122 Office 2013-SP2)"
},
{
"prop_deno": "extra2",
"prop_val": "value2",
"prop_tipo": "String"
"type": "boolean",
"label": "Optional",
"name": "optional",
"value": "",
"validations": [],
"pattern": null,
"propDescription": "Used to indicate the software optionality, e.g., optional in maven coordinates"
},
{
"type": "text",
"label": "Group",
"name": "group",
"value": "",
"validations": [
{
"name": "required",
"validator": "required",
"message": "The field is required"
}
],
"pattern": null,
"propDescription": "The name of 'group' the software artifact belongs to, e.g., groupId in maven coordinates, company name for retail software software such as 'Microsoft\u2122' (in Microsoft\u2122 Office 2013-SP2)"
},
{
"type": "text",
"label": "Description",
"name": "description",
"value": "",
"validations": [],
"pattern": null,
"propDescription": "A human oriented description of the software artifact being described"
},
{
"type": "text",
"label": "Version",
"name": "version",
"value": "",
"validations": [
{
"name": "required",
"validator": "required",
"message": "The field is required"
}
],
"pattern": null,
"propDescription": "The particular release of the software artifact, e.g., version in maven coordinates, the software version for retail software such as '2013-SP2' (in Microsoft\u2122 Office 2013-SP2)"
},
{
"type": "text",
"label": "Qualifier",
"name": "qualifier",
"value": "",
"validations": [],
"pattern": null,
"propDescription": "A qualifier for the software, e.g., packaging or scope in maven coordinates, target architecture for retail software such as x86 or amd64"
}
],
"prop_model": "aaaaaaa",
"prop_vendor": "bbbbb",
"prop_clockSpeed": "45"
"relationOptions": [
"IsIdentifiedBy"
],
"relation": "IsIdentifiedBy",
"min": "1",
"max": "1"
},
{
"facetName": "CPUFacet",
"max": "many",
"min": "1",
"facetDescription": "CPUFacet captures information on the Central Processing Unit (CPU) of the resource it is associated with. A resource which needs to indicate a multi-processor/multi-core CPU will consist of more than one CPUFacet. Even if more than one CPUFacet is associated with a resource (i.e., an {@link HostingNode}), we did not find any reason to differentiate the CPUs.",
"relationFacet": "HasRemoveAction",
"isAdded": true,
"extraProps": [
"name": "SoftwareFacet",
"description": "SoftwareFacet captures information on any software associated with the resource.",
"properties": null,
"guiProps": [
{
"prop_deno": "extra deno 1",
"prop_val": "extra value 1",
"prop_tipo": "String"
}
],
"prop_model": "ccccc",
"prop_vendor": "dddd",
"prop_clockSpeed": "124"
}
],
"MemoryFacet_HasPersistentMemory": [
{
"facetName": "MemoryFacet",
"max": "many",
"min": "1",
"facetDescription": "MemoryFacet captures information on computer memory equipping the resource and its usage. Any resource describing a computing machine must have at least two types of memories i.e., persistent and volatile. For such a reason, it has been identified the ConsistsOf relation called {@link HasMemory}. It is in charge of the specialisation {@link HasVolatileMemory} and {@link HasPersistentMemory} to clarify the semantics of the memory.",
"relationFacet": "HasPersistentMemory",
"isAdded": false,
"extraProps": [],
"prop_used": "2355",
"prop_unit": "MB",
"prop_size": "2466"
}
],
"MemoryFacet_HasVolatileMemory": [
{
"facetName": "MemoryFacet",
"max": "many",
"min": "1",
"facetDescription": "MemoryFacet captures information on computer memory equipping the resource and its usage. Any resource describing a computing machine must have at least two types of memories i.e., persistent and volatile. For such a reason, it has been identified the ConsistsOf relation called {@link HasMemory}. It is in charge of the specialisation {@link HasVolatileMemory} and {@link HasPersistentMemory} to clarify the semantics of the memory.",
"relationFacet": "HasVolatileMemory",
"isAdded": false,
"extraProps": [],
"prop_used": "222",
"prop_unit": "MB",
"prop_size": "1000"
}
],
"EventFacet_ConsistsOf": [
{
"facetName": "EventFacet",
"max": "many",
"min": "1",
"facetDescription": "EventFacet captures information on a certain event/happening characterising the life cycle of the resource. Examples of an event are the start time of a virtual machine or the activation time of an electronic service.",
"relationFacet": "ConsistsOf",
"isAdded": false,
"extraProps": [],
"prop_event": "ddddd",
"prop_date": "2024-04-24"
}
],
"StateFacet_ConsistsOf": [
{
"facetName": "StateFacet",
"max": "1",
"min": "1",
"facetDescription": "StateFacet captures information on state to be associated with the resource. The state is captured by any controlled vocabulary which is an integral part of the facet. Examples of usage are the state of service e.g., running or down or the state of a virtual machine e.g., activated or unreachable.",
"relationFacet": "ConsistsOf",
"isAdded": false,
"extraProps": [
{
"prop_deno": "custom A",
"prop_val": "valuecustom A",
"prop_tipo": "String"
"type": "text",
"label": "Name",
"name": "name",
"value": "",
"validations": [
{
"name": "required",
"validator": "required",
"message": "The field is required"
}
],
"pattern": null,
"propDescription": "The name of the software artifact being described, e.g., artifactId in maven coordinates, the software name for retail software such as 'Office' (in Microsoft\u2122 Office 2013-SP2)"
},
{
"prop_deno": "custom B",
"prop_val": "valuecustom B",
"prop_tipo": "String"
{
"type": "boolean",
"label": "Optional",
"name": "optional",
"value": "",
"validations": [],
"pattern": null,
"propDescription": "Used to indicate the software optionality, e.g., optional in maven coordinates"
},
{
"type": "text",
"label": "Group",
"name": "group",
"value": "",
"validations": [
{
"name": "required",
"validator": "required",
"message": "The field is required"
}
],
"pattern": null,
"propDescription": "The name of 'group' the software artifact belongs to, e.g., groupId in maven coordinates, company name for retail software software such as 'Microsoft\u2122' (in Microsoft\u2122 Office 2013-SP2)"
},
{
"type": "text",
"label": "Description",
"name": "description",
"value": "",
"validations": [],
"pattern": null,
"propDescription": "A human oriented description of the software artifact being described"
},
{
"type": "text",
"label": "Version",
"name": "version",
"value": "",
"validations": [
{
"name": "required",
"validator": "required",
"message": "The field is required"
}
],
"pattern": null,
"propDescription": "The particular release of the software artifact, e.g., version in maven coordinates, the software version for retail software such as '2013-SP2' (in Microsoft\u2122 Office 2013-SP2)"
},
{
"type": "text",
"label": "Qualifier",
"name": "qualifier",
"value": "",
"validations": [],
"pattern": null,
"propDescription": "A qualifier for the software, e.g., packaging or scope in maven coordinates, target architecture for retail software such as x86 or amd64"
}
],
"prop_value": "On"
"relationOptions": [
"ConsistsOf",
"HasAction",
"HasAddAction",
"HasRemoveAction",
"HasContact",
"HasContributor",
"HasCreator",
"HasCurator",
"HasDeveloper",
"HasMaintainer",
"HasManager",
"HasOwner",
"HasCoverage",
"HasSpatialCoverage",
"HasTemporalCoverage",
"HasMemory",
"HasPersistentMemory",
"HasVolatileMemory",
"IsIdentifiedBy"
],
"relation": "ConsistsOf",
"min": "0",
"max": "many"
},
{
"name": "AccessPointFacet",
"description": "AccessPointFacet captures information on an 'access point' of a resource, i.e., any web-based endpoint to programmatically interact with the resource via a known protocol. For example, it is used to define the network endpoint to contact the service. The endpoint can expose a well-known high-level protocol.",
"properties": null,
"guiProps": [
{
"type": "text",
"label": "Authorization",
"name": "authorization",
"value": "",
"validations": [],
"pattern": null,
"propDescription": "Contains authorisation information. e.g., a token, the couple username:password, etc."
},
{
"type": "text",
"label": "Endpoint",
"name": "endpoint",
"value": "",
"validations": [
{
"name": "required",
"validator": "required",
"message": "The field is required"
}
],
"pattern": null,
"propDescription": "The URI which characterises the specific endpoint instance."
},
{
"type": "text",
"label": "Protocol",
"name": "protocol",
"value": "",
"validations": [],
"pattern": null,
"propDescription": "The high-level protocol used by the access point. The String could contain the version if needed. e.g., Web Map Service (WMS) and not HyperText Transfer Protocol (HTTP) which is already contained in the URI."
},
{
"type": "text",
"label": "EntryName",
"name": "entryName",
"value": "",
"validations": [],
"pattern": null,
"propDescription": "A distinguishing string to be used by clients to identify the access point of interest."
},
{
"type": "text",
"label": "Description",
"name": "description",
"value": "",
"validations": [],
"pattern": null,
"propDescription": "A human-oriented text accompanying the access point."
}
],
"relationOptions": [
"ConsistsOf",
"HasAction",
"HasAddAction",
"HasRemoveAction",
"HasContact",
"HasContributor",
"HasCreator",
"HasCurator",
"HasDeveloper",
"HasMaintainer",
"HasManager",
"HasOwner",
"HasCoverage",
"HasSpatialCoverage",
"HasTemporalCoverage",
"HasMemory",
"HasPersistentMemory",
"HasVolatileMemory",
"IsIdentifiedBy"
],
"relation": "ConsistsOf",
"min": "1",
"max": "many"
},
{
"name": "EventFacet",
"description": "EventFacet captures information on a certain event/happening characterising the life cycle of the resource. Examples of an event are the start time of a virtual machine or the activation time of an electronic service.",
"properties": null,
"guiProps": [
{
"type": "text",
"label": "Event",
"name": "event",
"value": "",
"validations": [
{
"name": "required",
"validator": "required",
"message": "The field is required"
}
],
"pattern": null,
"propDescription": "The typology of event"
},
{
"type": "date",
"label": "Date",
"name": "date",
"value": "",
"validations": [
{
"name": "required",
"validator": "required",
"message": "The field is required"
}
],
"pattern": null,
"propDescription": "The time the event took place/occurred"
}
],
"relationOptions": [
"ConsistsOf",
"HasAction",
"HasAddAction",
"HasRemoveAction",
"HasContact",
"HasContributor",
"HasCreator",
"HasCurator",
"HasDeveloper",
"HasMaintainer",
"HasManager",
"HasOwner",
"HasCoverage",
"HasSpatialCoverage",
"HasTemporalCoverage",
"HasMemory",
"HasPersistentMemory",
"HasVolatileMemory",
"IsIdentifiedBy"
],
"relation": "ConsistsOf",
"min": "1",
"max": "many"
},
{
"name": "StateFacet",
"description": "StateFacet captures information on state to be associated with the resource. The state is captured by any controlled vocabulary which is an integral part of the facet. Examples of usage are the state of service e.g., running or down or the state of a virtual machine e.g., activated or unreachable.",
"properties": null,
"guiProps": [
{
"type": "text",
"label": "Value",
"name": "value",
"value": "",
"validations": [
{
"name": "required",
"validator": "required",
"message": "The field is required"
}
],
"pattern": null,
"propDescription": "The value of the state"
}
],
"relationOptions": [
"ConsistsOf",
"HasAction",
"HasAddAction",
"HasRemoveAction",
"HasContact",
"HasContributor",
"HasCreator",
"HasCurator",
"HasDeveloper",
"HasMaintainer",
"HasManager",
"HasOwner",
"HasCoverage",
"HasSpatialCoverage",
"HasTemporalCoverage",
"HasMemory",
"HasPersistentMemory",
"HasVolatileMemory",
"IsIdentifiedBy"
],
"relation": "ConsistsOf",
"min": "1",
"max": "1"
},
{
"name": "LicenseFacet",
"description": "LicenseFacet captures information on any license associated with the resource to capture the policies governing its exploitation and use. Example of use is the licence of a dataset e.g., Creative Commons Attribution (CC-BY) or the licence of software such as GPL. This facet is used to provide for human knowledge, but it is not excluded the usage by infrastructure services which enforces the respect of the licence e.g., a service which denies the usage of a dataset with Creative Commons Attribution No-Derivatives (CC-BY-ND)licence to produce a new dataset.",
"properties": null,
"guiProps": [
{
"type": "text",
"label": "Name",
"name": "name",
"value": "",
"validations": [
{
"name": "required",
"validator": "required",
"message": "The field is required"
}
],
"pattern": null,
"propDescription": "The common name of the license. e.g., European Union Public Licence (EUPL) 1.1, GNU General Public License (GPL) 2, Berkeley Software Distribution (BSD), Common Creative (CC)."
},
{
"type": "text",
"label": "TextURL",
"name": "textURL",
"value": "",
"validations": [
{
"name": "required",
"validator": "required",
"message": "The field is required"
}
],
"pattern": null,
"propDescription": "The URL to the actual text of the license."
}
],
"relationOptions": [
"ConsistsOf",
"HasAction",
"HasAddAction",
"HasRemoveAction",
"HasContact",
"HasContributor",
"HasCreator",
"HasCurator",
"HasDeveloper",
"HasMaintainer",
"HasManager",
"HasOwner",
"HasCoverage",
"HasSpatialCoverage",
"HasTemporalCoverage",
"HasMemory",
"HasPersistentMemory",
"HasVolatileMemory",
"IsIdentifiedBy"
],
"relation": "ConsistsOf",
"min": "0",
"max": "many"
}
],
"SimplePropertyFacet_ConsistsOf": [
{
"facetName": "SimplePropertyFacet",
"max": "many",
"min": "0",
"facetDescription": "Collect name-value property",
"relationFacet": "ConsistsOf",
"isAdded": false,
"extraProps": [],
"prop_value": "mmmmmm",
"prop_name": "ttttt"
}
],
"SoftwareFacet_ConsistsOf": [
{
"facetName": "SoftwareFacet",
"max": "many",
"min": "0",
"facetDescription": "SoftwareFacet captures information on any software associated with the resource.",
"relationFacet": "ConsistsOf",
"isAdded": false,
"extraProps": [],
"prop_name": "zzzzzz",
"prop_optional": true,
"prop_group": "ffffffff",
"prop_description": "description",
"prop_version": "14.9",
"prop_qualifier": "qualifier"
}
]
"children": []
}