From 1461e9ea1af60c7b68b6c52a208faa55fd70eee5 Mon Sep 17 00:00:00 2001 From: descamps <descamps@cines.fr> Date: Wed, 21 Apr 2021 16:56:40 +0200 Subject: [PATCH] KDE - 21/04/2021 - Merge Standalone into pastis_integration --- .../pastis/model/pua/PuaMetadataDetails.java | 6 +- .../pastis/util/PuaPastisValidator.java | 107 +++---- .../src/main/resources/application.properties | 1 - .../fr/gouv/vitamui/pastis/PastisTest.java | 117 ++++++++ .../swagger/pastis-rest-api-swagger.yaml | 271 ++++++++++++++++++ 5 files changed, 450 insertions(+), 52 deletions(-) create mode 100644 ui/ui-pastis/src/test/java/fr/gouv/vitamui/pastis/PastisTest.java create mode 100644 ui/ui-pastis/swagger/pastis-rest-api-swagger.yaml diff --git a/ui/ui-pastis/src/main/java/fr/gouv/vitamui/pastis/model/pua/PuaMetadataDetails.java b/ui/ui-pastis/src/main/java/fr/gouv/vitamui/pastis/model/pua/PuaMetadataDetails.java index c4da7e747..884419543 100644 --- a/ui/ui-pastis/src/main/java/fr/gouv/vitamui/pastis/model/pua/PuaMetadataDetails.java +++ b/ui/ui-pastis/src/main/java/fr/gouv/vitamui/pastis/model/pua/PuaMetadataDetails.java @@ -15,7 +15,7 @@ public class PuaMetadataDetails { JSONObject properties; List<String> required; PuaMetadata items; - List<String> enumerations; + List<String> enums; public String getType() { return type; @@ -82,10 +82,10 @@ public class PuaMetadataDetails { } public List<String> getEnumerations() { - return enumerations; + return enums; } public void setEnumerations(List<String> enumerations) { - this.enumerations = enumerations; + this.enums = enumerations; } } \ No newline at end of file diff --git a/ui/ui-pastis/src/main/java/fr/gouv/vitamui/pastis/util/PuaPastisValidator.java b/ui/ui-pastis/src/main/java/fr/gouv/vitamui/pastis/util/PuaPastisValidator.java index 1bd10d2ae..801249e71 100644 --- a/ui/ui-pastis/src/main/java/fr/gouv/vitamui/pastis/util/PuaPastisValidator.java +++ b/ui/ui-pastis/src/main/java/fr/gouv/vitamui/pastis/util/PuaPastisValidator.java @@ -4,7 +4,6 @@ import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.databind.MapperFeature; import com.fasterxml.jackson.databind.ObjectMapper; import fr.gouv.vitamui.pastis.model.ElementProperties; -import fr.gouv.vitamui.pastis.model.pua.PuaMetadata; import fr.gouv.vitamui.pastis.model.pua.PuaMetadataDetails; import fr.gouv.vitamui.pastis.model.seda.SedaNode; import org.json.JSONArray; @@ -16,14 +15,12 @@ import org.skyscreamer.jsonassert.JSONCompareMode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; - import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.lang.reflect.Field; import java.util.*; import java.util.stream.Collectors; - import static java.util.stream.Collectors.toList; import static java.util.stream.Collectors.toSet; @@ -200,17 +197,6 @@ public class PuaPastisValidator { for (ElementProperties el: elementsFromTree){ try { - SedaNode sedaElement = getSedaMetadata(el.getName()); - PuaMetadata puaMetadata = new PuaMetadata(); - PuaMetadataDetails puaMetadataDetails = new PuaMetadataDetails(); - getMetaDataFromSeda(el, puaMetadataDetails, sedaElement); - if(!el.getChildren().isEmpty() && !getRequiredProperties(el).isEmpty()) { - puaMetadataDetails.setRequired(getRequiredProperties(el)); - } - // Create a Map<PuaElementName,PuaElementDetails> - Map<String, PuaMetadataDetails> puaMap = new HashMap<>(); - puaMap.put(sedaElement.getName(),puaMetadataDetails); - if (el.getName().equals("Management")) { JSONObject management = getJSONFromManagement(el); jsonArray.put(management); @@ -218,19 +204,19 @@ public class PuaPastisValidator { jsonArray.toString().contains(el.getName())) { ElementProperties element = getElementById(elementsFromTree, el.getParentId()); if(element != null && element.getName().equals("Content")){ - Map<String, PuaMetadataDetails> notManagementMapElement = getJSONObjectFromElement(el,puaMap); + JSONObject notManagementMapElement = getJSONObjectFromElement(el); jsonArray.put(notManagementMapElement); }else{ continue; } } else if (!rulesToIgnore.contains(el.getName()) && !el.getName().equals("Content") && !el.getName().equals("Management")) { - Map<String, PuaMetadataDetails> notManagementMapElement = getJSONObjectFromElement(el,puaMap); + JSONObject notManagementMapElement = getJSONObjectFromElement(el); jsonArray.put(notManagementMapElement); } } catch (IOException e) { - e.printStackTrace(); + LOGGER.info(e.getMessage()); } } return jsonArray; @@ -280,8 +266,7 @@ public class PuaPastisValidator { grandChildrenOfRule.put(grandChild.getName(), childProperties); ruleTypeMetadataDetails.setProperties(grandChildrenOfRule); } else { - nonSpecialChildOfRuleDetails.setType(getPUAMetadataType(childElement.getName())); - nonSpecialChildOfRuleDetails.setDescription(grandChild.getDocumentation()); + getMetaDataFromSeda(grandChild, nonSpecialChildOfRuleDetails, node); nonSpecialChildOfRule.put(grandChild.getName(),nonSpecialChildOfRuleDetails); //Required field requiredNonSpecialChildren.add(grandChild.getName()); @@ -290,14 +275,14 @@ public class PuaPastisValidator { } } // 2. Once the children of special cases are processed, we put them into Rules -> items - JSONObject propretyOfItems = new JSONObject().put("properties", grandChildrenOfRule); - - propretyOfItems.put("required",requiredChildren); - - childrenOfRule.put("items",propretyOfItems); - propertiesRules.put("Rules", childrenOfRule); + if(!grandChildrenOfRule.isEmpty()) { + JSONObject propretyOfItems = new JSONObject().put("properties", grandChildrenOfRule); + propretyOfItems.put("required", requiredChildren); + childrenOfRule.put("items", propretyOfItems); + propertiesRules.put("Rules", childrenOfRule); + } - // 3. Convert to jsonobject via map and update its property + // 3. Convert to jsonobject via map and update its property JSONObject ruleTypeMetadata = new JSONObject(ruleTypeMetadataMap); ruleTypeMetadata.getJSONObject(childElement.getName()).put("properties",propertiesRules); if(!requiredNonSpecialChildren.isEmpty()) { @@ -307,11 +292,6 @@ public class PuaPastisValidator { Object details = nonSpecialChildOfRule.get(e); ruleTypeMetadata.getJSONObject(childElement.getName()).getJSONObject("properties").put(e.toString(),details); }); - - // 4. Set Apprasail Rule (or other rule ) to the root properties of the parent pua - Map puaParentProperties = new HashMap<String, PuaMetadataDetails>(); - puaParentProperties.put("properties", propertiesRules); - // 5. We retrieve parent properties and add more elements to root element properties pua.accumulate("properties", ruleTypeMetadata.toMap()); if(!rulesFound.isEmpty()) pua.put("required", rulesFound); @@ -333,14 +313,14 @@ public class PuaPastisValidator { JSONObject properties = pua; try{ JSONArray accumulatedProperties = pua.getJSONArray("properties"); - JSONArray accumulatedRequired = pua.getJSONArray("required"); String propertiesAsString = accumulatedProperties.toString() .substring(1,accumulatedProperties.toString().length() -1) .replaceAll("(},\\{)",","); properties = new JSONObject(propertiesAsString); JSONObject propertiesRequiredJson = new JSONObject(); propertiesRequiredJson.put("properties",properties); - propertiesRequiredJson.put("required",accumulatedRequired); + if(pua.keySet().contains("required")) + propertiesRequiredJson.put("required",pua.getJSONArray("required")); managementAsJSONObject.put("#management",propertiesRequiredJson); }catch (JSONException e){ LOGGER.info(e.getMessage()); @@ -410,29 +390,51 @@ public class PuaPastisValidator { * <p>Recursively converts an ElementProperty tree and its children, into a Map</p> * @return a HashMap containing a tree of Pua metadata and its children */ - public Map<String, PuaMetadataDetails> getJSONObjectFromElement(ElementProperties elementProperties, Map<String, PuaMetadataDetails> parentElement) + public JSONObject getJSONObjectFromElement(ElementProperties elementProperties) + throws IOException { + SedaNode sedaElement = getSedaMetadata(elementProperties.getName()); + PuaMetadataDetails puaMetadataDetails = new PuaMetadataDetails(); + getMetaDataFromSeda(elementProperties, puaMetadataDetails, sedaElement); + if(!elementProperties.getChildren().isEmpty() && !getRequiredProperties(elementProperties).isEmpty()) { + puaMetadataDetails.setRequired(getRequiredProperties(elementProperties)); + } + JSONObject json = new JSONObject(); + json.put(elementProperties.getName(),new JSONObject(puaMetadataDetails)); + if(!elementProperties.getChildren().isEmpty()) { + json.getJSONObject(elementProperties.getName()).put("properties", new JSONObject()); + getJSONObjectFromElement(elementProperties, json.getJSONObject(elementProperties.getName()).getJSONObject("properties")); + } + return json; + } + + public void getJSONObjectFromElement(ElementProperties elementProperties, JSONObject json) throws IOException { if (elementProperties.getChildren().size() > 0) { - Map <String, PuaMetadataDetails> childMap = new HashMap<>(); for (ElementProperties el:elementProperties.getChildren()){ PuaMetadataDetails puaMetadataDetails = new PuaMetadataDetails(); puaMetadataDetails.setType(getPUAMetadataType(el.getName())); puaMetadataDetails.setDescription(el.getDocumentation()); - childMap.put(el.getName(),puaMetadataDetails); - - if (el.getChildren().size() > 0) { - getJSONObjectFromElement(el,parentElement); + json.put(el.getName(),new JSONObject(puaMetadataDetails)); + if (!el.getChildren().isEmpty()) { + json.getJSONObject(el.getName()).put("properties",new JSONObject()); + getJSONObjectFromElement(el, json.getJSONObject(el.getName()).getJSONObject("properties")); } } - parentElement.get(elementProperties.getName()).setProperties(new JSONObject(childMap)); } - return parentElement; } + public List<String> getRequiredProperties(ElementProperties elementProperties){ List<String> listRequired = new ArrayList<>(); elementProperties.getChildren().forEach(child -> { - if(child.getCardinality().equals("0-1") || child.getCardinality().equals("1")) - listRequired.add(child.getName()); + try { + SedaNode sedaElement = getSedaMetadata(child.getName()); + if((child.getCardinality().equals("1-N") && sedaElement.getCardinality().equals("0-N")) + || (child.getCardinality().equals("1") && !sedaElement.getCardinality().equals("1")) + || sedaElement.getCardinality().equals("1")) + listRequired.add(child.getName()); + } catch (IOException e) { + LOGGER.info(e.getMessage()); + } }); return listRequired; } @@ -452,12 +454,16 @@ public class PuaPastisValidator { try { SedaNode sedaElement = getSedaMetadata(element.getName()); ElementProperties parent = getElementById(elementsFromTree, element.getParentId()); - if ( (parent!= null && parent.getName().equals("Content") && !sedaElement.getCardinality().equals("0-N")) - || (parent == null && element.getName().equals("ArchiveUnitProfile"))) { - list.add(element.getName()); + if ( (parent!= null && + (parent.getName().equals("Content") || element.getName().equals("ArchiveUnitProfile")))) { + if((element.getCardinality().equals("1-N") && sedaElement.getCardinality().equals("0-N")) + || (element.getCardinality().equals("1") && !sedaElement.getCardinality().equals("1")) + || sedaElement.getCardinality().equals("1")) { + list.add(element.getName()); + } } } catch (IOException e) { - e.printStackTrace(); + LOGGER.info(e.getMessage()); } }); return list; @@ -477,9 +483,14 @@ public class PuaPastisValidator { puaMetadataDetails.setMinItems(0); puaMetadataDetails.setMaxItems(1); } - if(!sedaElement.getEnumeration().isEmpty()){ + if(!sedaElement.getEnumeration().isEmpty() && el.getValue() == null){ puaMetadataDetails.setEnumerations(sedaElement.getEnumeration()); } + if(el.getValue() != null){ + ArrayList list = new ArrayList(); + list.add(el.getValue()); + puaMetadataDetails.setEnumerations(list); + } } diff --git a/ui/ui-pastis/src/main/resources/application.properties b/ui/ui-pastis/src/main/resources/application.properties index 313647e7e..620392d7d 100644 --- a/ui/ui-pastis/src/main/resources/application.properties +++ b/ui/ui-pastis/src/main/resources/application.properties @@ -3,7 +3,6 @@ spring.servlet.multipart.max-file-size=10MB spring.servlet.multipart.max-request-size=11MB spring.servlet.multipart.enabled=true - #Spring docs swagger springdoc.api-docs.path=/api-docs springdoc.swagger-ui.path=/open-api.html diff --git a/ui/ui-pastis/src/test/java/fr/gouv/vitamui/pastis/PastisTest.java b/ui/ui-pastis/src/test/java/fr/gouv/vitamui/pastis/PastisTest.java new file mode 100644 index 000000000..f01ac38d1 --- /dev/null +++ b/ui/ui-pastis/src/test/java/fr/gouv/vitamui/pastis/PastisTest.java @@ -0,0 +1,117 @@ +/* +Copyright © CINES - Centre Informatique National pour l'Enseignement Supérieur (2020) + +[dad@cines.fr] + +This software is a computer program whose purpose is to provide +a web application to create, edit, import and export archive +profiles based on the french SEDA standard +(https://redirect.francearchives.fr/seda/). + + +This software is governed by the CeCILL-C license under French law and +abiding by the rules of distribution of free software. You can use, +modify and/ or redistribute the software under the terms of the CeCILL-C +license as circulated by CEA, CNRS and INRIA at the following URL +"http://www.cecill.info". + +As a counterpart to the access to the source code and rights to copy, +modify and redistribute granted by the license, users are provided only +with a limited warranty and the software's author, the holder of the +economic rights, and the successive licensors have only limited +liability. + +In this respect, the user's attention is drawn to the risks associated +with loading, using, modifying and/or developing or reproducing the +software by the user in light of its specific status of free software, +that may mean that it is complicated to manipulate, and that also +therefore means that it is reserved for developers and experienced +professionals having in-depth computer knowledge. Users are therefore +encouraged to load and test the software's suitability as regards their +requirements in conditions enabling the security of their systems and/or +data to be ensured and, more generally, to use and operate it in the +same conditions as regards security. + +The fact that you are presently reading this means that you have had +knowledge of the CeCILL-C license and that you accept its terms. +*/ +package fr.gouv.vitamui.pastis; + +import com.fasterxml.jackson.databind.ObjectMapper; +import fr.gouv.vitamui.pastis.model.ElementProperties; +import fr.gouv.vitamui.pastis.model.factory.*; +import fr.gouv.vitamui.pastis.model.jaxb.*; +import fr.gouv.vitamui.pastis.util.PastisCustomCharacterEscapeHandler; +import fr.gouv.vitamui.pastis.util.PastisGetXmlJsonTree; +import fr.gouv.vitamui.pastis.util.PastisMarshaller; +import fr.gouv.vitamui.pastis.util.PastisSAX2Handler; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.junit4.SpringRunner; +import org.xml.sax.SAXException; +import org.xml.sax.XMLReader; +import org.xml.sax.helpers.XMLReaderFactory; + +import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBException; +import javax.xml.bind.Marshaller; +import java.io.*; +import java.net.URISyntaxException; + +@RunWith(SpringRunner.class) +@TestPropertySource(locations = "/application.properties") +public class PastisTest { + + private static final Logger LOGGER = LoggerFactory.getLogger(PastisTest.class); + + public PastisMarshaller pastisMarshaller = new PastisMarshaller(); + + @Value("${rng.base.file}") + private String rngFileName; + + @Value("${json.base.file}") + private String jsonFileName; + + @Test + public void testIfRngIsPresent() throws FileNotFoundException { + InputStream os = getClass().getClassLoader().getResourceAsStream(this.rngFileName); + } + + @Test + public void testIfRngCanBeGenerated() throws IOException, JAXBException { + // Map a json from file to ElementProperties object + InputStream jsonInputStream = getClass().getClassLoader().getResourceAsStream(jsonFileName); + ObjectMapper objectMapper = new ObjectMapper(); + ElementProperties mappedJson = objectMapper.readValue(jsonInputStream, ElementProperties.class); + mappedJson.initTree(mappedJson); + + String responseFromMarshaller = pastisMarshaller.getMarshalledObject(mappedJson); + Assert.assertFalse("RNG profile generated successfully", responseFromMarshaller.isEmpty()); + } + + @Test + public void testIfJSONCanBeGenerated() throws IOException, JAXBException, URISyntaxException,SAXException { + + PastisSAX2Handler handler = new PastisSAX2Handler(); + PastisGetXmlJsonTree getJson = new PastisGetXmlJsonTree(); + + XMLReader xmlReader = XMLReaderFactory.createXMLReader(); + xmlReader.setContentHandler(handler); + + ClassLoader loader = ClassLoader.getSystemClassLoader(); + + xmlReader.parse(loader.getResource(this.rngFileName).toURI().toString()); + String jsonTree = getJson.getJsonParsedTreeTest(handler.elementRNGRoot); + + Assert.assertNotNull("JSON profile generated successfully",jsonTree); + + } + + + +} diff --git a/ui/ui-pastis/swagger/pastis-rest-api-swagger.yaml b/ui/ui-pastis/swagger/pastis-rest-api-swagger.yaml new file mode 100644 index 000000000..ffa9835fb --- /dev/null +++ b/ui/ui-pastis/swagger/pastis-rest-api-swagger.yaml @@ -0,0 +1,271 @@ +openapi: 3.0.1 +info: + title: Pastis Rest Api +# description: + license: + name: CeCCIL-C + url: https://cecill.info/ + version: v0 +servers: + - url: http://{hostname}:{port} + description: local server + variables: + hostname: + default: localhost + description: API hostname + port: + default: '8080' + description: API port + - url: https://{hostname}:{port} + description: production server + variables: + hostname: + default: localhost + description: API hostname + port: + default: '8080' + description: API port +paths: + /new: + post: + tags: + - profile + operationId: loadProfileFromFile + description: Load profile from file + summary: Load profile from file + requestBody: + content: + multipart/form-data: + schema: + type: object + properties: + file: + type: string + format: binary + responses: + '200': + description: OK + content: + application/json: + schema: + type: string + /getarchiveunitprofile: + post: + tags: + - profile + operationId: getArchiveUnitProfile + description: Return archive unit profile + summary: Return archive unit profile + requestBody: + content: + application/json; charset=utf-8: + schema: + $ref: '#/components/schemas/ElementProperties' + required: true + responses: + '200': + description: OK + content: + application/json: + schema: + type: string + /getarchiveprofile: + post: + tags: + - profile + operationId: getArchiveProfile + description: Return archive profile + summary: Return archive profile + requestBody: + content: + application/json; charset=utf-8: + schema: + $ref: '#/components/schemas/ElementProperties' + required: true + responses: + '200': + description: OK + content: + application/xml: + schema: + type: string + /edit: + post: + tags: + - profile + operationId: loadProfile + description: Get specifique profile + summary: Get specifique profile + parameters: + - name: id + in: query + required: true + schema: + type: string + responses: + '200': + description: OK + content: + '*/*': + schema: + type: string + /createprofilefromfile: + post: + tags: + - profile + operationId: createprofilefromfile + description: Import profile (rng or pua) + summary: Import profile (rng or pua) + requestBody: + content: + multipart/form-data: + schema: + type: object + properties: + file: + type: string + format: binary + responses: + '200': + description: OK + content: + application/json: + schema: + type: string + /test: + get: + tags: + - profile + operationId: test + description: Just for test + responses: + '200': + description: OK + content: + '*/*': + schema: + type: string + /getprofiles: + get: + tags: + - profile + operationId: getFiles + description: Get all profiles + summary: Get all profiles + responses: + '200': + description: OK + content: + '*/*': + schema: + type: array + items: + $ref: '#/components/schemas/PastisProfile' + /getfile: + get: + tags: + - profile + operationId: getFile + responses: + '200': + description: OK + content: + text/plain: + schema: + type: string + /createprofile: + get: + tags: + - profile + operationId: createprofile + responses: + '200': + description: OK + content: + '*/*': + schema: + type: string +components: + schemas: + ElementProperties: + type: object + properties: + name: + type: string + type: + type: string + cardinality: + type: string + groupOrChoice: + type: string + valueOrData: + type: string + dataType: + type: string + value: + type: string + documentation: + type: string + level: + type: integer + format: int32 + id: + type: integer + format: int64 + parentId: + type: integer + format: int64 + choices: + type: array + items: + $ref: '#/components/schemas/ElementProperties' + children: + type: array + items: + $ref: '#/components/schemas/ElementProperties' + puaData: + allOf: + - $ref: '#/components/schemas/PuaData' + PuaData: + type: object + properties: + maximum: + type: integer + format: int32 + minimum: + type: integer + format: int32 + enum: + type: array + items: + type: string + pattern: + type: string + exclusiveMaximum: + type: boolean + additionalProperties: + type: boolean + exclusiveMinimum: + type: boolean + minLenght: + type: integer + format: int32 + maxLenght: + type: integer + format: int32 + PastisProfile: + type: object + properties: + type: + type: string + id: + type: integer + format: int64 + fileName: + type: string + baseName: + type: string + status: + type: string + lastModified: + type: string \ No newline at end of file -- GitLab