Add ves-openapi-manager implementation

Change-Id: I923fa43028c78989604547b365a3326a1f8e9548
Signed-off-by: Michal Banka <michal.banka@nokia.com>
Issue-ID: DCAEGEN2-2572
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..dd9be1b
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,31 @@
+# See https://help.github.com/ignore-files/ for more about ignoring files.
+
+# dependencies
+/node_modules
+
+# testing
+/coverage
+
+# production
+/build
+/artifactInfos
+
+# misc
+.DS_Store
+.env.local
+.env.development.local
+.env.test.local
+.env.production.local
+
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+
+
+# dependencies
+target
+*.iml
+.idea
+node_modules
+package-lock.json
+build
\ No newline at end of file
diff --git a/Changelog.md b/Changelog.md
new file mode 100644
index 0000000..812816c
--- /dev/null
+++ b/Changelog.md
@@ -0,0 +1,8 @@
+# Change Log
+All notable changes to this project will be documented in this file.
+
+The format is based on [Keep a Changelog](http://keepachangelog.com/)
+and this project adheres to [Semantic Versioning](http://semver.org/).
+
+## [1.0.0] - 09/02/2020
+        - [DCAEGEN2-2572](https://jira.onap.org/browse/DCAEGEN2-2572) - Add ves-openapi-manager implementation
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..67275d9
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,4 @@
+FROM onap/integration-java11:8.0.0
+COPY ./src/main/resources/schema-map.json /app/schema-map.json
+COPY target/ves-openapi-manager.jar /app/
+ENTRYPOINT java -jar /app/ves-openapi-manager.jar
\ No newline at end of file
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..fcf3f44
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,24 @@
+include environment.config
+
+all: build docker run cleanup
+
+port-forward:
+	@echo "Forwarding ports. To cancel forwarding press CTRL+C."
+	@$(SSH) -L 3904:$(WORKER_IP):$(MESSAGE_ROUTER_PORT) ubuntu@$(RKE_IP) -i $(SSH_LAB_KEY_PATH) -N
+
+build:
+	@echo "Building ves-openapi-manager"
+	@$(MVN) clean package
+
+docker:
+	@echo "Building ves-openapi-manager docker image"
+	@$(DOCKER) build --tag ves-openapi-manager:latest .
+
+run:
+	@echo "Starting ves-openapi-manager docker containers"
+	@$(COMPOSE) --file ./docker-compose.yml up
+
+cleanup:
+	@echo "Cleaning up ves-openapi-manager project, removing containers"
+	@rm -rf ./target
+	@$(COMPOSE) --file ./docker-compose.yml down
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..7d74f51
--- /dev/null
+++ b/README.md
@@ -0,0 +1,68 @@
+# VES OpenApi Manager
+
+# Description
+This application should partially validate incoming service distributions in SDC. It validates each artifact of type
+VES_EVENT. Its purpose is to check whether schemaReferences of stndDefined events included in VES_EVENT artifacts are
+matching the schemas which VES Collector contains.
+
+# Instructions
+
+## How to prepare environment for local ves-openapi-manager with lab connection
+
+1. Connect to lab and expose ports of message-router service by setting *spec.type* to NodePort.
+
+2. Add to /etc/hosts new entry:
+```
+<lab_worker_node_ip> sdc-be.onap
+```
+
+3. Get exposed port of 3904 internal port of message-router.
+   
+   Set up connection configuration in *environment.config* file.
+   - MESSAGE_ROUTER_PORT - exposed port of message-router.
+   - RKE_IP - IP of lab RKE node.
+   - WORKER_IP - IP of lab worker node.
+   - SSH_LAB_KEY_PATH - path to lab SSH key.
+    
+4. Local port forwarding is required to set up proper connection from local environment to message-router on the lab.
+   
+   Run this to enable port-forwarding (CTRL+C to end):
+   ```
+   make port-forwarding
+   ```
+## How to locally start ves-openapi-manager
+Currently, there are two common ways to run application, both described below.
+
+### Start in IntelliJ
+Right click on Main class, then Run or Debug button.
+
+### Start as Docker container
+Run:
+```
+make all
+```
+
+### Lab connection verification
+Correctly connected to lab application should print logs:
+```
+distribution client initialized successfuly
+```
+and
+```
+distribution client started successfuly
+```
+
+## How to use ves-openapi-manager
+After preparing environment, starting ves-openapi-manager and successful connection to lab, application will listen for
+service distributions taking place in SDC. 
+
+1. In SDC UI, create Service with Resource (e.g. PNF) containing VES_EVENT artifact.
+2. (Re)distribute created Service.
+
+When distribution takes place, ves-openapi-manager downloads each VES_EVENT artifact to validate its stndDefined events.
+
+Results of validation are visible in two places:
+- In SDC UI in Service -> Distributions view under specific distribution as component *ves-openapi-manager*.
+  It might take few minutes to show results after service distribution.
+- In logs of ves-openapi-manager, right after validation takes place.
+  
\ No newline at end of file
diff --git a/docker-compose.yml b/docker-compose.yml
new file mode 100644
index 0000000..d708fd8
--- /dev/null
+++ b/docker-compose.yml
@@ -0,0 +1,11 @@
+version: '3'
+
+services:
+  ves-openapi-manager:
+    image: ves-openapi-manager:latest
+    container_name: ves-openapi-manager
+    hostname: ves-openapi-manager
+    environment:
+      - SCHEMA_MAP_PATH=/app/schema-map.json
+      - ASDC_ADDRESS=sdc-be.onap:30204
+    network_mode: host
diff --git a/environment.config b/environment.config
new file mode 100644
index 0000000..0d34eef
--- /dev/null
+++ b/environment.config
@@ -0,0 +1,13 @@
+# Configuration file for ves-openapi-manager Makefile
+
+# consts
+COMPOSE = docker-compose
+MVN = mvn
+DOCKER = docker
+SSH = ssh
+
+# Port-forwarding configuration
+RKE_IP=10.129.36.103
+WORKER_IP=10.129.36.116
+MESSAGE_ROUTER_PORT=30000
+SSH_LAB_KEY_PATH=~/Scripts/labs_keys/onap-8414.pem
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..93140cd
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,235 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.onap.oparent</groupId>
+        <artifactId>oparent</artifactId>
+        <version>3.2.0</version>
+        <relativePath/>
+    </parent>
+
+    <groupId>org.onap.dcaegen2.platform</groupId>
+    <artifactId>ves-openapi-manager</artifactId>
+    <version>1.0.0</version>
+    <packaging>jar</packaging>
+
+    <properties>
+        <sdc-distribution-client.version>1.4.2</sdc-distribution-client.version>
+        <junit-jupiter.version>5.2.0</junit-jupiter.version>
+        <maven.compiler.source>11</maven.compiler.source>
+        <maven.compiler.target>11</maven.compiler.target>
+        <junit5.version>5.2.0</junit5.version>
+        <platform.version>1.2.0</platform.version>
+        <surefire.version>${mockito-core.version}</surefire.version>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
+        <hibernate-validator.version>6.1.6.Final</hibernate-validator.version>
+        <jackson-databind.version>2.9.4</jackson-databind.version>
+        <mockito-core.version>2.22.0</mockito-core.version>
+        <spring.version>2.4.2</spring.version>
+        <docker-maven-plugin.version>0.31.0</docker-maven-plugin.version>
+        <skipDockerPush>false</skipDockerPush>
+        <nexusproxy>https://nexus.onap.org</nexusproxy>
+        <snapshotNexusPath>/content/repositories/snapshots/</snapshotNexusPath>
+        <releaseNexusPath>/content/repositories/releases/</releaseNexusPath>
+        <stagingNexusPath>/content/repositories/staging/</stagingNexusPath>
+        <docker-image.namespace>onap</docker-image.namespace>
+        <docker-image.name.prefix>org.onap.dcaegen2.platform</docker-image.name.prefix>
+        <maven.build.timestamp.format>yyyyMMdd'T'HHmmss</maven.build.timestamp.format>
+        <maven-compiler-plugin.version>3.5.1</maven-compiler-plugin.version>
+        <bean-matchers.version>0.13</bean-matchers.version>
+    </properties>
+
+    <build>
+        <finalName>ves-openapi-manager</finalName>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <version>${surefire.version}</version>
+                <dependencies>
+                    <dependency>
+                        <groupId>org.junit.platform</groupId>
+                        <artifactId>junit-platform-surefire-provider</artifactId>
+                        <version>${platform.version}</version>
+                    </dependency>
+                    <dependency>
+                        <groupId>org.junit.jupiter</groupId>
+                        <artifactId>junit-jupiter-engine</artifactId>
+                        <version>${junit5.version}</version>
+                    </dependency>
+                </dependencies>
+            </plugin>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+                <version>${spring.version}</version>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>repackage</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <version>${maven-compiler-plugin.version}</version>
+                <configuration>
+                    <source>${maven.compiler.source}</source>
+                    <target>${maven.compiler.target}</target>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+    <profiles>
+        <profile>
+            <id>docker</id>
+            <activation>
+                <activeByDefault>false</activeByDefault>
+            </activation>
+            <properties>
+                <os.detected.name>linux</os.detected.name>
+                <os.detected.arch>x86_64</os.detected.arch>
+                <os.detected.classifier>${os.detected.name}-${os.detected.arch}</os.detected.classifier>
+            </properties>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>io.fabric8</groupId>
+                        <artifactId>docker-maven-plugin</artifactId>
+                        <version>${docker-maven-plugin.version}</version>
+                        <executions>
+                            <execution>
+                                <id>docker-build-image</id>
+                                <phase>package</phase>
+                                <goals>
+                                    <goal>build</goal>
+                                </goals>
+                            </execution>
+                            <execution>
+                                <id>docker-push-image</id>
+                                <phase>deploy</phase>
+                                <goals>
+                                    <goal>push</goal>
+                                </goals>
+                            </execution>
+                        </executions>
+                        <configuration>
+                            <skipPush>${skipDockerPush}</skipPush>
+                            <verbose>true</verbose>
+                            <imagePullPolicy>IfNotPresent</imagePullPolicy>
+                            <images>
+                                <image>
+                                    <alias>${project.artifactId}</alias>
+                                    <name>${docker-image.namespace}/${docker-image.name.prefix}.${project.artifactId}</name>
+                                    <registry>${onap.nexus.dockerregistry.daily}</registry>
+                                    <build>
+                                        <dockerFileDir>${project.basedir}</dockerFileDir>
+                                        <tags>
+                                            <tag>${project.version}-STAGE-${maven.build.timestamp}Z</tag>
+                                        </tags>
+                                    </build>
+                                </image>
+                            </images>
+                        </configuration>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <!-- Import dependency management from Spring Boot -->
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-dependencies</artifactId>
+                <version>${spring.version}</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+    <dependencies>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-web</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-configuration-processor</artifactId>
+            <optional>true</optional>
+        </dependency>
+        <dependency>
+            <groupId>org.hibernate</groupId>
+            <artifactId>hibernate-validator</artifactId>
+            <version>${hibernate-validator.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+            <optional>true</optional>
+        </dependency>
+        <dependency>
+            <groupId>org.onap.sdc.sdc-distribution-client</groupId>
+            <artifactId>sdc-distribution-client</artifactId>
+            <version>${sdc-distribution-client.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-databind</artifactId>
+            <version>${jackson-databind.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>com.fasterxml.jackson.dataformat</groupId>
+            <artifactId>jackson-dataformat-yaml</artifactId>
+            <version>${jackson-databind.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>com.fasterxml.jackson.datatype</groupId>
+            <artifactId>jackson-datatype-jsr310</artifactId>
+            <version>${jackson-databind.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-test</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-core</artifactId>
+            <version>${mockito-core.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.google.code.bean-matchers</groupId>
+            <artifactId>bean-matchers</artifactId>
+            <version>${bean-matchers.version}</version>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <distributionManagement>
+        <repository>
+            <id>ecomp-releases</id>
+            <name>Integration simulators Release Repository</name>
+            <url>${nexusproxy}${releaseNexusPath}</url>
+        </repository>
+        <snapshotRepository>
+            <id>ecomp-snapshots</id>
+            <name>Integration simulators Snapshot Repository</name>
+            <url>${nexusproxy}${snapshotNexusPath}</url>
+        </snapshotRepository>
+        <site>
+            <id>ecomp-site</id>
+            <url>dav:${nexusproxy}${sitePath}</url>
+        </site>
+    </distributionManagement>
+
+</project>
diff --git a/src/main/java/org/onap/ves/openapi/manager/Main.java b/src/main/java/org/onap/ves/openapi/manager/Main.java
new file mode 100644
index 0000000..209c05b
--- /dev/null
+++ b/src/main/java/org/onap/ves/openapi/manager/Main.java
@@ -0,0 +1,93 @@
+/*
+ * ============LICENSE_START=======================================================
+ * VES-OPENAPI-MANAGER
+ * ================================================================================
+ * Copyright (C) 2021 Nokia. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.ves.openapi.manager;
+
+import lombok.extern.log4j.Log4j2;
+import org.onap.sdc.api.results.IDistributionClientResult;
+import org.onap.sdc.http.HttpAsdcClient;
+import org.onap.sdc.http.HttpClientFactory;
+import org.onap.sdc.http.HttpRequestFactory;
+import org.onap.sdc.http.SdcConnectorClient;
+import org.onap.sdc.impl.DistributionClientImpl;
+import org.onap.ves.openapi.manager.config.DistributionClientConfig;
+import org.onap.ves.openapi.manager.service.ArtifactsCollector;
+import org.onap.ves.openapi.manager.service.ClientCallback;
+import org.onap.ves.openapi.manager.service.notification.ArtifactsCollectorStatusSender;
+import org.onap.ves.openapi.manager.service.notification.FinalStatusSender;
+import org.onap.ves.openapi.manager.service.notification.ValidationStatusSender;
+import org.onap.ves.openapi.manager.service.validation.ArtifactsValidator;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
+import org.springframework.context.annotation.Bean;
+
+import java.util.List;
+
+@Log4j2
+@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
+public class Main {
+
+    public static void main(String[] args) {
+        SpringApplication.run(Main.class, args);
+    }
+
+    /**
+     * Creates bean of SdcConnectorClient
+     * @param clientConfig DistributionClientConfig object with configuration properties
+     * @return SdcConnectorClient
+     */
+    @Bean
+    public SdcConnectorClient getSdcConnectorClient(DistributionClientConfig clientConfig) {
+        HttpRequestFactory requestFactory = new HttpRequestFactory(clientConfig.getUser(), clientConfig.getPassword());
+        HttpClientFactory clientFactory = new HttpClientFactory(clientConfig);
+        return new SdcConnectorClient(clientConfig, new HttpAsdcClient(clientConfig.getAsdcAddress(), clientFactory, requestFactory));
+    }
+
+    /**
+     * Creates bean of DistributionClientImpl
+     * @param clientConfig DistributionClientConfig object with configuration properties
+     * @param validators List of objects implementing ArtifactValidator interface
+     * @param artifactsCollector ArtifactsCollector object which downloads artifacts contents
+     * @return DistributionClientImpl
+     */
+    @Bean
+    public DistributionClientImpl getDistributionClientImpl(DistributionClientConfig clientConfig,
+                                                            List<ArtifactsValidator> validators,
+                                                            ArtifactsCollector artifactsCollector) {
+        DistributionClientImpl client = new DistributionClientImpl();
+        ClientCallback callback = new ClientCallback(validators, artifactsCollector,
+                new ArtifactsCollectorStatusSender(client), new ValidationStatusSender(client),
+                new FinalStatusSender(client));
+
+        log.info(ClientCallback.SEPARATOR);
+        IDistributionClientResult initResult = client.init(clientConfig, callback);
+        log.info(initResult.getDistributionMessageResult());
+        log.info(ClientCallback.SEPARATOR);
+
+        log.info(ClientCallback.SEPARATOR);
+        IDistributionClientResult startResult = client.start();
+        log.info(startResult.getDistributionMessageResult());
+        log.info(ClientCallback.SEPARATOR);
+
+        return client;
+    }
+
+}
diff --git a/src/main/java/org/onap/ves/openapi/manager/config/DistributionClientConfig.java b/src/main/java/org/onap/ves/openapi/manager/config/DistributionClientConfig.java
new file mode 100644
index 0000000..188cced
--- /dev/null
+++ b/src/main/java/org/onap/ves/openapi/manager/config/DistributionClientConfig.java
@@ -0,0 +1,130 @@
+/*
+ * ============LICENSE_START=======================================================
+ * VES-OPENAPI-MANAGER
+ * ================================================================================
+ * Copyright (C) 2021 Nokia. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.ves.openapi.manager.config;
+
+import lombok.Getter;
+import lombok.Setter;
+import org.onap.sdc.api.consumer.IConfiguration;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.context.annotation.Configuration;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * DistributionClientConfig - properties required by DistributionClientImpl, values mapped from application.yml
+ */
+@Configuration
+@ConfigurationProperties(prefix = "vesopenapimanager.distribution")
+@Getter
+@Setter
+public class DistributionClientConfig implements IConfiguration {
+
+    public static final String VES_EVENTS_ARTIFACT_TYPE = "VES_EVENTS";
+
+    private String asdcAddress;
+    private String msgBusAddress;
+    private String user;
+    private String password;
+    private Integer pollingInterval;
+    private Integer pollingTimeout;
+    private String consumerGroup;
+    private String environmentName;
+    private String consumerID;
+    private Boolean activateServerTLSAuth;
+    private Boolean isFilterInEmptyResources;
+    private Boolean isUseHttpsWithDmaap;
+
+    @Override
+    public String getAsdcAddress() {
+        return asdcAddress;
+    }
+
+    @Override
+    public List<String> getMsgBusAddress() {
+        return Collections.singletonList(msgBusAddress);
+    }
+
+    @Override
+    public String getUser() {
+        return user;
+    }
+
+    @Override
+    public String getPassword() {
+        return password;
+    }
+
+    @Override
+    public int getPollingInterval() {
+        return pollingInterval;
+    }
+
+    @Override
+    public int getPollingTimeout() {
+        return pollingTimeout;
+    }
+
+    @Override
+    public List<String> getRelevantArtifactTypes() {
+        return Collections.singletonList(VES_EVENTS_ARTIFACT_TYPE);
+    }
+
+    @Override
+    public String getConsumerGroup() {
+        return consumerGroup;
+    }
+
+    @Override
+    public String getEnvironmentName() {
+        return environmentName;
+    }
+
+    @Override
+    public String getConsumerID() {
+        return consumerID;
+    }
+
+    @Override
+    public String getKeyStorePath() {
+        return null;
+    }
+
+    @Override
+    public String getKeyStorePassword() {
+        return null;
+    }
+
+    @Override
+    public boolean activateServerTLSAuth() {
+        return activateServerTLSAuth;
+    }
+
+    @Override
+    public boolean isFilterInEmptyResources() {
+        return isFilterInEmptyResources;
+    }
+
+    @Override
+    public Boolean isUseHttpsWithDmaap() {
+        return isUseHttpsWithDmaap;
+    }
+}
diff --git a/src/main/java/org/onap/ves/openapi/manager/config/ValidatorProperties.java b/src/main/java/org/onap/ves/openapi/manager/config/ValidatorProperties.java
new file mode 100644
index 0000000..11beb22
--- /dev/null
+++ b/src/main/java/org/onap/ves/openapi/manager/config/ValidatorProperties.java
@@ -0,0 +1,39 @@
+/*
+ * ============LICENSE_START=======================================================
+ * VES-OPENAPI-MANAGER
+ * ================================================================================
+ * Copyright (C) 2021 Nokia. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.ves.openapi.manager.config;
+
+import lombok.Getter;
+import lombok.Setter;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * ValidatorProperties - properties required by SchemaReferenceValidator, values mapped from application.yml
+ */
+@Configuration
+@ConfigurationProperties(prefix = "vesopenapimanager.validation")
+@Getter
+@Setter
+public class ValidatorProperties {
+    private String schemaMapPath;
+    private String eventDomainPath;
+    private String eventSchemaReferencePath;
+}
diff --git a/src/main/java/org/onap/ves/openapi/manager/exceptions/ArtifactException.java b/src/main/java/org/onap/ves/openapi/manager/exceptions/ArtifactException.java
new file mode 100644
index 0000000..c51face
--- /dev/null
+++ b/src/main/java/org/onap/ves/openapi/manager/exceptions/ArtifactException.java
@@ -0,0 +1,26 @@
+/*
+ * ============LICENSE_START=======================================================
+ * VES-OPENAPI-MANAGER
+ * ================================================================================
+ * Copyright (C) 2021 Nokia. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.ves.openapi.manager.exceptions;
+
+import lombok.NoArgsConstructor;
+
+@NoArgsConstructor
+public class ArtifactException extends RuntimeException { }
diff --git a/src/main/java/org/onap/ves/openapi/manager/model/Artifact.java b/src/main/java/org/onap/ves/openapi/manager/model/Artifact.java
new file mode 100644
index 0000000..8cc54ac
--- /dev/null
+++ b/src/main/java/org/onap/ves/openapi/manager/model/Artifact.java
@@ -0,0 +1,37 @@
+/*
+ * ============LICENSE_START=======================================================
+ * VES-OPENAPI-MANAGER
+ * ================================================================================
+ * Copyright (C) 2021 Nokia. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.ves.openapi.manager.model;
+
+import lombok.AllArgsConstructor;
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import org.onap.sdc.api.notification.IArtifactInfo;
+
+/**
+ * Artifact - model class that contains description and content of artifact
+ */
+@AllArgsConstructor
+@Getter
+@EqualsAndHashCode
+public class Artifact {
+    private final IArtifactInfo description;
+    private final byte[] content;
+}
diff --git a/src/main/java/org/onap/ves/openapi/manager/model/ArtifactValidationResult.java b/src/main/java/org/onap/ves/openapi/manager/model/ArtifactValidationResult.java
new file mode 100644
index 0000000..f672bb5
--- /dev/null
+++ b/src/main/java/org/onap/ves/openapi/manager/model/ArtifactValidationResult.java
@@ -0,0 +1,40 @@
+/*
+ * ============LICENSE_START=======================================================
+ * VES-OPENAPI-MANAGER
+ * ================================================================================
+ * Copyright (C) 2021 Nokia. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.ves.openapi.manager.model;
+
+import lombok.AllArgsConstructor;
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import org.onap.sdc.api.notification.IArtifactInfo;
+import org.onap.ves.openapi.manager.service.validation.ArtifactsValidator;
+
+/**
+ * ArtifactValidationResult - model class for result of artifact validation
+ */
+@AllArgsConstructor
+@Getter
+@EqualsAndHashCode
+public class ArtifactValidationResult {
+    private final IArtifactInfo artifact;
+    private final boolean isValid;
+    private final String message;
+    private final ArtifactsValidator validator;
+}
diff --git a/src/main/java/org/onap/ves/openapi/manager/model/DistributionStatusMessage.java b/src/main/java/org/onap/ves/openapi/manager/model/DistributionStatusMessage.java
new file mode 100644
index 0000000..f25e6e0
--- /dev/null
+++ b/src/main/java/org/onap/ves/openapi/manager/model/DistributionStatusMessage.java
@@ -0,0 +1,63 @@
+/*
+ * ============LICENSE_START=======================================================
+ * VES-OPENAPI-MANAGER
+ * ================================================================================
+ * Copyright (C) 2021 Nokia. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.ves.openapi.manager.model;
+
+import lombok.AllArgsConstructor;
+import org.onap.sdc.api.consumer.IDistributionStatusMessage;
+import org.onap.sdc.utils.DistributionStatusEnum;
+
+/**
+ * DistributionStatusMessage - model class for operation status sent to SDC
+ */
+@AllArgsConstructor
+public class DistributionStatusMessage implements IDistributionStatusMessage {
+
+    private final String artifactUrl;
+    private final String distributionId;
+    private final String consumerId;
+    private final long timestamp;
+    private final DistributionStatusEnum status;
+
+    @Override
+    public String getArtifactURL() {
+        return artifactUrl;
+    }
+
+    @Override
+    public String getDistributionID() {
+        return distributionId;
+    }
+
+    @Override
+    public String getConsumerID() {
+        return consumerId;
+    }
+
+    @Override
+    public long getTimestamp() {
+        return timestamp;
+    }
+
+    @Override
+    public DistributionStatusEnum getStatus() {
+        return status;
+    }
+}
diff --git a/src/main/java/org/onap/ves/openapi/manager/model/FinalDistributionStatusMessage.java b/src/main/java/org/onap/ves/openapi/manager/model/FinalDistributionStatusMessage.java
new file mode 100644
index 0000000..de8836d
--- /dev/null
+++ b/src/main/java/org/onap/ves/openapi/manager/model/FinalDistributionStatusMessage.java
@@ -0,0 +1,59 @@
+/*
+ * ============LICENSE_START=======================================================
+ * VES-OPENAPI-MANAGER
+ * ================================================================================
+ * Copyright (C) 2021 Nokia. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.ves.openapi.manager.model;
+
+import lombok.AllArgsConstructor;
+import org.onap.sdc.api.consumer.IFinalDistrStatusMessage;
+import org.onap.sdc.utils.DistributionStatusEnum;
+
+
+/**
+ * FinalDistributionStatusMessage - model class for final ves-openapi-manager's validation message sent to SDC
+ */
+@AllArgsConstructor
+public class FinalDistributionStatusMessage implements IFinalDistrStatusMessage {
+
+    private final String distributionId;
+    private final long timestamp;
+    private final DistributionStatusEnum status;
+    private final String consumerId;
+
+    @Override
+    public String getDistributionID() {
+        return distributionId;
+    }
+
+    @Override
+    public long getTimestamp() {
+        return timestamp;
+    }
+
+    @Override
+    public DistributionStatusEnum getStatus() {
+        return status;
+    }
+
+    @Override
+    public String getConsumerID() {
+        return consumerId;
+    }
+
+}
diff --git a/src/main/java/org/onap/ves/openapi/manager/model/SchemaMap.java b/src/main/java/org/onap/ves/openapi/manager/model/SchemaMap.java
new file mode 100644
index 0000000..b275e7f
--- /dev/null
+++ b/src/main/java/org/onap/ves/openapi/manager/model/SchemaMap.java
@@ -0,0 +1,48 @@
+/*
+ * ============LICENSE_START=======================================================
+ * VES-OPENAPI-MANAGER
+ * ================================================================================
+ * Copyright (C) 2021 Nokia. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.ves.openapi.manager.model;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+
+import java.util.List;
+
+/**
+ * SchemaMap - model class with list of mappings of publicly stored OpenApi schemas to schemas stored locally in VES.
+ */
+@AllArgsConstructor
+@Getter
+public class SchemaMap {
+    private final List<Mapping> mappings;
+
+    /**
+     * Single schemas mapping,
+     * publicURL contains URL to externally located schema,
+     * localURL contains URL located in VES container
+     */
+    @NoArgsConstructor
+    @Getter
+    public static class Mapping {
+        private String publicURL;
+        private String localURL;
+    }
+}
diff --git a/src/main/java/org/onap/ves/openapi/manager/service/ArtifactsCollector.java b/src/main/java/org/onap/ves/openapi/manager/service/ArtifactsCollector.java
new file mode 100644
index 0000000..52f1b0e
--- /dev/null
+++ b/src/main/java/org/onap/ves/openapi/manager/service/ArtifactsCollector.java
@@ -0,0 +1,99 @@
+/*
+ * ============LICENSE_START=======================================================
+ * VES-OPENAPI-MANAGER
+ * ================================================================================
+ * Copyright (C) 2021 Nokia. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.ves.openapi.manager.service;
+
+import lombok.extern.log4j.Log4j2;
+import org.onap.sdc.api.notification.IArtifactInfo;
+import org.onap.sdc.http.SdcConnectorClient;
+import org.onap.sdc.impl.DistributionClientDownloadResultImpl;
+import org.onap.ves.openapi.manager.config.DistributionClientConfig;
+import org.onap.ves.openapi.manager.exceptions.ArtifactException;
+import org.onap.ves.openapi.manager.model.Artifact;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+/**
+ * ArtifactsCollector - Collector of artifact contents from distributed service,
+ * downloads artifacts contents basing on artifacts descriptions
+ */
+@Log4j2
+@Component
+public class ArtifactsCollector {
+
+    private static final String QUOTATION_MARK = "\"";
+    private static final int ARTIFACT_NAME_INDEX_NO_QUOTATION = 0;
+    private static final int ARTIFACT_NAME_INDEX_WITH_QUOTATION = 1;
+    private static final int NAME_WITH_QUOTATION_LENGTH = 2;
+
+    private final SdcConnectorClient sdcConnectorClient;
+
+    /**
+     * Constructor of ArtifactsCollector
+     * @param sdcConnectorClient SdcConnectorClient object
+     */
+    @Autowired
+    public ArtifactsCollector(SdcConnectorClient sdcConnectorClient) {
+        this.sdcConnectorClient = sdcConnectorClient;
+    }
+
+    /**
+     * @param artifacts List of IArtifactInfo objects, which are descriptions of artifacts
+     * @return List of Artifacts, each object contains description and content of artifact
+     */
+    public List<Artifact> pullArtifacts(List<IArtifactInfo> artifacts) {
+        log.info("Downloading artifacts");
+        List<IArtifactInfo> artifactDescriptions = filterVesEventsArtifacts(artifacts);
+        return artifactDescriptions.stream()
+                .map(sdcConnectorClient::downloadArtifact)
+                .map(downloadResult -> mapDownloadResultToArtifactContent(downloadResult, artifactDescriptions))
+                .collect(Collectors.toList());
+    }
+
+    private List<IArtifactInfo> filterVesEventsArtifacts(List<IArtifactInfo> artifacts) {
+        return artifacts.stream()
+                .filter(artifactInfo ->
+                        artifactInfo.getArtifactType().equals(DistributionClientConfig.VES_EVENTS_ARTIFACT_TYPE))
+                .collect(Collectors.toList());
+    }
+
+    private Artifact mapDownloadResultToArtifactContent(DistributionClientDownloadResultImpl downloadResult,
+                                                        List<IArtifactInfo> artifactDescriptions) {
+        String artifactName = parseArtifactName(downloadResult);
+        Optional<IArtifactInfo> artifact = findArtifactDescription(artifactDescriptions, artifactName);
+        return artifact.map(artifactInfo ->
+                new Artifact(artifactInfo, downloadResult.getArtifactPayload()))
+                .orElseThrow(ArtifactException::new);
+    }
+
+    private String parseArtifactName(DistributionClientDownloadResultImpl artifact) {
+        String[] name = artifact.getArtifactName().split(QUOTATION_MARK);
+        boolean doesContainQuotationMark = name.length >= NAME_WITH_QUOTATION_LENGTH;
+        return doesContainQuotationMark ? name[ARTIFACT_NAME_INDEX_WITH_QUOTATION] : name[ARTIFACT_NAME_INDEX_NO_QUOTATION];
+    }
+
+    private Optional<IArtifactInfo> findArtifactDescription(List<IArtifactInfo> artifacts, String artifactName) {
+        return artifacts.stream().filter(a -> a.getArtifactName().equals(artifactName)).findFirst();
+    }
+}
diff --git a/src/main/java/org/onap/ves/openapi/manager/service/ClientCallback.java b/src/main/java/org/onap/ves/openapi/manager/service/ClientCallback.java
new file mode 100644
index 0000000..720ab8c
--- /dev/null
+++ b/src/main/java/org/onap/ves/openapi/manager/service/ClientCallback.java
@@ -0,0 +1,143 @@
+/*
+ * ============LICENSE_START=======================================================
+ * VES-OPENAPI-MANAGER
+ * ================================================================================
+ * Copyright (C) 2021 Nokia. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.ves.openapi.manager.service;
+
+import lombok.extern.log4j.Log4j2;
+import org.onap.sdc.api.consumer.INotificationCallback;
+import org.onap.sdc.api.notification.IArtifactInfo;
+import org.onap.sdc.api.notification.INotificationData;
+import org.onap.sdc.api.notification.IResourceInstance;
+import org.onap.ves.openapi.manager.model.Artifact;
+import org.onap.ves.openapi.manager.model.ArtifactValidationResult;
+import org.onap.ves.openapi.manager.service.notification.ArtifactsCollectorStatusSender;
+import org.onap.ves.openapi.manager.service.notification.FinalStatusSender;
+import org.onap.ves.openapi.manager.service.notification.ValidationStatusSender;
+import org.onap.ves.openapi.manager.service.validation.ArtifactsValidator;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+@Log4j2
+public class ClientCallback implements INotificationCallback {
+
+    public static final String SEPARATOR = "===================================";
+    private final List<ArtifactsValidator> validators;
+    private final ValidationStatusSender validationStatusSender;
+    private final ArtifactsCollectorStatusSender artifactsCollectorStatusSender;
+    private final ArtifactsCollector artifactsCollector;
+    private final FinalStatusSender finalStatusSender;
+
+    /**
+     * Constructor of ClientCallback
+     * @param validators List of objects implementing ArtifactValidator interface
+     * @param artifactsCollector ArtifactsCollector object which downloads artifacts contents
+     * @param artifactsCollectorStatusSender ArtifactsCollectorStatusSender which sends download results back to SDC
+     * @param validationStatusSender ValidationStatusSender which sends validation results to SDC
+     * @param finalStatusSender FinalStatusSender which sends final status of VES OpenApi Manager workflow
+     */
+    public ClientCallback(List<ArtifactsValidator> validators, ArtifactsCollector artifactsCollector,
+                          ArtifactsCollectorStatusSender artifactsCollectorStatusSender,
+                          ValidationStatusSender validationStatusSender, FinalStatusSender finalStatusSender) {
+        this.validators = validators;
+        this.validationStatusSender = validationStatusSender;
+        this.artifactsCollectorStatusSender = artifactsCollectorStatusSender;
+        this.artifactsCollector = artifactsCollector;
+        this.finalStatusSender = finalStatusSender;
+    }
+
+    /**
+     * Callback method of distribution listener. Each time a service distribution takes place it's executed.
+     * @param service Distributed service information.
+     */
+    @Override
+    public void activateCallback(INotificationData service) {
+        logServiceInfo(service);
+        String distributionID = service.getDistributionID();
+
+        List<Artifact> artifacts = downloadArtifacts(service);
+        List<ArtifactValidationResult> validationResults = validate(distributionID, artifacts);
+        sendFinalStatus(distributionID, validationResults);
+    }
+
+    private void logServiceInfo(INotificationData service) {
+        log.info(SEPARATOR);
+        log.info("Distributed service information");
+        log.info("Service UUID: {}", service.getServiceUUID());
+        log.info("Service name: {}", service.getServiceName());
+        List<IResourceInstance> resources = service.getResources();
+        log.info("Service resources:");
+        resources.forEach(resource -> {
+            log.info(" - Resource: {}", resource.getResourceName());
+            log.info("   Artifacts:");
+            resource.getArtifacts().forEach(artifact -> log.info("   - Name: {}", artifact.getArtifactName()));
+        });
+        log.info(SEPARATOR);
+    }
+
+    private List<Artifact> downloadArtifacts(INotificationData service) {
+        List<IArtifactInfo> artifactInfos = getArtifactInfos(service);
+        List<Artifact> artifacts = artifactsCollector.pullArtifacts(artifactInfos);
+        sendDownloadStatuses(service, artifactInfos, artifacts);
+        return artifacts;
+    }
+
+    private void sendDownloadStatuses(INotificationData service, List<IArtifactInfo> artifactInfos, List<Artifact> artifacts) {
+        Map<Boolean, List<Artifact>> artifactsDownloadResults = artifacts.stream()
+                .collect(Collectors.partitioningBy(this::isContentDownloaded));
+        artifactsDownloadResults.get(Boolean.TRUE).forEach(artifact ->
+                artifactsCollectorStatusSender.sendDownloadOk(service.getDistributionID(), artifactInfos));
+        artifactsDownloadResults.get(Boolean.FALSE).forEach(artifact ->
+                artifactsCollectorStatusSender.sendDownloadError(service.getDistributionID(), artifactInfos));
+    }
+
+    private boolean isContentDownloaded(Artifact artifact) {
+        return artifact.getContent() != null && artifact.getContent().length > 0;
+    }
+
+    private List<IArtifactInfo> getArtifactInfos(INotificationData service) {
+        return service.getResources().stream()
+                .map(IResourceInstance::getArtifacts)
+                .flatMap(Collection::stream)
+                .collect(Collectors.toList());
+    }
+
+    private List<ArtifactValidationResult> validate(String distributionID, List<Artifact> artifacts) {
+        return validators.stream()
+                .map(validator -> {
+                    List<ArtifactValidationResult> validationResults = validator.validate(artifacts);
+                    validationStatusSender.send(distributionID, validationResults);
+                    return validationResults;
+                })
+                .flatMap(Collection::stream)
+                .collect(Collectors.toList());
+    }
+
+    private void sendFinalStatus(String distributionID, List<ArtifactValidationResult> validationResults) {
+        boolean areAllArtifactsValid = validationResults.stream().allMatch(ArtifactValidationResult::isValid);
+        if (areAllArtifactsValid) {
+            finalStatusSender.sendFinalStatusOk(distributionID);
+        } else {
+            finalStatusSender.sendFinalStatusError(distributionID);
+        }
+    }
+}
diff --git a/src/main/java/org/onap/ves/openapi/manager/service/notification/ArtifactsCollectorStatusSender.java b/src/main/java/org/onap/ves/openapi/manager/service/notification/ArtifactsCollectorStatusSender.java
new file mode 100644
index 0000000..0829855
--- /dev/null
+++ b/src/main/java/org/onap/ves/openapi/manager/service/notification/ArtifactsCollectorStatusSender.java
@@ -0,0 +1,93 @@
+/*
+ * ============LICENSE_START=======================================================
+ * VES-OPENAPI-MANAGER
+ * ================================================================================
+ * Copyright (C) 2021 Nokia. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.ves.openapi.manager.service.notification;
+
+
+import lombok.extern.log4j.Log4j2;
+import org.onap.sdc.api.notification.IArtifactInfo;
+import org.onap.sdc.api.results.IDistributionClientResult;
+import org.onap.sdc.impl.DistributionClientImpl;
+import org.onap.sdc.utils.DistributionStatusEnum;
+import org.onap.ves.openapi.manager.model.DistributionStatusMessage;
+
+import java.time.LocalDateTime;
+import java.time.ZoneOffset;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * ArtifactsCollectorStatusSender - sender of DistributionStatusMessage with artifacts download status to SDC
+ */
+@Log4j2
+public class ArtifactsCollectorStatusSender {
+
+    private static final String DOWNLOAD_ERROR_MESSAGE = "Couldn't download artifact";
+    private final DistributionClientImpl distributionClient;
+
+    /**
+     * Constructor of ArtifactsCollectorStatusSender
+     * @param distributionClient DistributionClientImpl object
+     */
+    public ArtifactsCollectorStatusSender(DistributionClientImpl distributionClient) {
+        this.distributionClient = distributionClient;
+    }
+
+    /**
+     * Sends DOWNLOAD_OK status to SDC
+     * @param distributionId Service distribution ID
+     * @param downloadedArtifacts Artifacts which are successfully downloaded
+     * @return List of IDistributionClientResult, results of sending status
+     */
+    public List<IDistributionClientResult> sendDownloadOk(String distributionId, List<IArtifactInfo> downloadedArtifacts) {
+        return downloadedArtifacts.stream()
+                .map(artifact -> {
+                    DistributionStatusMessage distributionMessage = getDownloadMessage(distributionId,
+                            artifact.getArtifactURL(), DistributionStatusEnum.DOWNLOAD_OK);
+                    return distributionClient.sendDownloadStatus(distributionMessage);
+                })
+                .collect(Collectors.toList());
+    }
+
+    /**
+     * Sends DOWNLOAD_ERROR status to SDC
+     * @param distributionId Service distribution ID
+     * @param downloadedArtifacts Artifacts which could not be downloaded
+     * @return List of IDistributionClientResult, results of sending status
+     */
+    public List<IDistributionClientResult> sendDownloadError(String distributionId, List<IArtifactInfo> downloadedArtifacts) {
+        return downloadedArtifacts.stream()
+                .map(artifact -> {
+                    DistributionStatusMessage distributionMessage = getDownloadMessage(distributionId,
+                            artifact.getArtifactURL(), DistributionStatusEnum.DOWNLOAD_ERROR);
+                    return distributionClient.sendDownloadStatus(distributionMessage, DOWNLOAD_ERROR_MESSAGE);
+                })
+                .collect(Collectors.toList());
+    }
+
+    private DistributionStatusMessage getDownloadMessage(String distributionId, String artifactURL, DistributionStatusEnum downloadStatus) {
+        return new DistributionStatusMessage(
+                artifactURL,
+                distributionId,
+                distributionClient.getConfiguration().getConsumerID(),
+                LocalDateTime.now().toInstant(ZoneOffset.UTC).toEpochMilli(),
+                downloadStatus);
+    }
+}
diff --git a/src/main/java/org/onap/ves/openapi/manager/service/notification/FinalStatusSender.java b/src/main/java/org/onap/ves/openapi/manager/service/notification/FinalStatusSender.java
new file mode 100644
index 0000000..e21ab70
--- /dev/null
+++ b/src/main/java/org/onap/ves/openapi/manager/service/notification/FinalStatusSender.java
@@ -0,0 +1,85 @@
+/*
+ * ============LICENSE_START=======================================================
+ * VES-OPENAPI-MANAGER
+ * ================================================================================
+ * Copyright (C) 2021 Nokia. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.ves.openapi.manager.service.notification;
+
+import lombok.extern.log4j.Log4j2;
+import org.onap.sdc.api.results.IDistributionClientResult;
+import org.onap.sdc.impl.DistributionClientImpl;
+import org.onap.sdc.utils.DistributionStatusEnum;
+import org.onap.ves.openapi.manager.model.FinalDistributionStatusMessage;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.time.LocalDateTime;
+import java.time.ZoneOffset;
+
+/**
+ * FinalStatusSender - sender of FinalDistributionStatusMessage with final status to SDC
+ */
+@Log4j2
+@Service
+public class FinalStatusSender {
+
+    private static final String INVALID_ARTIFACTS_MESSAGE = "At least one VES_EVENT artifact is invalid";
+    private final DistributionClientImpl distributionClient;
+
+    /**
+     * Constructor of FinalStatusSender
+     * @param distributionClient DistributionClientImpl object
+     */
+    @Autowired
+    public FinalStatusSender(DistributionClientImpl distributionClient) {
+        this.distributionClient = distributionClient;
+    }
+
+    /**
+     * Sends final message COMPONENT_DONE_OK to SDC
+     * @param distributionId Service distribution ID
+     * @return IDistributionClientResult, result of sending status
+     */
+    public IDistributionClientResult sendFinalStatusOk(String distributionId) {
+        DistributionStatusEnum okStatus = DistributionStatusEnum.COMPONENT_DONE_OK;
+        FinalDistributionStatusMessage message = getMessage(distributionId, okStatus);
+        log.info("All VES_EVENT artifacts are valid, sending final status {}", okStatus.name());
+        return distributionClient.sendFinalDistrStatus(message);
+    }
+
+    /**
+     * Sends final message COMPONENT_DONE_ERROR to SDC
+     * @param distributionId Service distribution ID
+     * @return IDistributionClientResult, result of sending status
+     */
+    public IDistributionClientResult sendFinalStatusError(String distributionId) {
+        DistributionStatusEnum errorStatus = DistributionStatusEnum.COMPONENT_DONE_ERROR;
+        FinalDistributionStatusMessage message = getMessage(distributionId, errorStatus);
+        log.info("At least one VES_EVENT artifact is invalid, sending final status {}", errorStatus.name());
+        return distributionClient.sendFinalDistrStatus(message, INVALID_ARTIFACTS_MESSAGE);
+    }
+
+    private FinalDistributionStatusMessage getMessage(String distributionId, DistributionStatusEnum distributionStatus) {
+        return new FinalDistributionStatusMessage(
+                distributionId,
+                LocalDateTime.now().toInstant(ZoneOffset.UTC).toEpochMilli(),
+                distributionStatus,
+                distributionClient.getConfiguration().getConsumerID());
+    }
+
+}
diff --git a/src/main/java/org/onap/ves/openapi/manager/service/notification/ValidationStatusSender.java b/src/main/java/org/onap/ves/openapi/manager/service/notification/ValidationStatusSender.java
new file mode 100644
index 0000000..ae11791
--- /dev/null
+++ b/src/main/java/org/onap/ves/openapi/manager/service/notification/ValidationStatusSender.java
@@ -0,0 +1,89 @@
+/*
+ * ============LICENSE_START=======================================================
+ * VES-OPENAPI-MANAGER
+ * ================================================================================
+ * Copyright (C) 2021 Nokia. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.ves.openapi.manager.service.notification;
+
+import lombok.extern.log4j.Log4j2;
+import org.onap.sdc.api.results.IDistributionClientResult;
+import org.onap.sdc.impl.DistributionClientImpl;
+import org.onap.sdc.utils.DistributionStatusEnum;
+import org.onap.ves.openapi.manager.model.ArtifactValidationResult;
+import org.onap.ves.openapi.manager.model.DistributionStatusMessage;
+
+import java.time.LocalDateTime;
+import java.time.ZoneOffset;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * ValidationStatusSender - sender of DistributionStatusMessage with validation status to SDC
+ */
+@Log4j2
+public class ValidationStatusSender {
+
+    private final DistributionClientImpl distributionClient;
+
+    /**
+     * Constructor of ValidationStatusSender
+     * @param distributionClient DistributionClientImpl object
+     */
+    public ValidationStatusSender(DistributionClientImpl distributionClient) {
+        this.distributionClient = distributionClient;
+    }
+
+    /**
+     * Sends validation result status to SDC
+     * @param distributionId Service distribution ID
+     * @param validationResults Validation results
+     * @return List of IDistributionClientResult, results of sending status
+     */
+    public List<IDistributionClientResult> send(String distributionId, List<ArtifactValidationResult> validationResults) {
+        return validationResults.stream()
+                .map(validationResult -> sendValidationResult(distributionId, validationResult))
+                .collect(Collectors.toList());
+    }
+
+    private IDistributionClientResult sendValidationResult(String distributionId, ArtifactValidationResult validationResult) {
+        DistributionStatusMessage distributionMessage = getDistributionMessage(distributionId, validationResult);
+        String artifactName = validationResult.getArtifact().getArtifactName();
+
+        if (validationResult.isValid()) {
+            log.warn("Artifact {} is valid", artifactName);
+            return distributionClient.sendDeploymentStatus(distributionMessage);
+        } else {
+            String message = validationResult.getMessage();
+            log.warn("Artifact {} is invalid", artifactName);
+            log.warn("Validation message: {}", message);
+            return distributionClient.sendDeploymentStatus(distributionMessage, message);
+        }
+    }
+
+    private DistributionStatusMessage getDistributionMessage(String distributionId, ArtifactValidationResult validationResult) {
+        String artifactURL = validationResult.getArtifact().getArtifactURL();
+        DistributionStatusEnum status = getErrorStatus(validationResult);
+        return new DistributionStatusMessage(artifactURL, distributionId,
+                distributionClient.getConfiguration().getConsumerID(),
+                LocalDateTime.now().toInstant(ZoneOffset.UTC).toEpochMilli(), status);
+    }
+
+    private DistributionStatusEnum getErrorStatus(ArtifactValidationResult validationResult) {
+        return validationResult.isValid() ? DistributionStatusEnum.DEPLOY_OK : DistributionStatusEnum.DEPLOY_ERROR;
+    }
+}
diff --git a/src/main/java/org/onap/ves/openapi/manager/service/serialization/SchemaMapDeserializer.java b/src/main/java/org/onap/ves/openapi/manager/service/serialization/SchemaMapDeserializer.java
new file mode 100644
index 0000000..8ea434e
--- /dev/null
+++ b/src/main/java/org/onap/ves/openapi/manager/service/serialization/SchemaMapDeserializer.java
@@ -0,0 +1,71 @@
+/*
+ * ============LICENSE_START=======================================================
+ * VES-OPENAPI-MANAGER
+ * ================================================================================
+ * Copyright (C) 2021 Nokia. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.ves.openapi.manager.service.serialization;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import lombok.extern.log4j.Log4j2;
+import org.onap.ves.openapi.manager.model.SchemaMap;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * SchemaMapDeserializer - Deserializer of JSON file contents to SchemaMap objects
+ */
+@Log4j2
+@Service
+public class SchemaMapDeserializer {
+
+    private static final String COULD_NOT_READ_SCHEMA_MAP_MESSAGE = "Couldn't read schema map from path: ";
+    private final ObjectMapper objectMapper;
+
+    /**
+     * Constructor of SchemaMapDeserializer
+     * @param objectMapper ObjectMapper for YAML parsing
+     */
+    @Autowired
+    public SchemaMapDeserializer(ObjectMapper objectMapper) {
+        this.objectMapper = objectMapper;
+    }
+
+    /**
+     * Deserializes file content to SchemaMap object
+     * @param schemaMapPath Path to file containing JSON formatted SchemaMap
+     * @return SchemaMap
+     */
+    public SchemaMap getSchemaMapFromFile(String schemaMapPath) {
+        SchemaMap schemaMap;
+        try {
+            File file = new File(schemaMapPath);
+            List<SchemaMap.Mapping> mappings = Arrays.asList(objectMapper.readValue(file, SchemaMap.Mapping[].class));
+            schemaMap = new SchemaMap(mappings);
+        } catch (IOException e) {
+            schemaMap = new SchemaMap(Collections.emptyList());
+            log.error(COULD_NOT_READ_SCHEMA_MAP_MESSAGE + schemaMapPath, e);
+        }
+        return schemaMap;
+    }
+}
diff --git a/src/main/java/org/onap/ves/openapi/manager/service/serialization/VesEventsArtifactDeserializer.java b/src/main/java/org/onap/ves/openapi/manager/service/serialization/VesEventsArtifactDeserializer.java
new file mode 100644
index 0000000..4bd6761
--- /dev/null
+++ b/src/main/java/org/onap/ves/openapi/manager/service/serialization/VesEventsArtifactDeserializer.java
@@ -0,0 +1,69 @@
+/*
+ * ============LICENSE_START=======================================================
+ * VES-OPENAPI-MANAGER
+ * ================================================================================
+ * Copyright (C) 2021 Nokia. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.ves.openapi.manager.service.serialization;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
+import com.fasterxml.jackson.dataformat.yaml.YAMLParser;
+import lombok.extern.log4j.Log4j2;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * VesEventsArtifactDeserializer - Deserializer of byte array of MultiDocument YAML to ObjectNodes
+ */
+@Log4j2
+@Service
+public class VesEventsArtifactDeserializer {
+
+    private static final String COULD_NOT_READ_ARTIFACT_CONTENT_MESSAGE = "Couldn't read artifact content";
+    private final ObjectMapper objectMapper;
+
+    /**
+     * Constructor of VesEventsArtifactDeserializer
+     * @param objectMapper ObjectMapper for YAML parsing
+     */
+    @Autowired
+    public VesEventsArtifactDeserializer(ObjectMapper objectMapper) {
+        this.objectMapper = objectMapper;
+    }
+
+    /**
+     * Deserializes MultiDocument YAML given as byte array
+     * @param data MultiDocument YAML as byte array
+     * @return List of ObjectNodes, each Document is separate ObjectNode
+     */
+    public List<ObjectNode> deserializeMultiDocumentYaml(byte[] data) {
+        List<ObjectNode> events = Collections.emptyList();
+        try {
+            YAMLParser yamlParser = new YAMLFactory().createParser(data);
+            events = objectMapper.readValues(yamlParser, ObjectNode.class).readAll();
+        } catch (IOException e) {
+            log.error(COULD_NOT_READ_ARTIFACT_CONTENT_MESSAGE, e);
+        }
+        return events;
+    }
+}
diff --git a/src/main/java/org/onap/ves/openapi/manager/service/validation/ArtifactsValidator.java b/src/main/java/org/onap/ves/openapi/manager/service/validation/ArtifactsValidator.java
new file mode 100644
index 0000000..5963c6b
--- /dev/null
+++ b/src/main/java/org/onap/ves/openapi/manager/service/validation/ArtifactsValidator.java
@@ -0,0 +1,35 @@
+/*
+ * ============LICENSE_START=======================================================
+ * VES-OPENAPI-MANAGER
+ * ================================================================================
+ * Copyright (C) 2021 Nokia. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.ves.openapi.manager.service.validation;
+
+import org.onap.ves.openapi.manager.model.Artifact;
+import org.onap.ves.openapi.manager.model.ArtifactValidationResult;
+
+import java.util.List;
+
+public interface ArtifactsValidator {
+    /**
+     * Validates given VES_EVENT type artifacts
+     * @param artifacts List of artifacts: descriptions and contents
+     * @return List of ArtifactValidationResult, validation results
+     */
+    List<ArtifactValidationResult> validate(List<Artifact> artifacts);
+}
diff --git a/src/main/java/org/onap/ves/openapi/manager/service/validation/SchemaReferenceValidator.java b/src/main/java/org/onap/ves/openapi/manager/service/validation/SchemaReferenceValidator.java
new file mode 100644
index 0000000..dd97b09
--- /dev/null
+++ b/src/main/java/org/onap/ves/openapi/manager/service/validation/SchemaReferenceValidator.java
@@ -0,0 +1,158 @@
+/*
+ * ============LICENSE_START=======================================================
+ * VES-OPENAPI-MANAGER
+ * ================================================================================
+ * Copyright (C) 2021 Nokia. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.ves.openapi.manager.service.validation;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import lombok.extern.log4j.Log4j2;
+import org.onap.sdc.api.notification.IArtifactInfo;
+import org.onap.ves.openapi.manager.config.ValidatorProperties;
+import org.onap.ves.openapi.manager.model.Artifact;
+import org.onap.ves.openapi.manager.model.ArtifactValidationResult;
+import org.onap.ves.openapi.manager.model.SchemaMap;
+import org.onap.ves.openapi.manager.service.serialization.SchemaMapDeserializer;
+import org.onap.ves.openapi.manager.service.serialization.VesEventsArtifactDeserializer;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import java.util.stream.StreamSupport;
+
+/**
+ * SchemaReferenceValidator - validator of schemaReference field in VES_EVENTS artifacts
+ */
+@Log4j2
+@Service
+public class SchemaReferenceValidator implements ArtifactsValidator {
+
+    private static final String HASH_KEY = "#";
+    private static final String NEW_LINE = "\n";
+    static final String STND_DEFINED_DOMAIN = "stndDefined";
+    static final String SCHEMA_REFERENCE_ERROR_MESSAGE = "Schema reference is not present in VES schema map.";
+
+    private final VesEventsArtifactDeserializer vesEventsArtifactDeserializer;
+    private final SchemaMapDeserializer schemaMapDeserializer;
+    private final ValidatorProperties validatorProperties;
+
+    /**
+     * Constructor of SchemaReferenceValidator
+     * @param vesEventsArtifactDeserializer Deserializer of VES_EVENT type artifact
+     * @param schemaMapDeserializer Deserializer of Schema Map
+     * @param validatorProperties Properties required by validator
+     */
+    @Autowired
+    public SchemaReferenceValidator(VesEventsArtifactDeserializer vesEventsArtifactDeserializer,
+                                    SchemaMapDeserializer schemaMapDeserializer,
+                                    ValidatorProperties validatorProperties) {
+        this.vesEventsArtifactDeserializer = vesEventsArtifactDeserializer;
+        this.schemaMapDeserializer = schemaMapDeserializer;
+        this.validatorProperties = validatorProperties;
+    }
+
+    /**
+     * Validates given VES_EVENT type artifacts
+     * @param artifacts List of artifacts: descriptions and contents
+     * @return List of ArtifactValidationResult, validation results
+     */
+    @Override
+    public List<ArtifactValidationResult> validate(List<Artifact> artifacts) {
+        return artifacts.stream()
+                .map(this::getArtifactValidationResult)
+                .collect(Collectors.toList());
+    }
+
+    private ArtifactValidationResult getArtifactValidationResult(Artifact artifact) {
+        List<JsonNode> stndDefinedEvents = getStndDefinedEvents(artifact.getContent());
+        List<String> publicUrls = getPublicUrls();
+
+        Set<String> invalidSchemaReferences = stndDefinedEvents.stream()
+                .map(event -> getInvalidReferences(event, publicUrls))
+                .flatMap(Collection::stream)
+                .collect(Collectors.toSet());
+
+        return getValidationResult(artifact.getDescription(), invalidSchemaReferences);
+    }
+
+    private ArtifactValidationResult getValidationResult(IArtifactInfo artifact, Set<String> invalidReferences) {
+        boolean isValid = invalidReferences.isEmpty();
+        String message = getMessage(invalidReferences);
+        return new ArtifactValidationResult(artifact, isValid, message, this);
+    }
+
+    private String getMessage(Set<String> invalidSchemaReferences) {
+        final String emptyMessage = "";
+        return invalidSchemaReferences.isEmpty() ? emptyMessage : generateMessage(invalidSchemaReferences);
+    }
+
+    private String generateMessage(Set<String> invalidSchemaReferences) {
+        return SCHEMA_REFERENCE_ERROR_MESSAGE + ":" + NEW_LINE + String.join(NEW_LINE, invalidSchemaReferences);
+    }
+
+    private List<JsonNode> getStndDefinedEvents(byte[] artifactData) {
+        List<ObjectNode> events = vesEventsArtifactDeserializer.deserializeMultiDocumentYaml(artifactData);
+        return events.stream()
+                .filter(this::isStndDefinedEvent)
+                .collect(Collectors.toList());
+    }
+
+    private boolean isStndDefinedEvent(ObjectNode event) {
+        String actualDomain = event.at(validatorProperties.getEventDomainPath()).asText();
+        return actualDomain.equals(STND_DEFINED_DOMAIN);
+    }
+
+    private List<String> getPublicUrls() {
+        String schemaMapPath = validatorProperties.getSchemaMapPath();
+        List<SchemaMap.Mapping> mappings = schemaMapDeserializer.getSchemaMapFromFile(schemaMapPath).getMappings();
+        return mappings.stream()
+                .map(SchemaMap.Mapping::getPublicURL)
+                .collect(Collectors.toList());
+    }
+
+    private List<String> getInvalidReferences(JsonNode event, List<String> publicUrls) {
+        String schemaReferencePath = validatorProperties.getEventSchemaReferencePath();
+        JsonNode schemaReference = event.at(schemaReferencePath);
+        Stream<JsonNode> schemaReferenceStream = Stream.empty();
+
+        if (schemaReference.isArray()) {
+            schemaReferenceStream = StreamSupport.stream(schemaReference.spliterator(), false);
+        } else if (schemaReference.isValueNode()) {
+            schemaReferenceStream = Stream.of(schemaReference);
+        }
+
+        return getInvalidSchemaReferences(schemaReferenceStream, publicUrls);
+    }
+
+    private List<String> getInvalidSchemaReferences(Stream<JsonNode> schemaReferencesStream, List<String> publicUrls) {
+        return schemaReferencesStream
+                .map(this::fetchUrlFromSchemaReference)
+                .filter(reference -> !publicUrls.contains(reference))
+                .collect(Collectors.toList());
+    }
+
+    private String fetchUrlFromSchemaReference(JsonNode schemaReference) {
+        final int urlPartIndex = 0;
+        return schemaReference.asText().split(HASH_KEY)[urlPartIndex];
+    }
+}
diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml
new file mode 100644
index 0000000..f179544
--- /dev/null
+++ b/src/main/resources/application.yml
@@ -0,0 +1,20 @@
+vesopenapimanager:
+  validation:
+    schemaMapPath: ${SCHEMA_MAP_PATH:./src/main/resources/schema-map.json}
+    eventDomainPath: /event/structure/commonEventHeader/structure/domain/value
+    eventSchemaReferencePath: /event/structure/stndDefinedFields/structure/schemaReference/value
+  distribution:
+    asdcAddress: ${ASDC_ADDRESS:sdc-be.onap:30204}
+    msgBusAddress: message-router.onap
+    user: dcae
+    password: Kp8bJ4SXszM0WXlhak3eHlcse2gAw84vaoGGmJvUy2U
+    pollingInterval: 20
+    pollingTimeout: 20
+    consumerGroup: ves-openapi-manager
+    environmentName: AUTO
+    consumerID: ves-openapi-manager
+    activateServerTLSAuth: false
+    isFilterInEmptyResources: false
+    isUseHttpsWithDmaap: false
+
+
diff --git a/src/main/resources/schema-map.json b/src/main/resources/schema-map.json
new file mode 100644
index 0000000..359014c
--- /dev/null
+++ b/src/main/resources/schema-map.json
@@ -0,0 +1,58 @@
+[
+  {
+    "publicURL": "https://forge.3gpp.org/rep/sa5/MnS/blob/SA88-Rel16/OpenAPI/faultMnS.yaml",
+    "localURL": "3gpp/rep/sa5/MnS/tree/SA88-Rel16/OpenAPI/5gcNrm.yaml"
+  },
+  {
+    "publicURL": "https://forge.3gpp.org/invalid.yaml",
+    "localURL": "3gpp/rep/sa5/MnS/tree/SA88-Rel16/OpenAPI/PerDataFileReportMnS.yaml"
+  },
+  {
+    "publicURL": "https://forge.3gpp.org/rep/sa5/MnS/tree/SA88-Rel16/OpenAPI/PerMeasJobCtlMnS.yaml",
+    "localURL": "3gpp/rep/sa5/MnS/tree/SA88-Rel16/OpenAPI/PerMeasJobCtlMnS.yaml"
+  },
+  {
+    "publicURL": "https://forge.3gpp.org/rep/sa5/MnS/tree/SA88-Rel16/OpenAPI/PerThresMonMnS.yaml",
+    "localURL": "3gpp/rep/sa5/MnS/tree/SA88-Rel16/OpenAPI/PerThresMonMnS.yaml"
+  },
+  {
+    "publicURL": "https://forge.3gpp.org/rep/sa5/MnS/tree/SA88-Rel16/OpenAPI/PerfDataStreamingMnS.yaml",
+    "localURL": "3gpp/rep/sa5/MnS/tree/SA88-Rel16/OpenAPI/PerfDataStreamingMnS.yaml"
+  },
+  {
+    "publicURL": "https://forge.3gpp.org/rep/sa5/MnS/tree/SA88-Rel16/OpenAPI/comDefs.yaml",
+    "localURL": "3gpp/rep/sa5/MnS/tree/SA88-Rel16/OpenAPI/comDefs.yaml"
+  },
+  {
+    "publicURL": "https://forge.3gpp.org/rep/sa5/MnS/tree/SA88-Rel16/OpenAPI/coslaNrm.yaml",
+    "localURL": "3gpp/rep/sa5/MnS/tree/SA88-Rel16/OpenAPI/coslaNrm.yaml"
+  },
+  {
+    "publicURL": "https://forge.3gpp.org/rep/sa5/MnS/tree/SA88-Rel16/OpenAPI/faultMnS.yaml",
+    "localURL": "3gpp/rep/sa5/MnS/tree/SA88-Rel16/OpenAPI/faultMnS.yaml"
+  },
+  {
+    "publicURL": "https://forge.3gpp.org/rep/sa5/MnS/blob/SA88-Rel16/OpenAPI/faultMnS.yaml",
+    "localURL": "3gpp/rep/sa5/MnS/tree/SA88-Rel16/OpenAPI/genericNrm.yaml"
+  },
+  {
+    "publicURL": "https://forge.3gpp.org/faultMnS_dcae.yaml",
+    "localURL": "3gpp/rep/sa5/MnS/tree/SA88-Rel16/OpenAPI/heartbeatNtf.yaml"
+  },
+  {
+    "publicURL": "https://forge.3gpp.org/faultMnS.yaml",
+    "localURL": "3gpp/rep/sa5/MnS/tree/SA88-Rel16/OpenAPI/nrNrm.yaml"
+  },
+  {
+    "publicURL": "https://forge.3gpp.org/rep/sa5/MnS/tree/SA88-Rel16/OpenAPI/provMnS.yaml",
+    "localURL": "3gpp/rep/sa5/MnS/tree/SA88-Rel16/OpenAPI/provMnS.yaml"
+  },
+  {
+    "publicURL": "https://forge.3gpp.org/rep/sa5/MnS/tree/SA88-Rel16/OpenAPI/sliceNrm.yaml",
+    "localURL": "3gpp/rep/sa5/MnS/tree/SA88-Rel16/OpenAPI/sliceNrm.yaml"
+  },
+  {
+    "publicURL": "https://forge.3gpp.org/rep/sa5/MnS/tree/SA88-Rel16/OpenAPI/streamingDataMnS.yaml",
+    "localURL": "3gpp/rep/sa5/MnS/tree/SA88-Rel16/OpenAPI/streamingDataMnS.yaml"
+  }
+]
\ No newline at end of file
diff --git a/src/test/java/org/onap/ves/openapi/manager/config/DistributionClientConfigTest.java b/src/test/java/org/onap/ves/openapi/manager/config/DistributionClientConfigTest.java
new file mode 100644
index 0000000..9118f82
--- /dev/null
+++ b/src/test/java/org/onap/ves/openapi/manager/config/DistributionClientConfigTest.java
@@ -0,0 +1,89 @@
+/*
+ * ============LICENSE_START=======================================================
+ * VES-OPENAPI-MANAGER
+ * ================================================================================
+ * Copyright (C) 2021 Nokia. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.ves.openapi.manager.config;
+
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.context.properties.bind.BindResult;
+import org.springframework.boot.context.properties.bind.Binder;
+import org.springframework.boot.context.properties.source.ConfigurationPropertySource;
+import org.springframework.boot.context.properties.source.MapConfigurationPropertySource;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class DistributionClientConfigTest {
+
+    @Test
+    void shouldCreateDistributionClientConfig() {
+        //given
+        String asdcAddress = "sdc-be.onap:8443";
+        String msgBusAddress = "message-router.onap";
+        String user = "dcae";
+        String password = "Kp8bJ4SXszM0WXlhak3eHlcse2gAw84vaoGGmJvUy2U";
+        Integer pollingInterval = 20;
+        Integer pollingTimeout = 20;
+        String consumerGroup = "ves-openapi-manager";
+        String environmentName = "AUTO";
+        String consumerID = "ves-openapi-manager";
+        Map<String, Object> properties = new HashMap<>();
+        properties.put("vesopenapimanager.distribution.asdcAddress", asdcAddress);
+        properties.put("vesopenapimanager.distribution.msgBusAddress", msgBusAddress);
+        properties.put("vesopenapimanager.distribution.user", user);
+        properties.put("vesopenapimanager.distribution.password", password);
+        properties.put("vesopenapimanager.distribution.pollingInterval", pollingInterval);
+        properties.put("vesopenapimanager.distribution.pollingTimeout", pollingTimeout);
+        properties.put("vesopenapimanager.distribution.consumerGroup", consumerGroup);
+        properties.put("vesopenapimanager.distribution.environmentName", environmentName);
+        properties.put("vesopenapimanager.distribution.consumerID", consumerID);
+        properties.put("vesopenapimanager.distribution.activateServerTLSAuth", false);
+        properties.put("vesopenapimanager.distribution.isFilterInEmptyResources", false);
+        properties.put("vesopenapimanager.distribution.isUseHttpsWithDmaap", false);
+        ConfigurationPropertySource source = new MapConfigurationPropertySource(properties);
+        Binder binder = new Binder(source);
+
+        //when
+        BindResult<DistributionClientConfig> result = binder.bind("vesopenapimanager.distribution", DistributionClientConfig.class);
+
+        //then
+        assertThat(result.isBound()).isTrue();
+        DistributionClientConfig config = result.get();
+        assertThat(config.getAsdcAddress()).isEqualTo(asdcAddress);
+        assertThat(config.getMsgBusAddress()).isEqualTo(List.of(msgBusAddress));
+        assertThat(config.getUser()).isEqualTo(user);
+        assertThat(config.getPassword()).isEqualTo(password);
+        assertThat(config.getPollingInterval()).isEqualTo(pollingInterval);
+        assertThat(config.getPollingTimeout()).isEqualTo(pollingTimeout);
+        assertThat(config.getRelevantArtifactTypes()).isEqualTo(Collections.singletonList("VES_EVENTS"));
+        assertThat(config.getConsumerGroup()).isEqualTo(consumerGroup);
+        assertThat(config.getEnvironmentName()).isEqualTo(environmentName);
+        assertThat(config.getConsumerID()).isEqualTo(consumerID);
+        assertThat(config.getKeyStorePath()).isNull();
+        assertThat(config.getKeyStorePassword()).isNull();
+        assertThat(config.activateServerTLSAuth()).isFalse();
+        assertThat(config.isFilterInEmptyResources()).isFalse();
+        assertThat(config.activateServerTLSAuth()).isFalse();
+        assertThat(config.isUseHttpsWithDmaap()).isFalse();
+    }
+}
diff --git a/src/test/java/org/onap/ves/openapi/manager/config/ValidatorPropertiesTest.java b/src/test/java/org/onap/ves/openapi/manager/config/ValidatorPropertiesTest.java
new file mode 100644
index 0000000..57b3725
--- /dev/null
+++ b/src/test/java/org/onap/ves/openapi/manager/config/ValidatorPropertiesTest.java
@@ -0,0 +1,60 @@
+/*
+ * ============LICENSE_START=======================================================
+ * VES-OPENAPI-MANAGER
+ * ================================================================================
+ * Copyright (C) 2021 Nokia. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.ves.openapi.manager.config;
+
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.context.properties.bind.BindResult;
+import org.springframework.boot.context.properties.bind.Binder;
+import org.springframework.boot.context.properties.source.ConfigurationPropertySource;
+import org.springframework.boot.context.properties.source.MapConfigurationPropertySource;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class ValidatorPropertiesTest {
+
+    @Test
+    void shouldCreateDistributionClientConfig() {
+        //given
+        String schemaMapPath = "schema-map.json";
+        String eventDomainPath = "/event/structure/commonEventHeader/structure/domain/value";
+        String eventSchemaReferencePath = "/event/structure/stndDefinedFields/structure/schemaReference/value";
+
+        Map<String, Object> properties = new HashMap<>();
+        properties.put("vesopenapimanager.validation.schemaMapPath", schemaMapPath);
+        properties.put("vesopenapimanager.validation.eventDomainPath", eventDomainPath);
+        properties.put("vesopenapimanager.validation.eventSchemaReferencePath", eventSchemaReferencePath);
+
+        ConfigurationPropertySource source = new MapConfigurationPropertySource(properties);
+        Binder binder = new Binder(source);
+
+        //when
+        BindResult<ValidatorProperties> result = binder.bind("vesopenapimanager.validation", ValidatorProperties.class);
+
+        //then
+        assertThat(result.isBound()).isTrue();
+        ValidatorProperties config = result.get();
+        assertThat(config.getSchemaMapPath()).isEqualTo(schemaMapPath);
+        assertThat(config.getEventDomainPath()).isEqualTo(eventDomainPath);
+        assertThat(config.getEventSchemaReferencePath()).isEqualTo(eventSchemaReferencePath);
+    }
+}
diff --git a/src/test/java/org/onap/ves/openapi/manager/model/DistributionStatusMessageTest.java b/src/test/java/org/onap/ves/openapi/manager/model/DistributionStatusMessageTest.java
new file mode 100644
index 0000000..e84b919
--- /dev/null
+++ b/src/test/java/org/onap/ves/openapi/manager/model/DistributionStatusMessageTest.java
@@ -0,0 +1,56 @@
+/*
+ * ============LICENSE_START=======================================================
+ * VES-OPENAPI-MANAGER
+ * ================================================================================
+ * Copyright (C) 2021 Nokia. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.ves.openapi.manager.model;
+
+import org.junit.jupiter.api.Test;
+import org.onap.sdc.utils.DistributionStatusEnum;
+
+import java.time.LocalDateTime;
+import java.time.ZoneOffset;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+
+public class DistributionStatusMessageTest {
+    @Test
+    void shouldCreateDistributionStatusMessage() {
+        //given
+        long timestamp = LocalDateTime.now().toInstant(ZoneOffset.UTC).toEpochMilli();
+        String consumerId = "ves-consumer";
+        String artifactUrl = "http://artifact-url:1234/test";
+        String distributionId = "distribution-id";
+
+        //when
+        DistributionStatusMessage distributionStatusMessage = new DistributionStatusMessage(
+                artifactUrl,
+                distributionId,
+                consumerId,
+                timestamp,
+                DistributionStatusEnum.DOWNLOAD_OK);
+
+        //then
+        assertThat(distributionStatusMessage.getArtifactURL()).isEqualTo(artifactUrl);
+        assertThat(distributionStatusMessage.getDistributionID()).isEqualTo(distributionId);
+        assertThat(distributionStatusMessage.getConsumerID()).isEqualTo(consumerId);
+        assertThat(distributionStatusMessage.getTimestamp()).isEqualTo(timestamp);
+        assertThat(distributionStatusMessage.getStatus()).isEqualTo(DistributionStatusEnum.DOWNLOAD_OK);
+    }
+}
diff --git a/src/test/java/org/onap/ves/openapi/manager/service/ServiceFunctionalTest.java b/src/test/java/org/onap/ves/openapi/manager/service/ServiceFunctionalTest.java
new file mode 100644
index 0000000..a3beb4e
--- /dev/null
+++ b/src/test/java/org/onap/ves/openapi/manager/service/ServiceFunctionalTest.java
@@ -0,0 +1,94 @@
+/*
+ * ============LICENSE_START=======================================================
+ * VES-OPENAPI-MANAGER
+ * ================================================================================
+ * Copyright (C) 2021 Nokia. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.ves.openapi.manager.service;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.junit.jupiter.api.Test;
+import org.onap.sdc.api.notification.INotificationData;
+import org.onap.sdc.http.SdcConnectorClient;
+import org.onap.sdc.impl.DistributionClientDownloadResultImpl;
+import org.onap.sdc.impl.DistributionClientImpl;
+import org.onap.sdc.utils.DistributionActionResultEnum;
+import org.onap.ves.openapi.manager.config.DistributionClientConfig;
+import org.onap.ves.openapi.manager.config.ValidatorProperties;
+import org.onap.ves.openapi.manager.service.notification.ArtifactsCollectorStatusSender;
+import org.onap.ves.openapi.manager.service.notification.FinalStatusSender;
+import org.onap.ves.openapi.manager.service.notification.ValidationStatusSender;
+import org.onap.ves.openapi.manager.service.serialization.SchemaMapDeserializer;
+import org.onap.ves.openapi.manager.service.serialization.VesEventsArtifactDeserializer;
+import org.onap.ves.openapi.manager.service.testModel.Service;
+import org.onap.ves.openapi.manager.service.validation.ArtifactsValidator;
+import org.onap.ves.openapi.manager.service.validation.SchemaReferenceValidator;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+class ServiceFunctionalTest {
+
+    private final DistributionClientImpl distributionClient = mock(DistributionClientImpl.class);
+    private final SdcConnectorClient sdcConnectorClient = mock(SdcConnectorClient.class);
+    private final DistributionClientConfig config = mock(DistributionClientConfig.class);
+    private final ValidatorProperties validatorProperties = mock(ValidatorProperties.class);
+
+    private final ObjectMapper objectMapper = new ObjectMapper();
+    private final VesEventsArtifactDeserializer vesEventsArtifactDeserializer = new VesEventsArtifactDeserializer(objectMapper);
+    private final SchemaMapDeserializer schemaMapDeserializer = new SchemaMapDeserializer(objectMapper);
+
+    private final List<ArtifactsValidator> validators = List.of(new SchemaReferenceValidator(vesEventsArtifactDeserializer, schemaMapDeserializer, validatorProperties));
+    private final ArtifactsCollector artifactsCollector = new ArtifactsCollector(sdcConnectorClient);
+    private final ArtifactsCollectorStatusSender artifactsCollectorStatusSender = new ArtifactsCollectorStatusSender(distributionClient);
+    private final ValidationStatusSender validationStatusSender = new ValidationStatusSender(distributionClient);
+    private final FinalStatusSender finalStatusSender = new FinalStatusSender(distributionClient);
+    private final ClientCallback clientCallback = new ClientCallback(validators, artifactsCollector, artifactsCollectorStatusSender, validationStatusSender, finalStatusSender);
+
+    private final INotificationData service = new Service();
+
+
+    @Test
+    void shouldNotThrowExceptionWhenCallingCallbackAndArtifactIsValid() throws IOException {
+        //given
+        byte[] payload = Files.readAllBytes(Paths.get("src/test/resources/ves_artifact_stndDefined_events.yaml"));
+        DistributionClientDownloadResultImpl message = new DistributionClientDownloadResultImpl(
+            DistributionActionResultEnum.SUCCESS, "message", "artifact-name", payload);
+        DistributionClientDownloadResultImpl responseStatus = new DistributionClientDownloadResultImpl(DistributionActionResultEnum.SUCCESS, "OK");
+
+        when(sdcConnectorClient.downloadArtifact(any())).thenReturn(message);
+        when(distributionClient.sendDownloadStatus(any())).thenReturn(responseStatus);
+        when(distributionClient.sendDeploymentStatus(any())).thenReturn(responseStatus);
+        when(distributionClient.getConfiguration()).thenReturn(config);
+        when(distributionClient.sendFinalDistrStatus(any())).thenReturn(responseStatus);
+        when(config.getConsumerID()).thenReturn("consumer-id");
+        when(validatorProperties.getSchemaMapPath()).thenReturn("src/test/resources/test-schema-map.json");
+        when(validatorProperties.getEventDomainPath()).thenReturn("/event/structure/commonEventHeader/structure/domain/value");
+        when(validatorProperties.getEventSchemaReferencePath()).thenReturn("/event/structure/stndDefinedFields/structure/schemaReference/value");
+
+        //when
+        //then
+        assertDoesNotThrow(() -> clientCallback.activateCallback(service));
+    }
+}
diff --git a/src/test/java/org/onap/ves/openapi/manager/service/notification/ArtifactsCollectorStatusSenderTest.java b/src/test/java/org/onap/ves/openapi/manager/service/notification/ArtifactsCollectorStatusSenderTest.java
new file mode 100644
index 0000000..377cc4b
--- /dev/null
+++ b/src/test/java/org/onap/ves/openapi/manager/service/notification/ArtifactsCollectorStatusSenderTest.java
@@ -0,0 +1,118 @@
+/*
+ * ============LICENSE_START=======================================================
+ * VES-OPENAPI-MANAGER
+ * ================================================================================
+ * Copyright (C) 2021 Nokia. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.ves.openapi.manager.service.notification;
+
+import org.junit.jupiter.api.Test;
+import org.onap.sdc.api.consumer.IConfiguration;
+import org.onap.sdc.api.notification.IArtifactInfo;
+import org.onap.sdc.api.results.IDistributionClientResult;
+import org.onap.sdc.impl.DistributionClientImpl;
+import org.onap.sdc.impl.DistributionClientResultImpl;
+import org.onap.sdc.utils.DistributionActionResultEnum;
+import org.onap.sdc.utils.DistributionStatusEnum;
+import org.onap.ves.openapi.manager.config.DistributionClientConfig;
+import org.onap.ves.openapi.manager.model.DistributionStatusMessage;
+import org.onap.ves.openapi.manager.service.testModel.ArtifactInfo;
+
+import java.time.LocalDateTime;
+import java.time.ZoneOffset;
+import java.util.List;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+class ArtifactsCollectorStatusSenderTest {
+
+    private final IConfiguration configuration = mock(IConfiguration.class);
+    private final DistributionClientImpl distributionClient = mock(DistributionClientImpl.class);
+    private final ArtifactsCollectorStatusSender sender = new ArtifactsCollectorStatusSender(distributionClient);
+
+    @Test
+    void shouldSuccessfullySendStatusToEachDownloadedArtifacts() {
+        //given
+        DistributionClientResultImpl expectedResponse = new DistributionClientResultImpl(
+                DistributionActionResultEnum.SUCCESS, "sample-response-message");
+        List<DistributionClientResultImpl> expectedResponses = List.of(expectedResponse, expectedResponse);
+
+        ArtifactInfo artifact = new ArtifactInfo(DistributionClientConfig.VES_EVENTS_ARTIFACT_TYPE);
+        List<IArtifactInfo> artifacts = List.of(artifact, artifact);
+        String consumerId = "ves-consumer";
+
+        when(distributionClient.sendDownloadStatus(any())).thenReturn(expectedResponse);
+        when(distributionClient.getConfiguration()).thenReturn(configuration);
+        when(configuration.getConsumerID()).thenReturn(consumerId);
+
+        //when
+        List<IDistributionClientResult> responses = sender.sendDownloadOk("distribution-id", artifacts);
+
+        //then
+        assertThat(responses).isEqualTo(expectedResponses);
+    }
+
+    @Test
+    void shouldSendDownloadErrorStatusToEachDownloadedArtifacts() {
+        //given
+        DistributionClientResultImpl expectedResponse = new DistributionClientResultImpl(
+                DistributionActionResultEnum.GENERAL_ERROR, "sample-response-message");
+        List<DistributionClientResultImpl> expectedResponses = List.of(expectedResponse, expectedResponse);
+
+        ArtifactInfo artifact = new ArtifactInfo(DistributionClientConfig.VES_EVENTS_ARTIFACT_TYPE);
+        List<IArtifactInfo> artifacts = List.of(artifact, artifact);
+        String consumerId = "ves-consumer";
+        when(distributionClient.sendDownloadStatus(any(),any())).thenReturn(expectedResponse);
+        when(distributionClient.getConfiguration()).thenReturn(configuration);
+        when(configuration.getConsumerID()).thenReturn(consumerId);
+        long timestamp = LocalDateTime.now().toInstant(ZoneOffset.UTC).toEpochMilli();
+
+        DistributionStatusMessage dm = new DistributionStatusMessage(
+                artifact.getArtifactURL(),
+                "distribution-id",
+                distributionClient.getConfiguration().getConsumerID(),
+                timestamp,
+                DistributionStatusEnum.DOWNLOAD_OK);
+
+        //when
+        List<IDistributionClientResult> responses = sender.sendDownloadError("distribution-id", artifacts);
+
+        //then
+        assertThat(responses).isEqualTo(expectedResponses);
+        assertThat(dm.getArtifactURL()).isEqualTo(artifact.getArtifactURL());
+        assertThat(dm.getDistributionID()).isEqualTo("distribution-id");
+        assertThat(dm.getTimestamp()).isEqualTo(timestamp);
+        assertThat(dm.getStatus()).isEqualTo(DistributionStatusEnum.DOWNLOAD_OK);
+    }
+
+    @Test
+    void shouldNotSendAnyStatusWhenNoArtifactsAreDownloaded() {
+        //given
+        List<DistributionClientResultImpl> expectedResponses = List.of();
+        List<IArtifactInfo> artifacts = List.of();
+
+        //when
+        List<IDistributionClientResult> responses = sender.sendDownloadOk("distribution-id", artifacts);
+
+        //then
+        assertThat(responses).isEqualTo(expectedResponses);
+    }
+
+}
\ No newline at end of file
diff --git a/src/test/java/org/onap/ves/openapi/manager/service/notification/ArtifactsCollectorTest.java b/src/test/java/org/onap/ves/openapi/manager/service/notification/ArtifactsCollectorTest.java
new file mode 100644
index 0000000..3da47f7
--- /dev/null
+++ b/src/test/java/org/onap/ves/openapi/manager/service/notification/ArtifactsCollectorTest.java
@@ -0,0 +1,106 @@
+/*
+ * ============LICENSE_START=======================================================
+ * VES-OPENAPI-MANAGER
+ * ================================================================================
+ * Copyright (C) 2021 Nokia. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.ves.openapi.manager.service.notification;
+
+import org.junit.jupiter.api.Test;
+import org.onap.sdc.api.notification.IArtifactInfo;
+import org.onap.sdc.http.SdcConnectorClient;
+import org.onap.sdc.impl.DistributionClientDownloadResultImpl;
+import org.onap.sdc.utils.DistributionActionResultEnum;
+import org.onap.ves.openapi.manager.config.DistributionClientConfig;
+import org.onap.ves.openapi.manager.exceptions.ArtifactException;
+import org.onap.ves.openapi.manager.model.Artifact;
+import org.onap.ves.openapi.manager.service.ArtifactsCollector;
+import org.onap.ves.openapi.manager.service.testModel.ArtifactInfo;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.List;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+class ArtifactsCollectorTest {
+
+    private static final String NOT_VES_ARTIFACT = "NOT_VES_ARTIFACT";
+    private final SdcConnectorClient sdcConnectorClient = mock(SdcConnectorClient.class);
+    private final ArtifactsCollector artifactsCollector = new ArtifactsCollector(sdcConnectorClient);
+
+    @Test
+    void shouldReturnArtifactsContentWhenVESArtifactsAreDistributed() throws IOException {
+        //given
+        IArtifactInfo vesArtifact = new ArtifactInfo(DistributionClientConfig.VES_EVENTS_ARTIFACT_TYPE);
+        List<IArtifactInfo> artifacts = List.of(vesArtifact);
+
+        byte[] payload = getVesArtifactPayload();
+        DistributionClientDownloadResultImpl artifactDownloadResult = new DistributionClientDownloadResultImpl(
+                DistributionActionResultEnum.SUCCESS, "sample-message", "artifact-name",
+                payload);
+        when(sdcConnectorClient.downloadArtifact(vesArtifact)).thenReturn(artifactDownloadResult);
+        List<Artifact> expectedArtifacts = List.of(new Artifact(vesArtifact, payload));
+
+        //when
+        List<Artifact> artifactContents = artifactsCollector.pullArtifacts(artifacts);
+
+        //then
+        assertThat(artifactContents).isEqualTo(expectedArtifacts);
+    }
+
+    @Test
+    void shouldNotReturnArtifactsContentWhenNoVESArtifactsAreDistributed() {
+        //given
+        IArtifactInfo notVesArtifact = new ArtifactInfo(NOT_VES_ARTIFACT);
+        List<IArtifactInfo> artifactsDefinitions = List.of(notVesArtifact);
+        List<Artifact> expectedArtifacts = List.of();
+
+        //when
+        List<Artifact> artifacts = artifactsCollector.pullArtifacts(artifactsDefinitions);
+
+        //then
+        assertThat(artifacts).isEqualTo(expectedArtifacts);
+    }
+
+    @Test
+    void shouldThrowExceptionWhenArtifactNameIsEmpty() {
+        //given
+        IArtifactInfo vesArtifact = new ArtifactInfo(DistributionClientConfig.VES_EVENTS_ARTIFACT_TYPE);
+        List<IArtifactInfo> artifacts = List.of(vesArtifact);
+
+        byte[] payload = new byte[0];
+        DistributionClientDownloadResultImpl artifactDownloadResult = new DistributionClientDownloadResultImpl(
+            DistributionActionResultEnum.SUCCESS, "sample-message", "",
+            payload);
+        when(sdcConnectorClient.downloadArtifact(vesArtifact)).thenReturn(artifactDownloadResult);
+
+        //when then
+        assertThrows(
+            ArtifactException.class,
+            () -> artifactsCollector.pullArtifacts(artifacts)
+        );
+    }
+
+    private byte[] getVesArtifactPayload() throws IOException {
+        return Files.readAllBytes(Paths.get("src/test/resources/ves_artifact_stndDefined_events.yaml"));
+    }
+}
diff --git a/src/test/java/org/onap/ves/openapi/manager/service/notification/ClientCallbackTest.java b/src/test/java/org/onap/ves/openapi/manager/service/notification/ClientCallbackTest.java
new file mode 100644
index 0000000..91dfda7
--- /dev/null
+++ b/src/test/java/org/onap/ves/openapi/manager/service/notification/ClientCallbackTest.java
@@ -0,0 +1,153 @@
+/*
+ * ============LICENSE_START=======================================================
+ * VES-OPENAPI-MANAGER
+ * ================================================================================
+ * Copyright (C) 2021 Nokia. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.ves.openapi.manager.service.notification;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.onap.sdc.api.notification.IArtifactInfo;
+import org.onap.sdc.api.notification.INotificationData;
+import org.onap.ves.openapi.manager.model.Artifact;
+import org.onap.ves.openapi.manager.model.ArtifactValidationResult;
+import org.onap.ves.openapi.manager.service.ArtifactsCollector;
+import org.onap.ves.openapi.manager.service.ClientCallback;
+import org.onap.ves.openapi.manager.service.testModel.Service;
+import org.onap.ves.openapi.manager.service.validation.ArtifactsValidator;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+class ClientCallbackTest {
+
+    private ArtifactsCollector artifactsCollector;
+    private ArtifactsCollectorStatusSender artifactsCollectorStatusSender;
+    private ValidationStatusSender validationStatusSender;
+    private FinalStatusSender finalStatusSender;
+    private ArtifactsValidator artifactsValidator;
+    private List<ArtifactsValidator> validators;
+    private ClientCallback callback;
+
+    @BeforeEach
+    void setUpMocks() {
+        artifactsCollector = mock(ArtifactsCollector.class);
+        artifactsCollectorStatusSender = mock(ArtifactsCollectorStatusSender.class);
+        validationStatusSender = mock(ValidationStatusSender.class);
+        finalStatusSender = mock(FinalStatusSender.class);
+        artifactsValidator = mock(ArtifactsValidator.class);
+        validators = new ArrayList<>();
+        validators.add(artifactsValidator);
+
+        callback = new ClientCallback(validators, artifactsCollector, artifactsCollectorStatusSender,
+                validationStatusSender, finalStatusSender);
+    }
+
+    @Test
+    void shouldNotValidateAnyArtifactsOrSendAnyStatusIfPulledArtifactsListIsEmpty() {
+        testClientCallback(0);
+    }
+
+    @Test
+    void shouldValidateArtifactUsingValidatorAndSendStatusIfPulledArtifactsListContainsOneArtifact() {
+        testClientCallback(1);
+    }
+
+    @Test
+    void shouldValidateArtifactsUsingValidatorAndSendStatusesIfPulledArtifactsListContainsMultipleArtifacts() {
+        testClientCallback(3);
+    }
+
+    @Test
+    void shouldValidateArtifactUsingMultipleValidatorsAndSendStatusIfPulledArtifactsListContainsOneArtifact() {
+        ArtifactsValidator extraArtifactsValidator = mock(ArtifactsValidator.class);
+        testClientCallback(1, extraArtifactsValidator);
+    }
+
+    @Test
+    void shouldValidateArtifactUsingMultipleValidatorsAndSendStatusIfPulledArtifactsListContainsMultipleArtifacts() {
+        ArtifactsValidator extraArtifactsValidator = mock(ArtifactsValidator.class);
+        testClientCallback(3, extraArtifactsValidator);
+    }
+
+    private void testClientCallback(int numberOfPulledArtefacts, ArtifactsValidator... extraValidators) {
+        // given
+        validators.addAll(Arrays.asList(extraValidators));
+        final INotificationData service = new Service(numberOfPulledArtefacts);
+        ExpectedTestParameters testParameters = setupCallbackTest(service);
+
+        // when
+        callback.activateCallback(service);
+
+        // then
+        verifyMockedMethodCalls(testParameters, numberOfPulledArtefacts);
+        for(ArtifactsValidator validator: extraValidators) {
+            verify(validator, times(1)).validate(eq(testParameters.getExpectedPulledArtifacts()));
+        }
+    }
+
+    private ExpectedTestParameters setupCallbackTest(INotificationData service) {
+        final List<IArtifactInfo> expectedArtifactsList = service.getResources().get(0).getArtifacts();
+
+        final List<Artifact> expectedPulledArtifacts = new ArrayList<>();
+        final List<ArtifactValidationResult> expectedValidationResults = new ArrayList<>();
+        for(IArtifactInfo artifactInfo: expectedArtifactsList) {
+            final Artifact artifact = setUpTestArtifact(artifactInfo);
+            expectedPulledArtifacts.add(artifact);
+            final ArtifactValidationResult expectedValidationResult = new ArtifactValidationResult(
+                artifactInfo, true, artifactInfo.getArtifactName() + " validated", artifactsValidator
+            );
+            expectedValidationResults.add(expectedValidationResult);
+        }
+
+        when(artifactsCollector.pullArtifacts(expectedArtifactsList)).thenReturn(expectedPulledArtifacts);
+        when(artifactsValidator.validate(expectedPulledArtifacts)).thenReturn(expectedValidationResults);
+
+        return new ExpectedTestParameters(expectedPulledArtifacts, expectedValidationResults, expectedArtifactsList);
+    }
+
+    private Artifact setUpTestArtifact(IArtifactInfo artifactInfo) {
+        final byte[] expectedArtifactContent = ("test content " + artifactInfo.getArtifactName()).getBytes();
+        return new Artifact(artifactInfo, expectedArtifactContent);
+    }
+
+    private void verifyMockedMethodCalls(ExpectedTestParameters testParameters, int numberOfPulledArtifacts) {
+        verify(artifactsCollector, times(1)).pullArtifacts(eq(testParameters.getExpectedArtifactsList()));
+        verify(artifactsCollectorStatusSender, times(numberOfPulledArtifacts)).sendDownloadOk(any(), eq(testParameters.getExpectedArtifactsList()));
+        verify(validationStatusSender, times(1)).send(any(), eq(testParameters.getExpectedValidationResults()));
+        validators.forEach(validator -> verify(validator, times(1)).validate(eq(testParameters.getExpectedPulledArtifacts())));
+    }
+
+    @Getter
+    @AllArgsConstructor
+    private static class ExpectedTestParameters {
+        final private List<Artifact> expectedPulledArtifacts;
+        final private List<ArtifactValidationResult> expectedValidationResults;
+        final private List<IArtifactInfo> expectedArtifactsList;
+    }
+}
diff --git a/src/test/java/org/onap/ves/openapi/manager/service/notification/ValidationStatusSenderTest.java b/src/test/java/org/onap/ves/openapi/manager/service/notification/ValidationStatusSenderTest.java
new file mode 100644
index 0000000..e4bb504
--- /dev/null
+++ b/src/test/java/org/onap/ves/openapi/manager/service/notification/ValidationStatusSenderTest.java
@@ -0,0 +1,84 @@
+/*
+ * ============LICENSE_START=======================================================
+ * VES-OPENAPI-MANAGER
+ * ================================================================================
+ * Copyright (C) 2021 Nokia. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.ves.openapi.manager.service.notification;
+
+import org.junit.jupiter.api.Test;
+import org.onap.sdc.api.consumer.IConfiguration;
+import org.onap.sdc.api.results.IDistributionClientResult;
+import org.onap.sdc.impl.DistributionClientImpl;
+import org.onap.sdc.impl.DistributionClientResultImpl;
+import org.onap.sdc.utils.DistributionActionResultEnum;
+import org.onap.ves.openapi.manager.config.DistributionClientConfig;
+import org.onap.ves.openapi.manager.model.ArtifactValidationResult;
+import org.onap.ves.openapi.manager.service.testModel.ArtifactInfo;
+import org.onap.ves.openapi.manager.service.validation.ArtifactsValidator;
+import org.onap.ves.openapi.manager.service.validation.SchemaReferenceValidator;
+
+import java.util.List;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+class ValidationStatusSenderTest {
+
+    private final ArtifactsValidator validator = mock(SchemaReferenceValidator.class);
+    private final IConfiguration configuration = mock(IConfiguration.class);
+    private final DistributionClientImpl distributionClient = mock(DistributionClientImpl.class);
+    private final ValidationStatusSender statusSender = new ValidationStatusSender(distributionClient);
+
+    @Test
+    void shouldSendValidationStatusForEachValidationResult() {
+        //given
+        String distributionId = "distribution-id";
+        String consumerId = "ves-consumer";
+
+        DistributionClientResultImpl sendingResult =
+                new DistributionClientResultImpl(DistributionActionResultEnum.SUCCESS, "sample-message");
+        List<IDistributionClientResult> expectedSendingResults = List.of(sendingResult, sendingResult);
+
+        ArtifactInfo artifact = new ArtifactInfo(DistributionClientConfig.VES_EVENTS_ARTIFACT_TYPE);
+        List<ArtifactValidationResult> validationResults = getValidationResults(artifact);
+
+        when(distributionClient.sendDeploymentStatus(any())).thenReturn(sendingResult);
+        when(distributionClient.sendDeploymentStatus(any(), anyString())).thenReturn(sendingResult);
+        when(distributionClient.getConfiguration()).thenReturn(configuration);
+        when(configuration.getConsumerID()).thenReturn(consumerId);
+
+        //when
+        List<IDistributionClientResult> sendingResults = statusSender.send(distributionId, validationResults);
+
+        //then
+        assertThat(sendingResults).isEqualTo(expectedSendingResults);
+
+    }
+
+    private List<ArtifactValidationResult> getValidationResults(ArtifactInfo artifactInfo) {
+        ArtifactValidationResult validationResultValid = new ArtifactValidationResult(artifactInfo, true,
+                "sample-message", validator);
+        ArtifactValidationResult validationResultInvalid = new ArtifactValidationResult(artifactInfo, false,
+                "sample-message", validator);
+        return List.of(validationResultValid, validationResultInvalid);
+    }
+
+}
\ No newline at end of file
diff --git a/src/test/java/org/onap/ves/openapi/manager/service/serialization/SchemaMapDeserializerTest.java b/src/test/java/org/onap/ves/openapi/manager/service/serialization/SchemaMapDeserializerTest.java
new file mode 100644
index 0000000..6b16c89
--- /dev/null
+++ b/src/test/java/org/onap/ves/openapi/manager/service/serialization/SchemaMapDeserializerTest.java
@@ -0,0 +1,84 @@
+/*
+ * ============LICENSE_START=======================================================
+ * VES-OPENAPI-MANAGER
+ * ================================================================================
+ * Copyright (C) 2021 Nokia. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.ves.openapi.manager.service.serialization;
+
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.junit.jupiter.api.Test;
+import org.onap.ves.openapi.manager.model.SchemaMap;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+class SchemaMapDeserializerTest {
+
+    final ObjectMapper objectMapper = new ObjectMapper();
+    private final SchemaMapDeserializer deserializer =
+            new SchemaMapDeserializer(objectMapper);
+
+    @Test
+    void shouldReturnSchemaMapFromFileWhenFileExists() {
+        //given
+        List<String> expectedPublicURLs = getExpectedPublicURLs();
+        List<String> expectedLocalURLs = getExpectedLocalURLs();
+        String schemaMapPath = "src/test/resources/test-schema-map.json";
+
+        //when
+        SchemaMap schemaMap = deserializer.getSchemaMapFromFile(schemaMapPath);
+
+        //then
+        assertThat(schemaMap.getMappings().size()).isEqualTo(3);
+        assertThat(schemaMap.getMappings().stream().map(SchemaMap.Mapping::getPublicURL).collect(Collectors.toList()))
+                .isEqualTo(expectedPublicURLs);
+        assertThat(schemaMap.getMappings().stream().map(SchemaMap.Mapping::getLocalURL).collect(Collectors.toList()))
+                .isEqualTo(expectedLocalURLs);
+    }
+
+    @Test
+    void shouldReturnEmptySchemaMapWhenFileDoesNotExist() {
+        //given
+        String schemaMapPath = "src/test/resources/not-existing-schema-map.yaml";
+
+        //when
+        SchemaMap schemaMap = deserializer.getSchemaMapFromFile(schemaMapPath);
+
+        //then
+        assertThat(schemaMap.getMappings().size()).isZero();
+    }
+
+    private List<String> getExpectedPublicURLs() {
+        return List.of(
+                "https://forge.3gpp.org/rep/sa5/MnS/tree/SA88-Rel16/OpenAPI/PerMeasJobCtlMnS.yaml",
+                "https://forge.3gpp.org/rep/sa5/MnS/tree/SA88-Rel16/OpenAPI/PerThresMonMnS.yaml",
+                "https://forge.3gpp.org/rep/sa5/MnS/tree/SA88-Rel16/OpenAPI/PerfDataStreamingMnS.yaml"
+        );
+    }
+
+    private List<String> getExpectedLocalURLs() {
+        return List.of(
+                "3gpp/rep/sa5/MnS/tree/SA88-Rel16/OpenAPI/PerMeasJobCtlMnS.yaml",
+                "3gpp/rep/sa5/MnS/tree/SA88-Rel16/OpenAPI/PerThresMonMnS.yaml",
+                "3gpp/rep/sa5/MnS/tree/SA88-Rel16/OpenAPI/PerfDataStreamingMnS.yaml"
+        );
+    }
+}
\ No newline at end of file
diff --git a/src/test/java/org/onap/ves/openapi/manager/service/serialization/VesEventsArtifactDeserializerTest.java b/src/test/java/org/onap/ves/openapi/manager/service/serialization/VesEventsArtifactDeserializerTest.java
new file mode 100644
index 0000000..6c35b18
--- /dev/null
+++ b/src/test/java/org/onap/ves/openapi/manager/service/serialization/VesEventsArtifactDeserializerTest.java
@@ -0,0 +1,73 @@
+/*
+ * ============LICENSE_START=======================================================
+ * VES-OPENAPI-MANAGER
+ * ================================================================================
+ * Copyright (C) 2021 Nokia. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.ves.openapi.manager.service.serialization;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.junit.jupiter.api.Test;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.List;
+import java.util.function.Predicate;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+class VesEventsArtifactDeserializerTest {
+
+    private static final String EVENT_DOMAIN_PATH = "/event/structure/commonEventHeader/structure/domain/value";
+    private static final String STND_DEFINED_DOMAIN = "stndDefined";
+    private final ObjectMapper objectMapper = new ObjectMapper();
+    private final VesEventsArtifactDeserializer deserializer = new VesEventsArtifactDeserializer(objectMapper);
+
+
+    @Test
+    void shouldReturnObjectNodesPerEachYamlDocumentWhenByteCodeIsCorrect() throws IOException {
+        //given
+        byte[] bytecode = Files.readAllBytes(Paths.get("src/test/resources/ves_artifact_stndDefined_events.yaml"));
+        int expectedDocuments = 3;
+
+        //when
+        List<ObjectNode> objectNodes = deserializer.deserializeMultiDocumentYaml(bytecode);
+
+        //then
+        assertThat(objectNodes.size()).isEqualTo(expectedDocuments);
+        assertThat(objectNodes).anyMatch(isStndDefinedEvent());
+    }
+
+    @Test
+    void shouldReturnEmptyListWhenInvalidYamlByteCodeIsGiven() {
+        //given
+        byte[] invalidBytecode = new byte[] {'i','n','v','a','l','i', 'd'};
+        //when
+        List<ObjectNode> objectNodes = deserializer.deserializeMultiDocumentYaml(invalidBytecode);
+
+        //then
+        assertThat(objectNodes.size()).isZero();
+    }
+
+    private Predicate<ObjectNode> isStndDefinedEvent() {
+        return event -> event.at(EVENT_DOMAIN_PATH).asText()
+                .equals(STND_DEFINED_DOMAIN);
+    }
+
+}
\ No newline at end of file
diff --git a/src/test/java/org/onap/ves/openapi/manager/service/testModel/ArtifactInfo.java b/src/test/java/org/onap/ves/openapi/manager/service/testModel/ArtifactInfo.java
new file mode 100644
index 0000000..5c7ea56
--- /dev/null
+++ b/src/test/java/org/onap/ves/openapi/manager/service/testModel/ArtifactInfo.java
@@ -0,0 +1,121 @@
+/*
+ * ============LICENSE_START=======================================================
+ * VES-OPENAPI-MANAGER
+ * ================================================================================
+ * Copyright (C) 2021 Nokia. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.ves.openapi.manager.service.testModel;
+
+import org.onap.sdc.api.notification.IArtifactInfo;
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+public class ArtifactInfo implements IArtifactInfo {
+
+    private final String artifactName;
+    private final String artifactType;
+    private final String artifactURL;
+    private String artifactChecksum;
+    private Integer artifactTimeout;
+    private String artifactDescription;
+    private String artifactVersion;
+    private String artifactUUID;
+    private IArtifactInfo generatedArtifact;
+    private List<IArtifactInfo> relatedArtifacts;
+
+    public ArtifactInfo(String artifactType) {
+        this.artifactName = "artifact-name";
+        this.artifactType = artifactType;
+        this.artifactURL = "http://artifact-url:1234/test";
+    }
+
+    private ArtifactInfo(IArtifactInfo iArtifactInfo) {
+        artifactName = iArtifactInfo.getArtifactName();
+        artifactType = iArtifactInfo.getArtifactType();
+        artifactURL = iArtifactInfo.getArtifactURL();
+        artifactChecksum = iArtifactInfo.getArtifactChecksum();
+        artifactDescription = iArtifactInfo.getArtifactDescription();
+        artifactTimeout = iArtifactInfo.getArtifactTimeout();
+        artifactVersion = iArtifactInfo.getArtifactVersion();
+        artifactUUID = iArtifactInfo.getArtifactUUID();
+        generatedArtifact = iArtifactInfo.getGeneratedArtifact();
+        relatedArtifacts = iArtifactInfo.getRelatedArtifacts();
+
+    }
+
+    @Override
+    public String getArtifactName() {
+        return artifactName;
+    }
+
+    @Override
+    public String getArtifactType() {
+        return artifactType;
+    }
+
+    @Override
+    public String getArtifactURL() {
+        return artifactURL;
+    }
+
+    @Override
+    public String getArtifactChecksum() {
+        return artifactChecksum;
+    }
+
+    @Override
+    public Integer getArtifactTimeout() {
+        return artifactTimeout;
+    }
+
+    @Override
+    public String getArtifactDescription() {
+        return artifactDescription;
+    }
+
+    @Override
+    public String getArtifactVersion() {
+        return artifactVersion;
+    }
+
+    @Override
+    public String getArtifactUUID() {
+        return artifactUUID;
+    }
+
+    @Override
+    public IArtifactInfo getGeneratedArtifact() {
+        return generatedArtifact;
+    }
+
+    @Override
+    public List<IArtifactInfo> getRelatedArtifacts() {
+        return relatedArtifacts;
+    }
+
+    public static List<ArtifactInfo> convertToArtifactInfoImpl(List<IArtifactInfo> list) {
+        List<ArtifactInfo> ret = new ArrayList<>();
+        if (list != null) {
+            for (IArtifactInfo artifactInfo : list) {
+                ret.add(new ArtifactInfo(artifactInfo));
+            }
+        }
+        return ret;
+    }
+}
diff --git a/src/test/java/org/onap/ves/openapi/manager/service/testModel/Resource.java b/src/test/java/org/onap/ves/openapi/manager/service/testModel/Resource.java
new file mode 100644
index 0000000..130de6f
--- /dev/null
+++ b/src/test/java/org/onap/ves/openapi/manager/service/testModel/Resource.java
@@ -0,0 +1,161 @@
+/*
+ * ============LICENSE_START=======================================================
+ * VES-OPENAPI-MANAGER
+ * ================================================================================
+ * Copyright (C) 2021 Nokia. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.ves.openapi.manager.service.testModel;
+
+import org.onap.sdc.api.notification.IArtifactInfo;
+import org.onap.sdc.api.notification.IResourceInstance;
+import org.onap.ves.openapi.manager.config.DistributionClientConfig;
+
+import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+public class Resource implements IResourceInstance {
+
+    private String resourceInstanceName;
+    private String resourceCustomizationUUID;
+    private String resourceName;
+    private String resourceVersion;
+    private String resourceType;
+    private String resourceUUID;
+    private String resourceInvariantUUID;
+    private String category;
+    private String subcategory;
+    private List<ArtifactInfo> artifacts;
+
+    public Resource(int numberOfArtifacts) {
+        this.artifacts = Stream.generate(() -> new ArtifactInfo(DistributionClientConfig.VES_EVENTS_ARTIFACT_TYPE))
+                .limit(numberOfArtifacts)
+                .collect(Collectors.toList());
+    }
+
+    public Resource() {
+        this.artifacts = List.of(new ArtifactInfo(DistributionClientConfig.VES_EVENTS_ARTIFACT_TYPE));
+    }
+
+    private Resource(IResourceInstance resourceInstance) {
+        resourceInstanceName = resourceInstance.getResourceInstanceName();
+        resourceCustomizationUUID = resourceInstance.getResourceCustomizationUUID();
+        resourceName = resourceInstance.getResourceName();
+        resourceVersion = resourceInstance.getResourceVersion();
+        resourceType = resourceInstance.getResourceType();
+        resourceUUID = resourceInstance.getResourceUUID();
+        resourceInvariantUUID = resourceInstance.getResourceInvariantUUID();
+        category = resourceInstance.getCategory();
+        subcategory = resourceInstance.getSubcategory();
+        artifacts = ArtifactInfo.convertToArtifactInfoImpl(resourceInstance.getArtifacts());
+    }
+
+    public static List<Resource> convertToJsonContainer(List<IResourceInstance> resources) {
+        return resources.stream().map(Resource::new).collect(Collectors.toList());
+    }
+
+    @Override
+    public String getResourceInstanceName() {
+        return resourceInstanceName;
+    }
+
+    public void setResourceInstanceName(String resourceInstanceName) {
+        this.resourceInstanceName = resourceInstanceName;
+    }
+
+    @Override
+    public String getResourceName() {
+        return resourceName;
+    }
+
+    public void setResourceName(String resourceName) {
+        this.resourceName = resourceName;
+    }
+
+    @Override
+    public String getResourceVersion() {
+        return resourceVersion;
+    }
+
+    public void setResourceVersion(String resourceVersion) {
+        this.resourceVersion = resourceVersion;
+    }
+
+    @Override
+    public String getResourceType() {
+        return resourceType;
+    }
+
+    public void setResourceType(String resourceType) {
+        this.resourceType = resourceType;
+    }
+
+    @Override
+    public String getResourceUUID() {
+        return resourceUUID;
+    }
+
+    public void setResourceUUID(String resourceUUID) {
+        this.resourceUUID = resourceUUID;
+    }
+
+    @Override
+    public List<IArtifactInfo> getArtifacts() {
+        return List.copyOf(artifacts);
+    }
+
+    public void setArtifacts(List<ArtifactInfo> artifacts) {
+        this.artifacts = artifacts;
+    }
+
+    public List<ArtifactInfo> getArtifactsImpl() {
+        return artifacts;
+    }
+
+    @Override
+    public String getResourceInvariantUUID() {
+        return resourceInvariantUUID;
+    }
+
+    public void setResourceInvariantUUID(String resourceInvariantUUID) {
+        this.resourceInvariantUUID = resourceInvariantUUID;
+    }
+
+    public String getResourceCustomizationUUID() {
+        return resourceCustomizationUUID;
+    }
+
+    public void setResourceCustomizationUUID(String resourceCustomizationUUID) {
+        this.resourceCustomizationUUID = resourceCustomizationUUID;
+    }
+
+    public String getCategory() {
+        return category;
+    }
+
+    public void setCategory(String category) {
+        this.category = category;
+    }
+
+    public String getSubcategory() {
+        return subcategory;
+    }
+
+    public void setSubcategory(String subcategory) {
+        this.subcategory = subcategory;
+    }
+}
diff --git a/src/test/java/org/onap/ves/openapi/manager/service/testModel/Service.java b/src/test/java/org/onap/ves/openapi/manager/service/testModel/Service.java
new file mode 100644
index 0000000..0dae772
--- /dev/null
+++ b/src/test/java/org/onap/ves/openapi/manager/service/testModel/Service.java
@@ -0,0 +1,136 @@
+/*
+ * ============LICENSE_START=======================================================
+ * VES-OPENAPI-MANAGER
+ * ================================================================================
+ * Copyright (C) 2021 Nokia. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.ves.openapi.manager.service.testModel;
+
+import org.onap.sdc.api.notification.IArtifactInfo;
+import org.onap.sdc.api.notification.INotificationData;
+import org.onap.sdc.api.notification.IResourceInstance;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class Service implements INotificationData {
+
+    private String distributionID;
+    private String serviceName;
+    private String serviceVersion;
+    private String serviceUUID;
+    private String serviceDescription;
+    private String serviceInvariantUUID;
+    private List<Resource> resources;
+    private List<ArtifactInfo> serviceArtifacts;
+    private String workloadContext;
+
+    public Service(int numberOfArtifacts) {
+        this.resources = List.of(new Resource(numberOfArtifacts));
+        this.serviceUUID = "UUID";
+        this.serviceName = "testService";
+    }
+
+    public Service() {
+        this.resources = List.of(new Resource());
+        this.serviceUUID = "UUID";
+        this.serviceName = "testService";
+    }
+
+    @Override
+    public String getDistributionID() {
+        return distributionID;
+    }
+
+    @Override
+    public String getServiceName() {
+        return serviceName;
+    }
+
+    @Override
+    public String getServiceVersion() {
+        return serviceVersion;
+    }
+
+    @Override
+    public String getServiceUUID() {
+        return serviceUUID;
+    }
+
+    public String getServiceDescription() {
+        return serviceDescription;
+    }
+
+    @Override
+    public String getWorkloadContext() {
+        return workloadContext;
+    }
+
+    @Override
+    public void setWorkloadContext(String workloadContext) {
+        this.workloadContext = workloadContext;
+    }
+
+    @Override
+    public String toString() {
+        return "NotificationDataImpl [distributionID=" + distributionID + ", serviceName=" + serviceName
+                + ", serviceVersion=" + serviceVersion + ", serviceUUID=" + serviceUUID + ", serviceDescription="
+                + serviceDescription + ", serviceInvariantUUID=" + serviceInvariantUUID + ", resources=" + resources
+                + ", serviceArtifacts=" + serviceArtifacts + ", workloadContext=" + workloadContext + "]";
+    }
+
+    @Override
+    public List<IResourceInstance> getResources() {
+        return List.copyOf(resources);
+    }
+
+    @Override
+    public List<IArtifactInfo> getServiceArtifacts() {
+        return List.copyOf(serviceArtifacts);
+    }
+
+    @Override
+    public String getServiceInvariantUUID() {
+        return serviceInvariantUUID;
+    }
+
+    @Override
+    public IArtifactInfo getArtifactMetadataByUUID(String artifactUUID) {
+        IArtifactInfo ret = findArtifactInfoByUUID(artifactUUID, serviceArtifacts);
+        if (ret == null && resources != null) {
+            for (Resource currResourceInstance : resources) {
+                ret = findArtifactInfoByUUID(artifactUUID, currResourceInstance.getArtifactsImpl());
+                if (ret != null) {
+                    break;
+                }
+            }
+        }
+        return ret;
+    }
+
+    private IArtifactInfo findArtifactInfoByUUID(String artifactUUID, List<ArtifactInfo> listToCheck) {
+        IArtifactInfo ret = null;
+        if (listToCheck != null) {
+            for (IArtifactInfo curr : listToCheck) {
+                if (curr.getArtifactUUID().equals(artifactUUID)) {
+                    ret = curr;
+                    break;
+                }
+            }
+        }
+        return ret;
+    }
+}
diff --git a/src/test/java/org/onap/ves/openapi/manager/service/validation/SchemaReferenceValidatorTest.java b/src/test/java/org/onap/ves/openapi/manager/service/validation/SchemaReferenceValidatorTest.java
new file mode 100644
index 0000000..fc17564
--- /dev/null
+++ b/src/test/java/org/onap/ves/openapi/manager/service/validation/SchemaReferenceValidatorTest.java
@@ -0,0 +1,120 @@
+/*
+ * ============LICENSE_START=======================================================
+ * VES-OPENAPI-MANAGER
+ * ================================================================================
+ * Copyright (C) 2021 Nokia. All rights reserved.
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.ves.openapi.manager.service.validation;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.apache.logging.log4j.util.Strings;
+import org.junit.jupiter.api.Test;
+import org.onap.sdc.api.notification.IArtifactInfo;
+import org.onap.ves.openapi.manager.config.DistributionClientConfig;
+import org.onap.ves.openapi.manager.config.ValidatorProperties;
+import org.onap.ves.openapi.manager.model.Artifact;
+import org.onap.ves.openapi.manager.model.ArtifactValidationResult;
+import org.onap.ves.openapi.manager.service.serialization.SchemaMapDeserializer;
+import org.onap.ves.openapi.manager.service.serialization.VesEventsArtifactDeserializer;
+import org.onap.ves.openapi.manager.service.testModel.ArtifactInfo;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+class SchemaReferenceValidatorTest {
+
+    private final ObjectMapper objectMapper = new ObjectMapper();
+    private final VesEventsArtifactDeserializer vesEventsArtifactDeserializer =
+            new VesEventsArtifactDeserializer(objectMapper);
+    private final SchemaMapDeserializer schemaMapDeserializer = new SchemaMapDeserializer(objectMapper);
+    private final String schemaMapPath = "src/test/resources/test-schema-map.json";
+    private final String eventDomain = "/event/structure/commonEventHeader/structure/domain/value";
+    private final String schemaReference = "/event/structure/stndDefinedFields/structure/schemaReference/value";
+    private final ValidatorProperties validatorProperties = mock(ValidatorProperties.class);
+
+    private final SchemaReferenceValidator validator = new SchemaReferenceValidator(vesEventsArtifactDeserializer,
+            schemaMapDeserializer, validatorProperties);
+
+    @Test
+    void shouldReturnSuccessValidationResultWhenValidVesArtifactIsGiven() throws IOException {
+        //given
+        ArtifactInfo artifactInfo = new ArtifactInfo(DistributionClientConfig.VES_EVENTS_ARTIFACT_TYPE);
+        List<Artifact> artifacts = getArtifacts(artifactInfo,
+                "src/test/resources/ves_artifact_stndDefined_events.yaml");
+        List<ArtifactValidationResult> expectedValidationResults =
+                getExpectedResults(artifactInfo, true, Strings.EMPTY);
+
+        when(validatorProperties.getSchemaMapPath()).thenReturn(schemaMapPath);
+        when(validatorProperties.getEventDomainPath()).thenReturn(eventDomain);
+        when(validatorProperties.getEventSchemaReferencePath()).thenReturn(schemaReference);
+
+        //when
+        List<ArtifactValidationResult> validationResults = validator.validate(artifacts);
+
+        //then
+        assertThat(validationResults).isEqualTo(expectedValidationResults);
+    }
+
+    @Test
+    void shouldReturnFailedValidationResultWhenInvalidVesArtifactIsGiven() throws IOException {
+        //given
+        ArtifactInfo artifactInfo = new ArtifactInfo(DistributionClientConfig.VES_EVENTS_ARTIFACT_TYPE);
+        List<Artifact> artifacts = getArtifacts(artifactInfo,
+                "src/test/resources/ves_artifact_invalid_stndDefined_events.yaml");
+        List<ArtifactValidationResult> expectedValidationResults = getExpectedResults(artifactInfo, false,
+                SchemaReferenceValidator.SCHEMA_REFERENCE_ERROR_MESSAGE);
+
+        when(validatorProperties.getSchemaMapPath()).thenReturn(schemaMapPath);
+        when(validatorProperties.getEventDomainPath()).thenReturn(eventDomain);
+        when(validatorProperties.getEventSchemaReferencePath()).thenReturn(schemaReference);
+
+        //when
+        List<ArtifactValidationResult> validationResults = validator.validate(artifacts);
+        List<String> validationResultsMessages =
+                validationResults.stream()
+                        .map(result -> SchemaReferenceValidator.SCHEMA_REFERENCE_ERROR_MESSAGE)
+                        .collect(Collectors.toList());
+
+        //then
+        assertThat(validationResults).usingElementComparatorIgnoringFields("message")
+                .isEqualTo(expectedValidationResults);
+        assertThat(validationResultsMessages)
+                .isNotEmpty()
+                .allMatch(message -> message.contains(SchemaReferenceValidator.SCHEMA_REFERENCE_ERROR_MESSAGE));
+    }
+
+    private List<Artifact> getArtifacts(IArtifactInfo artifactInfo, String artifactFilePath) throws IOException {
+        Path path = Paths.get(artifactFilePath);
+        byte[] artifactByteCode = Files.readAllBytes(path);
+        Artifact artifact = new Artifact(artifactInfo, artifactByteCode);
+        return List.of(artifact);
+    }
+
+    private List<ArtifactValidationResult> getExpectedResults(IArtifactInfo artifactInfo, boolean isValid, String message) {
+        ArtifactValidationResult result = new ArtifactValidationResult(artifactInfo, isValid,
+                message, validator);
+        return List.of(result);
+    }
+}
\ No newline at end of file
diff --git a/src/test/resources/test-schema-map.json b/src/test/resources/test-schema-map.json
new file mode 100644
index 0000000..2317f9f
--- /dev/null
+++ b/src/test/resources/test-schema-map.json
@@ -0,0 +1,14 @@
+[
+  {
+    "publicURL": "https://forge.3gpp.org/rep/sa5/MnS/tree/SA88-Rel16/OpenAPI/PerMeasJobCtlMnS.yaml",
+    "localURL": "3gpp/rep/sa5/MnS/tree/SA88-Rel16/OpenAPI/PerMeasJobCtlMnS.yaml"
+  },
+  {
+    "publicURL": "https://forge.3gpp.org/rep/sa5/MnS/tree/SA88-Rel16/OpenAPI/PerThresMonMnS.yaml",
+    "localURL": "3gpp/rep/sa5/MnS/tree/SA88-Rel16/OpenAPI/PerThresMonMnS.yaml"
+  },
+  {
+    "publicURL": "https://forge.3gpp.org/rep/sa5/MnS/tree/SA88-Rel16/OpenAPI/PerfDataStreamingMnS.yaml",
+    "localURL": "3gpp/rep/sa5/MnS/tree/SA88-Rel16/OpenAPI/PerfDataStreamingMnS.yaml"
+  }
+]
\ No newline at end of file
diff --git a/src/test/resources/ves_artifact_invalid_stndDefined_events.yaml b/src/test/resources/ves_artifact_invalid_stndDefined_events.yaml
new file mode 100644
index 0000000..08d4716
--- /dev/null
+++ b/src/test/resources/ves_artifact_invalid_stndDefined_events.yaml
@@ -0,0 +1,107 @@
+---
+event:
+  presence: required
+  action: [ any, any, null, null, null ]
+  comment: "
+        ALARM NAME: LogHasReachedFullCapacity,
+        ALARM DESCRIPTION: 'Log Has Reached Full Capacity',
+        ALARM EFFECT: 'See alarm OPI x/2223-ABC 123 4567/1 Uen',
+        MANAGED OBJECT CLASSES: Log,
+        EVENT TYPE: 'PROCESSINGERRORALARM',
+        PROBABLE CAUSE: 'FileError',
+        PROPOSED REPAIR ACTIONS: 'See alarm OPI x/3334-ABC 123 4567/1 Uen',
+        CLEARING TYPE: Automatic
+    "
+  structure:
+    commonEventHeader:
+      presence: required
+      structure:
+        version: {presence: required, value: 4.0.1}
+        vesEventListenerVersion: {presence: required, value: 7.0.1}
+        domain: {presence: required, value: fault}
+        eventName: {presence: required, value: Fault_MyPnf-Acme_LogHasReachedFullCapacity}
+        eventId: {presence: required}
+        sequence: {presence: required}
+        priority: {presence: required, value: Normal}
+        reportingEntityName: {presence: required}
+        sourceName: {presence: required}
+        nfVendorName: {presence: required, value: Acme}
+        startEpochMicrosec: {presence: required}
+        lastEpochMicrosec: {presence: required}
+        timeZoneOffset: {presence: required}
+    faultFields:
+      presence: required
+      structure:
+        faultFieldsVersion: {presence: required, value: 4.0}
+        alarmCondition: {presence: required, value: 'LogHasReachedFullCapacity'}
+        eventCategory: {presence: required, value: 'PROCESSINGERRORALARM'}
+        eventSourceType: {presence: required}
+        specificProblem: {presence: required, value: 'Log Has Reached Full Capacity'}
+        eventSeverity: {presence: required}
+        vfStatus: {presence: required, value: Active}
+        alarmAdditionalInformation: {presence: required, structure: {
+          keyValuePair: {presence: required, structure: {key: {presence: required, value: source},value: {presence: required}}},
+          keyValuePair: {presence: required, structure: {key: {presence: required, value: probableCause},value: {presence: required, value: 'FileError'}}},
+          keyValuePair: {presence: required, structure: {key: {presence: required, value: additionalText},value: {presence: optional}}},
+          keyValuePair: {presence: required, structure: {key: {presence: required, value: additionalInfo},value: {presence: optional}}}}
+        }
+...
+---
+event:
+  presence: required
+  comment: "stndDefined event to support 3GPP FaultSupervision NotifyNewAlarm notification"
+  structure:
+    commonEventHeader:
+      presence: required
+      structure:
+        domain: {presence: required, value: stndDefined}
+        eventName: {presence: required, value: stndDefined-gNB-Nokia-Notification}
+        priority: {presence: required, value: Normal}
+        eventId: {presence: required}
+        reportingEntityId: {presence: required}
+        reportingEntityName: {presence: required}
+        sequence: {presence: required, value: 0}
+        sourceId: {presence: required}
+        sourceName: {presence: required}
+        version: {presence: required, value: 4.1}
+        vesEventListenerVersion: {presence: required, value: 7.2}
+        startEpochMicrosec: {presence: required}
+        lastEpochMicrosec: {presence: required}
+        stndDefinedNamespace: {presence: required, value: "3GPP-FaultSupervision"}
+    stndDefinedFields:
+      presence: required
+      structure:
+        schemaReference: { presence: required, value: "https://forge.3gpp.org/rep/invalid" }
+        data: {presence: required}
+        stndDefinedFieldsVersion: {presence: required, value: "1.0"}
+
+...
+---
+event:
+  presence: required
+  comment: "stndDefined event to support 3GPP FaultSupervision NotifyNewAlarm notification"
+  structure:
+    commonEventHeader:
+      presence: required
+      structure:
+        domain: {presence: required, value: stndDefined}
+        eventName: {presence: required, value: stndDefined-gNB-Nokia-Notification}
+        priority: {presence: required, value: Normal}
+        eventId: {presence: required}
+        reportingEntityId: {presence: required}
+        reportingEntityName: {presence: required}
+        sequence: {presence: required, value: 0}
+        sourceId: {presence: required}
+        sourceName: {presence: required}
+        version: {presence: required, value: 4.1}
+        vesEventListenerVersion: {presence: required, value: 7.2}
+        startEpochMicrosec: {presence: required}
+        lastEpochMicrosec: {presence: required}
+        stndDefinedNamespace: {presence: required, value: "3GPP-FaultSupervision"}
+    stndDefinedFields:
+      presence: required
+      structure:
+        schemaReference: { presence: required, value: ["https://forge.3gpp.org/rep/sa5/MnS/tree/another_invalid.yaml", "https://forge.3gpp.org/rep/sa5/MnS/tree/SA88-Rel16/OpenAPI/PerfDataStreamingMnS.yaml"] }
+        data: {presence: required}
+        stndDefinedFieldsVersion: {presence: required, value: "1.0"}
+...
\ No newline at end of file
diff --git a/src/test/resources/ves_artifact_stndDefined_events.yaml b/src/test/resources/ves_artifact_stndDefined_events.yaml
new file mode 100644
index 0000000..11f805b
--- /dev/null
+++ b/src/test/resources/ves_artifact_stndDefined_events.yaml
@@ -0,0 +1,107 @@
+---
+event:
+  presence: required
+  action: [ any, any, null, null, null ]
+  comment: "
+        ALARM NAME: LogHasReachedFullCapacity,
+        ALARM DESCRIPTION: 'Log Has Reached Full Capacity',
+        ALARM EFFECT: 'See alarm OPI x/2223-ABC 123 4567/1 Uen',
+        MANAGED OBJECT CLASSES: Log,
+        EVENT TYPE: 'PROCESSINGERRORALARM',
+        PROBABLE CAUSE: 'FileError',
+        PROPOSED REPAIR ACTIONS: 'See alarm OPI x/3334-ABC 123 4567/1 Uen',
+        CLEARING TYPE: Automatic
+    "
+  structure:
+    commonEventHeader:
+      presence: required
+      structure:
+        version: {presence: required, value: 4.0.1}
+        vesEventListenerVersion: {presence: required, value: 7.0.1}
+        domain: {presence: required, value: fault}
+        eventName: {presence: required, value: Fault_MyPnf-Acme_LogHasReachedFullCapacity}
+        eventId: {presence: required}
+        sequence: {presence: required}
+        priority: {presence: required, value: Normal}
+        reportingEntityName: {presence: required}
+        sourceName: {presence: required}
+        nfVendorName: {presence: required, value: Acme}
+        startEpochMicrosec: {presence: required}
+        lastEpochMicrosec: {presence: required}
+        timeZoneOffset: {presence: required}
+    faultFields:
+      presence: required
+      structure:
+        faultFieldsVersion: {presence: required, value: 4.0}
+        alarmCondition: {presence: required, value: 'LogHasReachedFullCapacity'}
+        eventCategory: {presence: required, value: 'PROCESSINGERRORALARM'}
+        eventSourceType: {presence: required}
+        specificProblem: {presence: required, value: 'Log Has Reached Full Capacity'}
+        eventSeverity: {presence: required}
+        vfStatus: {presence: required, value: Active}
+        alarmAdditionalInformation: {presence: required, structure: {
+          keyValuePair: {presence: required, structure: {key: {presence: required, value: source},value: {presence: required}}},
+          keyValuePair: {presence: required, structure: {key: {presence: required, value: probableCause},value: {presence: required, value: 'FileError'}}},
+          keyValuePair: {presence: required, structure: {key: {presence: required, value: additionalText},value: {presence: optional}}},
+          keyValuePair: {presence: required, structure: {key: {presence: required, value: additionalInfo},value: {presence: optional}}}}
+        }
+...
+---
+event:
+  presence: required
+  comment: "stndDefined event to support 3GPP FaultSupervision NotifyNewAlarm notification"
+  structure:
+    commonEventHeader:
+      presence: required
+      structure:
+        domain: {presence: required, value: stndDefined}
+        eventName: {presence: required, value: stndDefined-gNB-Nokia-Notification}
+        priority: {presence: required, value: Normal}
+        eventId: {presence: required}
+        reportingEntityId: {presence: required}
+        reportingEntityName: {presence: required}
+        sequence: {presence: required, value: 0}
+        sourceId: {presence: required}
+        sourceName: {presence: required}
+        version: {presence: required, value: 4.1}
+        vesEventListenerVersion: {presence: required, value: 7.2}
+        startEpochMicrosec: {presence: required}
+        lastEpochMicrosec: {presence: required}
+        stndDefinedNamespace: {presence: required, value: "3GPP-FaultSupervision"}
+    stndDefinedFields:
+      presence: required
+      structure:
+        schemaReference: { presence: required, value: "https://forge.3gpp.org/rep/sa5/MnS/tree/SA88-Rel16/OpenAPI/PerMeasJobCtlMnS.yaml" }
+        data: {presence: required}
+        stndDefinedFieldsVersion: {presence: required, value: "1.0"}
+
+...
+---
+event:
+  presence: required
+  comment: "stndDefined event to support 3GPP FaultSupervision NotifyNewAlarm notification"
+  structure:
+    commonEventHeader:
+      presence: required
+      structure:
+        domain: {presence: required, value: stndDefined}
+        eventName: {presence: required, value: stndDefined-gNB-Nokia-Notification}
+        priority: {presence: required, value: Normal}
+        eventId: {presence: required}
+        reportingEntityId: {presence: required}
+        reportingEntityName: {presence: required}
+        sequence: {presence: required, value: 0}
+        sourceId: {presence: required}
+        sourceName: {presence: required}
+        version: {presence: required, value: 4.1}
+        vesEventListenerVersion: {presence: required, value: 7.2}
+        startEpochMicrosec: {presence: required}
+        lastEpochMicrosec: {presence: required}
+        stndDefinedNamespace: {presence: required, value: "3GPP-FaultSupervision"}
+    stndDefinedFields:
+      presence: required
+      structure:
+        schemaReference: { presence: required, value: ["https://forge.3gpp.org/rep/sa5/MnS/tree/SA88-Rel16/OpenAPI/PerThresMonMnS.yaml", "https://forge.3gpp.org/rep/sa5/MnS/tree/SA88-Rel16/OpenAPI/PerfDataStreamingMnS.yaml"] }
+        data: {presence: required}
+        stndDefinedFieldsVersion: {presence: required, value: "1.0"}
+...
\ No newline at end of file
diff --git a/version.properties b/version.properties
new file mode 100644
index 0000000..32acbf9
--- /dev/null
+++ b/version.properties
@@ -0,0 +1,6 @@
+major=1
+minor=0
+patch=0
+base_version=${major}.${minor}.${patch}
+release_version=${base_version}
+snapshot_version=${base_version}-SNAPSHOT
\ No newline at end of file