diff options
9 files changed, 414 insertions, 24 deletions
diff --git a/cps-parent/pom.xml b/cps-parent/pom.xml index 5aa65a25d..b7b64af3b 100644 --- a/cps-parent/pom.xml +++ b/cps-parent/pom.xml @@ -19,12 +19,12 @@ <app>org.onap.cps.Application</app> <base.image>openjdk:11-jre-slim</base.image> <java.version>11</java.version> - <jacoco-maven-plugin.version>0.8.6</jacoco-maven-plugin.version> <jib-maven-plugin.version>2.6.0</jib-maven-plugin.version> - <minimum-coverage>0.15</minimum-coverage> + <minimum-coverage>0.20</minimum-coverage> <nexusproxy>https://nexus.onap.org</nexusproxy> <onap.nexus.url>https://nexus.onap.org</onap.nexus.url> <oparent.version>3.1.0</oparent.version> + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <releaseNexusPath>/content/repositories/releases/</releaseNexusPath> <repository.name>nexus3.onap.org:10001/onap/cps-service</repository.name> <spring-boot-maven-plugin.version>2.3.3.RELEASE</spring-boot-maven-plugin.version> @@ -305,20 +305,21 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> - <version>3.0.0-M5</version> <configuration> - <argLine>@{argLine}</argLine> + <argLine>${surefireArgLine}</argLine> <useFile>false</useFile> <includes> <include>**/*Spec.java</include> <include>**/*Test.java</include> <!-- Just in case of having also "normal" JUnit tests --> </includes> + <excludes> + <exclude>**/IT*.java</exclude> + </excludes> </configuration> </plugin> <plugin> <groupId>org.jacoco</groupId> <artifactId>jacoco-maven-plugin</artifactId> - <version>${jacoco-maven-plugin.version}</version> <executions> <execution> <id>default-prepare-agent</id> @@ -332,6 +333,7 @@ <goal>check</goal> </goals> <configuration> + <dataFile>${project.build.directory}/code-coverage/jacoco-ut.exec</dataFile> <rules> <rule> <element>BUNDLE</element> diff --git a/cps-ri/src/main/java/org/onap/cps/spi/entities/SchemaSet.java b/cps-ri/src/main/java/org/onap/cps/spi/entities/SchemaSet.java new file mode 100644 index 000000000..fe67a6089 --- /dev/null +++ b/cps-ri/src/main/java/org/onap/cps/spi/entities/SchemaSet.java @@ -0,0 +1,71 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2020 Pantheon.tech + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.cps.spi.entities; + +import java.io.Serializable; +import java.util.Set; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.JoinTable; +import javax.persistence.ManyToMany; +import javax.persistence.ManyToOne; +import javax.persistence.Table; +import javax.validation.constraints.NotNull; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +/** + * Entity to store a Schema Set. + */ +@Getter +@Setter +@NoArgsConstructor +@Entity +@Table(name = "schema_set") +public class SchemaSet implements Serializable { + + private static final long serialVersionUID = 6665056955069047269L; + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Integer id; + + @NotNull + @Column + private String name; + + @NotNull + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "dataspace_id", referencedColumnName = "ID") + private Dataspace dataspace; + + @NotNull + @ManyToMany(fetch = FetchType.LAZY) + @JoinTable(name = "schema_set_yang_resources", + joinColumns = @JoinColumn(name = "schema_set_id"), + inverseJoinColumns = @JoinColumn(name = "yang_resource_id")) + private Set<YangResource> yangResources; +} diff --git a/cps-ri/src/main/java/org/onap/cps/spi/entities/YangResource.java b/cps-ri/src/main/java/org/onap/cps/spi/entities/YangResource.java new file mode 100644 index 000000000..862b7aea6 --- /dev/null +++ b/cps-ri/src/main/java/org/onap/cps/spi/entities/YangResource.java @@ -0,0 +1,63 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2020 Pantheon.tech + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.cps.spi.entities; + +import java.io.Serializable; +import java.util.Set; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.ManyToMany; +import javax.persistence.Table; +import javax.validation.constraints.NotNull; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +/** + * Entity to store a Yang files. + */ +@Getter +@Setter +@NoArgsConstructor +@Entity +@Table(name = "yang_resource") +public class YangResource implements Serializable { + + private static final long serialVersionUID = -4496883162142106774L; + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @NotNull + @Column + private String checksum; + + @NotNull + @Column + private String content; + + @ManyToMany(mappedBy = "yangResources") + private Set<SchemaSet> moduleSets; + +} diff --git a/cps-ri/src/main/java/org/onap/cps/spi/impl/ModelPersistenceServiceImpl.java b/cps-ri/src/main/java/org/onap/cps/spi/impl/ModelPersistenceServiceImpl.java index 03679b316..2207f7940 100755 --- a/cps-ri/src/main/java/org/onap/cps/spi/impl/ModelPersistenceServiceImpl.java +++ b/cps-ri/src/main/java/org/onap/cps/spi/impl/ModelPersistenceServiceImpl.java @@ -20,39 +20,92 @@ package org.onap.cps.spi.impl; -import org.onap.cps.exceptions.CpsValidationException; +import static org.onap.cps.exceptions.CpsExceptionBuilder.duplicateSchemaSetException; +import static org.onap.cps.exceptions.CpsExceptionBuilder.invalidDataspaceException; + +import com.google.common.collect.ImmutableSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; +import javax.transaction.Transactional; import org.onap.cps.spi.ModelPersistenceService; import org.onap.cps.spi.entities.Dataspace; -import org.onap.cps.spi.entities.Module; +import org.onap.cps.spi.entities.SchemaSet; +import org.onap.cps.spi.entities.YangResource; import org.onap.cps.spi.repository.DataspaceRepository; -import org.onap.cps.spi.repository.ModuleRepository; +import org.onap.cps.spi.repository.SchemaSetRepository; +import org.onap.cps.spi.repository.YangResourceRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.dao.DataIntegrityViolationException; import org.springframework.stereotype.Component; +import org.springframework.util.DigestUtils; @Component public class ModelPersistenceServiceImpl implements ModelPersistenceService { @Autowired - private ModuleRepository moduleRepository; + private YangResourceRepository yangResourceRepository; + + @Autowired + private SchemaSetRepository schemaSetRepository; @Autowired private DataspaceRepository dataspaceRepository; @Override public void storeModule(final String namespace, final String moduleContent, final String revision, - final String dataspaceName) { - final Dataspace dataspace = new Dataspace(dataspaceName); - if (Boolean.FALSE.equals(dataspaceRepository.existsByName(dataspaceName))) { - dataspaceRepository.save(dataspace); - } - dataspace.setId(dataspaceRepository.getByName(dataspaceName).getId()); - final Module module = new Module(namespace, moduleContent, revision, dataspace); + final String dataspaceName) { + // TODO this method should be removed as obsolete. + // Modules to be processed within schema sets only. + } + + @Override + @Transactional + public void storeSchemaSet(final String dataspaceName, final String schemaSetName, + final Set<String> yangResourcesAsStrings) { + + final Dataspace dataspace = dataspaceRepository.findByName(dataspaceName) + .orElseThrow(() -> invalidDataspaceException(dataspaceName)); + + final Set<YangResource> yangResources = synchronizeYangResources(yangResourcesAsStrings); + final SchemaSet schemaSet = new SchemaSet(); + schemaSet.setName(schemaSetName); + schemaSet.setDataspace(dataspace); + schemaSet.setYangResources(yangResources); try { - moduleRepository.save(module); - } catch (final DataIntegrityViolationException ex) { - throw new CpsValidationException("Duplicate Entry", - String.format("Module already exist in dataspace %s.", dataspaceName)); + schemaSetRepository.save(schemaSet); + } catch (final DataIntegrityViolationException e) { + throw duplicateSchemaSetException(dataspaceName, schemaSetName); + } + } + + private Set<YangResource> synchronizeYangResources(final Set<String> yangResourcesAsStrings) { + final Map<String, String> checksumToContentMap = yangResourcesAsStrings.stream() + .collect(Collectors.toMap( + content -> DigestUtils.md5DigestAsHex(content.getBytes()), + content -> content) + ); + + final List<YangResource> existingYangResources = + yangResourceRepository.findAllByChecksumIn(checksumToContentMap.keySet()); + existingYangResources.forEach(yangFile -> checksumToContentMap.remove(yangFile.getChecksum())); + + final List<YangResource> newYangResources = checksumToContentMap.entrySet().stream() + .map(entry -> { + final YangResource yangResource = new YangResource(); + yangResource.setChecksum(entry.getKey()); + yangResource.setContent(entry.getValue()); + return yangResource; + }).collect(Collectors.toList()); + if (!newYangResources.isEmpty()) { + yangResourceRepository.saveAll(newYangResources); } + + return ImmutableSet.<YangResource>builder() + .addAll(existingYangResources) + .addAll(newYangResources) + .build(); } + } diff --git a/cps-ri/src/main/java/org/onap/cps/spi/repository/SchemaSetRepository.java b/cps-ri/src/main/java/org/onap/cps/spi/repository/SchemaSetRepository.java new file mode 100644 index 000000000..f9746972f --- /dev/null +++ b/cps-ri/src/main/java/org/onap/cps/spi/repository/SchemaSetRepository.java @@ -0,0 +1,36 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2020 Pantheon.tech + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.cps.spi.repository; + +import java.util.List; +import java.util.Optional; +import javax.validation.constraints.NotNull; +import org.onap.cps.spi.entities.Dataspace; +import org.onap.cps.spi.entities.SchemaSet; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface SchemaSetRepository extends JpaRepository<SchemaSet, Integer> { + + List<SchemaSet> findAllByDataspace(@NotNull Dataspace dataspace); + + Optional<SchemaSet> findByDataspaceAndName(@NotNull Dataspace dataspace, @NotNull String name); +} diff --git a/cps-ri/src/main/java/org/onap/cps/spi/repository/YangResourceRepository.java b/cps-ri/src/main/java/org/onap/cps/spi/repository/YangResourceRepository.java new file mode 100644 index 000000000..47d3ea32c --- /dev/null +++ b/cps-ri/src/main/java/org/onap/cps/spi/repository/YangResourceRepository.java @@ -0,0 +1,34 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2020 Pantheon.tech + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.cps.spi.repository; + +import java.util.List; +import java.util.Set; +import javax.validation.constraints.NotNull; +import org.onap.cps.spi.entities.YangResource; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface YangResourceRepository extends JpaRepository<YangResource, Long> { + + List<YangResource> findAllByChecksumIn(@NotNull Set<String> checksum); + +} diff --git a/cps-ri/src/main/resources/schema.sql b/cps-ri/src/main/resources/schema.sql index 3fabc6c9f..d47f261ad 100755 --- a/cps-ri/src/main/resources/schema.sql +++ b/cps-ri/src/main/resources/schema.sql @@ -17,6 +17,30 @@ CREATE TABLE IF NOT EXISTS SCHEMA_NODE ID SERIAL PRIMARY KEY
);
+CREATE TABLE IF NOT EXISTS SCHEMA_SET
+(
+ ID SERIAL PRIMARY KEY,
+ NAME TEXT NOT NULL,
+ DATASPACE_ID BIGINT NOT NULL,
+ UNIQUE (NAME, DATASPACE_ID),
+ CONSTRAINT SCHEMA_SET_DATASPACE FOREIGN KEY (DATASPACE_ID) REFERENCES DATASPACE(ID) ON UPDATE CASCADE ON DELETE CASCADE
+);
+
+CREATE TABLE IF NOT EXISTS YANG_RESOURCE
+(
+ ID SERIAL PRIMARY KEY,
+ CHECKSUM TEXT NOT NULL,
+ CONTENT TEXT NOT NULL,
+ UNIQUE (CHECKSUM)
+);
+
+CREATE TABLE IF NOT EXISTS SCHEMA_SET_YANG_RESOURCES
+(
+ SCHEMA_SET_ID BIGINT NOT NULL,
+ YANG_RESOURCE_ID BIGINT NOT NULL REFERENCES YANG_RESOURCE(ID),
+ CONSTRAINT SCHEMA_SET_RESOURCE FOREIGN KEY (SCHEMA_SET_ID) REFERENCES SCHEMA_SET(ID) ON DELETE CASCADE
+);
+
CREATE TABLE IF NOT EXISTS MODULE
(
ID SERIAL PRIMARY KEY,
@@ -62,4 +86,4 @@ CREATE INDEX IF NOT EXISTS "FKI_RELATION_TYPE_ID_FK" ON RELATION USING CREATE INDEX IF NOT EXISTS "FKI_RELATIONS_FROM_ID_FK" ON RELATION USING BTREE(FROM_FRAGMENT_ID);
CREATE INDEX IF NOT EXISTS "FKI_RELATIONS_TO_ID_FK" ON RELATION USING BTREE(TO_FRAGMENT_ID);
CREATE INDEX IF NOT EXISTS "PERF_MODULE_MODULE_CONTENT" ON MODULE USING BTREE(MODULE_CONTENT);
-CREATE UNIQUE INDEX IF NOT EXISTS "UQ_FRAGMENT_XPATH"ON FRAGMENT USING btree(xpath COLLATE pg_catalog."default" text_pattern_ops, dataspace_id);
\ No newline at end of file +CREATE UNIQUE INDEX IF NOT EXISTS "UQ_FRAGMENT_XPATH"ON FRAGMENT USING btree(xpath COLLATE pg_catalog."default" text_pattern_ops, dataspace_id);
diff --git a/cps-service/src/main/java/org/onap/cps/exceptions/CpsExceptionBuilder.java b/cps-service/src/main/java/org/onap/cps/exceptions/CpsExceptionBuilder.java new file mode 100644 index 000000000..2acbb9232 --- /dev/null +++ b/cps-service/src/main/java/org/onap/cps/exceptions/CpsExceptionBuilder.java @@ -0,0 +1,94 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2020 Pantheon.tech + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.cps.exceptions; + +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +/** + * Utility class. + * Serves error message consistency for same error cases occurred in different CPS modules. + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class CpsExceptionBuilder { + + private static final String SCHEMA_SET_IS_INVALID = "Schema Set is invalid."; + + /** + * Generates validation error exception for case when requested dataspace is absent. + * + * @param dataspaceName dataspace name + */ + public static CpsException invalidDataspaceException(final String dataspaceName) { + return new CpsValidationException("Dataspace is invalid.", + String.format("Dataspace with name %s does not exist.", dataspaceName)); + } + + /** + * Generates validation error exception for case when requested schema set is absent for existing dataspace. + * + * @param dataspaceName dataspace name + * @param schemaSetName schema set name + */ + public static CpsException invalidSchemaSetException(final String dataspaceName, final String schemaSetName) { + return new CpsValidationException(SCHEMA_SET_IS_INVALID, + String.format("Schema Set with name %s was not found for dataspace %s.", schemaSetName, dataspaceName)); + } + + /** + * Returns validation error exception for case when SchemaSet contains no files. + */ + public static CpsException emptySchemaSetException() { + return new CpsValidationException(SCHEMA_SET_IS_INVALID, "Schema Set has no YANG resources to store"); + } + + /** + * Generates validation error exception for case when SchemaSet with same name already exists in the dataspace. + * + * @param dataspaceName dataspace name + * @param schemaSetName schema set name + */ + public static CpsException duplicateSchemaSetException(final String dataspaceName, final String schemaSetName) { + return new CpsValidationException(SCHEMA_SET_IS_INVALID, + String.format("Schema Set with name %s already exists for dataspace %s.", schemaSetName, dataspaceName)); + } + + /** + * Generates no data found exception for case when requested dataspace is absent. + * + * @param dataspaceName dataspace name + */ + public static CpsException dataspaceNotFoundException(final String dataspaceName) { + return new CpsNotFoundException("Dataspace was not found.", + String.format("Dataspace with name %s does not exist.", dataspaceName)); + } + + /** + * Generates no data found exception for case when requested SchemaSet is absent for existing dataspace. + * + * @param dataspaceName dataspace name + * @param schemaSetName schema set name + */ + public static CpsException schemaSetNotFoundException(final String dataspaceName, final String schemaSetName) { + return new CpsNotFoundException("Schema Set was not found.", + String.format("Schema Set with name %s was not found for dataspace %s.", schemaSetName, dataspaceName)); + } + +} diff --git a/cps-service/src/main/java/org/onap/cps/spi/ModelPersistenceService.java b/cps-service/src/main/java/org/onap/cps/spi/ModelPersistenceService.java index 3f0b3c110..9eed2807c 100755 --- a/cps-service/src/main/java/org/onap/cps/spi/ModelPersistenceService.java +++ b/cps-service/src/main/java/org/onap/cps/spi/ModelPersistenceService.java @@ -20,6 +20,8 @@ package org.onap.cps.spi; +import java.util.Set; + /** * Defines methods to access and manipulate data using the chosen database solution. */ @@ -28,12 +30,23 @@ public interface ModelPersistenceService { /** * Store the module from a yang model in the database. * - * @param namespace module namespace + * @param namespace module namespace * @param moduleContent module content - * @param revision module revision + * @param revision module revision * @param dataspaceName the name of the dataspace the module is associated with */ + @Deprecated void storeModule(final String namespace, final String moduleContent, final String revision, - final String dataspaceName); + final String dataspaceName); + + + /** + * Stores Schema Set. + * + * @param dataspaceName dataspace name + * @param schemaSetName schema set name + * @param yangResourcesAsStrings the content of YANG resources (files) + */ + void storeSchemaSet(String dataspaceName, String schemaSetName, Set<String> yangResourcesAsStrings); } |