diff --git a/.gitignore b/.gitignore
index ca6f1e4c..695f7bc7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -57,6 +57,7 @@ target
out
lib
bin
+data
.java-version
*.orig
*.rej
diff --git a/app/pom.xml b/app/pom.xml
index 2a57c86f..fc676c14 100644
--- a/app/pom.xml
+++ b/app/pom.xml
@@ -63,6 +63,10 @@ SPDX-License-Identifier: Apache-2.0
io.quarkus
quarkus-resteasy-reactive-jaxb
+
+ io.quarkus
+ quarkus-resteasy-reactive-jackson
+
io.quarkus
quarkus-websockets
@@ -191,6 +195,79 @@ SPDX-License-Identifier: Apache-2.0
+
+ org.openapitools
+ openapi-generator-maven-plugin
+ 7.8.0
+
+
+ generate-history-api
+
+ generate
+
+
+ ${project.basedir}/src/main/openapi/history-api.yaml
+ jaxrs-spec
+
+ quarkus
+ org.lfenergy.compas.scl.data.rest.api.scl
+ org.lfenergy.compas.scl.data.rest.api.scl.model
+ false
+
+ true
+ true
+ java8
+ true
+ true
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ org.codehaus.mojo
+ build-helper-maven-plugin
+ 3.6.0
+
+
+ generate-sources
+
+ add-source
+
+
+
+ ${project.build.directory}/generated-sources/openapi/src/gen/java
+
+
+
+
+
+
org.apache.maven.plugins
maven-surefire-plugin
@@ -201,6 +278,7 @@ SPDX-License-Identifier: Apache-2.0
+
@@ -242,7 +320,6 @@ SPDX-License-Identifier: Apache-2.0
native-image
-
true
@@ -267,7 +344,6 @@ SPDX-License-Identifier: Apache-2.0
sonar
-
target/jacoco-report/jacoco.xml,
@@ -278,7 +354,6 @@ SPDX-License-Identifier: Apache-2.0
release
-
true
diff --git a/app/src/main/java/org/lfenergy/compas/scl/data/config/FeatureFlagService.java b/app/src/main/java/org/lfenergy/compas/scl/data/config/FeatureFlagService.java
new file mode 100644
index 00000000..84e9582a
--- /dev/null
+++ b/app/src/main/java/org/lfenergy/compas/scl/data/config/FeatureFlagService.java
@@ -0,0 +1,21 @@
+package org.lfenergy.compas.scl.data.config;
+
+import jakarta.enterprise.context.ApplicationScoped;
+import org.eclipse.microprofile.config.inject.ConfigProperty;
+
+@ApplicationScoped
+public class FeatureFlagService {
+
+ @ConfigProperty(name = "scl-data-service.features.is-history-enabled", defaultValue = "true")
+ boolean isHistoryEnabled;
+ @ConfigProperty(name = "scl-data-service.features.keep-deleted-files", defaultValue = "true")
+ boolean keepDeletedFiles;
+
+ public boolean isHistoryEnabled() {
+ return isHistoryEnabled;
+ }
+
+ public boolean keepDeletedFiles() {
+ return keepDeletedFiles;
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/org/lfenergy/compas/scl/data/rest/api/archive/ArchiveResource.java b/app/src/main/java/org/lfenergy/compas/scl/data/rest/api/archive/ArchiveResource.java
new file mode 100644
index 00000000..abe53a0b
--- /dev/null
+++ b/app/src/main/java/org/lfenergy/compas/scl/data/rest/api/archive/ArchiveResource.java
@@ -0,0 +1,157 @@
+package org.lfenergy.compas.scl.data.rest.api.archive;
+
+import io.smallrye.mutiny.Uni;
+import io.smallrye.mutiny.infrastructure.Infrastructure;
+import jakarta.enterprise.context.RequestScoped;
+import jakarta.inject.Inject;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.eclipse.microprofile.jwt.JsonWebToken;
+import org.lfenergy.compas.scl.data.model.*;
+import org.lfenergy.compas.scl.data.rest.UserInfoProperties;
+import org.lfenergy.compas.scl.data.rest.api.archive.model.ArchivedResourceVersion;
+import org.lfenergy.compas.scl.data.rest.api.archive.model.*;
+import org.lfenergy.compas.scl.data.service.CompasSclDataService;
+
+import java.io.File;
+import java.time.OffsetDateTime;
+import java.util.UUID;
+
+@RequestScoped
+public class ArchiveResource implements ArchivingApi {
+
+ private static final Logger LOGGER = LogManager.getLogger(ArchiveResource.class);
+ private final CompasSclDataService compasSclDataService;
+ private final JsonWebToken jsonWebToken;
+ private final UserInfoProperties userInfoProperties;
+
+ @Inject
+ public ArchiveResource(CompasSclDataService compasSclDataService, JsonWebToken jsonWebToken, UserInfoProperties userInfoProperties) {
+ this.compasSclDataService = compasSclDataService;
+ this.jsonWebToken = jsonWebToken;
+ this.userInfoProperties = userInfoProperties;
+ }
+
+ @Override
+ public Uni archiveResource(UUID id, String version, String xAuthor, String xApprover, String contentType, String xFilename, File body) {
+ LOGGER.info("Archiving resource '{}' for scl resource with id '{}' and version '{}'", xFilename, id, version);
+ return compasSclDataService.archiveResource(id, version, xAuthor, xApprover, contentType, xFilename, body)
+ .runSubscriptionOn(Infrastructure.getDefaultExecutor())
+ .onItem()
+ .transform(this::mapToArchivedResource);
+ }
+
+ @Override
+ public Uni archiveSclResource(UUID id, String version) {
+ LOGGER.info("Archiving scl resource with id '{}' and version '{}'", id, version);
+ String approver = jsonWebToken.getClaim(userInfoProperties.name());
+ return compasSclDataService.archiveSclResource(id, new Version(version), approver)
+ .runSubscriptionOn(Infrastructure.getDefaultExecutor())
+ .onItem()
+ .transform(this::mapToArchivedResource);
+ }
+
+ @Override
+ public Uni retrieveArchivedResourceHistory(UUID id) {
+ LOGGER.info("Retrieving archived resource history for id '{}'", id);
+ return Uni.createFrom()
+ .item(() -> compasSclDataService.getArchivedResourceHistory(id))
+ .runSubscriptionOn(Infrastructure.getDefaultExecutor())
+ .onItem()
+ .transform(this::mapToArchivedResourcesHistory);
+ }
+
+ @Override
+ public Uni searchArchivedResources(ArchivedResourcesSearch archivedResourcesSearch) {
+ LOGGER.info("Retrieving archived resources with filter: {}", archivedResourcesSearch);
+ return Uni.createFrom()
+ .item(() -> getArchivedResourcesMetaItem(archivedResourcesSearch))
+ .runSubscriptionOn(Infrastructure.getDefaultExecutor())
+ .onItem()
+ .transform(this::mapToArchivedResources);
+ }
+
+ private IArchivedResourcesMetaItem getArchivedResourcesMetaItem(ArchivedResourcesSearch archivedResourcesSearch) {
+ String uuid = archivedResourcesSearch.getUuid();
+ if (uuid != null && !uuid.isBlank()) {
+ return compasSclDataService.searchArchivedResources(UUID.fromString(uuid));
+ }
+ String location = archivedResourcesSearch.getLocation();
+ String name = archivedResourcesSearch.getName();
+ String approver = archivedResourcesSearch.getApprover();
+ String contentType = archivedResourcesSearch.getContentType();
+ String type = archivedResourcesSearch.getType();
+ String voltage = archivedResourcesSearch.getVoltage();
+ OffsetDateTime from = archivedResourcesSearch.getFrom();
+ OffsetDateTime to = archivedResourcesSearch.getTo();
+ return compasSclDataService.searchArchivedResources(location, name, approver, contentType, type, voltage, from, to);
+ }
+
+ private ArchivedResource mapToArchivedResource(IAbstractArchivedResourceMetaItem archivedResource) {
+ return new ArchivedResource()
+ .uuid(archivedResource.getId())
+ .location(archivedResource.getLocationId())
+ .name(archivedResource.getName())
+ .note(archivedResource.getNote())
+ .author(archivedResource.getAuthor())
+ .approver(archivedResource.getApprover())
+ .type(archivedResource.getType())
+ .contentType(archivedResource.getContentType())
+ .voltage(archivedResource.getVoltage())
+ .version(archivedResource.getVersion())
+ .modifiedAt(archivedResource.getModifiedAt())
+ .archivedAt(archivedResource.getArchivedAt())
+ .fields(
+ archivedResource.getFields()
+ .stream()
+ .map(item -> new ResourceTag().key(item.getKey()).value(item.getValue())).toList()
+ );
+ }
+
+ private ArchivedResources mapToArchivedResources(IArchivedResourcesMetaItem archivedResources) {
+ return new ArchivedResources()
+ .resources(
+ archivedResources.getResources()
+ .stream()
+ .map(this::mapToArchivedResource)
+ .toList()
+ );
+ }
+
+ private ArchivedResourcesHistory mapToArchivedResourcesHistory(IArchivedResourcesHistoryMetaItem archivedResourcesHistoryMetaItem) {
+ return new ArchivedResourcesHistory()
+ .versions(
+ archivedResourcesHistoryMetaItem.getVersions()
+ .stream()
+ .map(this::mapToArchivedResourceVersion)
+ .toList()
+ );
+ }
+
+ private ArchivedResourceVersion mapToArchivedResourceVersion(IArchivedResourceVersion resourceVersion) {
+ return new ArchivedResourceVersion()
+ .uuid(resourceVersion.getId())
+ .location(resourceVersion.getLocation())
+ .name(resourceVersion.getName())
+ .note(resourceVersion.getNote())
+ .author(resourceVersion.getAuthor())
+ .approver(resourceVersion.getApprover())
+ .type(resourceVersion.getType())
+ .contentType(resourceVersion.getContentType())
+ .voltage(resourceVersion.getVoltage())
+ .version(resourceVersion.getVersion())
+ .modifiedAt(resourceVersion.getModifiedAt())
+ .archivedAt(resourceVersion.getArchivedAt())
+ .fields(resourceVersion.getFields()
+ .stream()
+ .map(field ->
+ new ResourceTag()
+ .key(field.getKey())
+ .value(field.getValue())
+ )
+ .toList()
+ )
+ .comment(resourceVersion.getComment())
+ .archived(resourceVersion.isArchived());
+ }
+}
diff --git a/app/src/main/java/org/lfenergy/compas/scl/data/rest/api/archive/ArchivingApi.java b/app/src/main/java/org/lfenergy/compas/scl/data/rest/api/archive/ArchivingApi.java
new file mode 100644
index 00000000..39a6fd15
--- /dev/null
+++ b/app/src/main/java/org/lfenergy/compas/scl/data/rest/api/archive/ArchivingApi.java
@@ -0,0 +1,42 @@
+package org.lfenergy.compas.scl.data.rest.api.archive;
+
+import io.smallrye.common.annotation.Blocking;
+import io.smallrye.mutiny.Uni;
+import jakarta.validation.Valid;
+import jakarta.ws.rs.*;
+import org.lfenergy.compas.scl.data.rest.api.archive.model.ArchivedResource;
+import org.lfenergy.compas.scl.data.rest.api.archive.model.ArchivedResources;
+import org.lfenergy.compas.scl.data.rest.api.archive.model.ArchivedResourcesHistory;
+import org.lfenergy.compas.scl.data.rest.api.archive.model.ArchivedResourcesSearch;
+
+import java.io.File;
+import java.util.UUID;
+
+
+@Path("/api/archive")
+@jakarta.annotation.Generated(value = "org.openapitools.codegen.languages.JavaJAXRSSpecServerCodegen", date = "2024-12-06T09:13:22.882514600+01:00[Europe/Vienna]", comments = "Generator version: 7.8.0")
+public interface ArchivingApi {
+
+ @POST
+ @Path("/referenced-resource/{id}/versions/{version}")
+ @Blocking
+ @Produces({ "application/json" })
+ Uni archiveResource(@PathParam("id") UUID id, @PathParam("version") String version, @HeaderParam("X-author") String xAuthor, @HeaderParam("X-approver") String xApprover, @HeaderParam("Content-Type") String contentType, @HeaderParam("X-filename") String xFilename, @Valid File body);
+
+ @POST
+ @Path("/scl/{id}/versions/{version}")
+ @Blocking
+ @Produces({ "application/json" })
+ Uni archiveSclResource(@PathParam("id") UUID id, @PathParam("version") String version);
+
+ @GET
+ @Path("/resources/{id}/versions")
+ @Produces({ "application/json" })
+ Uni retrieveArchivedResourceHistory(@PathParam("id") UUID id);
+
+ @POST
+ @Path("/resources/search")
+ @Consumes({ "application/json" })
+ @Produces({ "application/json" })
+ Uni searchArchivedResources(@Valid ArchivedResourcesSearch archivedResourcesSearch);
+}
diff --git a/app/src/main/java/org/lfenergy/compas/scl/data/rest/api/archive/model/ArchivedResource.java b/app/src/main/java/org/lfenergy/compas/scl/data/rest/api/archive/model/ArchivedResource.java
new file mode 100644
index 00000000..794f7cf8
--- /dev/null
+++ b/app/src/main/java/org/lfenergy/compas/scl/data/rest/api/archive/model/ArchivedResource.java
@@ -0,0 +1,357 @@
+package org.lfenergy.compas.scl.data.rest.api.archive.model;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.annotation.JsonTypeName;
+import jakarta.validation.Valid;
+import jakarta.validation.constraints.NotNull;
+
+import java.time.OffsetDateTime;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+
+
+@JsonTypeName("ArchivedResource")
+@jakarta.annotation.Generated(value = "org.openapitools.codegen.languages.JavaJAXRSSpecServerCodegen", date = "2024-11-18T15:39:21.464141400+01:00[Europe/Vienna]", comments = "Generator version: 7.8.0")
+public class ArchivedResource {
+ private String uuid;
+ private String location;
+ private String name;
+ private String note;
+ private String author;
+ private String approver;
+ private String type;
+ private String contentType;
+ private String voltage;
+ private String version;
+ private OffsetDateTime modifiedAt;
+ private OffsetDateTime archivedAt;
+ private @Valid List<@Valid ResourceTag> fields = new ArrayList<>();
+
+ /**
+ * Unique resource identifier
+ **/
+ public ArchivedResource uuid(String uuid) {
+ this.uuid = uuid;
+ return this;
+ }
+
+
+ @JsonProperty("uuid")
+ @NotNull public String getUuid() {
+ return uuid;
+ }
+
+ @JsonProperty("uuid")
+ public void setUuid(String uuid) {
+ this.uuid = uuid;
+ }
+
+ /**
+ * Location of the resource, might be empty
+ **/
+ public ArchivedResource location(String location) {
+ this.location = location;
+ return this;
+ }
+
+
+ @JsonProperty("location")
+ public String getLocation() {
+ return location;
+ }
+
+ @JsonProperty("location")
+ public void setLocation(String location) {
+ this.location = location;
+ }
+
+ /**
+ * Resource name
+ **/
+ public ArchivedResource name(String name) {
+ this.name = name;
+ return this;
+ }
+
+
+ @JsonProperty("name")
+ @NotNull public String getName() {
+ return name;
+ }
+
+ @JsonProperty("name")
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ /**
+ * Versioning note
+ **/
+ public ArchivedResource note(String note) {
+ this.note = note;
+ return this;
+ }
+
+
+ @JsonProperty("note")
+ public String getNote() {
+ return note;
+ }
+
+ @JsonProperty("note")
+ public void setNote(String note) {
+ this.note = note;
+ }
+
+ /**
+ * Modifying author
+ **/
+ public ArchivedResource author(String author) {
+ this.author = author;
+ return this;
+ }
+
+
+ @JsonProperty("author")
+ @NotNull public String getAuthor() {
+ return author;
+ }
+
+ @JsonProperty("author")
+ public void setAuthor(String author) {
+ this.author = author;
+ }
+
+ /**
+ * Name of the approver
+ **/
+ public ArchivedResource approver(String approver) {
+ this.approver = approver;
+ return this;
+ }
+
+
+ @JsonProperty("approver")
+ public String getApprover() {
+ return approver;
+ }
+
+ @JsonProperty("approver")
+ public void setApprover(String approver) {
+ this.approver = approver;
+ }
+
+ /**
+ * Content type
+ **/
+ public ArchivedResource type(String type) {
+ this.type = type;
+ return this;
+ }
+
+
+ @JsonProperty("type")
+ public String getType() {
+ return type;
+ }
+
+ @JsonProperty("type")
+ public void setType(String type) {
+ this.type = type;
+ }
+
+ /**
+ * Content type
+ **/
+ public ArchivedResource contentType(String contentType) {
+ this.contentType = contentType;
+ return this;
+ }
+
+
+ @JsonProperty("contentType")
+ @NotNull public String getContentType() {
+ return contentType;
+ }
+
+ @JsonProperty("contentType")
+ public void setContentType(String contentType) {
+ this.contentType = contentType;
+ }
+
+ /**
+ * Content type
+ **/
+ public ArchivedResource voltage(String voltage) {
+ this.voltage = voltage;
+ return this;
+ }
+
+
+ @JsonProperty("voltage")
+ public String getVoltage() {
+ return voltage;
+ }
+
+ @JsonProperty("voltage")
+ public void setVoltage(String voltage) {
+ this.voltage = voltage;
+ }
+
+ /**
+ * Version
+ **/
+ public ArchivedResource version(String version) {
+ this.version = version;
+ return this;
+ }
+
+
+ @JsonProperty("version")
+ @NotNull public String getVersion() {
+ return version;
+ }
+
+ @JsonProperty("version")
+ public void setVersion(String version) {
+ this.version = version;
+ }
+
+ /**
+ **/
+ public ArchivedResource modifiedAt(OffsetDateTime modifiedAt) {
+ this.modifiedAt = modifiedAt;
+ return this;
+ }
+
+
+ @JsonProperty("modifiedAt")
+ @NotNull public OffsetDateTime getModifiedAt() {
+ return modifiedAt;
+ }
+
+ @JsonProperty("modifiedAt")
+ public void setModifiedAt(OffsetDateTime modifiedAt) {
+ this.modifiedAt = modifiedAt;
+ }
+
+ /**
+ **/
+ public ArchivedResource archivedAt(OffsetDateTime archivedAt) {
+ this.archivedAt = archivedAt;
+ return this;
+ }
+
+
+ @JsonProperty("archivedAt")
+ @NotNull public OffsetDateTime getArchivedAt() {
+ return archivedAt;
+ }
+
+ @JsonProperty("archivedAt")
+ public void setArchivedAt(OffsetDateTime archivedAt) {
+ this.archivedAt = archivedAt;
+ }
+
+ /**
+ **/
+ public ArchivedResource fields(List<@Valid ResourceTag> fields) {
+ this.fields = fields;
+ return this;
+ }
+
+
+ @JsonProperty("fields")
+ @NotNull @Valid public List<@Valid ResourceTag> getFields() {
+ return fields;
+ }
+
+ @JsonProperty("fields")
+ public void setFields(List<@Valid ResourceTag> fields) {
+ this.fields = fields;
+ }
+
+ public ArchivedResource addFieldsItem(ResourceTag fieldsItem) {
+ if (this.fields == null) {
+ this.fields = new ArrayList<>();
+ }
+
+ this.fields.add(fieldsItem);
+ return this;
+ }
+
+ public ArchivedResource removeFieldsItem(ResourceTag fieldsItem) {
+ if (fieldsItem != null && this.fields != null) {
+ this.fields.remove(fieldsItem);
+ }
+
+ return this;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ ArchivedResource archivedResource = (ArchivedResource) o;
+ return Objects.equals(this.uuid, archivedResource.uuid) &&
+ Objects.equals(this.location, archivedResource.location) &&
+ Objects.equals(this.name, archivedResource.name) &&
+ Objects.equals(this.note, archivedResource.note) &&
+ Objects.equals(this.author, archivedResource.author) &&
+ Objects.equals(this.approver, archivedResource.approver) &&
+ Objects.equals(this.type, archivedResource.type) &&
+ Objects.equals(this.contentType, archivedResource.contentType) &&
+ Objects.equals(this.voltage, archivedResource.voltage) &&
+ Objects.equals(this.version, archivedResource.version) &&
+ Objects.equals(this.modifiedAt, archivedResource.modifiedAt) &&
+ Objects.equals(this.archivedAt, archivedResource.archivedAt) &&
+ Objects.equals(this.fields, archivedResource.fields);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(uuid, location, name, note, author, approver, type, contentType, voltage, version, modifiedAt, archivedAt, fields);
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("class ArchivedResource {\n");
+
+ sb.append(" uuid: ").append(toIndentedString(uuid)).append("\n");
+ sb.append(" location: ").append(toIndentedString(location)).append("\n");
+ sb.append(" name: ").append(toIndentedString(name)).append("\n");
+ sb.append(" note: ").append(toIndentedString(note)).append("\n");
+ sb.append(" author: ").append(toIndentedString(author)).append("\n");
+ sb.append(" approver: ").append(toIndentedString(approver)).append("\n");
+ sb.append(" type: ").append(toIndentedString(type)).append("\n");
+ sb.append(" contentType: ").append(toIndentedString(contentType)).append("\n");
+ sb.append(" voltage: ").append(toIndentedString(voltage)).append("\n");
+ sb.append(" version: ").append(toIndentedString(version)).append("\n");
+ sb.append(" modifiedAt: ").append(toIndentedString(modifiedAt)).append("\n");
+ sb.append(" archivedAt: ").append(toIndentedString(archivedAt)).append("\n");
+ sb.append(" fields: ").append(toIndentedString(fields)).append("\n");
+ sb.append("}");
+ return sb.toString();
+ }
+
+ /**
+ * Convert the given object to string with each line indented by 4 spaces
+ * (except the first line).
+ */
+ private String toIndentedString(Object o) {
+ if (o == null) {
+ return "null";
+ }
+ return o.toString().replace("\n", "\n ");
+ }
+
+
+}
+
diff --git a/app/src/main/java/org/lfenergy/compas/scl/data/rest/api/archive/model/ArchivedResourceVersion.java b/app/src/main/java/org/lfenergy/compas/scl/data/rest/api/archive/model/ArchivedResourceVersion.java
new file mode 100644
index 00000000..9a0e70f0
--- /dev/null
+++ b/app/src/main/java/org/lfenergy/compas/scl/data/rest/api/archive/model/ArchivedResourceVersion.java
@@ -0,0 +1,401 @@
+package org.lfenergy.compas.scl.data.rest.api.archive.model;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.annotation.JsonTypeName;
+import jakarta.validation.Valid;
+import jakarta.validation.constraints.NotNull;
+
+import java.time.OffsetDateTime;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+
+
+@JsonTypeName("ArchivedResourceVersion")
+@jakarta.annotation.Generated(value = "org.openapitools.codegen.languages.JavaJAXRSSpecServerCodegen", date = "2024-12-06T09:13:22.882514600+01:00[Europe/Vienna]", comments = "Generator version: 7.8.0")
+public class ArchivedResourceVersion {
+ private String uuid;
+ private String location;
+ private String name;
+ private String note;
+ private String author;
+ private String approver;
+ private String type;
+ private String contentType;
+ private String voltage;
+ private String version;
+ private OffsetDateTime modifiedAt;
+ private OffsetDateTime archivedAt;
+ private @Valid List<@Valid ResourceTag> fields = new ArrayList<>();
+ private String comment;
+ private Boolean archived = false;
+
+ /**
+ * Unique resource identifier
+ **/
+ public ArchivedResourceVersion uuid(String uuid) {
+ this.uuid = uuid;
+ return this;
+ }
+
+
+ @JsonProperty("uuid")
+ @NotNull public String getUuid() {
+ return uuid;
+ }
+
+ @JsonProperty("uuid")
+ public void setUuid(String uuid) {
+ this.uuid = uuid;
+ }
+
+ /**
+ * Location of the resource, might be empty
+ **/
+ public ArchivedResourceVersion location(String location) {
+ this.location = location;
+ return this;
+ }
+
+
+ @JsonProperty("location")
+ public String getLocation() {
+ return location;
+ }
+
+ @JsonProperty("location")
+ public void setLocation(String location) {
+ this.location = location;
+ }
+
+ /**
+ * Resource name
+ **/
+ public ArchivedResourceVersion name(String name) {
+ this.name = name;
+ return this;
+ }
+
+
+ @JsonProperty("name")
+ @NotNull public String getName() {
+ return name;
+ }
+
+ @JsonProperty("name")
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ /**
+ * Versioning note
+ **/
+ public ArchivedResourceVersion note(String note) {
+ this.note = note;
+ return this;
+ }
+
+
+ @JsonProperty("note")
+ public String getNote() {
+ return note;
+ }
+
+ @JsonProperty("note")
+ public void setNote(String note) {
+ this.note = note;
+ }
+
+ /**
+ * Modifying author
+ **/
+ public ArchivedResourceVersion author(String author) {
+ this.author = author;
+ return this;
+ }
+
+
+ @JsonProperty("author")
+ @NotNull public String getAuthor() {
+ return author;
+ }
+
+ @JsonProperty("author")
+ public void setAuthor(String author) {
+ this.author = author;
+ }
+
+ /**
+ * Name of the approver
+ **/
+ public ArchivedResourceVersion approver(String approver) {
+ this.approver = approver;
+ return this;
+ }
+
+
+ @JsonProperty("approver")
+ public String getApprover() {
+ return approver;
+ }
+
+ @JsonProperty("approver")
+ public void setApprover(String approver) {
+ this.approver = approver;
+ }
+
+ /**
+ * Content type
+ **/
+ public ArchivedResourceVersion type(String type) {
+ this.type = type;
+ return this;
+ }
+
+
+ @JsonProperty("type")
+ public String getType() {
+ return type;
+ }
+
+ @JsonProperty("type")
+ public void setType(String type) {
+ this.type = type;
+ }
+
+ /**
+ * Content type
+ **/
+ public ArchivedResourceVersion contentType(String contentType) {
+ this.contentType = contentType;
+ return this;
+ }
+
+
+ @JsonProperty("contentType")
+ @NotNull public String getContentType() {
+ return contentType;
+ }
+
+ @JsonProperty("contentType")
+ public void setContentType(String contentType) {
+ this.contentType = contentType;
+ }
+
+ /**
+ * Content type
+ **/
+ public ArchivedResourceVersion voltage(String voltage) {
+ this.voltage = voltage;
+ return this;
+ }
+
+
+ @JsonProperty("voltage")
+ public String getVoltage() {
+ return voltage;
+ }
+
+ @JsonProperty("voltage")
+ public void setVoltage(String voltage) {
+ this.voltage = voltage;
+ }
+
+ /**
+ * Version
+ **/
+ public ArchivedResourceVersion version(String version) {
+ this.version = version;
+ return this;
+ }
+
+
+ @JsonProperty("version")
+ @NotNull public String getVersion() {
+ return version;
+ }
+
+ @JsonProperty("version")
+ public void setVersion(String version) {
+ this.version = version;
+ }
+
+ /**
+ **/
+ public ArchivedResourceVersion modifiedAt(OffsetDateTime modifiedAt) {
+ this.modifiedAt = modifiedAt;
+ return this;
+ }
+
+
+ @JsonProperty("modifiedAt")
+ @NotNull public OffsetDateTime getModifiedAt() {
+ return modifiedAt;
+ }
+
+ @JsonProperty("modifiedAt")
+ public void setModifiedAt(OffsetDateTime modifiedAt) {
+ this.modifiedAt = modifiedAt;
+ }
+
+ /**
+ **/
+ public ArchivedResourceVersion archivedAt(OffsetDateTime archivedAt) {
+ this.archivedAt = archivedAt;
+ return this;
+ }
+
+
+ @JsonProperty("archivedAt")
+ @NotNull public OffsetDateTime getArchivedAt() {
+ return archivedAt;
+ }
+
+ @JsonProperty("archivedAt")
+ public void setArchivedAt(OffsetDateTime archivedAt) {
+ this.archivedAt = archivedAt;
+ }
+
+ /**
+ **/
+ public ArchivedResourceVersion fields(List<@Valid ResourceTag> fields) {
+ this.fields = fields;
+ return this;
+ }
+
+
+ @JsonProperty("fields")
+ @NotNull @Valid public List<@Valid ResourceTag> getFields() {
+ return fields;
+ }
+
+ @JsonProperty("fields")
+ public void setFields(List<@Valid ResourceTag> fields) {
+ this.fields = fields;
+ }
+
+ public ArchivedResourceVersion addFieldsItem(ResourceTag fieldsItem) {
+ if (this.fields == null) {
+ this.fields = new ArrayList<>();
+ }
+
+ this.fields.add(fieldsItem);
+ return this;
+ }
+
+ public ArchivedResourceVersion removeFieldsItem(ResourceTag fieldsItem) {
+ if (fieldsItem != null && this.fields != null) {
+ this.fields.remove(fieldsItem);
+ }
+
+ return this;
+ }
+ /**
+ * Comment given when uploading the data resource
+ **/
+ public ArchivedResourceVersion comment(String comment) {
+ this.comment = comment;
+ return this;
+ }
+
+
+ @JsonProperty("comment")
+ public String getComment() {
+ return comment;
+ }
+
+ @JsonProperty("comment")
+ public void setComment(String comment) {
+ this.comment = comment;
+ }
+
+ /**
+ * Defines if given data resource is archived
+ **/
+ public ArchivedResourceVersion archived(Boolean archived) {
+ this.archived = archived;
+ return this;
+ }
+
+
+ @JsonProperty("archived")
+ @NotNull public Boolean getArchived() {
+ return archived;
+ }
+
+ @JsonProperty("archived")
+ public void setArchived(Boolean archived) {
+ this.archived = archived;
+ }
+
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ ArchivedResourceVersion archivedResourceVersion = (ArchivedResourceVersion) o;
+ return Objects.equals(this.uuid, archivedResourceVersion.uuid) &&
+ Objects.equals(this.location, archivedResourceVersion.location) &&
+ Objects.equals(this.name, archivedResourceVersion.name) &&
+ Objects.equals(this.note, archivedResourceVersion.note) &&
+ Objects.equals(this.author, archivedResourceVersion.author) &&
+ Objects.equals(this.approver, archivedResourceVersion.approver) &&
+ Objects.equals(this.type, archivedResourceVersion.type) &&
+ Objects.equals(this.contentType, archivedResourceVersion.contentType) &&
+ Objects.equals(this.voltage, archivedResourceVersion.voltage) &&
+ Objects.equals(this.version, archivedResourceVersion.version) &&
+ Objects.equals(this.modifiedAt, archivedResourceVersion.modifiedAt) &&
+ Objects.equals(this.archivedAt, archivedResourceVersion.archivedAt) &&
+ Objects.equals(this.fields, archivedResourceVersion.fields) &&
+ Objects.equals(this.comment, archivedResourceVersion.comment) &&
+ Objects.equals(this.archived, archivedResourceVersion.archived);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(uuid, location, name, note, author, approver, type, contentType, voltage, version, modifiedAt, archivedAt, fields, comment, archived);
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("class ArchivedResourceVersion {\n");
+
+ sb.append(" uuid: ").append(toIndentedString(uuid)).append("\n");
+ sb.append(" location: ").append(toIndentedString(location)).append("\n");
+ sb.append(" name: ").append(toIndentedString(name)).append("\n");
+ sb.append(" note: ").append(toIndentedString(note)).append("\n");
+ sb.append(" author: ").append(toIndentedString(author)).append("\n");
+ sb.append(" approver: ").append(toIndentedString(approver)).append("\n");
+ sb.append(" type: ").append(toIndentedString(type)).append("\n");
+ sb.append(" contentType: ").append(toIndentedString(contentType)).append("\n");
+ sb.append(" voltage: ").append(toIndentedString(voltage)).append("\n");
+ sb.append(" version: ").append(toIndentedString(version)).append("\n");
+ sb.append(" modifiedAt: ").append(toIndentedString(modifiedAt)).append("\n");
+ sb.append(" archivedAt: ").append(toIndentedString(archivedAt)).append("\n");
+ sb.append(" fields: ").append(toIndentedString(fields)).append("\n");
+ sb.append(" comment: ").append(toIndentedString(comment)).append("\n");
+ sb.append(" archived: ").append(toIndentedString(archived)).append("\n");
+ sb.append("}");
+ return sb.toString();
+ }
+
+ /**
+ * Convert the given object to string with each line indented by 4 spaces
+ * (except the first line).
+ */
+ private String toIndentedString(Object o) {
+ if (o == null) {
+ return "null";
+ }
+ return o.toString().replace("\n", "\n ");
+ }
+
+
+}
+
diff --git a/app/src/main/java/org/lfenergy/compas/scl/data/rest/api/archive/model/ArchivedResources.java b/app/src/main/java/org/lfenergy/compas/scl/data/rest/api/archive/model/ArchivedResources.java
new file mode 100644
index 00000000..f760ce27
--- /dev/null
+++ b/app/src/main/java/org/lfenergy/compas/scl/data/rest/api/archive/model/ArchivedResources.java
@@ -0,0 +1,94 @@
+package org.lfenergy.compas.scl.data.rest.api.archive.model;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.annotation.JsonTypeName;
+import jakarta.validation.Valid;
+import jakarta.validation.constraints.NotNull;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+
+
+@JsonTypeName("ArchivedResources")
+@jakarta.annotation.Generated(value = "org.openapitools.codegen.languages.JavaJAXRSSpecServerCodegen", date = "2024-11-18T15:39:21.464141400+01:00[Europe/Vienna]", comments = "Generator version: 7.8.0")
+public class ArchivedResources {
+ private @Valid List<@Valid ArchivedResource> resources = new ArrayList<>();
+
+ /**
+ **/
+ public ArchivedResources resources(List<@Valid ArchivedResource> resources) {
+ this.resources = resources;
+ return this;
+ }
+
+
+ @JsonProperty("resources")
+ @NotNull @Valid public List<@Valid ArchivedResource> getResources() {
+ return resources;
+ }
+
+ @JsonProperty("resources")
+ public void setResources(List<@Valid ArchivedResource> resources) {
+ this.resources = resources;
+ }
+
+ public ArchivedResources addResourcesItem(ArchivedResource resourcesItem) {
+ if (this.resources == null) {
+ this.resources = new ArrayList<>();
+ }
+
+ this.resources.add(resourcesItem);
+ return this;
+ }
+
+ public ArchivedResources removeResourcesItem(ArchivedResource resourcesItem) {
+ if (resourcesItem != null && this.resources != null) {
+ this.resources.remove(resourcesItem);
+ }
+
+ return this;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ ArchivedResources archivedResources = (ArchivedResources) o;
+ return Objects.equals(this.resources, archivedResources.resources);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(resources);
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("class ArchivedResources {\n");
+
+ sb.append(" resources: ").append(toIndentedString(resources)).append("\n");
+ sb.append("}");
+ return sb.toString();
+ }
+
+ /**
+ * Convert the given object to string with each line indented by 4 spaces
+ * (except the first line).
+ */
+ private String toIndentedString(Object o) {
+ if (o == null) {
+ return "null";
+ }
+ return o.toString().replace("\n", "\n ");
+ }
+
+
+}
+
diff --git a/app/src/main/java/org/lfenergy/compas/scl/data/rest/api/archive/model/ArchivedResourcesHistory.java b/app/src/main/java/org/lfenergy/compas/scl/data/rest/api/archive/model/ArchivedResourcesHistory.java
new file mode 100644
index 00000000..07aae09e
--- /dev/null
+++ b/app/src/main/java/org/lfenergy/compas/scl/data/rest/api/archive/model/ArchivedResourcesHistory.java
@@ -0,0 +1,94 @@
+package org.lfenergy.compas.scl.data.rest.api.archive.model;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.annotation.JsonTypeName;
+import jakarta.validation.Valid;
+import jakarta.validation.constraints.NotNull;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+
+
+@JsonTypeName("ArchivedResourcesHistory")
+@jakarta.annotation.Generated(value = "org.openapitools.codegen.languages.JavaJAXRSSpecServerCodegen", date = "2024-12-06T09:13:22.882514600+01:00[Europe/Vienna]", comments = "Generator version: 7.8.0")
+public class ArchivedResourcesHistory {
+ private @Valid List versions = new ArrayList<>();
+
+ /**
+ **/
+ public ArchivedResourcesHistory versions(List versions) {
+ this.versions = versions;
+ return this;
+ }
+
+
+ @JsonProperty("versions")
+ @NotNull @Valid public List<@Valid ArchivedResourceVersion> getVersions() {
+ return versions;
+ }
+
+ @JsonProperty("versions")
+ public void setVersions(List versions) {
+ this.versions = versions;
+ }
+
+ public ArchivedResourcesHistory addVersionsItem(ArchivedResourceVersion versionsItem) {
+ if (this.versions == null) {
+ this.versions = new ArrayList<>();
+ }
+
+ this.versions.add(versionsItem);
+ return this;
+ }
+
+ public ArchivedResourcesHistory removeVersionsItem(ArchivedResourceVersion versionsItem) {
+ if (versionsItem != null && this.versions != null) {
+ this.versions.remove(versionsItem);
+ }
+
+ return this;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ ArchivedResourcesHistory archivedResourcesHistory = (ArchivedResourcesHistory) o;
+ return Objects.equals(this.versions, archivedResourcesHistory.versions);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(versions);
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("class ArchivedResourcesHistory {\n");
+
+ sb.append(" versions: ").append(toIndentedString(versions)).append("\n");
+ sb.append("}");
+ return sb.toString();
+ }
+
+ /**
+ * Convert the given object to string with each line indented by 4 spaces
+ * (except the first line).
+ */
+ private String toIndentedString(Object o) {
+ if (o == null) {
+ return "null";
+ }
+ return o.toString().replace("\n", "\n ");
+ }
+
+
+}
+
diff --git a/app/src/main/java/org/lfenergy/compas/scl/data/rest/api/archive/model/ArchivedResourcesSearch.java b/app/src/main/java/org/lfenergy/compas/scl/data/rest/api/archive/model/ArchivedResourcesSearch.java
new file mode 100644
index 00000000..d682cd1e
--- /dev/null
+++ b/app/src/main/java/org/lfenergy/compas/scl/data/rest/api/archive/model/ArchivedResourcesSearch.java
@@ -0,0 +1,252 @@
+package org.lfenergy.compas.scl.data.rest.api.archive.model;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.annotation.JsonTypeName;
+
+import java.time.OffsetDateTime;
+import java.util.Objects;
+
+
+
+@JsonTypeName("ArchivedResourcesSearch")
+@jakarta.annotation.Generated(value = "org.openapitools.codegen.languages.JavaJAXRSSpecServerCodegen", date = "2024-11-18T15:39:21.464141400+01:00[Europe/Vienna]", comments = "Generator version: 7.8.0")
+public class ArchivedResourcesSearch {
+ private String uuid;
+ private String location;
+ private String name;
+ private String approver;
+ private String contentType;
+ private String type;
+ private String voltage;
+ private OffsetDateTime from;
+ private OffsetDateTime to;
+
+ /**
+ * If uuid is set no other filter must be set
+ **/
+ public ArchivedResourcesSearch uuid(String uuid) {
+ this.uuid = uuid;
+ return this;
+ }
+
+
+ @JsonProperty("uuid")
+ public String getUuid() {
+ return uuid;
+ }
+
+ @JsonProperty("uuid")
+ public void setUuid(String uuid) {
+ this.uuid = uuid;
+ }
+
+ /**
+ * Exact match of a location
+ **/
+ public ArchivedResourcesSearch location(String location) {
+ this.location = location;
+ return this;
+ }
+
+
+ @JsonProperty("location")
+ public String getLocation() {
+ return location;
+ }
+
+ @JsonProperty("location")
+ public void setLocation(String location) {
+ this.location = location;
+ }
+
+ /**
+ * Partially match allowed
+ **/
+ public ArchivedResourcesSearch name(String name) {
+ this.name = name;
+ return this;
+ }
+
+
+ @JsonProperty("name")
+ public String getName() {
+ return name;
+ }
+
+ @JsonProperty("name")
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ /**
+ * Fulltext match which can be retrieved via extra endpoint
+ **/
+ public ArchivedResourcesSearch approver(String approver) {
+ this.approver = approver;
+ return this;
+ }
+
+
+ @JsonProperty("approver")
+ public String getApprover() {
+ return approver;
+ }
+
+ @JsonProperty("approver")
+ public void setApprover(String approver) {
+ this.approver = approver;
+ }
+
+ /**
+ * Fulltext match set to one of the supported scl types: SSD, IID, ICD, SCD, CID, SED, ISD, STD, etc.
+ **/
+ public ArchivedResourcesSearch contentType(String contentType) {
+ this.contentType = contentType;
+ return this;
+ }
+
+
+ @JsonProperty("contentType")
+ public String getContentType() {
+ return contentType;
+ }
+
+ @JsonProperty("contentType")
+ public void setContentType(String contentType) {
+ this.contentType = contentType;
+ }
+
+ /**
+ * Type of the documented entity eg. Schütz, Leittechnik, etc
+ **/
+ public ArchivedResourcesSearch type(String type) {
+ this.type = type;
+ return this;
+ }
+
+
+ @JsonProperty("type")
+ public String getType() {
+ return type;
+ }
+
+ @JsonProperty("type")
+ public void setType(String type) {
+ this.type = type;
+ }
+
+ /**
+ * Voltage of the documented entity eg. 110, 220, 380
+ **/
+ public ArchivedResourcesSearch voltage(String voltage) {
+ this.voltage = voltage;
+ return this;
+ }
+
+
+ @JsonProperty("voltage")
+ public String getVoltage() {
+ return voltage;
+ }
+
+ @JsonProperty("voltage")
+ public void setVoltage(String voltage) {
+ this.voltage = voltage;
+ }
+
+ /**
+ * Starting date from where resources have been archived. Use ISO 8601 format (e.g., 2024-10-22T14:48:00Z).
+ **/
+ public ArchivedResourcesSearch from(OffsetDateTime from) {
+ this.from = from;
+ return this;
+ }
+
+
+ @JsonProperty("from")
+ public OffsetDateTime getFrom() {
+ return from;
+ }
+
+ @JsonProperty("from")
+ public void setFrom(OffsetDateTime from) {
+ this.from = from;
+ }
+
+ /**
+ * Ending date from where resources have been archived. Use ISO 8601 format (e.g., 2024-10-22T14:48:00Z).
+ **/
+ public ArchivedResourcesSearch to(OffsetDateTime to) {
+ this.to = to;
+ return this;
+ }
+
+
+ @JsonProperty("to")
+ public OffsetDateTime getTo() {
+ return to;
+ }
+
+ @JsonProperty("to")
+ public void setTo(OffsetDateTime to) {
+ this.to = to;
+ }
+
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ ArchivedResourcesSearch archivedResourcesSearch = (ArchivedResourcesSearch) o;
+ return Objects.equals(this.uuid, archivedResourcesSearch.uuid) &&
+ Objects.equals(this.location, archivedResourcesSearch.location) &&
+ Objects.equals(this.name, archivedResourcesSearch.name) &&
+ Objects.equals(this.approver, archivedResourcesSearch.approver) &&
+ Objects.equals(this.contentType, archivedResourcesSearch.contentType) &&
+ Objects.equals(this.type, archivedResourcesSearch.type) &&
+ Objects.equals(this.voltage, archivedResourcesSearch.voltage) &&
+ Objects.equals(this.from, archivedResourcesSearch.from) &&
+ Objects.equals(this.to, archivedResourcesSearch.to);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(uuid, location, name, approver, contentType, type, voltage, from, to);
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("class ArchivedResourcesSearch {\n");
+
+ sb.append(" uuid: ").append(toIndentedString(uuid)).append("\n");
+ sb.append(" location: ").append(toIndentedString(location)).append("\n");
+ sb.append(" name: ").append(toIndentedString(name)).append("\n");
+ sb.append(" approver: ").append(toIndentedString(approver)).append("\n");
+ sb.append(" contentType: ").append(toIndentedString(contentType)).append("\n");
+ sb.append(" type: ").append(toIndentedString(type)).append("\n");
+ sb.append(" voltage: ").append(toIndentedString(voltage)).append("\n");
+ sb.append(" from: ").append(toIndentedString(from)).append("\n");
+ sb.append(" to: ").append(toIndentedString(to)).append("\n");
+ sb.append("}");
+ return sb.toString();
+ }
+
+ /**
+ * Convert the given object to string with each line indented by 4 spaces
+ * (except the first line).
+ */
+ private String toIndentedString(Object o) {
+ if (o == null) {
+ return "null";
+ }
+ return o.toString().replace("\n", "\n ");
+ }
+
+
+}
+
diff --git a/app/src/main/java/org/lfenergy/compas/scl/data/rest/api/archive/model/ResourceTag.java b/app/src/main/java/org/lfenergy/compas/scl/data/rest/api/archive/model/ResourceTag.java
new file mode 100644
index 00000000..53477aa8
--- /dev/null
+++ b/app/src/main/java/org/lfenergy/compas/scl/data/rest/api/archive/model/ResourceTag.java
@@ -0,0 +1,98 @@
+package org.lfenergy.compas.scl.data.rest.api.archive.model;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.annotation.JsonTypeName;
+import jakarta.validation.constraints.NotNull;
+
+import java.util.Objects;
+
+
+
+@JsonTypeName("ResourceTag")
+@jakarta.annotation.Generated(value = "org.openapitools.codegen.languages.JavaJAXRSSpecServerCodegen", date = "2024-11-18T07:52:46.875467800+01:00[Europe/Vienna]", comments = "Generator version: 7.8.0")
+public class ResourceTag {
+ private String key;
+ private String value;
+
+ /**
+ * Tag key
+ **/
+ public ResourceTag key(String key) {
+ this.key = key;
+ return this;
+ }
+
+
+ @JsonProperty("key")
+ @NotNull public String getKey() {
+ return key;
+ }
+
+ @JsonProperty("key")
+ public void setKey(String key) {
+ this.key = key;
+ }
+
+ /**
+ * Tag value
+ **/
+ public ResourceTag value(String value) {
+ this.value = value;
+ return this;
+ }
+
+
+ @JsonProperty("value")
+ @NotNull public String getValue() {
+ return value;
+ }
+
+ @JsonProperty("value")
+ public void setValue(String value) {
+ this.value = value;
+ }
+
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ ResourceTag resourceTag = (ResourceTag) o;
+ return Objects.equals(this.key, resourceTag.key) &&
+ Objects.equals(this.value, resourceTag.value);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(key, value);
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("class ResourceTag {\n");
+
+ sb.append(" key: ").append(toIndentedString(key)).append("\n");
+ sb.append(" value: ").append(toIndentedString(value)).append("\n");
+ sb.append("}");
+ return sb.toString();
+ }
+
+ /**
+ * Convert the given object to string with each line indented by 4 spaces
+ * (except the first line).
+ */
+ private String toIndentedString(Object o) {
+ if (o == null) {
+ return "null";
+ }
+ return o.toString().replace("\n", "\n ");
+ }
+
+
+}
+
diff --git a/app/src/main/java/org/lfenergy/compas/scl/data/rest/api/locations/LocationsApi.java b/app/src/main/java/org/lfenergy/compas/scl/data/rest/api/locations/LocationsApi.java
new file mode 100644
index 00000000..d8d023bb
--- /dev/null
+++ b/app/src/main/java/org/lfenergy/compas/scl/data/rest/api/locations/LocationsApi.java
@@ -0,0 +1,53 @@
+package org.lfenergy.compas.scl.data.rest.api.locations;
+
+import io.smallrye.common.annotation.Blocking;
+import io.smallrye.mutiny.Uni;
+import jakarta.validation.Valid;
+import jakarta.validation.constraints.NotNull;
+import jakarta.ws.rs.*;
+import org.lfenergy.compas.scl.data.rest.api.locations.model.Location;
+
+import java.util.List;
+import java.util.UUID;
+
+@Path("/api/locations")
+public interface LocationsApi {
+ @POST
+ @Blocking
+ @Path("/{locationId}/resources/{uuid}/assign")
+ @Produces({ "application/json" })
+ Uni assignResourceToLocation(@PathParam("locationId") UUID locationId, @PathParam("uuid") UUID uuid);
+
+ @POST
+ @Blocking
+ @Consumes({ "application/json" })
+ @Produces({ "application/json" })
+ Uni createLocation(@Valid @NotNull Location location);
+
+ @DELETE
+ @Blocking
+ @Path("/{locationId}")
+ @Produces({ "application/json" })
+ Uni deleteLocation(@PathParam("locationId") UUID locationId);
+
+ @GET
+ @Path("/{locationId}")
+ @Produces({ "application/json" })
+ Uni getLocation(@PathParam("locationId") UUID locationId);
+
+ @GET
+ @Produces({ "application/json" })
+ Uni> getLocations(@QueryParam("page") Integer page, @QueryParam("pageSize") @DefaultValue("25") Integer pageSize);
+
+ @POST
+ @Blocking
+ @Path("/{locationId}/resources/{uuid}/unassign")
+ @Produces({ "application/json" })
+ Uni unassignResourceFromLocation(@PathParam("locationId") UUID locationId,@PathParam("uuid") UUID uuid);
+
+ @PUT
+ @Path("/{locationId}")
+ @Consumes({ "application/json" })
+ @Produces({ "application/json" })
+ Uni updateLocation(@PathParam("locationId") UUID locationId,@Valid @NotNull Location location);
+}
diff --git a/app/src/main/java/org/lfenergy/compas/scl/data/rest/api/locations/LocationsResource.java b/app/src/main/java/org/lfenergy/compas/scl/data/rest/api/locations/LocationsResource.java
new file mode 100644
index 00000000..916875a9
--- /dev/null
+++ b/app/src/main/java/org/lfenergy/compas/scl/data/rest/api/locations/LocationsResource.java
@@ -0,0 +1,107 @@
+package org.lfenergy.compas.scl.data.rest.api.locations;
+
+import io.smallrye.mutiny.Uni;
+import io.smallrye.mutiny.infrastructure.Infrastructure;
+import jakarta.enterprise.context.RequestScoped;
+import jakarta.inject.Inject;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.eclipse.microprofile.jwt.JsonWebToken;
+import org.lfenergy.compas.scl.data.model.ILocationMetaItem;
+import org.lfenergy.compas.scl.data.rest.UserInfoProperties;
+import org.lfenergy.compas.scl.data.rest.api.locations.model.Location;
+import org.lfenergy.compas.scl.data.service.CompasSclDataService;
+
+import java.util.List;
+import java.util.Objects;
+import java.util.UUID;
+
+@RequestScoped
+public class LocationsResource implements LocationsApi {
+ private static final Logger LOGGER = LogManager.getLogger(LocationsResource.class);
+
+ private final CompasSclDataService compasSclDataService;
+ private final JsonWebToken jsonWebToken;
+ private final UserInfoProperties userInfoProperties;
+
+ @Inject
+ public LocationsResource(CompasSclDataService compasSclDataService, JsonWebToken jsonWebToken, UserInfoProperties userInfoProperties) {
+ this.compasSclDataService = compasSclDataService;
+ this.jsonWebToken = jsonWebToken;
+ this.userInfoProperties = userInfoProperties;
+ }
+
+ @Override
+ public Uni assignResourceToLocation(UUID locationId, UUID uuid) {
+ LOGGER.info("Assigning resource '{}' to location '{}'", uuid, locationId);
+ compasSclDataService.assignResourceToLocation(locationId, uuid);
+ return Uni.createFrom().nullItem();
+ }
+
+ @Override
+ public Uni createLocation(Location location) {
+ LOGGER.info("Creating location '{}'", location.getName());
+ return compasSclDataService.createLocation(
+ location.getKey(),
+ location.getName(),
+ location.getDescription()
+ )
+ .runSubscriptionOn(Infrastructure.getDefaultExecutor())
+ .onItem()
+ .transform(this::mapToLocation);
+ }
+
+ @Override
+ public Uni deleteLocation(UUID locationId) {
+ LOGGER.info("Deleting location with ID '{}'", locationId);
+ compasSclDataService.deleteLocation(locationId);
+ return Uni.createFrom().nullItem();
+ }
+
+ @Override
+ public Uni getLocation(UUID locationId) {
+ LOGGER.info("Retrieving location for ID '{}'", locationId);
+ return Uni.createFrom()
+ .item(() -> compasSclDataService.findLocationByUUID(locationId))
+ .runSubscriptionOn(Infrastructure.getDefaultExecutor())
+ .onItem()
+ .transform(this::mapToLocation);
+ }
+
+ @Override
+ public Uni> getLocations(Integer page, Integer pageSize) {
+ int pageLocation = Objects.requireNonNullElse(page, 0);
+ LOGGER.info("Retrieving locations for page '{}' and pageSize '{}'", pageLocation, pageSize);
+ return Uni.createFrom()
+ .item(() -> compasSclDataService.listLocations(pageLocation, pageSize))
+ .runSubscriptionOn(Infrastructure.getDefaultExecutor())
+ .onItem()
+ .transform(list -> list.stream().map(this::mapToLocation).toList());
+ }
+
+ @Override
+ public Uni unassignResourceFromLocation(UUID locationId, UUID uuid) {
+ LOGGER.info("Unassigning resource '{}' from location '{}'", uuid, locationId);
+ compasSclDataService.unassignResourceFromLocation(locationId, uuid);
+ return Uni.createFrom().nullItem();
+ }
+
+ @Override
+ public Uni updateLocation(UUID locationId, Location location) {
+ LOGGER.info("Updating resource '{}'", locationId);
+ return Uni.createFrom()
+ .item(() -> compasSclDataService.updateLocation(locationId, location.getKey(), location.getName(), location.getDescription()))
+ .runSubscriptionOn(Infrastructure.getDefaultExecutor())
+ .onItem()
+ .transform(this::mapToLocation);
+ }
+
+ private Location mapToLocation(ILocationMetaItem location) {
+ return new Location()
+ .uuid(location.getId())
+ .name(location.getName())
+ .key(location.getKey())
+ .description(location.getDescription())
+ .assignedResources(location.getAssignedResources());
+ }
+}
diff --git a/app/src/main/java/org/lfenergy/compas/scl/data/rest/api/locations/model/ErrorResponseDto.java b/app/src/main/java/org/lfenergy/compas/scl/data/rest/api/locations/model/ErrorResponseDto.java
new file mode 100644
index 00000000..8cf43e3b
--- /dev/null
+++ b/app/src/main/java/org/lfenergy/compas/scl/data/rest/api/locations/model/ErrorResponseDto.java
@@ -0,0 +1,119 @@
+package org.lfenergy.compas.scl.data.rest.api.locations.model;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.annotation.JsonTypeName;
+import jakarta.validation.constraints.NotNull;
+
+import java.time.OffsetDateTime;
+import java.util.Objects;
+
+
+
+@JsonTypeName("ErrorResponseDto")
+@jakarta.annotation.Generated(value = "org.openapitools.codegen.languages.JavaJAXRSSpecServerCodegen", date = "2024-11-18T07:52:46.875467800+01:00[Europe/Vienna]", comments = "Generator version: 7.8.0")
+public class ErrorResponseDto {
+ private OffsetDateTime timestamp;
+ private String code;
+ private String message;
+
+ /**
+ * 2017-07-21T17:32:28Z.
+ **/
+ public ErrorResponseDto timestamp(OffsetDateTime timestamp) {
+ this.timestamp = timestamp;
+ return this;
+ }
+
+
+ @JsonProperty("timestamp")
+ @NotNull public OffsetDateTime getTimestamp() {
+ return timestamp;
+ }
+
+ @JsonProperty("timestamp")
+ public void setTimestamp(OffsetDateTime timestamp) {
+ this.timestamp = timestamp;
+ }
+
+ /**
+ **/
+ public ErrorResponseDto code(String code) {
+ this.code = code;
+ return this;
+ }
+
+
+ @JsonProperty("code")
+ @NotNull public String getCode() {
+ return code;
+ }
+
+ @JsonProperty("code")
+ public void setCode(String code) {
+ this.code = code;
+ }
+
+ /**
+ **/
+ public ErrorResponseDto message(String message) {
+ this.message = message;
+ return this;
+ }
+
+
+ @JsonProperty("message")
+ @NotNull public String getMessage() {
+ return message;
+ }
+
+ @JsonProperty("message")
+ public void setMessage(String message) {
+ this.message = message;
+ }
+
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ ErrorResponseDto errorResponseDto = (ErrorResponseDto) o;
+ return Objects.equals(this.timestamp, errorResponseDto.timestamp) &&
+ Objects.equals(this.code, errorResponseDto.code) &&
+ Objects.equals(this.message, errorResponseDto.message);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(timestamp, code, message);
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("class ErrorResponseDto {\n");
+
+ sb.append(" timestamp: ").append(toIndentedString(timestamp)).append("\n");
+ sb.append(" code: ").append(toIndentedString(code)).append("\n");
+ sb.append(" message: ").append(toIndentedString(message)).append("\n");
+ sb.append("}");
+ return sb.toString();
+ }
+
+ /**
+ * Convert the given object to string with each line indented by 4 spaces
+ * (except the first line).
+ */
+ private String toIndentedString(Object o) {
+ if (o == null) {
+ return "null";
+ }
+ return o.toString().replace("\n", "\n ");
+ }
+
+
+}
+
diff --git a/app/src/main/java/org/lfenergy/compas/scl/data/rest/api/locations/model/Location.java b/app/src/main/java/org/lfenergy/compas/scl/data/rest/api/locations/model/Location.java
new file mode 100644
index 00000000..c4a5cb59
--- /dev/null
+++ b/app/src/main/java/org/lfenergy/compas/scl/data/rest/api/locations/model/Location.java
@@ -0,0 +1,164 @@
+package org.lfenergy.compas.scl.data.rest.api.locations.model;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.annotation.JsonTypeName;
+import jakarta.validation.constraints.NotNull;
+
+import java.util.Objects;
+
+
+
+@JsonTypeName("Location")
+@jakarta.annotation.Generated(value = "org.openapitools.codegen.languages.JavaJAXRSSpecServerCodegen", date = "2024-11-18T07:52:46.875467800+01:00[Europe/Vienna]", comments = "Generator version: 7.8.0")
+public class Location {
+ private String uuid;
+ private String key;
+ private String name;
+ private String description;
+ private Integer assignedResources;
+
+ /**
+ * Unique location uuid generated by backend during creation
+ **/
+ public Location uuid(String uuid) {
+ this.uuid = uuid;
+ return this;
+ }
+
+
+ @JsonProperty("uuid")
+ public String getUuid() {
+ return uuid;
+ }
+
+ @JsonProperty("uuid")
+ public void setUuid(String uuid) {
+ this.uuid = uuid;
+ }
+
+ /**
+ * Location key, defined once manually when creating a location
+ **/
+ public Location key(String key) {
+ this.key = key;
+ return this;
+ }
+
+
+ @JsonProperty("key")
+ @NotNull public String getKey() {
+ return key;
+ }
+
+ @JsonProperty("key")
+ public void setKey(String key) {
+ this.key = key;
+ }
+
+ /**
+ * Location name
+ **/
+ public Location name(String name) {
+ this.name = name;
+ return this;
+ }
+
+
+ @JsonProperty("name")
+ @NotNull public String getName() {
+ return name;
+ }
+
+ @JsonProperty("name")
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ /**
+ * Location description
+ **/
+ public Location description(String description) {
+ this.description = description;
+ return this;
+ }
+
+
+ @JsonProperty("description")
+ public String getDescription() {
+ return description;
+ }
+
+ @JsonProperty("description")
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ /**
+ * Number of resources assigned to this location
+ **/
+ public Location assignedResources(Integer assignedResources) {
+ this.assignedResources = assignedResources;
+ return this;
+ }
+
+
+ @JsonProperty("assignedResources")
+ public Integer getAssignedResources() {
+ return assignedResources;
+ }
+
+ @JsonProperty("assignedResources")
+ public void setAssignedResources(Integer assignedResources) {
+ this.assignedResources = assignedResources;
+ }
+
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ Location location = (Location) o;
+ return Objects.equals(this.uuid, location.uuid) &&
+ Objects.equals(this.key, location.key) &&
+ Objects.equals(this.name, location.name) &&
+ Objects.equals(this.description, location.description) &&
+ Objects.equals(this.assignedResources, location.assignedResources);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(uuid, key, name, description, assignedResources);
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("class Location {\n");
+
+ sb.append(" uuid: ").append(toIndentedString(uuid)).append("\n");
+ sb.append(" key: ").append(toIndentedString(key)).append("\n");
+ sb.append(" name: ").append(toIndentedString(name)).append("\n");
+ sb.append(" description: ").append(toIndentedString(description)).append("\n");
+ sb.append(" assignedResources: ").append(toIndentedString(assignedResources)).append("\n");
+ sb.append("}");
+ return sb.toString();
+ }
+
+ /**
+ * Convert the given object to string with each line indented by 4 spaces
+ * (except the first line).
+ */
+ private String toIndentedString(Object o) {
+ if (o == null) {
+ return "null";
+ }
+ return o.toString().replace("\n", "\n ");
+ }
+
+
+}
+
diff --git a/app/src/main/java/org/lfenergy/compas/scl/data/rest/api/locations/model/Locations.java b/app/src/main/java/org/lfenergy/compas/scl/data/rest/api/locations/model/Locations.java
new file mode 100644
index 00000000..509099f0
--- /dev/null
+++ b/app/src/main/java/org/lfenergy/compas/scl/data/rest/api/locations/model/Locations.java
@@ -0,0 +1,116 @@
+package org.lfenergy.compas.scl.data.rest.api.locations.model;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.annotation.JsonTypeName;
+import jakarta.validation.Valid;
+import jakarta.validation.constraints.NotNull;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+
+
+@JsonTypeName("Locations")
+@jakarta.annotation.Generated(value = "org.openapitools.codegen.languages.JavaJAXRSSpecServerCodegen", date = "2024-11-18T07:52:46.875467800+01:00[Europe/Vienna]", comments = "Generator version: 7.8.0")
+public class Locations {
+ private @Valid List<@Valid Location> locations = new ArrayList<>();
+ private Pagination pagination;
+
+ /**
+ * List of locations
+ **/
+ public Locations locations(List<@Valid Location> locations) {
+ this.locations = locations;
+ return this;
+ }
+
+
+ @JsonProperty("locations")
+ @NotNull @Valid public List<@Valid Location> getLocations() {
+ return locations;
+ }
+
+ @JsonProperty("locations")
+ public void setLocations(List<@Valid Location> locations) {
+ this.locations = locations;
+ }
+
+ public Locations addLocationsItem(Location locationsItem) {
+ if (this.locations == null) {
+ this.locations = new ArrayList<>();
+ }
+
+ this.locations.add(locationsItem);
+ return this;
+ }
+
+ public Locations removeLocationsItem(Location locationsItem) {
+ if (locationsItem != null && this.locations != null) {
+ this.locations.remove(locationsItem);
+ }
+
+ return this;
+ }
+ /**
+ **/
+ public Locations pagination(Pagination pagination) {
+ this.pagination = pagination;
+ return this;
+ }
+
+
+ @JsonProperty("pagination")
+ @Valid public Pagination getPagination() {
+ return pagination;
+ }
+
+ @JsonProperty("pagination")
+ public void setPagination(Pagination pagination) {
+ this.pagination = pagination;
+ }
+
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ Locations locations = (Locations) o;
+ return Objects.equals(this.locations, locations.locations) &&
+ Objects.equals(this.pagination, locations.pagination);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(locations, pagination);
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("class Locations {\n");
+
+ sb.append(" locations: ").append(toIndentedString(locations)).append("\n");
+ sb.append(" pagination: ").append(toIndentedString(pagination)).append("\n");
+ sb.append("}");
+ return sb.toString();
+ }
+
+ /**
+ * Convert the given object to string with each line indented by 4 spaces
+ * (except the first line).
+ */
+ private String toIndentedString(Object o) {
+ if (o == null) {
+ return "null";
+ }
+ return o.toString().replace("\n", "\n ");
+ }
+
+
+}
+
diff --git a/app/src/main/java/org/lfenergy/compas/scl/data/rest/api/locations/model/Pagination.java b/app/src/main/java/org/lfenergy/compas/scl/data/rest/api/locations/model/Pagination.java
new file mode 100644
index 00000000..717f7983
--- /dev/null
+++ b/app/src/main/java/org/lfenergy/compas/scl/data/rest/api/locations/model/Pagination.java
@@ -0,0 +1,96 @@
+package org.lfenergy.compas.scl.data.rest.api.locations.model;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.annotation.JsonTypeName;
+import jakarta.validation.constraints.NotNull;
+
+import java.util.Objects;
+
+
+
+@JsonTypeName("Pagination")
+@jakarta.annotation.Generated(value = "org.openapitools.codegen.languages.JavaJAXRSSpecServerCodegen", date = "2024-11-18T07:52:46.875467800+01:00[Europe/Vienna]", comments = "Generator version: 7.8.0")
+public class Pagination {
+ private Integer page;
+ private Integer pageSize;
+
+ /**
+ **/
+ public Pagination page(Integer page) {
+ this.page = page;
+ return this;
+ }
+
+
+ @JsonProperty("page")
+ @NotNull public Integer getPage() {
+ return page;
+ }
+
+ @JsonProperty("page")
+ public void setPage(Integer page) {
+ this.page = page;
+ }
+
+ /**
+ **/
+ public Pagination pageSize(Integer pageSize) {
+ this.pageSize = pageSize;
+ return this;
+ }
+
+
+ @JsonProperty("pageSize")
+ @NotNull public Integer getPageSize() {
+ return pageSize;
+ }
+
+ @JsonProperty("pageSize")
+ public void setPageSize(Integer pageSize) {
+ this.pageSize = pageSize;
+ }
+
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ Pagination pagination = (Pagination) o;
+ return Objects.equals(this.page, pagination.page) &&
+ Objects.equals(this.pageSize, pagination.pageSize);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(page, pageSize);
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("class Pagination {\n");
+
+ sb.append(" page: ").append(toIndentedString(page)).append("\n");
+ sb.append(" pageSize: ").append(toIndentedString(pageSize)).append("\n");
+ sb.append("}");
+ return sb.toString();
+ }
+
+ /**
+ * Convert the given object to string with each line indented by 4 spaces
+ * (except the first line).
+ */
+ private String toIndentedString(Object o) {
+ if (o == null) {
+ return "null";
+ }
+ return o.toString().replace("\n", "\n ");
+ }
+
+
+}
+
diff --git a/app/src/main/java/org/lfenergy/compas/scl/data/rest/api/scl/HistoryResource.java b/app/src/main/java/org/lfenergy/compas/scl/data/rest/api/scl/HistoryResource.java
new file mode 100644
index 00000000..899b5f17
--- /dev/null
+++ b/app/src/main/java/org/lfenergy/compas/scl/data/rest/api/scl/HistoryResource.java
@@ -0,0 +1,132 @@
+package org.lfenergy.compas.scl.data.rest.api.scl;
+
+import io.smallrye.mutiny.Uni;
+import io.smallrye.mutiny.infrastructure.Infrastructure;
+import jakarta.enterprise.context.RequestScoped;
+import jakarta.inject.Inject;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.lfenergy.compas.scl.data.model.IHistoryMetaItem;
+import org.lfenergy.compas.scl.data.model.Version;
+import org.lfenergy.compas.scl.data.rest.api.scl.model.*;
+import org.lfenergy.compas.scl.data.service.CompasSclDataService;
+import org.lfenergy.compas.scl.extensions.model.SclFileType;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.rmi.UnexpectedException;
+import java.time.OffsetDateTime;
+import java.util.List;
+import java.util.UUID;
+
+@RequestScoped
+public class HistoryResource implements HistoryApi {
+ private static final Logger LOGGER = LogManager.getLogger(HistoryResource.class);
+
+ private final CompasSclDataService compasSclDataService;
+
+ @Inject
+ public HistoryResource(CompasSclDataService compasSclDataService) {
+ this.compasSclDataService = compasSclDataService;
+ }
+
+ @Override
+ public Uni searchForResources(DataResourceSearch dataResourceSearch) {
+ LOGGER.info("Triggering search with filter: {}", dataResourceSearch);
+ return Uni.createFrom()
+ .item(() -> getHistoryMetaItems(dataResourceSearch))
+ .onItem().transform(items -> new DataResourcesResult().results(items.stream().map(this::mapToDataResource).toList()))
+ .onFailure().recoverWithItem(e -> {
+ LOGGER.error("Unexpected error while searching for resources", e);
+ return new DataResourcesResult(); // Return an empty result or handle as needed
+ });
+ }
+
+ private List getHistoryMetaItems(DataResourceSearch dataResourceSearch) {
+ String uuid = dataResourceSearch.getUuid();
+
+ if (uuid != null) {
+ return compasSclDataService.listHistory(UUID.fromString(uuid));
+ }
+
+ SclFileType type = dataResourceSearch.getType() != null ? SclFileType.valueOf(dataResourceSearch.getType()) : null;
+ String name = dataResourceSearch.getName();
+ String author = dataResourceSearch.getAuthor();
+ OffsetDateTime from = dataResourceSearch.getFrom();
+ OffsetDateTime to = dataResourceSearch.getTo();
+
+ if (type != null || name != null || author != null || from != null || to != null) {
+ return compasSclDataService.listHistory(type, name, author, from, to);
+ }
+
+ return compasSclDataService.listHistory();
+ }
+
+ private DataResource mapToDataResource(IHistoryMetaItem e) {
+ return new DataResource()
+ .uuid(UUID.fromString(e.getId()))
+ .name(e.getName())
+ .author(e.getAuthor())
+ .type(e.getType())
+ .changedAt(e.getChangedAt())
+ .version(e.getVersion())
+ .available(e.isAvailable())
+ .deleted(e.isDeleted())
+ .location(e.getLocation());
+ }
+
+ @Override
+ public Uni retrieveDataResourceHistory(UUID id) {
+ LOGGER.info("Retrieving history for data resource ID: {}", id);
+ return Uni.createFrom()
+ .item(() -> compasSclDataService.listHistoryVersionsByUUID(id))
+ .runSubscriptionOn(Infrastructure.getDefaultExecutor())
+ .onItem().transform(versions -> new DataResourceHistory().versions(versions.stream().map(this::mapToDataResourceVersion).toList()));
+ }
+
+ private DataResourceVersion mapToDataResourceVersion(IHistoryMetaItem e) {
+ return new DataResourceVersion()
+ .uuid(UUID.fromString(e.getId()))
+ .name(e.getName())
+ .author(e.getAuthor())
+ .type(e.getType())
+ .changedAt(e.getChangedAt())
+ .version(e.getVersion())
+ .available(e.isAvailable())
+ .deleted(e.isDeleted())
+ .comment(e.getComment())
+ .archived(e.isArchived())
+ .location(e.getLocation());
+ }
+
+ @Override
+ public Uni retrieveDataResourceByVersion(UUID id, String version) {
+ LOGGER.info("Retrieving data resource for ID: {} and version: {}", id, version);
+ return Uni.createFrom()
+ .item(() -> compasSclDataService.findByUUID(id, new Version(version)))
+ .runSubscriptionOn(Infrastructure.getDefaultExecutor())
+ .onItem().transformToUni(this::createTempFileWithData)
+ .onFailure().transform(e -> {
+ LOGGER.error("Failed to retrieve or create temp file", e);
+ return new UnexpectedException("Error while retrieving data resource", (Exception) e);
+ });
+ }
+
+ private Uni createTempFileWithData(String data) {
+ return Uni.createFrom()
+ .item(() -> createTempFile(data))
+ .runSubscriptionOn(Infrastructure.getDefaultExecutor());
+ }
+
+ private File createTempFile(String data) {
+ try {
+ Path tempFile = Files.createTempFile("resource_", ".tmp");
+ Files.writeString(tempFile, data);
+ return tempFile.toFile();
+ } catch (IOException e) {
+ throw new RuntimeException("Error creating or writing to temp file", e);
+ }
+ }
+}
diff --git a/app/src/main/java/org/lfenergy/compas/scl/data/rest/monitoring/ReadinessHealthCheck.java b/app/src/main/java/org/lfenergy/compas/scl/data/rest/monitoring/ReadinessHealthCheck.java
index f082bfed..89832c22 100644
--- a/app/src/main/java/org/lfenergy/compas/scl/data/rest/monitoring/ReadinessHealthCheck.java
+++ b/app/src/main/java/org/lfenergy/compas/scl/data/rest/monitoring/ReadinessHealthCheck.java
@@ -3,17 +3,28 @@
// SPDX-License-Identifier: Apache-2.0
package org.lfenergy.compas.scl.data.rest.monitoring;
+import jakarta.inject.Inject;
import org.eclipse.microprofile.health.HealthCheck;
import org.eclipse.microprofile.health.HealthCheckResponse;
import org.eclipse.microprofile.health.Readiness;
import jakarta.enterprise.context.ApplicationScoped;
+import org.lfenergy.compas.scl.data.config.FeatureFlagService;
@Readiness
@ApplicationScoped
public class ReadinessHealthCheck implements HealthCheck {
+
+ @Inject
+ FeatureFlagService featureFlagService;
+
@Override
public HealthCheckResponse call() {
- return HealthCheckResponse.up("System Ready");
+
+ return HealthCheckResponse
+ .named("System Ready")
+ .up()
+ .withData("isHistoryEnabled", featureFlagService.isHistoryEnabled())
+ .build();
}
}
\ No newline at end of file
diff --git a/app/src/main/java/org/lfenergy/compas/scl/data/rest/v1/CompasSclDataResource.java b/app/src/main/java/org/lfenergy/compas/scl/data/rest/v1/CompasSclDataResource.java
index fba0497c..82dba724 100644
--- a/app/src/main/java/org/lfenergy/compas/scl/data/rest/v1/CompasSclDataResource.java
+++ b/app/src/main/java/org/lfenergy/compas/scl/data/rest/v1/CompasSclDataResource.java
@@ -6,20 +6,21 @@
import io.quarkus.security.Authenticated;
import io.smallrye.common.annotation.Blocking;
import io.smallrye.mutiny.Uni;
+import jakarta.enterprise.context.RequestScoped;
+import jakarta.inject.Inject;
+import jakarta.validation.Valid;
+import jakarta.ws.rs.*;
+import jakarta.ws.rs.core.MediaType;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.eclipse.microprofile.jwt.JsonWebToken;
+import org.lfenergy.compas.scl.data.config.FeatureFlagService;
import org.lfenergy.compas.scl.data.model.Version;
import org.lfenergy.compas.scl.data.rest.UserInfoProperties;
import org.lfenergy.compas.scl.data.rest.v1.model.*;
import org.lfenergy.compas.scl.data.service.CompasSclDataService;
import org.lfenergy.compas.scl.extensions.model.SclFileType;
-import jakarta.enterprise.context.RequestScoped;
-import jakarta.inject.Inject;
-import jakarta.validation.Valid;
-import jakarta.ws.rs.*;
-import jakarta.ws.rs.core.MediaType;
import java.util.UUID;
import static org.lfenergy.compas.scl.data.rest.Constants.*;
@@ -38,6 +39,9 @@ public class CompasSclDataResource {
@Inject
UserInfoProperties userInfoProperties;
+ @Inject
+ FeatureFlagService featureFlagService;
+
@Inject
public CompasSclDataResource(CompasSclDataService compasSclDataService) {
this.compasSclDataService = compasSclDataService;
@@ -54,8 +58,9 @@ public Uni create(@PathParam(TYPE_PATH_PARAM) SclFileType type,
LOGGER.trace("Username used for Who {}", who);
var response = new CreateResponse();
- response.setSclData(compasSclDataService.create(type, request.getName(), who, request.getComment(),
- request.getSclData()));
+ response.setSclData(compasSclDataService.create(
+ type, request.getName(), who, request.getComment(), request.getSclData(), featureFlagService.isHistoryEnabled()
+ ));
return Uni.createFrom().item(response);
}
@@ -117,7 +122,7 @@ public Uni update(@PathParam(TYPE_PATH_PARAM) SclFileType type,
var response = new UpdateResponse();
response.setSclData(compasSclDataService.update(type, id, request.getChangeSetType(), who, request.getComment(),
- request.getSclData()));
+ request.getSclData(), featureFlagService.isHistoryEnabled()));
return Uni.createFrom().item(response);
}
@@ -128,7 +133,7 @@ public Uni update(@PathParam(TYPE_PATH_PARAM) SclFileType type,
public Uni deleteAll(@PathParam(TYPE_PATH_PARAM) SclFileType type,
@PathParam(ID_PATH_PARAM) UUID id) {
LOGGER.info("Removing all versions of SCL File {} for type {} from storage.", id, type);
- compasSclDataService.delete(type, id);
+ compasSclDataService.delete(type, id, featureFlagService.keepDeletedFiles());
return Uni.createFrom().nullItem();
}
@@ -140,7 +145,7 @@ public Uni deleteVersion(@PathParam(TYPE_PATH_PARAM) SclFileType type,
@PathParam(ID_PATH_PARAM) UUID id,
@PathParam(VERSION_PATH_PARAM) Version version) {
LOGGER.info("Removing version {} of SCL File {} for type {} from storage.", version, id, type);
- compasSclDataService.delete(type, id, version);
+ compasSclDataService.deleteVersion(type, id, version, featureFlagService.keepDeletedFiles());
return Uni.createFrom().nullItem();
}
@@ -149,7 +154,7 @@ public Uni deleteVersion(@PathParam(TYPE_PATH_PARAM) SclFileType type,
@Consumes(MediaType.APPLICATION_XML)
@Produces(MediaType.APPLICATION_XML)
public Uni checkDuplicateName(@PathParam(TYPE_PATH_PARAM) SclFileType type,
- @Valid DuplicateNameCheckRequest request) {
+ @Valid DuplicateNameCheckRequest request) {
LOGGER.info("Checking for duplicate SCL File name.");
var response = new DuplicateNameCheckResponse();
diff --git a/app/src/main/openapi/archiving-api.yaml b/app/src/main/openapi/archiving-api.yaml
new file mode 100644
index 00000000..8034881f
--- /dev/null
+++ b/app/src/main/openapi/archiving-api.yaml
@@ -0,0 +1,758 @@
+openapi: 3.0.3
+info:
+ title: CoMPAS SCL Data Archiving API
+ version: 1.0.0
+
+servers:
+ - url: https://demo.compas.energy
+ description: DSOM Versatel Production URL
+
+tags:
+ - name: locations
+ description: Endpoints managing locations and assigning resources
+ - name: archiving
+ description: Archiving related endpoints
+
+security:
+ - open-id-connect:
+ - read
+ - write
+ - admin
+
+paths:
+ /api/locations:
+ post:
+ tags:
+ - locations
+ summary: Create location
+ security:
+ - open-id-connect: [ admin ]
+ operationId: createLocation
+ requestBody:
+ description: Location information
+ required: true
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/Location'
+ responses:
+ '200':
+ description: Successfully generated location
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/Location'
+ '400':
+ description: One of the specified Parameters is not valid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponseDto'
+ '401':
+ description: Authentication information is missing or invalid
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponseDto'
+ default:
+ description: Unexpected Error, cannot handle request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponseDto'
+ get:
+ tags:
+ - locations
+ summary: Retrieve locations
+ operationId: getLocations
+ parameters:
+ - name: page
+ in: query
+ description: Page number starting by 1
+ required: false
+ schema:
+ type: integer
+ format: int32
+ - name: pageSize
+ in: query
+ description: Page size must be > 0
+ required: false
+ schema:
+ type: integer
+ format: int32
+ default: 25
+ responses:
+ '200':
+ description: Successfully retrieved locations
+ content:
+ application/json:
+ schema:
+ type: array
+ items:
+ $ref: '#/components/schemas/Location'
+ '400':
+ description: One of the specified Parameters is not valid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponseDto'
+ '401':
+ description: Authentication information is missing or invalid
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponseDto'
+ default:
+ description: Unexpected Error, cannot handle request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponseDto'
+ /api/locations/{locationId}:
+ parameters:
+ - name: locationId
+ in: path
+ description: Unique location identifier
+ required: true
+ schema:
+ type: string
+ format: uuid
+ get:
+ tags:
+ - locations
+ summary: Retrieve location
+ operationId: getLocation
+ responses:
+ '200':
+ description: Successfully retrieved location
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/Location'
+ '400':
+ description: One of the specified Parameters is not valid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponseDto'
+ '401':
+ description: Authentication information is missing or invalid
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponseDto'
+ '404':
+ description: Unable to find location with locationId
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponseDto'
+ default:
+ description: Unexpected Error, cannot handle request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponseDto'
+ put:
+ tags:
+ - locations
+ summary: Update location
+ operationId: updateLocation
+ requestBody:
+ description: Location information, location uuid will be ignored
+ required: true
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/Location'
+ responses:
+ '200':
+ description: Successfully generated location
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/Location'
+ '400':
+ description: One of the specified Parameters is not valid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponseDto'
+ '401':
+ description: Authentication information is missing or invalid
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponseDto'
+ default:
+ description: Unexpected Error, cannot handle request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponseDto'
+ delete:
+ tags:
+ - locations
+ summary: Delete location
+ description: Deleting a location is only allowed when location has no resources assigned
+ operationId: deleteLocation
+ responses:
+ '204':
+ description: Successfully deleted location
+ '400':
+ description: One of the specified Parameters is not valid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponseDto'
+ '401':
+ description: Authentication information is missing or invalid
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponseDto'
+ '404':
+ description: Unable to find location with locationId
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponseDto'
+ default:
+ description: Unexpected Error, cannot handle request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponseDto'
+ /api/locations/{locationId}/resources/{uuid}/assign:
+ post:
+ tags:
+ - locations
+ summary: Assigns a resource
+ security:
+ - open-id-connect: [ write ]
+ description: |-
+ Assigns or moves a resource to the specified location. If resource already assigned, the previous assignment
+ will be removed.
+ operationId: assignResourceToLocation
+ parameters:
+ - name: locationId
+ in: path
+ description: Unique location identifier
+ required: true
+ schema:
+ type: string
+ format: uuid
+ - name: uuid
+ in: path
+ description: Unique resource identifier
+ required: true
+ schema:
+ type: string
+ format: uuid
+ responses:
+ '204':
+ description: Successfully assigned the resource to the location
+ '400':
+ description: One of the specified Parameters is not valid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponseDto'
+ '401':
+ description: Authentication information is missing or invalid
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponseDto'
+ '404':
+ description: Unable to find location or resource
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponseDto'
+ default:
+ description: Unexpected Error, cannot handle request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponseDto'
+ /api/locations/{locationId}/resources/{uuid}/unassign:
+ post:
+ tags:
+ - locations
+ summary: Unassigns a resource
+ security:
+ - open-id-connect: [ write ]
+ description: |-
+ Removes the assignment of a resource from the assigned location.
+ operationId: unassignResourceFromLocation
+ parameters:
+ - name: locationId
+ in: path
+ description: Unique location identifier
+ required: true
+ schema:
+ type: string
+ format: uuid
+ - name: uuid
+ in: path
+ description: Unique resource identifier
+ required: true
+ schema:
+ type: string
+ format: uuid
+ responses:
+ '204':
+ description: Successfully unassigned the resource from the location
+ '400':
+ description: One of the specified Parameters is not valid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponseDto'
+ '401':
+ description: Authentication information is missing or invalid
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponseDto'
+ '404':
+ description: Unable to find location or resource
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponseDto'
+ default:
+ description: Unexpected Error, cannot handle request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponseDto'
+
+ /api/archive/scl/{id}/versions/{version}:
+ parameters:
+ - name: id
+ in: path
+ description: Unique data resource identifier
+ required: true
+ schema:
+ type: string
+ format: uuid
+ - name: version
+ in: path
+ description: Data resource version
+ required: true
+ schema:
+ type: string
+ post:
+ tags:
+ - archiving
+ summary: Archive an existing scl file
+ security:
+ - open-id-connect: [ write ]
+ operationId: archiveSclResource
+ responses:
+ '200':
+ description: Successfully generated location
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ArchivedResource'
+ '400':
+ description: One of the specified Parameters is not valid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponseDto'
+ '401':
+ description: Authentication information is missing or invalid
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponseDto'
+ '403':
+ description: Authorization failed
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponseDto'
+ '404':
+ description: Unable to find location or resource
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponseDto'
+ default:
+ description: Unexpected Error, cannot handle request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponseDto'
+ /api/archive/referenced-resource/{id}/versions/{version}:
+ parameters:
+ - name: id
+ in: path
+ description: Unique data resource identifier
+ required: true
+ schema:
+ type: string
+ format: uuid
+ - name: version
+ in: path
+ description: Data resource version
+ required: true
+ schema:
+ type: string
+ post:
+ tags:
+ - archiving
+ summary: Archive resource linked to existing resource
+ security:
+ - open-id-connect: [ write ]
+ operationId: archiveResource
+ parameters:
+ - in: header
+ name: X-author
+ description: Name of the author who created the file and send for approval
+ schema:
+ type: string
+ - in: header
+ name: X-approver
+ description: Name of the approver
+ schema:
+ type: string
+ - in: header
+ name: Content-Type
+ description: File content type
+ schema:
+ type: string
+ - in: header
+ name: X-filename
+ description: File name
+ schema:
+ type: string
+ requestBody:
+ content:
+ application/octet-stream:
+ schema:
+ type: string
+ format: binary
+ responses:
+ '200':
+ description: Successfully generated location
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ArchivedResource'
+ '401':
+ description: Authentication information is missing or invalid
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponseDto'
+ '403':
+ description: Authorization failed
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponseDto'
+ '404':
+ description: Unable to find location or resource
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponseDto'
+ default:
+ description: Unexpected Error, cannot handle request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponseDto'
+ /api/archive/resources/search:
+ post:
+ tags:
+ - archiving
+ summary: Search for archived resources
+ security:
+ - open-id-connect: [ read ]
+ operationId: searchArchivedResources
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ArchivedResourcesSearch'
+ responses:
+ '200':
+ description: Successfully generated location
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ArchivedResources'
+ '401':
+ description: Authentication information is missing or invalid
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponseDto'
+ '403':
+ description: Authorization failed
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponseDto'
+ '404':
+ description: Unable to find location or resource
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponseDto'
+ default:
+ description: Unexpected Error, cannot handle request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponseDto'
+ /api/archive/resources/{id}/versions:
+ get:
+ tags:
+ - archiving
+ description: Search for all versions for a given resource uuid
+ operationId: retrieveArchivedResourceHistory
+ parameters:
+ - name: id
+ in: path
+ description: Unique data resource identifier
+ required: true
+ schema:
+ type: string
+ format: uuid
+ responses:
+ '200':
+ description: Succefully retrieved data resource versions
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ArchivedResourcesHistory'
+ '400':
+ description: One of the specified Parameters is not valid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponseDto'
+ '401':
+ description: Authentication information is missing or invalid
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponseDto'
+ '404':
+ description: Unable to finde data resource with given unique identifier
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponseDto'
+ default:
+ description: Unexpected Error, cannot handle request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponseDto'
+
+components:
+ securitySchemes:
+ open-id-connect: # <--- Arbitrary name for the security scheme. Used to refer to it from elsewhere.
+ type: openIdConnect
+ openIdConnectUrl: https://example.com/.well-known/openid-configuration
+
+ schemas:
+ Locations:
+ type: object
+ required:
+ - locations
+ properties:
+ locations:
+ type: array
+ description: "List of locations"
+ items:
+ $ref: '#/components/schemas/Location'
+ pagination:
+ $ref: '#/components/schemas/Pagination'
+ Pagination:
+ type: object
+ required:
+ - page
+ - pageSize
+ properties:
+ page:
+ type: integer
+ format: int32
+ pageSize:
+ type: integer
+ format: int32
+ Location:
+ type: object
+ required:
+ - key
+ - name
+ properties:
+ uuid:
+ type: string
+ description: "Unique location uuid generated by backend during creation"
+ key:
+ type: string
+ description: "Location key, defined once manually when creating a location"
+ name:
+ type: string
+ description: "Location name"
+ description:
+ type: string
+ description: "Location description"
+ assignedResources:
+ type: integer
+ format: int32
+ description: "Number of resources assigned to this location"
+ ArchivedResourcesHistory:
+ type: object
+ required:
+ - versions
+ properties:
+ versions:
+ type: array
+ items:
+ $ref: '#/components/schemas/ArchivedResourceVersion'
+ ArchivedResourceVersion:
+ allOf:
+ - $ref: '#/components/schemas/ArchivedResource'
+ - type: object
+ required:
+ - archived
+ properties:
+ comment:
+ type: string
+ description: "Comment given when uploading the data resource"
+ archived:
+ type: boolean
+ description: "Defines if given data resource is archived"
+ default: false
+ ArchivedResource:
+ type: object
+ required:
+ - uuid
+ - name
+ - author
+ - contentType
+ - version
+ - fields
+ - modifiedAt
+ - archivedAt
+ properties:
+ uuid:
+ type: string
+ description: "Unique resource identifier"
+ location:
+ type: string
+ description: "Location of the resource, might be empty"
+ name:
+ type: string
+ description: "Resource name"
+ note:
+ type: string
+ description: "Versioning note"
+ author:
+ type: string
+ description: "Modifying author"
+ approver:
+ type: string
+ description: "Name of the approver"
+ type:
+ type: string
+ description: "Content type"
+ contentType:
+ type: string
+ description: "Content type"
+ voltage:
+ type: string
+ description: "Content type"
+ version:
+ type: string
+ description: "Version"
+ modifiedAt:
+ type: string
+ format: date-time
+ archivedAt:
+ type: string
+ format: date-time
+ fields:
+ type: array
+ items:
+ $ref: '#/components/schemas/ResourceTag'
+ ResourceTag:
+ type: object
+ required:
+ - key
+ - value
+ properties:
+ key:
+ type: string
+ description: "Tag key"
+ value:
+ type: string
+ description: "Tag value"
+ ArchivedResourcesSearch:
+ type: object
+ properties:
+ uuid:
+ type: string
+ description: "If uuid is set no other filter must be set"
+ location:
+ type: string
+ description: "Exact match of a location"
+ name:
+ type: string
+ description: "Partially match allowed"
+ approver:
+ type: string
+ description: "Fulltext match which can be retrieved via extra endpoint"
+ contentType:
+ type: string
+ description: "Fulltext match set to one of the supported scl types: SSD, IID, ICD, SCD, CID, SED, ISD, STD, etc."
+ type:
+ type: string
+ description: "Type of the documented entity eg. Schütz, Leittechnik, etc"
+ voltage:
+ type: string
+ description: "Voltage of the documented entity eg. 110, 220, 380"
+ from:
+ type: string
+ format: date-time
+ description: "Starting date from where resources have been archived. Use ISO 8601 format (e.g., 2024-10-22T14:48:00Z)."
+ to:
+ type: string
+ format: date-time
+ description: "Ending date from where resources have been archived. Use ISO 8601 format (e.g., 2024-10-22T14:48:00Z)."
+ ArchivedResources:
+ type: object
+ required:
+ - resources
+ properties:
+ resources:
+ type: array
+ items:
+ $ref: '#/components/schemas/ArchivedResource'
+ ErrorResponseDto:
+ required:
+ - timestamp
+ - code
+ - message
+ type: object
+ properties:
+ timestamp:
+ type: string
+ description: 2017-07-21T17:32:28Z.
+ format: 'date-time'
+ code:
+ type: string
+ example: RESOURCE_NOT_FOUND
+ message:
+ type: string
+ example: Unable to find resource with id 'xy'.
\ No newline at end of file
diff --git a/app/src/main/openapi/data-resource-mockup.png b/app/src/main/openapi/data-resource-mockup.png
new file mode 100644
index 00000000..f2978dff
Binary files /dev/null and b/app/src/main/openapi/data-resource-mockup.png differ
diff --git a/app/src/main/openapi/history-api.yaml b/app/src/main/openapi/history-api.yaml
new file mode 100644
index 00000000..dc2cb5f3
--- /dev/null
+++ b/app/src/main/openapi/history-api.yaml
@@ -0,0 +1,276 @@
+openapi: 3.0.3
+info:
+ title: CoMPAS SCL Data Service History API
+ version: 1.0.0
+
+servers:
+ - url: https://demo.compas.energy
+ description: DSOM Versatel Production URL
+
+tags:
+ - name: history
+ description: Endpoints managing history of scl files
+
+security:
+ - open-id-connect:
+ - read
+
+paths:
+ /api/scl/search:
+ post:
+ tags:
+ - history
+ description: Trigger search enabled by the search filter
+ summary: Trigger search enabled by the search filter
+ operationId: searchForResources
+ requestBody:
+ description: Search filter
+ required: true
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/DataResourceSearch'
+ responses:
+ '200':
+ description: Successfully retrieved data resources meta data for given search query
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/DataResourcesResult'
+ '400':
+ description: One of the specified Parameters is not valid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponseDto'
+ '401':
+ description: Authentication information is missing or invalid
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponseDto'
+ default:
+ description: Unexpected Error, cannot handle request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponseDto'
+ /api/scl/{id}/versions:
+ get:
+ tags:
+ - history
+ description: Trigger search enabled by the search filter
+ summary: Trigger search enabled by the search filter
+ operationId: retrieveDataResourceHistory
+ parameters:
+ - name: id
+ in: path
+ description: Unique data resource identifier
+ required: true
+ schema:
+ type: string
+ format: uuid
+ responses:
+ '200':
+ description: Succefully retrieved data resource versions
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/DataResourceHistory'
+ '400':
+ description: One of the specified Parameters is not valid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponseDto'
+ '401':
+ description: Authentication information is missing or invalid
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponseDto'
+ '404':
+ description: Unable to finde data resource with given unique identifier
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponseDto'
+ default:
+ description: Unexpected Error, cannot handle request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponseDto'
+ /api/scl/{id}/version/{version}:
+ get:
+ tags:
+ - history
+ description: Retrieve data resource for a specific version
+ summary: Retrieve data resource for a specific version
+ operationId: retrieveDataResourceByVersion
+ parameters:
+ - name: id
+ in: path
+ description: Unique data resource identifier
+ required: true
+ schema:
+ type: string
+ format: uuid
+ - name: version
+ in: path
+ description: Combined with unique identifier this combination defines a specific document
+ required: true
+ schema:
+ type: string
+ responses:
+ '200':
+ description: Succefully retrieved data resource
+ content:
+ application/octet-stream:
+ schema:
+ type: string
+ format: binary
+ '401':
+ description: Authentication information is missing or invalid
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponseDto'
+ '404':
+ description: One of the specified Parameters is not valid.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponseDto'
+ default:
+ description: Unexpected Error, cannot handle request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponseDto'
+
+components:
+ securitySchemes:
+ open-id-connect: # <--- Arbitrary name for the security scheme. Used to refer to it from elsewhere.
+ type: openIdConnect
+ openIdConnectUrl: https://example.com/.well-known/openid-configuration
+
+ schemas:
+ DataResourceSearch:
+ type: object
+ properties:
+ uuid:
+ type: string
+ description: "If uuid is set no other filter must be set"
+ type:
+ type: string
+ description: "Fulltext match set to one of the supported scl types: SSD, IID, ICD, SCD, CID, SED, ISD, STD, etc."
+ name:
+ type: string
+ description: "Partially match allowed"
+ location:
+ type: string
+ description: "The location associated with the resource"
+ author:
+ type: string
+ description: "Fulltext match which can be retrieved via extra endpoint"
+ from:
+ type: string
+ format: date-time
+ description: "Starting date and time for filtering results. Use ISO 8601 format (e.g., 2024-10-22T14:48:00Z)."
+ to:
+ type: string
+ format: date-time
+ description: "Ending date and time for filtering results. Use ISO 8601 format (e.g., 2024-10-22T14:48:00Z)."
+ DataResourcesResult:
+ type: object
+ required:
+ - results
+ properties:
+ results:
+ type: array
+ items:
+ $ref: '#/components/schemas/DataResource'
+ DataResource:
+ type: object
+ required:
+ - uuid
+ - name
+ - author
+ - type
+ - changedAt
+ - version
+ - available
+ - deleted
+ properties:
+ uuid:
+ type: string
+ format: uuid
+ description: "Unique identifier"
+ name:
+ type: string
+ description: "Name of the resource"
+ author:
+ type: string
+ description: "Name of the author last changed the document"
+ type:
+ type: string
+ description: "One of the supported types: SSD, IID, ICD, SCD, CID, SED, ISD, STD, etc."
+ changedAt:
+ type: string
+ format: date-time
+ description: "Point in time of last modification/upload"
+ version:
+ type: string
+ description: "Generated version by the scl-data-service"
+ available:
+ type: boolean
+ description: "Defines if a resource is available as download or version was created while uploading a file"
+ default: true
+ deleted:
+ type: boolean
+ description: "Defines if a resource is marked as deleted"
+ default: false
+ location:
+ type: string
+ description: "The location associated with the resource"
+ DataResourceHistory:
+ type: object
+ required:
+ - versions
+ properties:
+ versions:
+ type: array
+ items:
+ $ref: '#/components/schemas/DataResourceVersion'
+ DataResourceVersion:
+ allOf:
+ - $ref: '#/components/schemas/DataResource'
+ - type: object
+ required:
+ - archived
+ properties:
+ comment:
+ type: string
+ description: "Comment given when uploading the data resource"
+ archived:
+ type: boolean
+ description: "Defines if given data resource is archived"
+ default: false
+ ErrorResponseDto:
+ required:
+ - timestamp
+ - code
+ - message
+ type: object
+ properties:
+ timestamp:
+ type: string
+ description: 2017-07-21T17:32:28Z.
+ format: 'date-time'
+ code:
+ type: string
+ example: TASK_NOT_FOUND
+ message:
+ type: string
+ example: Es wurde kein Task mit der id 'IdontExist' gefunden.
diff --git a/app/src/main/resources/application.properties b/app/src/main/resources/application.properties
index 88e84e90..bb7de6bd 100644
--- a/app/src/main/resources/application.properties
+++ b/app/src/main/resources/application.properties
@@ -258,4 +258,41 @@ quarkus.http.auth.permission.STD_READ_GET_VERSION_WS.policy=STD_READ
quarkus.http.auth.permission.STD_CREATE_POST_WS.paths=/compas-scl-data-service/scl-ws/v1/STD/create
quarkus.http.auth.permission.STD_CREATE_POST_WS.policy=STD_CREATE
quarkus.http.auth.permission.STD_UPDATE_PUT_WS.paths=/compas-scl-data-service/scl-ws/v1/STD/update
-quarkus.http.auth.permission.STD_UPDATE_PUT_WS.policy=STD_UPDATE
\ No newline at end of file
+quarkus.http.auth.permission.STD_UPDATE_PUT_WS.policy=STD_UPDATE
+
+#quarkus.http.auth.permission.ARCHIVE_API_READ_GET_RESOURCE_VERSION.paths=/compas-scl-data-service/api/resources/*
+#quarkus.http.auth.permission.ARCHIVE_API_READ_GET_RESOURCE_VERSION.methods=GET
+#quarkus.http.auth.permission.ARCHIVE_API_READ_GET_RESOURCE_VERSION.policy=ARCHIVE_API_READ
+#
+#quarkus.http.auth.permission.ARCHIVE_API_POST_RESOURCE.paths=/compas-scl-data-service/api/resources/*
+#quarkus.http.auth.permission.ARCHIVE_API_POST_RESOURCE.methods=POST
+#quarkus.http.auth.permission.ARCHIVE_API_POST_RESOURCE.policy=ARCHIVE_API_CREATE
+#
+#quarkus.http.auth.permission.LOCATIONS_API_GET_RESOURCE.paths=/compas-scl-data-service/api/locations/*
+#quarkus.http.auth.permission.LOCATIONS_API_GET_RESOURCE.methods=GET
+#quarkus.http.auth.permission.LOCATIONS_API_GET_RESOURCE.policy=ARCHIVE_API_GET
+#
+#quarkus.http.auth.permission.LOCATIONS_API_POST_RESOURCE.paths=/compas-scl-data-service/api/locations/*
+#quarkus.http.auth.permission.LOCATIONS_API_POST_RESOURCE.methods=POST
+#quarkus.http.auth.permission.LOCATIONS_API_POST_RESOURCE.policy=ARCHIVE_API_CREATE
+#
+#quarkus.http.auth.permission.LOCATIONS_API_PUT_RESOURCE.paths=/compas-scl-data-service/api/locations/*
+#quarkus.http.auth.permission.LOCATIONS_API_PUT_RESOURCE.methods=PUT
+#quarkus.http.auth.permission.LOCATIONS_API_PUT_RESOURCE.policy=ARCHIVE_API_UPDATE
+#
+#quarkus.http.auth.permission.LOCATIONS_API_DELETE_RESOURCE.paths=/compas-scl-data-service/api/locations/*
+#quarkus.http.auth.permission.LOCATIONS_API_DELETE_RESOURCE.methods=DELETE
+#quarkus.http.auth.permission.LOCATIONS_API_DELETE_RESOURCE.policy=ARCHIVE_API_DELETE
+
+quarkus.http.auth.permission.api.paths=/compas-scl-data-service/api/*
+quarkus.http.auth.permission.api.policy=permit
+
+# Feature flag to enable or disable the history feature
+scl-data-service.features.is-history-enabled=true
+# Feature flag to enable or disable persistent delete mode
+scl-data-service.features.keep-deleted-files=true
+
+scl-data-service.archiving.filesystem.location=${FILESYSTEM_LOCATION_PATH:/work/locations}
+scl-data-service.archiving.connector.enabled=${ELO_CONNECTOR_ENABLED:false}
+
+quarkus.rest-client.elo-connector-client.url=${ELO_CONNECTOR_BASE_URL:http://elo-connector:8080/compas-elo-connector/api}
\ No newline at end of file
diff --git a/app/src/test/java/org/lfenergy/compas/scl/data/rest/api/archive/ArchiveResourceTest.java b/app/src/test/java/org/lfenergy/compas/scl/data/rest/api/archive/ArchiveResourceTest.java
new file mode 100644
index 00000000..5c687efe
--- /dev/null
+++ b/app/src/test/java/org/lfenergy/compas/scl/data/rest/api/archive/ArchiveResourceTest.java
@@ -0,0 +1,168 @@
+package org.lfenergy.compas.scl.data.rest.api.archive;
+
+import io.quarkus.test.InjectMock;
+import io.quarkus.test.common.http.TestHTTPEndpoint;
+import io.quarkus.test.junit.QuarkusTest;
+import io.quarkus.test.security.TestSecurity;
+import io.restassured.response.Response;
+import io.smallrye.mutiny.Uni;
+import jakarta.ws.rs.core.MediaType;
+import org.eclipse.microprofile.jwt.JsonWebToken;
+import org.junit.jupiter.api.Test;
+import org.lfenergy.compas.scl.data.model.*;
+import org.lfenergy.compas.scl.data.rest.api.archive.model.ArchivedResource;
+import org.lfenergy.compas.scl.data.rest.api.archive.model.ArchivedResources;
+import org.lfenergy.compas.scl.data.rest.api.archive.model.ArchivedResourcesHistory;
+import org.lfenergy.compas.scl.data.service.CompasSclDataService;
+
+import java.io.File;
+import java.nio.file.Paths;
+import java.util.List;
+import java.util.UUID;
+
+import static io.restassured.RestAssured.given;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.when;
+
+@QuarkusTest
+@TestHTTPEndpoint(ArchiveResource.class)
+@TestSecurity(user = "test-user")
+class ArchiveResourceTest {
+
+ @InjectMock
+ private CompasSclDataService compasSclDataService;
+ @InjectMock
+ private JsonWebToken jwt;
+
+ @Test
+ void archiveSclResource_WhenCalled_ThenReturnsArchivedResource() {
+ UUID uuid = UUID.randomUUID();
+ String name = "Name";
+ String version = "1.0.0";
+ IAbstractArchivedResourceMetaItem testData = new ArchivedSclResourceTestDataBuilder().setId(uuid.toString()).build();
+ when(jwt.getClaim("name")).thenReturn("");
+ when(compasSclDataService.archiveSclResource(uuid, new Version(version), "")).thenReturn(Uni.createFrom().item(testData));
+ Response response = given()
+ .contentType(MediaType.APPLICATION_JSON)
+ .when().post("/scl/" + uuid + "/versions/" + version)
+ .then()
+ .statusCode(200)
+ .extract()
+ .response();
+
+ ArchivedResource result = response.as(ArchivedResource.class);
+ assertEquals(uuid, UUID.fromString(result.getUuid()));
+ assertEquals(name, result.getName());
+ assertEquals(version, result.getVersion());
+ }
+
+ @Test
+ void archiveResource_WhenCalled_ThenReturnsArchivedResource() {
+ UUID uuid = UUID.randomUUID();
+ String name = "Name";
+ String version = "1.0.0";
+ IAbstractArchivedResourceMetaItem testData = new ArchivedReferencedResourceTestDataBuilder().setId(uuid.toString()).build();
+ File f = Paths.get("src","test","resources","scl", "icd_import_ied_test.scd").toFile();
+ when(compasSclDataService.archiveResource(eq(uuid), eq(version), eq(null), eq(null), eq("application/json"), eq(null), any(File.class))).thenReturn(Uni.createFrom().item(testData));
+ Response response = given()
+ .contentType(MediaType.APPLICATION_JSON)
+ .body(f)
+ .when().post("/referenced-resource/" + uuid + "/versions/" + version)
+ .then()
+ .statusCode(200)
+ .extract()
+ .response();
+
+ ArchivedResource result = response.as(ArchivedResource.class);
+ assertEquals(uuid, UUID.fromString(result.getUuid()));
+ assertEquals(name, result.getName());
+ assertEquals(version, result.getVersion());
+ }
+
+ @Test
+ void searchArchivedResources_WhenCalledWithUuid_ThenReturnsMatchingArchivedResources() {
+ UUID uuid = UUID.randomUUID();
+ String name = "Name";
+ String version = "1.0.0";
+ IAbstractArchivedResourceMetaItem testData1 = new ArchivedSclResourceTestDataBuilder().setId(uuid.toString()).setName(name).setVersion(version).build();
+ IAbstractArchivedResourceMetaItem testData2 = new ArchivedSclResourceTestDataBuilder().setId(uuid.toString()).setName(name).setVersion("1.0.1").build();
+ IArchivedResourcesMetaItem archivedResources = new ArchivedResourcesMetaItem(List.of(testData1, testData2));
+
+ when(compasSclDataService.searchArchivedResources(uuid)).thenReturn(archivedResources);
+ Response response = given()
+ .contentType(MediaType.APPLICATION_JSON)
+ .body("{\"uuid\": \"" + uuid + "\"}")
+ .when().post("/resources/search")
+ .then()
+ .statusCode(200)
+ .extract()
+ .response();
+
+ ArchivedResources result = response.as(ArchivedResources.class);
+ assertEquals(uuid, UUID.fromString(result.getResources().get(0).getUuid()));
+ assertEquals(name, result.getResources().get(0).getName());
+ assertEquals(version, result.getResources().get(0).getVersion());
+ }
+
+ @Test
+ void searchArchivedResources_WhenCalledWithoutUuid_ThenReturnsMatchingArchivedResources() {
+ UUID uuid = UUID.randomUUID();
+ UUID uuid2 = UUID.randomUUID();
+ String name = "Name";
+ String version = "1.0.0";
+ IAbstractArchivedResourceMetaItem testData1 = new ArchivedSclResourceTestDataBuilder().setId(uuid.toString()).setName(name).setVersion(version).build();
+ IAbstractArchivedResourceMetaItem testData2 = new ArchivedSclResourceTestDataBuilder().setId(uuid2.toString()).setName(name).setVersion(version).build();
+ IArchivedResourcesMetaItem archivedResources = new ArchivedResourcesMetaItem(List.of(testData1, testData2));
+
+ when(compasSclDataService.searchArchivedResources(null, name, null,null,null,null,null, null)).thenReturn(archivedResources);
+ Response response = given()
+ .contentType(MediaType.APPLICATION_JSON)
+ .body("{\"name\": \"Name\"}")
+ .when().post("/resources/search")
+ .then()
+ .statusCode(200)
+ .extract()
+ .response();
+
+ ArchivedResources result = response.as(ArchivedResources.class);
+ assertEquals(uuid, UUID.fromString(result.getResources().get(0).getUuid()));
+ assertEquals(name, result.getResources().get(0).getName());
+ assertEquals(version, result.getResources().get(0).getVersion());
+ assertEquals(uuid2, UUID.fromString(result.getResources().get(1).getUuid()));
+ assertEquals(name, result.getResources().get(1).getName());
+ assertEquals(version, result.getResources().get(1).getVersion());
+ }
+
+ @Test
+ void retrieveArchivedResourceHistory_WhenCalledWithUuid_ThenReturnsMatchingArchivedResources() {
+ UUID resourceUuid = UUID.randomUUID();
+ UUID uuid1 = UUID.randomUUID();
+ UUID uuid2 = UUID.randomUUID();
+ String name = "Name";
+ String version1 = "1.0.0";
+ String version2 = "1.0.1";
+ IArchivedResourceVersion testData1 = new ArchivedResourceVersionTestDataBuilder().setId(uuid1.toString()).setName(name).setVersion(version1).build();
+ IArchivedResourceVersion testData2 = new ArchivedResourceVersionTestDataBuilder().setId(uuid2.toString()).setName(name).setVersion(version2).build();
+ IArchivedResourcesHistoryMetaItem archivedResources = new ArchivedResourcesHistoryMetaItem(List.of(testData1, testData2));
+
+ when(compasSclDataService.getArchivedResourceHistory(resourceUuid)).thenReturn(archivedResources);
+ Response response = given()
+ .contentType(MediaType.APPLICATION_JSON)
+ .when().get("/resources/"+ resourceUuid +"/versions")
+ .then()
+ .statusCode(200)
+ .extract()
+ .response();
+
+ ArchivedResourcesHistory result = response.as(ArchivedResourcesHistory.class);
+ assertEquals(uuid1, UUID.fromString(result.getVersions().get(0).getUuid()));
+ assertEquals(name, result.getVersions().get(0).getName());
+ assertEquals(version1, result.getVersions().get(0).getVersion());
+ assertEquals(uuid2, UUID.fromString(result.getVersions().get(1).getUuid()));
+ assertEquals(name, result.getVersions().get(1).getName());
+ assertEquals(version2, result.getVersions().get(1).getVersion());
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/test/java/org/lfenergy/compas/scl/data/rest/api/archive/ArchivedReferencedResourceTestDataBuilder.java b/app/src/test/java/org/lfenergy/compas/scl/data/rest/api/archive/ArchivedReferencedResourceTestDataBuilder.java
new file mode 100644
index 00000000..d9a0fb0a
--- /dev/null
+++ b/app/src/test/java/org/lfenergy/compas/scl/data/rest/api/archive/ArchivedReferencedResourceTestDataBuilder.java
@@ -0,0 +1,95 @@
+package org.lfenergy.compas.scl.data.rest.api.archive;
+
+import org.lfenergy.compas.scl.data.model.ArchivedReferencedResourceMetaItem;
+import org.lfenergy.compas.scl.data.model.IAbstractArchivedResourceMetaItem;
+import org.lfenergy.compas.scl.data.model.IResourceTagItem;
+
+import java.time.Instant;
+import java.time.OffsetDateTime;
+import java.time.ZoneId;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+
+public class ArchivedReferencedResourceTestDataBuilder {
+ // Default values
+ private String id = UUID.randomUUID().toString();
+ private String name = "Name";
+ private String version = "1.0.0";
+ private String location = "some location";
+ private String note = "some note";
+ private String author = "user1";
+ private String approver = "user2";
+ private String type = "some type";
+ private String contentType = "contentType1";
+ private OffsetDateTime modifiedAt = null;
+ private OffsetDateTime archivedAt = Instant.now().atZone(ZoneId.systemDefault()).toOffsetDateTime();
+ private List fields = new ArrayList<>();
+
+ public ArchivedReferencedResourceTestDataBuilder() {
+ }
+
+ public ArchivedReferencedResourceTestDataBuilder setId(String id) {
+ this.id = id;
+ return this;
+ }
+
+ public ArchivedReferencedResourceTestDataBuilder setName(String name) {
+ this.name = name;
+ return this;
+ }
+
+ public ArchivedReferencedResourceTestDataBuilder setVersion(String version) {
+ this.version = version;
+ return this;
+ }
+
+ public ArchivedReferencedResourceTestDataBuilder setLocation(String location) {
+ this.location = location;
+ return this;
+ }
+
+ public ArchivedReferencedResourceTestDataBuilder setNote(String note) {
+ this.note = note;
+ return this;
+ }
+
+ public ArchivedReferencedResourceTestDataBuilder setAuthor(String author) {
+ this.author = author;
+ return this;
+ }
+
+ public ArchivedReferencedResourceTestDataBuilder setApprover(String approver) {
+ this.approver = approver;
+ return this;
+ }
+
+ public ArchivedReferencedResourceTestDataBuilder setType(String type) {
+ this.type = type;
+ return this;
+ }
+
+ public ArchivedReferencedResourceTestDataBuilder setContentType(String contentType) {
+ this.contentType = contentType;
+ return this;
+ }
+
+ public ArchivedReferencedResourceTestDataBuilder setModifiedAt(OffsetDateTime modifiedAt) {
+ this.modifiedAt = modifiedAt;
+ return this;
+ }
+
+ public ArchivedReferencedResourceTestDataBuilder setArchivedAt(OffsetDateTime archivedAt) {
+ this.archivedAt = archivedAt;
+ return this;
+ }
+
+ public ArchivedReferencedResourceTestDataBuilder setFields(List fields) {
+ this.fields = fields;
+ return this;
+ }
+
+ public IAbstractArchivedResourceMetaItem build() {
+ return new ArchivedReferencedResourceMetaItem(id, name, version, author, approver, type, contentType, location, fields, modifiedAt, archivedAt, note);
+ }
+}
diff --git a/app/src/test/java/org/lfenergy/compas/scl/data/rest/api/archive/ArchivedResourceVersionTestDataBuilder.java b/app/src/test/java/org/lfenergy/compas/scl/data/rest/api/archive/ArchivedResourceVersionTestDataBuilder.java
new file mode 100644
index 00000000..a4db872d
--- /dev/null
+++ b/app/src/test/java/org/lfenergy/compas/scl/data/rest/api/archive/ArchivedResourceVersionTestDataBuilder.java
@@ -0,0 +1,99 @@
+package org.lfenergy.compas.scl.data.rest.api.archive;
+
+import org.lfenergy.compas.scl.data.model.*;
+
+import java.time.Instant;
+import java.time.OffsetDateTime;
+import java.time.ZoneId;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+
+public class ArchivedResourceVersionTestDataBuilder {
+ // Default values
+ private String id = UUID.randomUUID().toString();
+ private String name = "Name";
+ private String version = "1.0.0";
+ private String location = "some location";
+ private String note = "some note";
+ private String author = "user1";
+ private String approver = "user2";
+ private String type = "some type";
+ private String contentType = "contentType1";
+ private String voltage = "100";
+ private OffsetDateTime modifiedAt = null;
+ private OffsetDateTime archivedAt = Instant.now().atZone(ZoneId.systemDefault()).toOffsetDateTime();
+ private List fields = new ArrayList<>();
+
+ public ArchivedResourceVersionTestDataBuilder() {
+ }
+
+ public ArchivedResourceVersionTestDataBuilder setId(String id) {
+ this.id = id;
+ return this;
+ }
+
+ public ArchivedResourceVersionTestDataBuilder setName(String name) {
+ this.name = name;
+ return this;
+ }
+
+ public ArchivedResourceVersionTestDataBuilder setVersion(String version) {
+ this.version = version;
+ return this;
+ }
+
+ public ArchivedResourceVersionTestDataBuilder setLocation(String location) {
+ this.location = location;
+ return this;
+ }
+
+ public ArchivedResourceVersionTestDataBuilder setNote(String note) {
+ this.note = note;
+ return this;
+ }
+
+ public ArchivedResourceVersionTestDataBuilder setAuthor(String author) {
+ this.author = author;
+ return this;
+ }
+
+ public ArchivedResourceVersionTestDataBuilder setApprover(String approver) {
+ this.approver = approver;
+ return this;
+ }
+
+ public ArchivedResourceVersionTestDataBuilder setType(String type) {
+ this.type = type;
+ return this;
+ }
+
+ public ArchivedResourceVersionTestDataBuilder setContentType(String contentType) {
+ this.contentType = contentType;
+ return this;
+ }
+
+ public ArchivedResourceVersionTestDataBuilder setVoltage(String voltage) {
+ this.voltage = voltage;
+ return this;
+ }
+
+ public ArchivedResourceVersionTestDataBuilder setModifiedAt(OffsetDateTime modifiedAt) {
+ this.modifiedAt = modifiedAt;
+ return this;
+ }
+
+ public ArchivedResourceVersionTestDataBuilder setArchivedAt(OffsetDateTime archivedAt) {
+ this.archivedAt = archivedAt;
+ return this;
+ }
+
+ public ArchivedResourceVersionTestDataBuilder setFields(List fields) {
+ this.fields = fields;
+ return this;
+ }
+
+ public IArchivedResourceVersion build() {
+ return new ArchivedResourceVersion(id, name, version, location, note, author, approver, type, contentType, voltage, fields, modifiedAt, archivedAt, note, true);
+ }
+}
diff --git a/app/src/test/java/org/lfenergy/compas/scl/data/rest/api/archive/ArchivedSclResourceTestDataBuilder.java b/app/src/test/java/org/lfenergy/compas/scl/data/rest/api/archive/ArchivedSclResourceTestDataBuilder.java
new file mode 100644
index 00000000..07b55519
--- /dev/null
+++ b/app/src/test/java/org/lfenergy/compas/scl/data/rest/api/archive/ArchivedSclResourceTestDataBuilder.java
@@ -0,0 +1,99 @@
+package org.lfenergy.compas.scl.data.rest.api.archive;
+
+import org.lfenergy.compas.scl.data.model.*;
+
+import java.time.Instant;
+import java.time.OffsetDateTime;
+import java.time.ZoneId;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+
+public class ArchivedSclResourceTestDataBuilder {
+ // Default values
+ private String id = UUID.randomUUID().toString();
+ private String name = "Name";
+ private String version = "1.0.0";
+ private String location = "some location";
+ private String note = "some note";
+ private String author = "user1";
+ private String approver = "user2";
+ private String type = "some type";
+ private String contentType = "contentType1";
+ private String voltage = "100";
+ private OffsetDateTime modifiedAt = null;
+ private OffsetDateTime archivedAt = Instant.now().atZone(ZoneId.systemDefault()).toOffsetDateTime();
+ private List fields = new ArrayList<>();
+
+ public ArchivedSclResourceTestDataBuilder() {
+ }
+
+ public ArchivedSclResourceTestDataBuilder setId(String id) {
+ this.id = id;
+ return this;
+ }
+
+ public ArchivedSclResourceTestDataBuilder setName(String name) {
+ this.name = name;
+ return this;
+ }
+
+ public ArchivedSclResourceTestDataBuilder setVersion(String version) {
+ this.version = version;
+ return this;
+ }
+
+ public ArchivedSclResourceTestDataBuilder setLocation(String location) {
+ this.location = location;
+ return this;
+ }
+
+ public ArchivedSclResourceTestDataBuilder setNote(String note) {
+ this.note = note;
+ return this;
+ }
+
+ public ArchivedSclResourceTestDataBuilder setAuthor(String author) {
+ this.author = author;
+ return this;
+ }
+
+ public ArchivedSclResourceTestDataBuilder setApprover(String approver) {
+ this.approver = approver;
+ return this;
+ }
+
+ public ArchivedSclResourceTestDataBuilder setType(String type) {
+ this.type = type;
+ return this;
+ }
+
+ public ArchivedSclResourceTestDataBuilder setContentType(String contentType) {
+ this.contentType = contentType;
+ return this;
+ }
+
+ public ArchivedSclResourceTestDataBuilder setVoltage(String voltage) {
+ this.voltage = voltage;
+ return this;
+ }
+
+ public ArchivedSclResourceTestDataBuilder setModifiedAt(OffsetDateTime modifiedAt) {
+ this.modifiedAt = modifiedAt;
+ return this;
+ }
+
+ public ArchivedSclResourceTestDataBuilder setArchivedAt(OffsetDateTime archivedAt) {
+ this.archivedAt = archivedAt;
+ return this;
+ }
+
+ public ArchivedSclResourceTestDataBuilder setFields(List fields) {
+ this.fields = fields;
+ return this;
+ }
+
+ public IAbstractArchivedResourceMetaItem build() {
+ return new ArchivedSclResourceMetaItem(id, name, version, author, approver, type, contentType, location, fields, modifiedAt, archivedAt, note, voltage);
+ }
+}
diff --git a/app/src/test/java/org/lfenergy/compas/scl/data/rest/api/locations/LocationResourceTestDataBuilder.java b/app/src/test/java/org/lfenergy/compas/scl/data/rest/api/locations/LocationResourceTestDataBuilder.java
new file mode 100644
index 00000000..10442dbc
--- /dev/null
+++ b/app/src/test/java/org/lfenergy/compas/scl/data/rest/api/locations/LocationResourceTestDataBuilder.java
@@ -0,0 +1,47 @@
+package org.lfenergy.compas.scl.data.rest.api.locations;
+
+import org.lfenergy.compas.scl.data.model.ILocationMetaItem;
+import org.lfenergy.compas.scl.data.model.LocationMetaItem;
+
+import java.util.UUID;
+
+public class LocationResourceTestDataBuilder {
+ // Default values
+ private String id = UUID.randomUUID().toString();
+ private String name = "Name";
+ private String key = "Key";
+ private String description = "Description";
+ private int assignedResources = 0;
+
+ public LocationResourceTestDataBuilder() {
+ }
+
+ public LocationResourceTestDataBuilder setId(String id) {
+ this.id = id;
+ return this;
+ }
+
+ public LocationResourceTestDataBuilder setName(String name) {
+ this.name = name;
+ return this;
+ }
+
+ public LocationResourceTestDataBuilder setKey(String key) {
+ this.key = key;
+ return this;
+ }
+
+ public LocationResourceTestDataBuilder setDescription(String description) {
+ this.description = description;
+ return this;
+ }
+
+ public LocationResourceTestDataBuilder setAssignedResources(int assignedResources) {
+ this.assignedResources = assignedResources;
+ return this;
+ }
+
+ public ILocationMetaItem build() {
+ return new LocationMetaItem(id, key, name, description, assignedResources);
+ }
+}
diff --git a/app/src/test/java/org/lfenergy/compas/scl/data/rest/api/locations/LocationsResourceTest.java b/app/src/test/java/org/lfenergy/compas/scl/data/rest/api/locations/LocationsResourceTest.java
new file mode 100644
index 00000000..86dfc0e7
--- /dev/null
+++ b/app/src/test/java/org/lfenergy/compas/scl/data/rest/api/locations/LocationsResourceTest.java
@@ -0,0 +1,218 @@
+package org.lfenergy.compas.scl.data.rest.api.locations;
+
+import io.quarkus.test.InjectMock;
+import io.quarkus.test.common.http.TestHTTPEndpoint;
+import io.quarkus.test.junit.QuarkusTest;
+import io.quarkus.test.security.TestSecurity;
+import io.restassured.response.Response;
+import io.smallrye.mutiny.Uni;
+import jakarta.ws.rs.core.MediaType;
+import org.eclipse.microprofile.jwt.JsonWebToken;
+import org.junit.jupiter.api.Test;
+import org.lfenergy.compas.scl.data.exception.CompasSclDataServiceException;
+import org.lfenergy.compas.scl.data.model.ILocationMetaItem;
+import org.lfenergy.compas.scl.data.rest.api.locations.model.Location;
+import org.lfenergy.compas.scl.data.service.CompasSclDataService;
+
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.UUID;
+
+import static io.restassured.RestAssured.given;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.lfenergy.compas.scl.data.exception.CompasSclDataServiceErrorCode.POSTGRES_INSERT_ERROR_CODE;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.when;
+
+@QuarkusTest
+@TestHTTPEndpoint(LocationsResource.class)
+@TestSecurity(user = "test-user")
+class LocationsResourceTest {
+
+ @InjectMock
+ private CompasSclDataService compasSclDataService;
+ @InjectMock
+ private JsonWebToken jwt;
+
+ @Test
+ void createLocation_WhenCalled_ThenReturnsCreatedLocation() {
+ UUID uuid = UUID.randomUUID();
+ String key = "Key";
+ String name = "Name";
+ String description = "Description";
+ Location location = new Location();
+ location.setUuid(uuid.toString());
+ location.setKey(key);
+ location.setName(name);
+ location.setDescription(description);
+ ILocationMetaItem testData = new LocationResourceTestDataBuilder().setId(uuid.toString()).build();
+
+ when(jwt.getClaim("name")).thenReturn("test user");
+ when(compasSclDataService.createLocation(key, name, description)).thenReturn(Uni.createFrom().item(testData));
+ Response response = given()
+ .contentType(MediaType.APPLICATION_JSON)
+ .body(location)
+ .when().post("/")
+ .then()
+ .statusCode(200)
+ .extract()
+ .response();
+
+ Location result = response.as(Location.class);
+ assertEquals(uuid, UUID.fromString(result.getUuid()));
+ assertEquals(key, result.getKey());
+ assertEquals(name, result.getName());
+ assertEquals(description, result.getDescription());
+ }
+
+ @Test
+ void deleteLocation_WhenCalled_ThenDeletesLocation() {
+ UUID uuid = UUID.randomUUID();
+ ILocationMetaItem testData = new LocationResourceTestDataBuilder().setId(uuid.toString()).build();
+ when(compasSclDataService.findLocationByUUID(uuid)).thenReturn(testData);
+ given()
+ .contentType(MediaType.APPLICATION_JSON)
+ .when().delete("/" + uuid)
+ .then()
+ .statusCode(204)
+ .extract()
+ .response();
+ }
+
+ @Test
+ void getLocation_WhenCalled_ThenReturnsLocation() {
+ UUID uuid = UUID.randomUUID();
+ String key = "Key";
+ String name = "Name";
+ String description = "Description";
+ Location location = new Location();
+ location.setUuid(uuid.toString());
+ location.setKey(key);
+ location.setName(name);
+ location.setDescription(description);
+ ILocationMetaItem testData = new LocationResourceTestDataBuilder().setId(uuid.toString()).build();
+
+ when(compasSclDataService.findLocationByUUID(uuid)).thenReturn(testData);
+
+ Response response = given()
+ .contentType(MediaType.APPLICATION_JSON)
+ .body(location)
+ .when().get("/" + uuid)
+ .then()
+ .statusCode(200)
+ .extract()
+ .response();
+
+ Location result = response.as(Location.class);
+ assertEquals(uuid, UUID.fromString(result.getUuid()));
+ assertEquals(key, result.getKey());
+ assertEquals(name, result.getName());
+ assertEquals(description, result.getDescription());
+ }
+
+ @Test
+ void getLocations_WhenCalled_ThenReturnsLocations() {
+ UUID uuid = UUID.randomUUID();
+ UUID uuid2 = UUID.randomUUID();
+ String key = "Key";
+ String name = "Name";
+ String description = "Description";
+ Location location = new Location();
+ location.setUuid(uuid.toString());
+ location.setKey(key);
+ location.setName(name);
+ location.setDescription(description);
+ ILocationMetaItem testData = new LocationResourceTestDataBuilder().setId(uuid.toString()).build();
+ ILocationMetaItem testData2 = new LocationResourceTestDataBuilder().setId(uuid2.toString()).build();
+
+ when(compasSclDataService.listLocations(0, 25)).thenReturn(List.of(testData, testData2));
+
+ Response response = given()
+ .contentType(MediaType.APPLICATION_JSON)
+ .body(location)
+ .when().get("/")
+ .then()
+ .statusCode(200)
+ .extract()
+ .response();
+
+ List result = response.as(List.class);
+ List mappedResult = new ArrayList<>();
+ result.stream().map(entry ->
+ new Location()
+ .uuid((String) entry.get("uuid"))
+ .key((String) entry.get("key"))
+ .name((String) entry.get("name"))
+ .description((String) entry.get("description")))
+ .forEach(mappedResult::add);
+ assertEquals(uuid, UUID.fromString(mappedResult.get(0).getUuid()));
+ assertEquals(key, mappedResult.get(0).getKey());
+ assertEquals(name, mappedResult.get(0).getName());
+ assertEquals(description, mappedResult.get(0).getDescription());
+ assertEquals(uuid2, UUID.fromString(mappedResult.get(1).getUuid()));
+ assertEquals(key, mappedResult.get(1).getKey());
+ assertEquals(name, mappedResult.get(1).getName());
+ assertEquals(description, mappedResult.get(1).getDescription());
+ }
+
+ @Test
+ void unassignResourceFromLocation_WhenSqlExceptionOccurs_ThenReturns500StatusCode() {
+ UUID locationUuid = UUID.randomUUID();
+ UUID resourceUuid = UUID.randomUUID();
+
+ doThrow(new CompasSclDataServiceException(POSTGRES_INSERT_ERROR_CODE, "Error unassigning SCL Resource from Location in database!"))
+ .when(compasSclDataService).unassignResourceFromLocation(locationUuid, resourceUuid);
+
+ given()
+ .contentType(MediaType.APPLICATION_JSON)
+ .when().post("/"+locationUuid+"/resources/"+resourceUuid+"/unassign")
+ .then()
+ .statusCode(500);
+ }
+
+ @Test
+ void assignResourceToLocation_WhenSqlExceptionOccurs_ThenReturns500StatusCode() {
+ UUID locationUuid = UUID.randomUUID();
+ UUID resourceUuid = UUID.randomUUID();
+
+ doThrow(new CompasSclDataServiceException(POSTGRES_INSERT_ERROR_CODE, "Error assigning SCL Resource from Location in database!"))
+ .when(compasSclDataService).assignResourceToLocation(locationUuid, resourceUuid);
+
+ given()
+ .contentType(MediaType.APPLICATION_JSON)
+ .when().post("/"+locationUuid+"/resources/"+resourceUuid+"/assign")
+ .then()
+ .statusCode(500);
+ }
+
+ @Test
+ void updateLocation_WhenCalled_ThenReturnsUpdatedLocation() {
+ UUID uuid = UUID.randomUUID();
+ String key = "Key";
+ String name = "Name";
+ String description = "Description";
+ Location location = new Location();
+ location.setUuid(uuid.toString());
+ location.setKey(key);
+ location.setName(name);
+ location.setDescription(description);
+ ILocationMetaItem testData = new LocationResourceTestDataBuilder().setId(uuid.toString()).build();
+
+ when(compasSclDataService.updateLocation(uuid, key, name, description)).thenReturn(testData);
+ Response response = given()
+ .contentType(MediaType.APPLICATION_JSON)
+ .body(location)
+ .when().put("/" + uuid)
+ .then()
+ .statusCode(200)
+ .extract()
+ .response();
+
+ Location result = response.as(Location.class);
+ assertEquals(uuid, UUID.fromString(result.getUuid()));
+ assertEquals(key, result.getKey());
+ assertEquals(name, result.getName());
+ assertEquals(description, result.getDescription());
+ }
+}
\ No newline at end of file
diff --git a/app/src/test/java/org/lfenergy/compas/scl/data/rest/api/scl/HistoryResourceTest.java b/app/src/test/java/org/lfenergy/compas/scl/data/rest/api/scl/HistoryResourceTest.java
new file mode 100644
index 00000000..62ab490b
--- /dev/null
+++ b/app/src/test/java/org/lfenergy/compas/scl/data/rest/api/scl/HistoryResourceTest.java
@@ -0,0 +1,122 @@
+package org.lfenergy.compas.scl.data.rest.api.scl;
+
+import io.quarkus.test.InjectMock;
+import io.quarkus.test.common.http.TestHTTPEndpoint;
+import io.quarkus.test.junit.QuarkusTest;
+import io.quarkus.test.security.TestSecurity;
+import jakarta.ws.rs.core.MediaType;
+import org.junit.jupiter.api.Test;
+
+import org.lfenergy.compas.scl.data.model.IHistoryMetaItem;
+import org.lfenergy.compas.scl.data.model.Version;
+import org.lfenergy.compas.scl.data.rest.api.scl.model.DataResourceHistory;
+import org.lfenergy.compas.scl.data.rest.api.scl.model.DataResourceSearch;
+import org.lfenergy.compas.scl.data.rest.api.scl.model.DataResourcesResult;
+import org.lfenergy.compas.scl.data.service.CompasSclDataService;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+
+import static io.restassured.RestAssured.given;
+import static org.junit.jupiter.api.Assertions.*;
+import static org.mockito.Mockito.*;
+
+@QuarkusTest
+@TestHTTPEndpoint(HistoryResource.class)
+@TestSecurity(user = "test-user")
+class HistoryResourceTest {
+
+ @InjectMock
+ private CompasSclDataService compasSclDataService;
+
+ @Test
+ void searchForResources_WhenCalled_ThenReturnsDataResourcesResult() {
+ var search = new DataResourceSearch(); // Populate with necessary data
+
+ List testData = new ArrayList<>();
+ testData.add(new HistoryResourceTestdataBuilder().build());
+
+ when(compasSclDataService.listHistory()).thenReturn(testData);
+
+ var response = given()
+ .contentType(MediaType.APPLICATION_JSON)
+ .body(search)
+ .when().post("/search")
+ .then()
+ .statusCode(200)
+ .extract()
+ .response();
+
+ var dataResourcesResult = response.as(DataResourcesResult.class);
+ assertNotNull(dataResourcesResult);
+ }
+
+ @Test
+ void searchForResourcesWithUUIDFilter_WhenCalled_ThenReturnsOnlyMatchingData() {
+ var uuid = UUID.randomUUID();
+ var search = new DataResourceSearch();
+ search.setUuid(uuid.toString());
+
+ List testData = new ArrayList<>();
+ testData.add(new HistoryResourceTestdataBuilder().setId(uuid.toString()).build());
+
+ when(compasSclDataService.listHistory(uuid)).thenReturn(testData);
+
+ var response = given()
+ .contentType(MediaType.APPLICATION_JSON)
+ .body(search)
+ .when().post("/search")
+ .then()
+ .statusCode(200)
+ .extract()
+ .response();
+
+ var dataResourcesResult = response.as(DataResourcesResult.class);
+ assertNotNull(dataResourcesResult);
+ assertEquals(1, dataResourcesResult.getResults().size(), "Expected exactly 1 matching result");
+ }
+
+ @Test
+ void retrieveDataResourceHistory_WhenCalled_ThenReturnsEmptyDataResourceHistory() {
+ var uuid = UUID.randomUUID();
+ List testData = new ArrayList<>();
+ testData.add(new HistoryResourceTestdataBuilder().setId(uuid.toString()).build());
+
+ when(compasSclDataService.listHistoryVersionsByUUID(uuid)).thenReturn(testData);
+
+ var response = given()
+ .pathParam("id", uuid)
+ .when().get("/{id}/versions")
+ .then()
+ .statusCode(200)
+ .extract()
+ .response();
+
+ var dataResourceHistory = response.as(DataResourceHistory.class);
+ assertNotNull(dataResourceHistory);
+ }
+
+ @Test
+ void retrieveDataResourceByVersion_WhenCalled_ThenReturnsHelloWorldBinaryData() {
+ var id = UUID.randomUUID();
+ var version = "1.0.0";
+
+ when(compasSclDataService.findByUUID(id, new Version(version))).thenReturn("hello world");
+
+ var response = given()
+ .pathParam("id", id)
+ .pathParam("version", version)
+ .when().get("/{id}/version/{version}")
+ .then()
+ .statusCode(200)
+ .extract()
+ .response();
+
+ byte[] responseData = response.asByteArray();
+
+ assertNotNull(responseData);
+ String expectedContent = "hello world";
+ assertArrayEquals(expectedContent.getBytes(), responseData);
+ }
+}
diff --git a/app/src/test/java/org/lfenergy/compas/scl/data/rest/api/scl/HistoryResourceTestdataBuilder.java b/app/src/test/java/org/lfenergy/compas/scl/data/rest/api/scl/HistoryResourceTestdataBuilder.java
new file mode 100644
index 00000000..80986820
--- /dev/null
+++ b/app/src/test/java/org/lfenergy/compas/scl/data/rest/api/scl/HistoryResourceTestdataBuilder.java
@@ -0,0 +1,78 @@
+package org.lfenergy.compas.scl.data.rest.api.scl;
+
+import org.lfenergy.compas.scl.data.model.HistoryMetaItem;
+import org.lfenergy.compas.scl.data.model.IHistoryMetaItem;
+
+import java.time.OffsetDateTime;
+import java.util.UUID;
+
+public class HistoryResourceTestdataBuilder {
+
+ // Default values
+ private String id = UUID.randomUUID().toString();
+ private String name = "Name";
+ private String version = "1.0.0";
+ private String type = "SSD";
+ private String author = "Test";
+ private String comment = "Created";
+ private String location = "some location";
+ private OffsetDateTime changedAt = OffsetDateTime.now();
+ private boolean archived = false;
+ private boolean available = false;
+ private boolean deleted = false;
+
+ public HistoryResourceTestdataBuilder setId(String id) {
+ this.id = id;
+ return this;
+ }
+
+ public HistoryResourceTestdataBuilder setName(String name) {
+ this.name = name;
+ return this;
+ }
+
+ public HistoryResourceTestdataBuilder setVersion(String version) {
+ this.version = version;
+ return this;
+ }
+
+ public HistoryResourceTestdataBuilder setType(String type) {
+ this.type = type;
+ return this;
+ }
+
+ public HistoryResourceTestdataBuilder setAuthor(String author) {
+ this.author = author;
+ return this;
+ }
+
+ public HistoryResourceTestdataBuilder setComment(String comment) {
+ this.comment = comment;
+ return this;
+ }
+
+ public HistoryResourceTestdataBuilder setChangedAt(OffsetDateTime changedAt) {
+ this.changedAt = changedAt;
+ return this;
+ }
+
+ public HistoryResourceTestdataBuilder setArchived(boolean archived) {
+ this.archived = archived;
+ return this;
+ }
+
+ public HistoryResourceTestdataBuilder setAvailable(boolean available) {
+ this.available = available;
+ return this;
+ }
+
+ public HistoryResourceTestdataBuilder setDeleted(boolean deleted) {
+ this.deleted = deleted;
+ return this;
+ }
+
+ // Build method to create a new HistoryMetaItem object
+ public IHistoryMetaItem build() {
+ return new HistoryMetaItem(id, name, version, type, author, comment, location, changedAt, archived, available, deleted);
+ }
+}
diff --git a/app/src/test/java/org/lfenergy/compas/scl/data/rest/monitoring/LivenessHealthCheckIT.java b/app/src/test/java/org/lfenergy/compas/scl/data/rest/monitoring/LivenessHealthCheckIT.java
new file mode 100644
index 00000000..435d7cc7
--- /dev/null
+++ b/app/src/test/java/org/lfenergy/compas/scl/data/rest/monitoring/LivenessHealthCheckIT.java
@@ -0,0 +1,12 @@
+// SPDX-FileCopyrightText: 2021 Alliander N.V.
+//
+// SPDX-License-Identifier: Apache-2.0
+
+package org.lfenergy.compas.scl.data.rest.monitoring;
+
+import io.quarkus.test.junit.QuarkusIntegrationTest;
+
+@QuarkusIntegrationTest
+class LivenessHealthCheckIT extends LivenessHealthCheckTest {
+ // Execute the same tests but in native mode.
+}
\ No newline at end of file
diff --git a/app/src/test/java/org/lfenergy/compas/scl/data/rest/monitoring/LivenessHealthCheckTest.java b/app/src/test/java/org/lfenergy/compas/scl/data/rest/monitoring/LivenessHealthCheckTest.java
new file mode 100644
index 00000000..a5a5f6f5
--- /dev/null
+++ b/app/src/test/java/org/lfenergy/compas/scl/data/rest/monitoring/LivenessHealthCheckTest.java
@@ -0,0 +1,24 @@
+// SPDX-FileCopyrightText: 2021 Alliander N.V.
+//
+// SPDX-License-Identifier: Apache-2.0
+package org.lfenergy.compas.scl.data.rest.monitoring;
+
+import io.quarkus.test.junit.QuarkusTest;
+import org.junit.jupiter.api.Test;
+
+import static io.restassured.RestAssured.given;
+import static org.hamcrest.Matchers.equalTo;
+
+
+@QuarkusTest
+class LivenessHealthCheckTest {
+
+ @Test
+ void testLivenessEndpoint() {
+ given()
+ .when().get("/q/health/live")
+ .then()
+ .statusCode(200);
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/test/java/org/lfenergy/compas/scl/data/rest/monitoring/ReadinessHealthCheckIT.java b/app/src/test/java/org/lfenergy/compas/scl/data/rest/monitoring/ReadinessHealthCheckIT.java
new file mode 100644
index 00000000..015a495c
--- /dev/null
+++ b/app/src/test/java/org/lfenergy/compas/scl/data/rest/monitoring/ReadinessHealthCheckIT.java
@@ -0,0 +1,12 @@
+// SPDX-FileCopyrightText: 2021 Alliander N.V.
+//
+// SPDX-License-Identifier: Apache-2.0
+
+package org.lfenergy.compas.scl.data.rest.monitoring;
+
+import io.quarkus.test.junit.QuarkusIntegrationTest;
+
+@QuarkusIntegrationTest
+class ReadinessHealthCheckIT extends ReadinessHealthCheckTest {
+ // Execute the same tests but in native mode.
+}
\ No newline at end of file
diff --git a/app/src/test/java/org/lfenergy/compas/scl/data/rest/monitoring/ReadinessHealthCheckTest.java b/app/src/test/java/org/lfenergy/compas/scl/data/rest/monitoring/ReadinessHealthCheckTest.java
new file mode 100644
index 00000000..d668e2dc
--- /dev/null
+++ b/app/src/test/java/org/lfenergy/compas/scl/data/rest/monitoring/ReadinessHealthCheckTest.java
@@ -0,0 +1,63 @@
+// SPDX-FileCopyrightText: 2021 Alliander N.V.
+//
+// SPDX-License-Identifier: Apache-2.0
+package org.lfenergy.compas.scl.data.rest.monitoring;
+
+import io.quarkus.test.InjectMock;
+import io.quarkus.test.junit.QuarkusTest;
+import org.junit.jupiter.api.Test;
+import org.lfenergy.compas.scl.data.config.FeatureFlagService;
+
+import static io.restassured.RestAssured.given;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.mockito.Mockito.when;
+
+@QuarkusTest
+class ReadinessHealthCheckTest {
+
+ @InjectMock
+ private FeatureFlagService featureFlagService;
+
+ @Test
+ void testReadinessEndpoint() {
+ given()
+ .when().get("/q/health/ready")
+ .then()
+ .statusCode(200);
+ }
+
+ @Test
+ void testReadinessEndpoint_whenCalledWithHistoryFeatureEnabled_ThenRetrieveIsHistoryEnabledTrue() {
+ // Mock the feature flag to be enabled
+ when(featureFlagService.isHistoryEnabled()).thenReturn(true);
+
+ var response = given()
+ .when().get("/q/health/ready")
+ .then()
+ .statusCode(200)
+ .extract()
+ .response();
+
+ assertNotNull(response);
+ assertEquals(response.jsonPath().getString("checks.find { it.name == 'System Ready' }.name"), "System Ready");
+ assertEquals(response.jsonPath().getBoolean("checks.find { it.name == 'System Ready' }.data.isHistoryEnabled"), true);
+ }
+
+ @Test
+ void testReadinessEndpoint_whenCalledWithHistoryFeatureDisabled_ThenRetrieveIsHistoryEnabledFalse() {
+ // Mock the feature flag to be disabled
+ when(featureFlagService.isHistoryEnabled()).thenReturn(false);
+
+ var response = given()
+ .when().get("/q/health/ready")
+ .then()
+ .statusCode(200)
+ .extract()
+ .response();
+
+ assertNotNull(response);
+ assertEquals(response.jsonPath().getString("checks.find { it.name == 'System Ready' }.name"), "System Ready");
+ assertEquals(response.jsonPath().getBoolean("checks.find { it.name == 'System Ready' }.data.isHistoryEnabled"), false);
+ }
+}
\ No newline at end of file
diff --git a/app/src/test/java/org/lfenergy/compas/scl/data/rest/v1/CompasSclDataResourceAsEditorTest.java b/app/src/test/java/org/lfenergy/compas/scl/data/rest/v1/CompasSclDataResourceAsEditorTest.java
index e25f7798..c776585d 100644
--- a/app/src/test/java/org/lfenergy/compas/scl/data/rest/v1/CompasSclDataResourceAsEditorTest.java
+++ b/app/src/test/java/org/lfenergy/compas/scl/data/rest/v1/CompasSclDataResourceAsEditorTest.java
@@ -11,14 +11,15 @@
import io.quarkus.test.security.jwt.JwtSecurity;
import io.restassured.http.ContentType;
import org.junit.jupiter.api.Test;
+import org.lfenergy.compas.scl.data.config.FeatureFlagService;
import org.lfenergy.compas.scl.data.model.ChangeSetType;
-import org.lfenergy.compas.scl.data.xml.HistoryItem;
-import org.lfenergy.compas.scl.data.xml.Item;
import org.lfenergy.compas.scl.data.model.Version;
import org.lfenergy.compas.scl.data.rest.v1.model.CreateRequest;
import org.lfenergy.compas.scl.data.rest.v1.model.DuplicateNameCheckRequest;
import org.lfenergy.compas.scl.data.rest.v1.model.UpdateRequest;
import org.lfenergy.compas.scl.data.service.CompasSclDataService;
+import org.lfenergy.compas.scl.data.xml.HistoryItem;
+import org.lfenergy.compas.scl.data.xml.Item;
import org.lfenergy.compas.scl.extensions.model.SclFileType;
import java.io.IOException;
@@ -46,6 +47,9 @@ class CompasSclDataResourceAsEditorTest {
@InjectMock
private CompasSclDataService compasSclDataService;
+ @InjectMock
+ private FeatureFlagService featureFlagService;
+
@Test
void list_WhenCalled_ThenItemResponseRetrieved() {
var type = SclFileType.SCD;
@@ -159,7 +163,7 @@ void create_WhenCalled_ThenServiceCalledAndUUIDRetrieved() throws IOException {
request.setComment(comment);
request.setSclData(scl);
- when(compasSclDataService.create(type, name, USERNAME, comment, scl)).thenReturn(scl);
+ when(compasSclDataService.create(type, name, USERNAME, comment, scl, false)).thenReturn(scl);
var response = given()
.pathParam(TYPE_PATH_PARAM, type)
@@ -171,8 +175,8 @@ void create_WhenCalled_ThenServiceCalledAndUUIDRetrieved() throws IOException {
.extract()
.response();
+ verify(compasSclDataService).create(type, name, USERNAME, comment, scl, false);
assertEquals(scl, response.xmlPath().getString("CreateWsResponse.SclData"));
- verify(compasSclDataService).create(type, name, USERNAME, comment, scl);
}
@Test
@@ -215,7 +219,7 @@ void update_WhenCalled_ThenServiceCalledAndNewUUIDRetrieved() throws IOException
request.setComment(comment);
request.setSclData(scl);
- when(compasSclDataService.update(type, uuid, changeSetType, USERNAME, comment, scl)).thenReturn(scl);
+ when(compasSclDataService.update(type, uuid, changeSetType, USERNAME, comment, scl, false)).thenReturn(scl);
var response = given()
.pathParam(TYPE_PATH_PARAM, type)
@@ -229,7 +233,7 @@ void update_WhenCalled_ThenServiceCalledAndNewUUIDRetrieved() throws IOException
.response();
assertEquals(scl, response.xmlPath().getString("CreateWsResponse.SclData"));
- verify(compasSclDataService).update(type, uuid, changeSetType, USERNAME, comment, scl);
+ verify(compasSclDataService).update(type, uuid, changeSetType, USERNAME, comment, scl, false);
}
@Test
@@ -237,7 +241,7 @@ void deleteAll_WhenCalled_ThenServiceCalled() {
var uuid = UUID.randomUUID();
var type = SclFileType.SCD;
- doNothing().when(compasSclDataService).delete(type, uuid);
+ doNothing().when(compasSclDataService).delete(type, uuid, false);
given()
.pathParam(TYPE_PATH_PARAM, type)
@@ -246,7 +250,7 @@ void deleteAll_WhenCalled_ThenServiceCalled() {
.then()
.statusCode(204);
- verify(compasSclDataService).delete(type, uuid);
+ verify(compasSclDataService).delete(type, uuid, false);
}
@Test
@@ -255,7 +259,7 @@ void deleteVersion_WhenCalled_ThenServiceCalled() {
var type = SclFileType.SCD;
var version = new Version(1, 2, 3);
- doNothing().when(compasSclDataService).delete(type, uuid, version);
+ doNothing().when(compasSclDataService).deleteVersion(type, uuid, version, false);
given()
.pathParam(TYPE_PATH_PARAM, type)
@@ -265,7 +269,7 @@ void deleteVersion_WhenCalled_ThenServiceCalled() {
.then()
.statusCode(204);
- verify(compasSclDataService).delete(type, uuid, version);
+ verify(compasSclDataService).deleteVersion(type, uuid, version, false);
}
@Test
@@ -320,7 +324,7 @@ private String readSCL() throws IOException {
try (var inputStream = getClass().getResourceAsStream("/scl/icd_import_ied_test.scd")) {
assert inputStream != null;
- return new String(inputStream.readAllBytes());
+ return new String(inputStream.readAllBytes()).replace("\r\n", "\n");
}
}
}
diff --git a/app/src/test/java/org/lfenergy/compas/scl/data/rest/v1/CompasSclDataResourceAsReaderTest.java b/app/src/test/java/org/lfenergy/compas/scl/data/rest/v1/CompasSclDataResourceAsReaderTest.java
index 9b18ea5c..edb5a2ee 100644
--- a/app/src/test/java/org/lfenergy/compas/scl/data/rest/v1/CompasSclDataResourceAsReaderTest.java
+++ b/app/src/test/java/org/lfenergy/compas/scl/data/rest/v1/CompasSclDataResourceAsReaderTest.java
@@ -3,18 +3,19 @@
// SPDX-License-Identifier: Apache-2.0
package org.lfenergy.compas.scl.data.rest.v1;
+import io.quarkus.test.InjectMock;
import io.quarkus.test.common.http.TestHTTPEndpoint;
import io.quarkus.test.junit.QuarkusTest;
import io.quarkus.test.security.TestSecurity;
import io.restassured.http.ContentType;
import org.junit.jupiter.api.Test;
import org.lfenergy.compas.scl.data.model.ChangeSetType;
-import org.lfenergy.compas.scl.data.xml.HistoryItem;
-import org.lfenergy.compas.scl.data.xml.Item;
import org.lfenergy.compas.scl.data.model.Version;
import org.lfenergy.compas.scl.data.rest.v1.model.CreateRequest;
import org.lfenergy.compas.scl.data.rest.v1.model.UpdateRequest;
import org.lfenergy.compas.scl.data.service.CompasSclDataService;
+import org.lfenergy.compas.scl.data.xml.HistoryItem;
+import org.lfenergy.compas.scl.data.xml.Item;
import org.lfenergy.compas.scl.extensions.model.SclFileType;
import java.io.IOException;
@@ -28,7 +29,6 @@
import static org.lfenergy.compas.scl.data.SclDataServiceConstants.SCL_NS_URI;
import static org.lfenergy.compas.scl.data.rest.Constants.*;
import static org.mockito.Mockito.*;
-import io.quarkus.test.InjectMock;
@QuarkusTest
@TestHTTPEndpoint(CompasSclDataResource.class)
@@ -192,7 +192,7 @@ void deleteAll_WhenCalled_ThenServiceCalled() {
var uuid = UUID.randomUUID();
var type = SclFileType.SCD;
- doNothing().when(compasSclDataService).delete(type, uuid);
+ doNothing().when(compasSclDataService).delete(type, uuid, false);
given()
.pathParam(TYPE_PATH_PARAM, type)
@@ -210,7 +210,7 @@ void deleteVersion_WhenCalled_ThenServiceCalled() {
var type = SclFileType.SCD;
var version = new Version(1, 2, 3);
- doNothing().when(compasSclDataService).delete(type, uuid, version);
+ doNothing().when(compasSclDataService).deleteVersion(type, uuid, version, false);
given()
.pathParam(TYPE_PATH_PARAM, type)
diff --git a/docker-compose.yml b/docker-compose.yml
new file mode 100644
index 00000000..5bb6231d
--- /dev/null
+++ b/docker-compose.yml
@@ -0,0 +1,62 @@
+version: '3.8'
+
+services:
+ postgres:
+ image: postgres:latest
+ container_name: compas_postgresql
+ environment:
+ POSTGRES_PASSWORD: postgres
+ POSTGRES_DB: compas
+ PGDATA: /var/lib/postgresql/data/compas
+ volumes:
+ - ./data:/var/lib/postgresql/data
+ ports:
+ - "5432:5432"
+ networks:
+ - compas-network
+
+ keycloak:
+ image: compas_keycloak:latest # Assuming the Keycloak image is already built
+ container_name: compas_keycloak
+ environment:
+ KEYCLOAK_ADMIN: admin
+ KEYCLOAK_ADMIN_PASSWORD: admin
+ ports:
+ - "8089:8080"
+ networks:
+ - compas-network
+
+ compas-scl-data-service:
+ image: lfenergy/compas-scl-data-service:0.15.3-SNAPSHOT-postgresql # Use the latest compas-scl-data-service image, you can generate it with ./mvnw package -Pnative-image
+ container_name: compas-scl-data-service
+ depends_on:
+ - postgres
+ ports:
+ - "8080:8080"
+ environment:
+ POSTGRESQL_HOST: postgres
+ POSTGRESQL_PORT: 5432
+ POSTGRESQL_DB: compas
+ POSTGRESQL_USERNAME: postgres
+ POSTGRESQL_PASSWORD: postgres
+ # JWT Security Variables
+ JWT_VERIFY_KEY: http://localhost:8089/auth/realms/compas/protocol/openid-connect/certs
+ JWT_VERIFY_ISSUER: http://localhost:8089/auth/realms/compas
+ JWT_VERIFY_CLIENT_ID: scl-data-service
+ JWT_GROUPS_PATH: resource_access/scl-data-service/roles
+ # UserInfo variables
+ USERINFO_NAME_CLAIMNAME: name
+ USERINFO_WHO_CLAIMNAME: name
+ USERINFO_SESSION_WARNING: 20
+ USERINFO_SESSION_EXPIRES: 30
+ # ELO Connector
+ ELO_CONNECTOR_ENABLED: true
+ ELO_CONNECTOR_BASE_URL: http://elo-connector:8080/compas-elo-connector/api
+ restart: on-failure # will restart until it's success
+ networks:
+ - compas-network
+
+networks:
+ compas-network:
+ name: compas-network
+ driver: bridge
diff --git a/repository-postgresql/pom.xml b/repository-postgresql/pom.xml
index 7e8b9f98..addc60e5 100644
--- a/repository-postgresql/pom.xml
+++ b/repository-postgresql/pom.xml
@@ -85,6 +85,14 @@ SPDX-License-Identifier: Apache-2.0
org.jboss.jandex
jandex-maven-plugin
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+ 15
+ 15
+
+
diff --git a/repository-postgresql/src/main/java/org/lfenergy/compas/scl/data/model/AbstractArchivedResourceMetaItem.java b/repository-postgresql/src/main/java/org/lfenergy/compas/scl/data/model/AbstractArchivedResourceMetaItem.java
new file mode 100644
index 00000000..29b60917
--- /dev/null
+++ b/repository-postgresql/src/main/java/org/lfenergy/compas/scl/data/model/AbstractArchivedResourceMetaItem.java
@@ -0,0 +1,60 @@
+package org.lfenergy.compas.scl.data.model;
+
+import java.time.OffsetDateTime;
+import java.util.List;
+
+public abstract class AbstractArchivedResourceMetaItem implements IAbstractArchivedResourceMetaItem, IAbstractItem {
+
+ private final String author;
+ private final String approver;
+ private final String type;
+ private final String contentType;
+ private final String locationId;
+ private final List fields;
+ private final OffsetDateTime modifiedAt;
+ private final OffsetDateTime archivedAt;
+
+ public AbstractArchivedResourceMetaItem(String author, String approver, String type, String contentType, String locationId, List fields, OffsetDateTime modifiedAt, OffsetDateTime archivedAt) {
+ this.author = author;
+ this.approver = approver;
+ this.type = type;
+ this.contentType = contentType;
+ this.locationId = locationId;
+ this.fields = fields;
+ this.modifiedAt = modifiedAt;
+ this.archivedAt = archivedAt;
+ }
+
+ public String getAuthor() {
+ return author;
+ }
+
+ public String getApprover() {
+ return approver;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public String getContentType() {
+ return contentType;
+ }
+
+ @Override
+ public String getLocationId() {
+ return locationId;
+ }
+
+ public List getFields() {
+ return fields;
+ }
+
+ public OffsetDateTime getModifiedAt() {
+ return modifiedAt;
+ }
+
+ public OffsetDateTime getArchivedAt() {
+ return archivedAt;
+ }
+}
diff --git a/repository-postgresql/src/main/java/org/lfenergy/compas/scl/data/model/AbstractItem.java b/repository-postgresql/src/main/java/org/lfenergy/compas/scl/data/model/AbstractItem.java
index 2a6d267b..f6799355 100644
--- a/repository-postgresql/src/main/java/org/lfenergy/compas/scl/data/model/AbstractItem.java
+++ b/repository-postgresql/src/main/java/org/lfenergy/compas/scl/data/model/AbstractItem.java
@@ -8,10 +8,12 @@ public abstract class AbstractItem implements IAbstractItem {
private final String id;
private final String name;
private final String version;
- protected AbstractItem(final String id, final String name, final String version) {
+ private final String locationId;
+ protected AbstractItem(final String id, final String name, final String version, final String locationId) {
this.id = id;
this.name = name;
this.version = version;
+ this.locationId = locationId;
}
public String getId() {
@@ -26,4 +28,8 @@ public String getVersion() {
return version;
}
+ public String getLocationId() {
+ return locationId;
+ }
+
}
diff --git a/repository-postgresql/src/main/java/org/lfenergy/compas/scl/data/model/ArchivedReferencedResourceMetaItem.java b/repository-postgresql/src/main/java/org/lfenergy/compas/scl/data/model/ArchivedReferencedResourceMetaItem.java
new file mode 100644
index 00000000..a9153c10
--- /dev/null
+++ b/repository-postgresql/src/main/java/org/lfenergy/compas/scl/data/model/ArchivedReferencedResourceMetaItem.java
@@ -0,0 +1,39 @@
+package org.lfenergy.compas.scl.data.model;
+
+import java.time.OffsetDateTime;
+import java.util.List;
+
+public class ArchivedReferencedResourceMetaItem extends AbstractArchivedResourceMetaItem implements IAbstractArchivedResourceMetaItem {
+
+ String id;
+ String name;
+ String version;
+ String comment;
+
+ public ArchivedReferencedResourceMetaItem(String id, String name, String version, String author, String approver, String type, String contentType, String locationId, List fields, OffsetDateTime modifiedAt, OffsetDateTime archivedAt, String comment) {
+ super(author, approver, type, contentType, locationId, fields, modifiedAt, archivedAt);
+ this.id = id;
+ this.name = name;
+ this.version = version;
+ this.comment = comment;
+ }
+
+ @Override
+ public String getId() {
+ return id;
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public String getVersion() {
+ return version;
+ }
+
+ public String getComment() {
+ return comment;
+ }
+}
diff --git a/repository-postgresql/src/main/java/org/lfenergy/compas/scl/data/model/ArchivedResourceVersion.java b/repository-postgresql/src/main/java/org/lfenergy/compas/scl/data/model/ArchivedResourceVersion.java
new file mode 100644
index 00000000..11b2c128
--- /dev/null
+++ b/repository-postgresql/src/main/java/org/lfenergy/compas/scl/data/model/ArchivedResourceVersion.java
@@ -0,0 +1,96 @@
+package org.lfenergy.compas.scl.data.model;
+
+import java.time.OffsetDateTime;
+import java.util.List;
+
+public class ArchivedResourceVersion extends AbstractItem implements IArchivedResourceVersion {
+
+ String location;
+ String note;
+ String author;
+ String approver;
+ String type;
+ String contentType;
+ String voltage;
+ OffsetDateTime modifiedAt;
+ OffsetDateTime archivedAt;
+ List fields;
+ String comment;
+ boolean archived;
+
+ public ArchivedResourceVersion(String id, String name, String version, String location, String note, String author, String approver, String type, String contentType, String voltage, List fields, OffsetDateTime modifiedAt, OffsetDateTime archivedAt, String comment, boolean archived) {
+ super(id, name, version, null);
+ this.location = location;
+ this.note = note;
+ this.author = author;
+ this.approver = approver;
+ this.type = type;
+ this.contentType = contentType;
+ this.voltage = voltage;
+ this.modifiedAt = modifiedAt;
+ this.archivedAt = archivedAt;
+ this.fields = fields;
+ this.comment = comment;
+ this.archived = archived;
+ }
+
+ @Override
+ public String getLocation() {
+ return location;
+ }
+
+ @Override
+ public String getNote() {
+ return note;
+ }
+
+ @Override
+ public String getAuthor() {
+ return author;
+ }
+
+ @Override
+ public String getApprover() {
+ return approver;
+ }
+
+ @Override
+ public String getType() {
+ return type;
+ }
+
+ @Override
+ public String getContentType() {
+ return contentType;
+ }
+
+ @Override
+ public String getVoltage() {
+ return voltage;
+ }
+
+ @Override
+ public OffsetDateTime getModifiedAt() {
+ return modifiedAt;
+ }
+
+ @Override
+ public OffsetDateTime getArchivedAt() {
+ return archivedAt;
+ }
+
+ @Override
+ public List getFields() {
+ return fields;
+ }
+
+ @Override
+ public String getComment() {
+ return comment;
+ }
+
+ @Override
+ public boolean isArchived() {
+ return archived;
+ }
+}
diff --git a/repository-postgresql/src/main/java/org/lfenergy/compas/scl/data/model/ArchivedResourcesHistoryMetaItem.java b/repository-postgresql/src/main/java/org/lfenergy/compas/scl/data/model/ArchivedResourcesHistoryMetaItem.java
new file mode 100644
index 00000000..bafd8dfe
--- /dev/null
+++ b/repository-postgresql/src/main/java/org/lfenergy/compas/scl/data/model/ArchivedResourcesHistoryMetaItem.java
@@ -0,0 +1,21 @@
+package org.lfenergy.compas.scl.data.model;
+
+import java.util.List;
+
+public class ArchivedResourcesHistoryMetaItem implements IArchivedResourcesHistoryMetaItem {
+
+ List versions;
+
+ public ArchivedResourcesHistoryMetaItem(List versions) {
+ this.versions = versions;
+ }
+
+ @Override
+ public List getVersions() {
+ return versions;
+ }
+
+ public void setVersions(List versions) {
+ this.versions = versions;
+ }
+}
diff --git a/repository-postgresql/src/main/java/org/lfenergy/compas/scl/data/model/ArchivedResourcesMetaItem.java b/repository-postgresql/src/main/java/org/lfenergy/compas/scl/data/model/ArchivedResourcesMetaItem.java
new file mode 100644
index 00000000..e4e4cc33
--- /dev/null
+++ b/repository-postgresql/src/main/java/org/lfenergy/compas/scl/data/model/ArchivedResourcesMetaItem.java
@@ -0,0 +1,17 @@
+package org.lfenergy.compas.scl.data.model;
+
+import java.util.List;
+
+public class ArchivedResourcesMetaItem implements IArchivedResourcesMetaItem {
+
+ List archivedResources;
+
+ public ArchivedResourcesMetaItem(List archivedResources) {
+ this.archivedResources = archivedResources;
+ }
+
+ @Override
+ public List getResources() {
+ return archivedResources;
+ }
+}
diff --git a/repository-postgresql/src/main/java/org/lfenergy/compas/scl/data/model/ArchivedSclResourceMetaItem.java b/repository-postgresql/src/main/java/org/lfenergy/compas/scl/data/model/ArchivedSclResourceMetaItem.java
new file mode 100644
index 00000000..5348123c
--- /dev/null
+++ b/repository-postgresql/src/main/java/org/lfenergy/compas/scl/data/model/ArchivedSclResourceMetaItem.java
@@ -0,0 +1,45 @@
+package org.lfenergy.compas.scl.data.model;
+
+import java.time.OffsetDateTime;
+import java.util.List;
+
+public class ArchivedSclResourceMetaItem extends AbstractArchivedResourceMetaItem implements IAbstractArchivedResourceMetaItem {
+ String id;
+ String name;
+ String version;
+ String note;
+ String voltage;
+
+ public ArchivedSclResourceMetaItem(String id, String name, String version, String author, String approver, String type, String contentType, String location, List fields, OffsetDateTime modifiedAt, OffsetDateTime archivedAt, String note, String voltage) {
+ super(author, approver, type, contentType, location, fields, modifiedAt, archivedAt);
+ this.id = id;
+ this.name = name;
+ this.version = version;
+ this.note = note;
+ this.voltage = voltage;
+ }
+
+
+ public String getNote() {
+ return note;
+ }
+
+ public String getVoltage() {
+ return voltage;
+ }
+
+ @Override
+ public String getId() {
+ return id;
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public String getVersion() {
+ return version;
+ }
+}
diff --git a/repository-postgresql/src/main/java/org/lfenergy/compas/scl/data/model/HistoryItem.java b/repository-postgresql/src/main/java/org/lfenergy/compas/scl/data/model/HistoryItem.java
index 73ea526d..43fb5c38 100644
--- a/repository-postgresql/src/main/java/org/lfenergy/compas/scl/data/model/HistoryItem.java
+++ b/repository-postgresql/src/main/java/org/lfenergy/compas/scl/data/model/HistoryItem.java
@@ -9,7 +9,7 @@ public class HistoryItem extends AbstractItem implements IHistoryItem {
private final String what;
public HistoryItem(final String id, final String name, final String version, final String who, final String when, final String what) {
- super(id, name, version);
+ super(id, name, version, null);
this.who = who;
this.when = when;
this.what = what;
diff --git a/repository-postgresql/src/main/java/org/lfenergy/compas/scl/data/model/HistoryMetaItem.java b/repository-postgresql/src/main/java/org/lfenergy/compas/scl/data/model/HistoryMetaItem.java
new file mode 100644
index 00000000..65265fd5
--- /dev/null
+++ b/repository-postgresql/src/main/java/org/lfenergy/compas/scl/data/model/HistoryMetaItem.java
@@ -0,0 +1,66 @@
+package org.lfenergy.compas.scl.data.model;
+
+import java.time.OffsetDateTime;
+
+public class HistoryMetaItem extends AbstractItem implements IHistoryMetaItem {
+ private final String type;
+ private final String author;
+ private final String comment;
+ private final String location;
+ private final OffsetDateTime changedAt;
+ private final boolean archived;
+ private final boolean available;
+ private final boolean deleted;
+
+ public HistoryMetaItem(String id, String name, String version, String type, String author, String comment, String location,OffsetDateTime changedAt, boolean archived, boolean available, boolean deleted) {
+ super(id, name, version, null);
+ this.type = type;
+ this.author = author;
+ this.comment = comment;
+ this.location = location;
+ this.changedAt = changedAt;
+ this.archived = archived;
+ this.available = available;
+ this.deleted = deleted;
+ }
+
+ @Override
+ public String getType() {
+ return type;
+ }
+
+ @Override
+ public String getAuthor() {
+ return author;
+ }
+
+ @Override
+ public String getComment() {
+ return comment;
+ }
+
+ @Override
+ public String getLocation() {
+ return location;
+ }
+
+ @Override
+ public OffsetDateTime getChangedAt() {
+ return changedAt;
+ }
+
+ @Override
+ public boolean isArchived() {
+ return archived;
+ }
+
+ @Override
+ public boolean isAvailable() {
+ return available;
+ }
+
+ @Override
+ public boolean isDeleted() {
+ return deleted;
+ }
+}
diff --git a/repository-postgresql/src/main/java/org/lfenergy/compas/scl/data/model/Item.java b/repository-postgresql/src/main/java/org/lfenergy/compas/scl/data/model/Item.java
index 97739bf8..d665a467 100644
--- a/repository-postgresql/src/main/java/org/lfenergy/compas/scl/data/model/Item.java
+++ b/repository-postgresql/src/main/java/org/lfenergy/compas/scl/data/model/Item.java
@@ -10,7 +10,7 @@ public class Item extends AbstractItem implements IItem {
private final List labels;
public Item(final String id, final String name, final String version, final List labels) {
- super(id, name, version);
+ super(id, name, version, null);
this.labels = labels;
}
diff --git a/repository-postgresql/src/main/java/org/lfenergy/compas/scl/data/model/LocationMetaItem.java b/repository-postgresql/src/main/java/org/lfenergy/compas/scl/data/model/LocationMetaItem.java
new file mode 100644
index 00000000..d99f4fc2
--- /dev/null
+++ b/repository-postgresql/src/main/java/org/lfenergy/compas/scl/data/model/LocationMetaItem.java
@@ -0,0 +1,42 @@
+package org.lfenergy.compas.scl.data.model;
+
+public class LocationMetaItem implements ILocationMetaItem {
+
+ String id;
+ String key;
+ String name;
+ String description;
+ int assignedResources;
+
+ public LocationMetaItem(String id, String key, String name, String description, int assignedResources) {
+ this.id = id;
+ this.key = key;
+ this.name = name;
+ this.description = description;
+ this.assignedResources = assignedResources;
+ }
+
+ @Override
+ public String getId() {
+ return id;
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public String getKey() {
+ return key;
+ }
+
+ @Override
+ public String getDescription() {
+ return description;
+ }
+
+ public int getAssignedResources() {
+ return assignedResources;
+ }
+}
diff --git a/repository-postgresql/src/main/java/org/lfenergy/compas/scl/data/model/ResourceTagItem.java b/repository-postgresql/src/main/java/org/lfenergy/compas/scl/data/model/ResourceTagItem.java
new file mode 100644
index 00000000..8b2fcbd2
--- /dev/null
+++ b/repository-postgresql/src/main/java/org/lfenergy/compas/scl/data/model/ResourceTagItem.java
@@ -0,0 +1,28 @@
+package org.lfenergy.compas.scl.data.model;
+
+public class ResourceTagItem implements IResourceTagItem {
+ String id;
+ String key;
+ String value;
+
+ public ResourceTagItem(String id, String key, String value) {
+ this.id = id;
+ this.key = key;
+ this.value = value;
+ }
+
+ @Override
+ public String getId() {
+ return this.id;
+ }
+
+ @Override
+ public String getKey() {
+ return this.key;
+ }
+
+ @Override
+ public String getValue() {
+ return this.value;
+ }
+}
diff --git a/repository-postgresql/src/main/java/org/lfenergy/compas/scl/data/model/SclMetaInfo.java b/repository-postgresql/src/main/java/org/lfenergy/compas/scl/data/model/SclMetaInfo.java
index ad25e47e..060bf722 100644
--- a/repository-postgresql/src/main/java/org/lfenergy/compas/scl/data/model/SclMetaInfo.java
+++ b/repository-postgresql/src/main/java/org/lfenergy/compas/scl/data/model/SclMetaInfo.java
@@ -5,7 +5,7 @@
public class SclMetaInfo extends AbstractItem {
- public SclMetaInfo(final String id, final String name, final String version) {
- super(id, name, version);
+ public SclMetaInfo(final String id, final String name, final String version, final String locationId) {
+ super(id, name, version, locationId);
}
}
diff --git a/repository-postgresql/src/main/java/org/lfenergy/compas/scl/data/repository/postgresql/CompasSclDataPostgreSQLRepository.java b/repository-postgresql/src/main/java/org/lfenergy/compas/scl/data/repository/postgresql/CompasSclDataPostgreSQLRepository.java
index f6685b08..0040aaad 100644
--- a/repository-postgresql/src/main/java/org/lfenergy/compas/scl/data/repository/postgresql/CompasSclDataPostgreSQLRepository.java
+++ b/repository-postgresql/src/main/java/org/lfenergy/compas/scl/data/repository/postgresql/CompasSclDataPostgreSQLRepository.java
@@ -4,24 +4,24 @@
package org.lfenergy.compas.scl.data.repository.postgresql;
+import jakarta.enterprise.context.ApplicationScoped;
+import jakarta.inject.Inject;
+import jakarta.transaction.TransactionManager;
+import jakarta.transaction.Transactional;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
import org.lfenergy.compas.scl.data.exception.CompasNoDataFoundException;
import org.lfenergy.compas.scl.data.exception.CompasSclDataServiceException;
import org.lfenergy.compas.scl.data.model.*;
import org.lfenergy.compas.scl.data.repository.CompasSclDataRepository;
import org.lfenergy.compas.scl.extensions.model.SclFileType;
-import jakarta.enterprise.context.ApplicationScoped;
-import jakarta.inject.Inject;
import javax.sql.DataSource;
-import jakarta.transaction.Transactional;
-import java.sql.Array;
-import java.sql.Connection;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.UUID;
+import java.sql.*;
+import java.time.Instant;
+import java.time.OffsetDateTime;
+import java.time.ZoneOffset;
+import java.util.*;
import static jakarta.transaction.Transactional.TxType.REQUIRED;
import static jakarta.transaction.Transactional.TxType.SUPPORTS;
@@ -34,13 +34,36 @@ public class CompasSclDataPostgreSQLRepository implements CompasSclDataRepositor
private static final String MINOR_VERSION_FIELD = "minor_version";
private static final String PATCH_VERSION_FIELD = "patch_version";
private static final String NAME_FIELD = "name";
+ private static final String KEY_FIELD = "key";
+ private static final String VALUE_FIELD = "value";
+ private static final String LOCATIONMETAITEM_DESCRIPTION_FIELD = "description";
private static final String SCL_DATA_FIELD = "scl_data";
private static final String HITEM_WHO_FIELD = "hitem_who";
private static final String HITEM_WHEN_FIELD = "hitem_when";
private static final String HITEM_WHAT_FIELD = "hitem_what";
+ private static final String HISTORYMETAITEM_TYPE_FIELD = "type";
+ private static final String HISTORYMETAITEM_AUTHOR_FIELD = "author";
+ private static final String HISTORYMETAITEM_COMMENT_FIELD = "comment";
+ private static final String HISTORYMETAITEM_CHANGEDAT_FIELD = "changedAt";
+ private static final String HISTORYMETAITEM_AVAILABLE_FIELD = "available";
+ private static final String HISTORYMETAITEM_ARCHIVED_FIELD = "archived";
+ private static final String HISTORYMETAITEM_IS_DELETED_FIELD = "is_deleted";
+ private static final String ARCHIVEMETAITEM_LOCATION_FIELD = "location";
+ private static final String ARCHIVEMETAITEM_AUTHOR_FIELD = "author";
+ private static final String ARCHIVEMETAITEM_APPROVER_FIELD = "approver";
+ private static final String ARCHIVEMETAITEM_TYPE_FIELD = "type";
+ private static final String ARCHIVEMETAITEM_CONTENT_TYPE_FIELD = "content_type";
+ private static final String ARCHIVEMETAITEM_VOLTAGE_FIELD = "voltage";
+ private static final String ARCHIVEMETAITEM_MODIFIED_AT_FIELD = "modified_at";
+ private static final String ARCHIVEMETAITEM_ARCHIVED_AT_FIELD = "archived_at";
+
+ private static final Logger LOGGER = LogManager.getLogger(CompasSclDataPostgreSQLRepository.class);
private final DataSource dataSource;
+ @Inject
+ TransactionManager tm;
+
@Inject
public CompasSclDataPostgreSQLRepository(DataSource dataSource) {
this.dataSource = dataSource;
@@ -56,6 +79,7 @@ public List list(SclFileType type) {
from (select distinct on (scl_file.id) *
from scl_file
where scl_file.type = ?
+ and scl_file.is_deleted = false
order by scl_file.id
, scl_file.major_version desc
, scl_file.minor_version desc
@@ -77,7 +101,6 @@ left outer join (
try (var connection = dataSource.getConnection();
var stmt = connection.prepareStatement(sql)) {
stmt.setString(1, type.name());
-
try (var resultSet = stmt.executeQuery()) {
while (resultSet.next()) {
items.add(new Item(resultSet.getString(ID_FIELD),
@@ -115,6 +138,7 @@ left outer join (
and scl_data.patch_version = scl_file.patch_version
where scl_file.id = ?
and scl_file.type = ?
+ and scl_file.is_deleted = false
order by scl_file.major_version
, scl_file.minor_version
, scl_file.patch_version
@@ -184,6 +208,37 @@ public String findByUUID(SclFileType type, UUID id, Version version) {
}
}
+ @Override
+ @Transactional(SUPPORTS)
+ public String findByUUID(UUID id, Version version) {
+ var sql = """
+ select scl_file.scl_data
+ from scl_file
+ where scl_file.id = ?
+ and scl_file.major_version = ?
+ and scl_file.minor_version = ?
+ and scl_file.patch_version = ?
+ """;
+
+ try (var connection = dataSource.getConnection();
+ var stmt = connection.prepareStatement(sql)) {
+ stmt.setObject(1, id);
+ stmt.setInt(2, version.getMajorVersion());
+ stmt.setInt(3, version.getMinorVersion());
+ stmt.setInt(4, version.getPatchVersion());
+
+ try (var resultSet = stmt.executeQuery()) {
+ if (resultSet.next()) {
+ return resultSet.getString(SCL_DATA_FIELD);
+ }
+ var message = String.format("No record found with ID '%s' and version '%s'", id, version);
+ throw new CompasNoDataFoundException(message);
+ }
+ } catch (SQLException exp) {
+ throw new CompasSclDataServiceException(POSTGRES_SELECT_ERROR_CODE, "Error select scl data from database!", exp);
+ }
+ }
+
@Override
@Transactional(SUPPORTS)
public boolean hasDuplicateSclName(SclFileType type, String name) {
@@ -217,10 +272,11 @@ select distinct on (scl_file.id) scl_file.name
@Transactional(SUPPORTS)
public IAbstractItem findMetaInfoByUUID(SclFileType type, UUID id) {
var sql = """
- select scl_file.id, scl_file.name, scl_file.major_version, scl_file.minor_version, scl_file.patch_version
+ select scl_file.id, scl_file.name, scl_file.major_version, scl_file.minor_version, scl_file.patch_version, scl_file.location_id
from scl_file
where scl_file.id = ?
and scl_file.type = ?
+ and scl_file.is_deleted = false
order by scl_file.major_version desc, scl_file.minor_version desc, scl_file.patch_version desc
""";
@@ -234,7 +290,8 @@ public IAbstractItem findMetaInfoByUUID(SclFileType type, UUID id) {
if (resultSet.next()) {
return new SclMetaInfo(resultSet.getString(ID_FIELD),
resultSet.getString(NAME_FIELD),
- createVersion(resultSet));
+ createVersion(resultSet),
+ resultSet.getString("location_id"));
}
var message = String.format("No meta info found for type '%s' with ID '%s'", type, id);
throw new CompasNoDataFoundException(message);
@@ -322,7 +379,26 @@ public void delete(SclFileType type, UUID id) {
@Override
@Transactional(REQUIRED)
- public void delete(SclFileType type, UUID id, Version version) {
+ public void softDelete(SclFileType type, UUID id) {
+ var sql = """
+ UPDATE scl_file
+ SET is_deleted = true
+ WHERE scl_file.id = ?
+ AND scl_file.type = ?
+ """;
+ try (var connection = dataSource.getConnection();
+ var sclStmt = connection.prepareStatement(sql)) {
+ sclStmt.setObject(1, id);
+ sclStmt.setObject(2, type.name());
+ sclStmt.executeUpdate();
+ } catch (SQLException exp) {
+ throw new CompasSclDataServiceException(POSTGRES_INSERT_ERROR_CODE, "Error marking SCL as deleted in database!", exp);
+ }
+ }
+
+ @Override
+ @Transactional(REQUIRED)
+ public void deleteVersion(SclFileType type, UUID id, Version version) {
var sql = """
delete from scl_file
where scl_file.id = ?
@@ -345,6 +421,32 @@ public void delete(SclFileType type, UUID id, Version version) {
}
}
+ @Override
+ @Transactional(REQUIRED)
+ public void softDeleteVersion(SclFileType type, UUID id, Version version) {
+ var sql = """
+ UPDATE scl_file
+ SET is_deleted = true
+ WHERE scl_file.id = ?
+ AND scl_file.type = ?
+ AND scl_file.major_version = ?
+ AND scl_file.minor_version = ?
+ AND scl_file.patch_version = ?
+ """;
+
+ try (var connection = dataSource.getConnection();
+ var sclStmt = connection.prepareStatement(sql)) {
+ sclStmt.setObject(1, id);
+ sclStmt.setString(2, type.name());
+ sclStmt.setInt(3, version.getMajorVersion());
+ sclStmt.setInt(4, version.getMinorVersion());
+ sclStmt.setInt(5, version.getPatchVersion());
+ sclStmt.executeUpdate();
+ } catch (SQLException exp) {
+ throw new CompasSclDataServiceException(POSTGRES_INSERT_ERROR_CODE, "Error marking SCL version as deleted in database!", exp);
+ }
+ }
+
private String createVersion(ResultSet resultSet) throws SQLException {
var version = new Version(resultSet.getInt(MAJOR_VERSION_FIELD),
resultSet.getInt(MINOR_VERSION_FIELD),
@@ -364,4 +466,1252 @@ private List createLabelList(Array sqlArray) throws SQLException {
}
return labelsList;
}
-}
+
+ @Override
+ @Transactional(SUPPORTS)
+ public List listHistory() {
+ String sql = """
+ SELECT subquery.id
+ , subquery.major_version
+ , subquery.minor_version
+ , subquery.patch_version
+ , subquery.type
+ , subquery.name
+ , subquery.creation_date as changedAt
+ , subquery.created_by as author
+ , subquery.id IN (SELECT ar.scl_file_id FROM archived_resource ar) as archived
+ , true as available
+ , subquery.is_deleted
+ , l.id as location
+ , (XPATH('/scl:Hitem/@what', subquery.header,
+ ARRAY [ARRAY ['scl', 'http://www.iec.ch/61850/2003/SCL']]))[1]::varchar as comment
+ FROM (SELECT DISTINCT ON (scl_file.id) scl_file.*,
+ UNNEST(
+ XPATH(
+ '(/scl:SCL/scl:Header//scl:Hitem[(not(@revision) or @revision="") and @version="' ||
+ scl_file.major_version || '.' || scl_file.minor_version || '.' ||
+ scl_file.patch_version || '"])[1]'
+ , scl_file.scl_data::xml
+ , ARRAY [ARRAY ['scl', 'http://www.iec.ch/61850/2003/SCL']]))
+ as header
+ FROM scl_file
+ ORDER BY scl_file.id,
+ scl_file.major_version DESC,
+ scl_file.minor_version DESC,
+ scl_file.patch_version DESC) subquery
+ LEFT JOIN location l
+ ON location_id = l.id
+ ORDER BY subquery.name;
+ """;
+ return executeHistoryQuery(sql, Collections.emptyList());
+ }
+
+ @Override
+ @Transactional(SUPPORTS)
+ public List listHistory(UUID id) {
+ String sql = """
+ SELECT subquery.id
+ , subquery.major_version
+ , subquery.minor_version
+ , subquery.patch_version
+ , subquery.type
+ , subquery.name
+ , subquery.creation_date as changedAt
+ , subquery.created_by as author
+ , subquery.id IN (SELECT ar.scl_file_id FROM archived_resource ar) as archived
+ , true as available
+ , subquery.is_deleted
+ , l.id as location
+ , (XPATH('/scl:Hitem/@what', subquery.header,
+ ARRAY [ARRAY ['scl', 'http://www.iec.ch/61850/2003/SCL']]))[1]::varchar as comment
+ FROM (SELECT DISTINCT ON (scl_file.id) scl_file.*,
+ UNNEST(
+ XPATH(
+ '(/scl:SCL/scl:Header//scl:Hitem[(not(@revision) or @revision="") and @version="' ||
+ scl_file.major_version || '.' || scl_file.minor_version || '.' ||
+ scl_file.patch_version || '"])[1]'
+ , scl_file.scl_data::xml
+ , ARRAY [ARRAY ['scl', 'http://www.iec.ch/61850/2003/SCL']]))
+ as header
+ FROM scl_file
+ WHERE scl_file.id = ?
+ ORDER BY scl_file.id,
+ scl_file.major_version DESC,
+ scl_file.minor_version DESC,
+ scl_file.patch_version DESC) subquery
+ LEFT JOIN location l
+ ON location_id = l.id
+ ORDER BY subquery.name;
+ """;
+ return executeHistoryQuery(sql, Collections.singletonList(id));
+ }
+
+ @Override
+ @Transactional(SUPPORTS)
+ public List listHistory(SclFileType type, String name, String author, OffsetDateTime from, OffsetDateTime to) {
+ StringBuilder sqlBuilder = new StringBuilder("""
+ SELECT subquery.id
+ , subquery.major_version
+ , subquery.minor_version
+ , subquery.patch_version
+ , subquery.type
+ , subquery.name
+ , subquery.creation_date as changedAt
+ , subquery.created_by as author
+ , subquery.id IN (SELECT ar.scl_file_id FROM archived_resource ar) as archived
+ , true as available
+ , subquery.is_deleted
+ , l.id as location
+ , (XPATH('/scl:Hitem/@what', subquery.header,
+ ARRAY [ARRAY ['scl', 'http://www.iec.ch/61850/2003/SCL']]))[1]::varchar as comment
+ FROM (SELECT DISTINCT ON (scl_file.id) scl_file.*,
+ UNNEST(
+ XPATH(
+ '(/scl:SCL/scl:Header//scl:Hitem[(not(@revision) or @revision="") and @version="' ||
+ scl_file.major_version || '.' || scl_file.minor_version || '.' ||
+ scl_file.patch_version || '"])[1]'
+ , scl_file.scl_data::xml
+ , ARRAY [ARRAY ['scl', 'http://www.iec.ch/61850/2003/SCL']]))
+ as header
+ FROM scl_file
+ ORDER BY scl_file.id,
+ scl_file.major_version DESC,
+ scl_file.minor_version DESC,
+ scl_file.patch_version DESC) subquery
+ LEFT JOIN location l
+ ON location_id = l.id
+ WHERE 1=1
+ """);
+
+ List