# Eclipse
.checkstyle
+.classpath
+target/
.sts4-cache
.project
.settings
.pydevproject
infer-out/
-.vscode
\ No newline at end of file
+.vscode
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<projectDescription>
- <name>docs</name>
- <comment></comment>
- <projects>
- </projects>
- <buildSpec>
- </buildSpec>
- <natures>
- </natures>
-</projectDescription>
:depth: 3
:local:
-The Non-RT RIC consists of two parts, described in the sections below:
+The Non-RT RIC consists of three parts, described in the sections below:
* The Policy Agent
* The SDNC A1 Controller
+ * The rAPP Catalogue
Policy Agent
See the README.md file in the nonrtric/sdnc-a1-controller repo for info about how to use it.
+rAPP Catalogue
+==============
+
+The Non RT-RIC Service Catalogue provides a way for services to register themselves for other services to discover.
+
+See `RAC API <./rac-api.html>`_ for how to use the API.
+
+.. |swagger-icon| image:: ./images/swagger.png
+ :width: 40px
+
+.. |yaml-icon| image:: ./images/yaml_logo.png
+ :width: 40px
+
+
+.. csv-table::
+ :header: "API name", "|swagger-icon|", "|yaml-icon|"
+ :widths: 10,5, 5
+
+ "RAC API", ":download:`link <../r-app-catalogue/api/rac-api.json>`", ":download:`link <../r-app-catalogue/api/rac-api.yaml>`"
+
Complementary tools
===================
linkcheck_ignore = [
'http://localhost.*',
'http://127.0.0.1.*',
- 'https://gerrit.o-ran-sc.org.*'
+ 'https://gerrit.o-ran-sc.org.*',
+ './rac-api.html' #Generated file that doesn't exist at link check.
]
+extensions = ['sphinxcontrib.redoc', 'sphinx.ext.intersphinx',]
+
+redoc = [
+ {
+ 'name': 'RAC API',
+ 'page': 'rac-api',
+ 'spec': '../r-app-catalogue/api/rac-api.json',
+ 'embed': True,
+ }
+ ]
+
+redoc_uri = 'https://cdn.jsdelivr.net/npm/redoc@next/bundles/redoc.standalone.js'
+
#intershpinx mapping with other projects
intersphinx_mapping = {}
-sphinx
-sphinx-rtd-theme
-sphinxcontrib-httpdomain
-recommonmark
-lfdocs-conf
+tox
+Sphinx>=2,<4
+doc8
+docutils
+setuptools
+six
+sphinx_rtd_theme>=0.4.3
+sphinxcontrib-needs>=0.2.3
+sphinxcontrib-swaggerdoc
+sphinx_bootstrap_theme
+sphinxcontrib-redoc
+lfdocs-conf
\ No newline at end of file
{
"basePath": "/",
"paths": {
- "/producer_simulator/supervision": {"get": {
- "summary": "Producer supervision",
+ "/producer_simulator/ei_job": {"post": {
+ "summary": "Callback for EI job creation",
"deprecated": false,
"produces": ["application/json"],
- "operationId": "producerSupervisionUsingGET",
+ "operationId": "jobCreatedCallbackUsingPOST",
"responses": {
- "200": {
- "schema": {"type": "string"},
- "description": "OK"
- },
+ "200": {"description": "OK"},
+ "201": {"description": "Created"},
"401": {"description": "Unauthorized"},
"403": {"description": "Forbidden"},
"404": {"description": "Not Found"}
},
- "tags": ["Producer Callbacks"]
+ "parameters": [{
+ "schema": {"$ref": "#/definitions/producer_ei_job_request"},
+ "in": "body",
+ "name": "request",
+ "description": "request",
+ "required": true
+ }],
+ "tags": ["Producer Callbacks"],
+ "consumes": ["application/json"]
}},
"/A1-EI/v1/eitypes/{eiTypeId}": {"get": {
"summary": "Individual EI type",
}],
"tags": ["A1-EI (enrichment information)"]
}},
+ "/consumer_simulator/eijobs/{eiJobId}/status": {"post": {
+ "summary": "Callback for EI job status",
+ "deprecated": false,
+ "produces": ["application/json"],
+ "operationId": "jobStatusCallbackUsingPOST",
+ "responses": {
+ "200": {"description": "OK"},
+ "201": {"description": "Created"},
+ "401": {"description": "Unauthorized"},
+ "403": {"description": "Forbidden"},
+ "404": {"description": "Not Found"}
+ },
+ "parameters": [
+ {
+ "in": "path",
+ "name": "eiJobId",
+ "description": "eiJobId",
+ "type": "string",
+ "required": true
+ },
+ {
+ "schema": {"$ref": "#/definitions/EiJobStatusObject"},
+ "in": "body",
+ "name": "status",
+ "description": "status",
+ "required": true
+ }
+ ],
+ "tags": ["Consumer Callbacks"],
+ "consumes": ["application/json"]
+ }},
"/ei-producer/v1/eitypes": {"get": {
"summary": "EI type identifiers",
"deprecated": false,
},
"tags": ["A1-EI (enrichment information)"]
}},
- "/producer_simulator/job_deleted": {"post": {
- "summary": "Callback for EI job deletion",
- "deprecated": false,
- "produces": ["application/json"],
- "operationId": "jobDeletedCallbackUsingPOST",
- "responses": {
- "200": {"description": "OK"},
- "201": {"description": "Created"},
- "401": {"description": "Unauthorized"},
- "403": {"description": "Forbidden"},
- "404": {"description": "Not Found"}
- },
- "parameters": [{
- "schema": {"$ref": "#/definitions/producer_ei_job_request"},
- "in": "body",
- "name": "request",
- "description": "request",
- "required": true
- }],
- "tags": ["Producer Callbacks"],
- "consumes": ["application/json"]
- }},
"/ei-producer/v1/eiproducers/{eiProducerId}/status": {"get": {
"summary": "EI producer status",
"deprecated": false,
}],
"tags": ["Enrichment Data Producer API"]
}},
+ "/producer_simulator/ei_job/{eiJobId}": {"delete": {
+ "summary": "Callback for EI job deletion",
+ "deprecated": false,
+ "produces": ["application/json"],
+ "operationId": "jobDeletedCallbackUsingDELETE",
+ "responses": {
+ "200": {"description": "OK"},
+ "401": {"description": "Unauthorized"},
+ "204": {"description": "No Content"},
+ "403": {"description": "Forbidden"}
+ },
+ "parameters": [{
+ "in": "path",
+ "name": "eiJobId",
+ "description": "eiJobId",
+ "type": "string",
+ "required": true
+ }],
+ "tags": ["Producer Callbacks"]
+ }},
"/ei-producer/v1/eiproducers": {"get": {
"summary": "EI producer identifiers",
"deprecated": false,
"consumes": ["application/json"]
}
},
+ "/producer_simulator/health_check": {"get": {
+ "summary": "Producer supervision",
+ "deprecated": false,
+ "produces": ["application/json"],
+ "operationId": "producerSupervisionUsingGET",
+ "responses": {
+ "200": {
+ "schema": {"type": "string"},
+ "description": "OK"
+ },
+ "401": {"description": "Unauthorized"},
+ "403": {"description": "Forbidden"},
+ "404": {"description": "Not Found"}
+ },
+ "tags": ["Producer Callbacks"]
+ }},
"/ei-producer/v1/eiproducers/{eiProducerId}/eijobs": {"get": {
"summary": "EI job definitions",
"deprecated": false,
"required": true
}],
"tags": ["A1-EI (enrichment information)"]
- }},
- "/producer_simulator/job_created": {"post": {
- "summary": "Callback for EI job creation",
- "deprecated": false,
- "produces": ["application/json"],
- "operationId": "jobCreatedCallbackUsingPOST",
- "responses": {
- "200": {"description": "OK"},
- "201": {"description": "Created"},
- "401": {"description": "Unauthorized"},
- "403": {"description": "Forbidden"},
- "404": {"description": "Not Found"}
- },
- "parameters": [{
- "schema": {"$ref": "#/definitions/producer_ei_job_request"},
- "in": "body",
- "name": "request",
- "description": "request",
- "required": true
- }],
- "tags": ["Producer Callbacks"],
- "consumes": ["application/json"]
}}
},
- "host": "localhost:42127",
+ "host": "localhost:41549",
"definitions": {
"producer_ei_job_request": {
"description": "The body of the EI producer callbacks for EI job creation and deletion",
"type": "object",
"title": "producer_registration_info",
"required": [
- "ei_job_creation_callback_url",
- "ei_job_deletion_callback_url",
+ "ei_job_callback_url",
"ei_producer_supervision_callback_url",
"supported_ei_types"
],
"type": "array",
"items": {"$ref": "#/definitions/producer_ei_type_registration_info"}
},
- "ei_job_creation_callback_url": {
- "description": "callback for job creation",
- "type": "string"
- },
- "ei_job_deletion_callback_url": {
- "description": "callback for job deletion",
- "type": "string"
- },
"ei_producer_supervision_callback_url": {
"description": "callback for producer supervision",
"type": "string"
+ },
+ "ei_job_callback_url": {
+ "description": "callback for EI job",
+ "type": "string"
}
}
},
"name": "A1-EI (enrichment information)",
"description": "Consumer Controller"
},
+ {
+ "name": "Consumer Callbacks",
+ "description": "Consumer Simulator Controller"
+ },
{
"name": "Enrichment Data Producer API",
"description": "Producer Controller"
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.catalina.connector.Connector;
-import org.oransc.enrichment.clients.ProducerCallbacks;
import org.oransc.enrichment.configuration.ApplicationConfig;
import org.oransc.enrichment.repository.EiJobs;
import org.oransc.enrichment.repository.EiProducers;
return this.applicationConfig;
}
- @Bean
- public ProducerCallbacks getProducerCallbacks() {
- return new ProducerCallbacks(this.applicationConfig);
- }
-
private static Connector getHttpConnector(int httpPort) {
Connector connector = new Connector(TomcatServletWebServerFactory.DEFAULT_PROTOCOL);
connector.setScheme("http");
--- /dev/null
+/*-
+ * ========================LICENSE_START=================================
+ * O-RAN-SC
+ * %%
+ * Copyright (C) 2020 Nordix Foundation
+ * %%
+ * 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.oransc.enrichment.controllers.consumer;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+
+import java.lang.invoke.MethodHandles;
+
+import org.oransc.enrichment.clients.AsyncRestClient;
+import org.oransc.enrichment.clients.AsyncRestClientFactory;
+import org.oransc.enrichment.configuration.ApplicationConfig;
+import org.oransc.enrichment.repository.EiJob;
+import org.oransc.enrichment.repository.EiJobs;
+import org.oransc.enrichment.repository.EiProducer;
+import org.oransc.enrichment.repository.EiType;
+import org.oransc.enrichment.repository.EiTypes;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+/**
+ * Callbacks to the EiProducer
+ */
+@Component
+@SuppressWarnings("java:S3457") // No need to call "toString()" method as formatting and string ..
+public class ConsumerCallbacks {
+
+ private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+ private static Gson gson = new GsonBuilder().create();
+
+ private final AsyncRestClient restClient;
+ private final EiTypes eiTypes;
+ private final EiJobs eiJobs;
+
+ @Autowired
+ public ConsumerCallbacks(ApplicationConfig config, EiTypes eiTypes, EiJobs eiJobs) {
+ AsyncRestClientFactory restClientFactory = new AsyncRestClientFactory(config.getWebClientConfig());
+ this.restClient = restClientFactory.createRestClient("");
+ this.eiTypes = eiTypes;
+ this.eiJobs = eiJobs;
+ }
+
+ public void notifyConsumersProducerDeleted(EiProducer eiProducer) {
+ for (EiType type : eiProducer.getEiTypes()) {
+ if (this.eiTypes.get(type.getId()) == null) {
+ for (EiJob job : this.eiJobs.getJobsForType(type)) {
+ noifyJobOwner(job, new ConsumerEiJobStatus(ConsumerEiJobStatus.EiJobStatusValues.DISABLED));
+ }
+ }
+ }
+ }
+
+ public void notifyConsumersTypeAdded(EiType eiType) {
+ for (EiJob job : this.eiJobs.getJobsForType(eiType)) {
+ noifyJobOwner(job, new ConsumerEiJobStatus(ConsumerEiJobStatus.EiJobStatusValues.ENABLED));
+ }
+ }
+
+ private void noifyJobOwner(EiJob job, ConsumerEiJobStatus status) {
+ if (!job.jobStatusUrl().isEmpty()) {
+ String body = gson.toJson(status);
+ this.restClient.post(job.jobStatusUrl(), body) //
+ .subscribe(notUsed -> logger.debug("Consumer notified OK {}", job.id()), //
+ throwable -> logger.warn("Consumer notify failed {} {}", job.jobStatusUrl(), throwable.toString()), //
+ null);
+ }
+ }
+
+}
import io.swagger.annotations.ApiResponses;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.List;
+import java.util.Vector;
import org.everit.json.schema.Schema;
import org.everit.json.schema.loader.SchemaLoader;
import org.json.JSONObject;
-import org.oransc.enrichment.clients.ProducerCallbacks;
import org.oransc.enrichment.configuration.ApplicationConfig;
import org.oransc.enrichment.controllers.ErrorResponse;
import org.oransc.enrichment.controllers.VoidResponse;
+import org.oransc.enrichment.controllers.producer.ProducerCallbacks;
import org.oransc.enrichment.exceptions.ServiceException;
import org.oransc.enrichment.repository.EiJob;
import org.oransc.enrichment.repository.EiJobs;
+import org.oransc.enrichment.repository.EiProducer;
import org.oransc.enrichment.repository.EiType;
import org.oransc.enrichment.repository.EiTypes;
import org.oransc.enrichment.repository.ImmutableEiJob;
@Autowired
ProducerCallbacks producerCallbacks;
- private static Gson gson = new GsonBuilder() //
- .create(); //
+ private static Gson gson = new GsonBuilder().create();
@GetMapping(path = "/eitypes", produces = MediaType.APPLICATION_JSON_VALUE)
@ApiOperation(value = "EI type identifiers", notes = "")
List<String> result = new ArrayList<>();
if (owner != null) {
for (EiJob job : this.eiJobs.getJobsForOwner(owner)) {
- if (eiTypeId == null || job.type().getId().equals(eiTypeId)) {
+ if (eiTypeId == null || job.typeId().equals(eiTypeId)) {
result.add(job.id());
}
}
}
}
+ private Collection<EiProducer> getProducers(EiJob eiJob) {
+ try {
+ return this.eiTypes.getType(eiJob.typeId()).getProducers();
+ } catch (Exception e) {
+ return new Vector<>();
+ }
+ }
+
private ConsumerEiJobStatus toEiJobStatus(EiJob job) {
- // TODO
- return new ConsumerEiJobStatus(ConsumerEiJobStatus.EiJobStatusValues.ENABLED);
+ for (EiProducer producer : getProducers(job)) {
+ if (producer.isAvailable()) {
+ return new ConsumerEiJobStatus(ConsumerEiJobStatus.EiJobStatusValues.ENABLED);
+ }
+ }
+ return new ConsumerEiJobStatus(ConsumerEiJobStatus.EiJobStatusValues.DISABLED);
}
@DeleteMapping(path = "/eijobs/{eiJobId}", produces = MediaType.APPLICATION_JSON_VALUE)
validateJsonObjectAgainstSchema(eiType.getJobDataSchema(), eiJobInfo.jobData);
EiJob existingEiJob = this.eiJobs.get(eiJobId);
- if (existingEiJob != null && !existingEiJob.type().getId().equals(eiJobInfo.eiTypeId)) {
+ if (existingEiJob != null && !existingEiJob.typeId().equals(eiJobInfo.eiTypeId)) {
throw new ServiceException("Not allowed to change type for existing EI job", HttpStatus.CONFLICT);
}
return Mono.just(toEiJob(eiJobInfo, eiJobId, eiType));
}
}
- // Status TBD
-
private EiJob toEiJob(ConsumerEiJobInfo info, String id, EiType type) {
return ImmutableEiJob.builder() //
.id(id) //
- .type(type) //
+ .typeId(type.getId()) //
.owner(info.owner) //
.jobData(info.jobData) //
- .targetUri(info.targetUri) //
+ .targetUrl(info.targetUri) //
+ .jobStatusUrl(info.statusNotificationUri == null ? "" : info.statusNotificationUri) //
.build();
}
}
private ConsumerEiJobInfo toEiJobInfo(EiJob s) {
- return new ConsumerEiJobInfo(s.type().getId(), s.jobData(), s.owner(), s.targetUri());
+ return new ConsumerEiJobInfo(s.typeId(), s.jobData(), s.owner(), s.targetUrl(), s.jobStatusUrl());
}
}
public ConsumerEiJobInfo() {
}
- public ConsumerEiJobInfo(String eiTypeId, Object jobData, String owner, String targetUri) {
+ public ConsumerEiJobInfo(String eiTypeId, Object jobData, String owner, String targetUri,
+ String statusNotificationUri) {
this.eiTypeId = eiTypeId;
this.jobData = jobData;
this.owner = owner;
this.targetUri = targetUri;
+ this.statusNotificationUri = statusNotificationUri;
}
}
@ApiModelProperty(value = OPERATIONAL_STATE_DESCRIPTION, name = "eiJobStatus", required = true)
@SerializedName("eiJobStatus")
@JsonProperty(value = "eiJobStatus", required = true)
- public final EiJobStatusValues state;
+ public EiJobStatusValues state;
+
+ public ConsumerEiJobStatus() {
+ }
public ConsumerEiJobStatus(EiJobStatusValues state) {
this.state = state;
* ========================LICENSE_END===================================
*/
-package org.oransc.enrichment.clients;
+package org.oransc.enrichment.controllers.producer;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import java.lang.invoke.MethodHandles;
+import java.util.Collection;
+import java.util.Vector;
+import org.oransc.enrichment.clients.AsyncRestClient;
+import org.oransc.enrichment.clients.AsyncRestClientFactory;
import org.oransc.enrichment.configuration.ApplicationConfig;
import org.oransc.enrichment.repository.EiJob;
import org.oransc.enrichment.repository.EiProducer;
+import org.oransc.enrichment.repository.EiTypes;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
/**
* Callbacks to the EiProducer
*/
+@Component
@SuppressWarnings("java:S3457") // No need to call "toString()" method as formatting and string ..
public class ProducerCallbacks {
private static Gson gson = new GsonBuilder().create();
private final AsyncRestClient restClient;
+ private final EiTypes eiTypes;
- public ProducerCallbacks(ApplicationConfig config) {
+ @Autowired
+ public ProducerCallbacks(ApplicationConfig config, EiTypes eiTypes) {
AsyncRestClientFactory restClientFactory = new AsyncRestClientFactory(config.getWebClientConfig());
this.restClient = restClientFactory.createRestClient("");
+ this.eiTypes = eiTypes;
}
public void notifyProducersJobDeleted(EiJob eiJob) {
- ProducerJobInfo request = new ProducerJobInfo(eiJob);
- String body = gson.toJson(request);
- for (EiProducer producer : eiJob.type().getProducers()) {
- restClient.post(producer.getJobDeletionCallbackUrl(), body) //
- .subscribe(notUsed -> logger.debug("Job deleted OK {}", producer.getId()), //
- throwable -> logger.warn("Job delete failed {}", producer.getId(), throwable.toString()), null);
+ for (EiProducer producer : getProducers(eiJob)) {
+ String url = producer.getJobCallbackUrl() + "/" + eiJob.id();
+ restClient.delete(url) //
+ .subscribe(notUsed -> logger.debug("Producer job deleted OK {}", producer.getId()), //
+ throwable -> logger.warn("Producer job delete failed {} {}", producer.getId(),
+ throwable.getMessage()),
+ null);
}
}
* @return the number of producers that returned OK
*/
public Mono<Integer> notifyProducersJobStarted(EiJob eiJob) {
- return Flux.fromIterable(eiJob.type().getProducers()) //
+ return Flux.fromIterable(getProducers(eiJob)) //
.flatMap(eiProducer -> notifyProducerJobStarted(eiProducer, eiJob)) //
.collectList() //
.flatMap(okResponses -> Mono.just(Integer.valueOf(okResponses.size()))); //
ProducerJobInfo request = new ProducerJobInfo(eiJob);
String body = gson.toJson(request);
- return restClient.post(producer.getJobCreationCallbackUrl(), body)
+ return restClient.post(producer.getJobCallbackUrl(), body)
.doOnNext(resp -> logger.debug("Job subscription started OK {}", producer.getId()))
.onErrorResume(throwable -> {
logger.warn("Job subscription failed {}", producer.getId(), throwable.toString());
});
}
+ private Collection<EiProducer> getProducers(EiJob eiJob) {
+ try {
+ return this.eiTypes.getType(eiJob.typeId()).getProducers();
+ } catch (Exception e) {
+ return new Vector<>();
+ }
+ }
+
}
import java.util.Collection;
import java.util.List;
-import org.oransc.enrichment.clients.ProducerCallbacks;
-import org.oransc.enrichment.clients.ProducerJobInfo;
import org.oransc.enrichment.controllers.ErrorResponse;
import org.oransc.enrichment.controllers.VoidResponse;
+import org.oransc.enrichment.controllers.consumer.ConsumerCallbacks;
import org.oransc.enrichment.controllers.producer.ProducerRegistrationInfo.ProducerEiTypeRegistrationInfo;
import org.oransc.enrichment.repository.EiJob;
import org.oransc.enrichment.repository.EiJobs;
@Autowired
ProducerCallbacks producerCallbacks;
+ @Autowired
+ ConsumerCallbacks consumerCallbacks;
+
@GetMapping(path = ProducerConsts.API_ROOT + "/eitypes", produces = MediaType.APPLICATION_JSON_VALUE)
@ApiOperation(value = "EI type identifiers", notes = "")
@ApiResponses(
ProducerStatusInfo.OperationalState opState =
producer.isAvailable() ? ProducerStatusInfo.OperationalState.ENABLED
: ProducerStatusInfo.OperationalState.DISABLED;
+ this.logger.debug("opState {}", opState);
return new ProducerStatusInfo(opState);
}
private void purgeTypes(Collection<EiType> types) {
for (EiType type : types) {
if (type.getProducerIds().isEmpty()) {
- this.eiTypes.deregisterType(type, this.eiJobs);
+ this.eiTypes.remove(type);
}
}
}
try {
final EiProducer producer = this.eiProducers.getProducer(eiProducerId);
this.eiProducers.deregisterProducer(producer, this.eiTypes, this.eiJobs);
+ this.consumerCallbacks.notifyConsumersProducerDeleted(producer);
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
} catch (Exception e) {
return ErrorResponse.create(e, HttpStatus.NOT_FOUND);
if (type == null) {
type = new EiType(typeInfo.eiTypeId, typeInfo.jobDataSchema);
this.eiTypes.put(type);
+ this.consumerCallbacks.notifyConsumersTypeAdded(type);
}
return type;
}
EiProducer createProducer(Collection<EiType> types, String producerId, ProducerRegistrationInfo registrationInfo) {
- return new EiProducer(producerId, types, registrationInfo.jobCreationCallbackUrl,
- registrationInfo.jobDeletionCallbackUrl, registrationInfo.producerSupervisionCallbackUrl);
+ return new EiProducer(producerId, types, registrationInfo.jobCallbackUrl,
+ registrationInfo.producerSupervisionCallbackUrl);
}
private EiProducer registerProducer(String producerId, ProducerRegistrationInfo registrationInfo) {
for (EiType type : p.getEiTypes()) {
types.add(toEiTypeRegistrationInfo(type));
}
- return new ProducerRegistrationInfo(types, p.getJobCreationCallbackUrl(), p.getJobDeletionCallbackUrl(),
- p.getProducerSupervisionCallbackUrl());
+ return new ProducerRegistrationInfo(types, p.getJobCallbackUrl(), p.getProducerSupervisionCallbackUrl());
}
private ProducerEiTypeRegistrationInfo toEiTypeRegistrationInfo(EiType type) {
* ========================LICENSE_END===================================
*/
-package org.oransc.enrichment.clients;
+package org.oransc.enrichment.controllers.producer;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.gson.annotations.SerializedName;
}
public ProducerJobInfo(EiJob job) {
- this(job.jobData(), job.id(), job.type().getId(), job.targetUri());
+ this(job.jobData(), job.id(), job.typeId(), job.targetUrl());
}
public ProducerJobInfo() {
@JsonProperty(value = "supported_ei_types", required = true)
public Collection<ProducerEiTypeRegistrationInfo> types;
- @ApiModelProperty(value = "callback for job creation", required = true)
- @SerializedName("ei_job_creation_callback_url")
- @JsonProperty(value = "ei_job_creation_callback_url", required = true)
- public String jobCreationCallbackUrl;
-
- @ApiModelProperty(value = "callback for job deletion", required = true)
- @SerializedName("ei_job_deletion_callback_url")
- @JsonProperty(value = "ei_job_deletion_callback_url", required = true)
- public String jobDeletionCallbackUrl;
+ @ApiModelProperty(value = "callback for EI job", required = true)
+ @SerializedName("ei_job_callback_url")
+ @JsonProperty(value = "ei_job_callback_url", required = true)
+ public String jobCallbackUrl;
@ApiModelProperty(value = "callback for producer supervision", required = true)
@SerializedName("ei_producer_supervision_callback_url")
@JsonProperty(value = "ei_producer_supervision_callback_url", required = true)
public String producerSupervisionCallbackUrl;
- public ProducerRegistrationInfo(Collection<ProducerEiTypeRegistrationInfo> types, String jobCreationCallbackUrl,
- String jobDeletionCallbackUrl, String producerSupervisionCallbackUrl) {
+ public ProducerRegistrationInfo(Collection<ProducerEiTypeRegistrationInfo> types, String jobCallbackUrl,
+ String producerSupervisionCallbackUrl) {
this.types = types;
- this.jobCreationCallbackUrl = jobCreationCallbackUrl;
- this.jobDeletionCallbackUrl = jobDeletionCallbackUrl;
+ this.jobCallbackUrl = jobCallbackUrl;
this.producerSupervisionCallbackUrl = producerSupervisionCallbackUrl;
}
String id();
- EiType type();
+ String typeId();
String owner();
Object jobData();
- String targetUri();
+ String targetUrl();
+
+ String jobStatusUrl();
}
public synchronized void put(EiJob job) {
allEiJobs.put(job.id(), job);
- jobsByType.put(job.type().getId(), job.id(), job);
+ jobsByType.put(job.typeId(), job.id(), job);
jobsByOwner.put(job.owner(), job.id(), job);
}
public synchronized void remove(EiJob job) {
this.allEiJobs.remove(job.id());
- jobsByType.remove(job.type().getId(), job.id());
+ jobsByType.remove(job.typeId(), job.id());
jobsByOwner.remove(job.owner(), job.id());
}
private final Collection<EiType> eiTypes;
@Getter
- private final String jobCreationCallbackUrl;
-
- @Getter
- private final String jobDeletionCallbackUrl;
+ private final String jobCallbackUrl;
@Getter
private final String producerSupervisionCallbackUrl;
private int unresponsiveCounter = 0;
- public EiProducer(String id, Collection<EiType> eiTypes, String jobCreationCallbackUrl,
- String jobDeletionCallbackUrl, String producerSupervisionCallbackUrl) {
+ public EiProducer(String id, Collection<EiType> eiTypes, String jobCallbackUrl,
+ String producerSupervisionCallbackUrl) {
this.id = id;
this.eiTypes = eiTypes;
- this.jobCreationCallbackUrl = jobCreationCallbackUrl;
- this.jobDeletionCallbackUrl = jobDeletionCallbackUrl;
+ this.jobCallbackUrl = jobCallbackUrl;
this.producerSupervisionCallbackUrl = producerSupervisionCallbackUrl;
}
@SuppressWarnings("squid:S2629") // Invoke method(s) only conditionally
public class EiProducers {
private final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
- private Map<String, EiProducer> allEiProducers = new HashMap<>();
+ private final Map<String, EiProducer> allEiProducers = new HashMap<>();
public synchronized void put(EiProducer producer) {
allEiProducers.put(producer.getId(), producer);
-
}
public synchronized Collection<EiProducer> getAllProducers() {
this.logger.error("Bug, no producer found");
}
if (type.getProducerIds().isEmpty()) {
- eiTypes.deregisterType(type, eiJobs);
+ eiTypes.remove(type);
}
}
}
public synchronized void clear() {
this.allEiTypes.clear();
}
-
- public void deregisterType(EiType type, EiJobs eiJobs) {
- this.remove(type);
- for (EiJob job : eiJobs.getJobsForType(type.getId())) {
- eiJobs.remove(job);
- this.logger.warn("Deleted job {} because no producers left", job.id());
- }
- }
-
}
import org.oransc.enrichment.clients.AsyncRestClient;
import org.oransc.enrichment.clients.AsyncRestClientFactory;
import org.oransc.enrichment.configuration.ApplicationConfig;
+import org.oransc.enrichment.controllers.consumer.ConsumerCallbacks;
import org.oransc.enrichment.repository.EiJobs;
import org.oransc.enrichment.repository.EiProducer;
import org.oransc.enrichment.repository.EiProducers;
private final EiJobs eiJobs;
private final EiTypes eiTypes;
private final AsyncRestClient restClient;
+ private final ConsumerCallbacks consumerCallbacks;
@Autowired
public ProducerSupervision(ApplicationConfig applicationConfig, EiProducers eiProducers, EiJobs eiJobs,
- EiTypes eiTypes) {
+ EiTypes eiTypes, ConsumerCallbacks consumerCallbacks) {
AsyncRestClientFactory restClientFactory = new AsyncRestClientFactory(applicationConfig.getWebClientConfig());
this.restClient = restClientFactory.createRestClient("");
this.eiJobs = eiJobs;
this.eiProducers = eiProducers;
this.eiTypes = eiTypes;
+ this.consumerCallbacks = consumerCallbacks;
}
@Scheduled(fixedRate = 1000 * 60 * 5)
producer.setAliveStatus(false);
if (producer.isDead()) {
this.eiProducers.deregisterProducer(producer, this.eiTypes, this.eiJobs);
+ this.consumerCallbacks.notifyConsumersProducerDeleted(producer);
}
}
import org.junit.jupiter.api.extension.ExtendWith;
import org.oransc.enrichment.clients.AsyncRestClient;
import org.oransc.enrichment.clients.AsyncRestClientFactory;
-import org.oransc.enrichment.clients.ProducerJobInfo;
import org.oransc.enrichment.configuration.ApplicationConfig;
import org.oransc.enrichment.configuration.ImmutableWebClientConfig;
import org.oransc.enrichment.configuration.WebClientConfig;
+import org.oransc.enrichment.controller.ConsumerSimulatorController;
import org.oransc.enrichment.controller.ProducerSimulatorController;
import org.oransc.enrichment.controllers.consumer.ConsumerConsts;
import org.oransc.enrichment.controllers.consumer.ConsumerEiJobInfo;
+import org.oransc.enrichment.controllers.consumer.ConsumerEiJobStatus;
import org.oransc.enrichment.controllers.consumer.ConsumerEiTypeInfo;
import org.oransc.enrichment.controllers.producer.ProducerConsts;
+import org.oransc.enrichment.controllers.producer.ProducerJobInfo;
import org.oransc.enrichment.controllers.producer.ProducerRegistrationInfo;
import org.oransc.enrichment.controllers.producer.ProducerRegistrationInfo.ProducerEiTypeRegistrationInfo;
import org.oransc.enrichment.controllers.producer.ProducerStatusInfo;
private final String EI_TYPE_ID = "typeId";
private final String EI_PRODUCER_ID = "producerId";
private final String EI_JOB_PROPERTY = "\"property1\"";
+ private final String EI_JOB_ID = "jobId";
@Autowired
ApplicationContext context;
@Autowired
ProducerSimulatorController producerSimulator;
+ @Autowired
+ ConsumerSimulatorController consumerSimulator;
+
@Autowired
ProducerSupervision producerSupervision;
this.eiTypes.clear();
this.eiProducers.clear();
this.producerSimulator.getTestResults().reset();
+ this.consumerSimulator.getTestResults().reset();
}
@AfterEach
void testGetEiJobStatus() throws Exception {
putEiProducerWithOneType(EI_PRODUCER_ID, EI_TYPE_ID);
putEiJob(EI_TYPE_ID, "jobId");
- String url = ConsumerConsts.API_ROOT + "/eijobs/jobId/status";
- String rsp = restClient().get(url).block();
- assertThat(rsp).contains("ENABLED");
- }
- // Status TBD
+ verifyJobStatus("jobId", "ENABLED");
+ }
@Test
void testDeleteEiJob() throws Exception {
ProducerSimulatorController.TestResults simulatorResults = this.producerSimulator.getTestResults();
await().untilAsserted(() -> assertThat(simulatorResults.jobsStopped.size()).isEqualTo(1));
- assertThat(simulatorResults.jobsStopped.get(0).id).isEqualTo("jobId");
+ assertThat(simulatorResults.jobsStopped.get(0)).isEqualTo("jobId");
}
@Test
String url = ConsumerConsts.API_ROOT + "/eijobs/jobId";
// The element with name "property1" is mandatory in the schema
- ConsumerEiJobInfo jobInfo =
- new ConsumerEiJobInfo("typeId", jsonObject("{ \"XXstring\" : \"value\" }"), "owner", "targetUri");
+ ConsumerEiJobInfo jobInfo = new ConsumerEiJobInfo("typeId", jsonObject("{ \"XXstring\" : \"value\" }"), "owner",
+ "targetUri", "jobStatusUrl");
String body = gson.toJson(jobInfo);
testErrorCode(restClient().put(url, body), HttpStatus.CONFLICT, "Json validation failure");
putEiJob("typeId1", "jobId");
String url = ConsumerConsts.API_ROOT + "/eijobs/jobId";
- String body = gson.toJson(eiJobInfo("typeId2"));
+ String body = gson.toJson(eiJobInfo("typeId2", "jobId"));
testErrorCode(restClient().put(url, body), HttpStatus.CONFLICT,
"Not allowed to change type for existing EI job");
}
putEiJob(EI_TYPE_ID, "jobId");
assertThat(this.eiJobs.size()).isEqualTo(1);
- String url = ProducerConsts.API_ROOT + "/eiproducers/eiProducerId";
- restClient().deleteForEntity(url).block();
+ deleteEiProducer("eiProducerId");
assertThat(this.eiProducers.size()).isEqualTo(1);
assertThat(this.eiTypes.getType(EI_TYPE_ID).getProducerIds()).doesNotContain("eiProducerId");
- assertThat(this.eiJobs.size()).isEqualTo(1);
+ verifyJobStatus("jobId", "ENABLED");
- String url2 = ProducerConsts.API_ROOT + "/eiproducers/eiProducerId2";
- restClient().deleteForEntity(url2).block();
+ deleteEiProducer("eiProducerId2");
assertThat(this.eiProducers.size()).isZero();
assertThat(this.eiTypes.size()).isZero();
- assertThat(this.eiJobs.size()).isZero();
+ verifyJobStatus("jobId", "DISABLED");
+ }
+
+ @Test
+ void testJobStatusNotifications() throws JsonMappingException, JsonProcessingException, ServiceException {
+ putEiProducerWithOneType("eiProducerId", EI_TYPE_ID);
+ putEiJob(EI_TYPE_ID, "jobId");
+
+ deleteEiProducer("eiProducerId");
+ assertThat(this.eiTypes.size()).isZero(); // The type is gone
+ assertThat(this.eiJobs.size()).isEqualTo(1); // The job remains
+ ConsumerSimulatorController.TestResults consumerResults = this.consumerSimulator.getTestResults();
+ await().untilAsserted(() -> assertThat(consumerResults.status.size()).isEqualTo(1));
+ assertThat(consumerResults.status.get(0).state).isEqualTo(ConsumerEiJobStatus.EiJobStatusValues.DISABLED);
+
+ putEiProducerWithOneType("eiProducerId", EI_TYPE_ID);
+ await().untilAsserted(() -> assertThat(consumerResults.status.size()).isEqualTo(2));
+ assertThat(consumerResults.status.get(1).state).isEqualTo(ConsumerEiJobStatus.EiJobStatusValues.ENABLED);
}
@Test
assertThat(resp.getBody()).contains(EI_PRODUCER_ID);
}
- private void assertProducerOpState(String producerId,
- ProducerStatusInfo.OperationalState expectedOperationalState) {
- String statusUrl = ProducerConsts.API_ROOT + "/eiproducers/" + producerId + "/status";
- ResponseEntity<String> resp = restClient().getForEntity(statusUrl).block();
- ProducerStatusInfo statusInfo = gson.fromJson(resp.getBody(), ProducerStatusInfo.class);
- assertThat(statusInfo.opState).isEqualTo(expectedOperationalState);
- }
-
@Test
void testProducerSupervision() throws JsonMappingException, JsonProcessingException, ServiceException {
putEiProducerWithOneTypeRejecting("simulateProducerError", EI_TYPE_ID);
+ {
+ // Create a job
+ putEiProducerWithOneType(EI_PRODUCER_ID, EI_TYPE_ID);
+ putEiJob(EI_TYPE_ID, "jobId");
+ deleteEiProducer(EI_PRODUCER_ID);
+ }
+
assertThat(this.eiProducers.size()).isEqualTo(1);
assertThat(this.eiTypes.size()).isEqualTo(1);
assertProducerOpState("simulateProducerError", ProducerStatusInfo.OperationalState.ENABLED);
assertThat(this.eiProducers.size()).isEqualTo(1);
assertProducerOpState("simulateProducerError", ProducerStatusInfo.OperationalState.DISABLED);
- // After 3 failed checks, the producer shall be deregisterred
+ // After 3 failed checks, the producer and the type shall be deregisterred
this.producerSupervision.createTask().blockLast();
assertThat(this.eiProducers.size()).isEqualTo(0);
assertThat(this.eiTypes.size()).isEqualTo(0);
+
+ // Job disabled status notification shall be received
+ ConsumerSimulatorController.TestResults consumerResults = this.consumerSimulator.getTestResults();
+ await().untilAsserted(() -> assertThat(consumerResults.status.size()).isEqualTo(1));
+ assertThat(consumerResults.status.get(0).state).isEqualTo(ConsumerEiJobStatus.EiJobStatusValues.DISABLED);
}
@Test
assertThat(resp.getBody()).contains("hunky dory");
}
+ private void deleteEiProducer(String eiProducerId) {
+ String url = ProducerConsts.API_ROOT + "/eiproducers/" + eiProducerId;
+ restClient().deleteForEntity(url).block();
+ }
+
+ private void verifyJobStatus(String jobId, String expStatus) {
+ String url = ConsumerConsts.API_ROOT + "/eijobs/" + jobId + "/status";
+ String rsp = restClient().get(url).block();
+ assertThat(rsp).contains(expStatus);
+ }
+
+ private void assertProducerOpState(String producerId,
+ ProducerStatusInfo.OperationalState expectedOperationalState) {
+ String statusUrl = ProducerConsts.API_ROOT + "/eiproducers/" + producerId + "/status";
+ ResponseEntity<String> resp = restClient().getForEntity(statusUrl).block();
+ ProducerStatusInfo statusInfo = gson.fromJson(resp.getBody(), ProducerStatusInfo.class);
+ assertThat(statusInfo.opState).isEqualTo(expectedOperationalState);
+ }
+
ProducerEiTypeRegistrationInfo producerEiTypeRegistrationInfo(String typeId)
throws JsonMappingException, JsonProcessingException {
return new ProducerEiTypeRegistrationInfo(jsonSchemaObject(), typeId);
Collection<ProducerEiTypeRegistrationInfo> types = new ArrayList<>();
types.add(producerEiTypeRegistrationInfo(typeId));
return new ProducerRegistrationInfo(types, //
- baseUrl() + ProducerSimulatorController.JOB_CREATED_ERROR_URL,
- baseUrl() + ProducerSimulatorController.JOB_DELETED_ERROR_URL,
+ baseUrl() + ProducerSimulatorController.JOB_ERROR_URL,
baseUrl() + ProducerSimulatorController.SUPERVISION_ERROR_URL);
}
Collection<ProducerEiTypeRegistrationInfo> types = new ArrayList<>();
types.add(producerEiTypeRegistrationInfo(typeId));
return new ProducerRegistrationInfo(types, //
- baseUrl() + ProducerSimulatorController.JOB_CREATED_URL,
- baseUrl() + ProducerSimulatorController.JOB_DELETED_URL,
- baseUrl() + ProducerSimulatorController.SUPERVISION_URL);
+ baseUrl() + ProducerSimulatorController.JOB_URL, baseUrl() + ProducerSimulatorController.SUPERVISION_URL);
}
- ConsumerEiJobInfo eiJobInfo() throws JsonMappingException, JsonProcessingException {
- return eiJobInfo(EI_TYPE_ID);
+ private ConsumerEiJobInfo eiJobInfo() throws JsonMappingException, JsonProcessingException {
+ return eiJobInfo(EI_TYPE_ID, EI_JOB_ID);
}
- ConsumerEiJobInfo eiJobInfo(String typeId) throws JsonMappingException, JsonProcessingException {
- return new ConsumerEiJobInfo(typeId, jsonObject(), "owner", "targetUri");
+ ConsumerEiJobInfo eiJobInfo(String typeId, String eiJobId) throws JsonMappingException, JsonProcessingException {
+ return new ConsumerEiJobInfo(typeId, jsonObject(), "owner", "targetUri",
+ baseUrl() + ConsumerSimulatorController.getJobStatusUrl(eiJobId));
}
- Object jsonObject(String json) {
+ private Object jsonObject(String json) {
try {
return JsonParser.parseString(json).getAsJsonObject();
} catch (Exception e) {
}
}
- Object jsonSchemaObject() {
+ private Object jsonSchemaObject() {
// a json schema with one mandatory property named "string"
String schemaStr = "{" //
+ "\"$schema\": \"http://json-schema.org/draft-04/schema#\"," //
return jsonObject(schemaStr);
}
- Object jsonObject() {
+ private Object jsonObject() {
return jsonObject("{ " + EI_JOB_PROPERTY + " : \"value\" }");
}
throws JsonMappingException, JsonProcessingException, ServiceException {
String url = ConsumerConsts.API_ROOT + "/eijobs/" + jobId;
- String body = gson.toJson(eiJobInfo(eiTypeId));
+ String body = gson.toJson(eiJobInfo(eiTypeId, jobId));
restClient().putForEntity(url, body).block();
return this.eiJobs.getJob(jobId);
--- /dev/null
+/*-
+ * ========================LICENSE_START=================================
+ * O-RAN-SC
+ * %%
+ * Copyright (C) 2020 Nordix Foundation
+ * %%
+ * 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.oransc.enrichment.controller;
+
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiResponse;
+import io.swagger.annotations.ApiResponses;
+
+import java.lang.invoke.MethodHandles;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import lombok.Getter;
+
+import org.oransc.enrichment.controllers.VoidResponse;
+import org.oransc.enrichment.controllers.consumer.ConsumerEiJobStatus;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController("ConsumerSimulatorController")
+@Api(tags = {"Consumer Callbacks"})
+public class ConsumerSimulatorController {
+
+ private final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+
+ public static class TestResults {
+
+ public List<ConsumerEiJobStatus> status = Collections.synchronizedList(new ArrayList<ConsumerEiJobStatus>());
+
+ public void reset() {
+ status.clear();
+ }
+ }
+
+ @Getter
+ private TestResults testResults = new TestResults();
+
+ public static String getJobStatusUrl(String eiJobId) {
+ return "/consumer_simulator/eijobs/" + eiJobId + "/status";
+ }
+
+ @PostMapping(path = "/consumer_simulator/eijobs/{eiJobId}/status", produces = MediaType.APPLICATION_JSON_VALUE)
+ @ApiOperation(value = "Callback for EI job status", notes = "")
+ @ApiResponses(
+ value = { //
+ @ApiResponse(code = 200, message = "OK", response = VoidResponse.class)} //
+ )
+ public ResponseEntity<Object> jobStatusCallback( //
+ @PathVariable("eiJobId") String eiJobId, //
+ @RequestBody ConsumerEiJobStatus status) {
+ logger.info("Job status callback status: {} eiJobId: {}", status.state, eiJobId);
+ this.testResults.status.add(status);
+ return new ResponseEntity<>(HttpStatus.OK);
+ }
+
+}
import lombok.Getter;
-import org.oransc.enrichment.clients.ProducerJobInfo;
import org.oransc.enrichment.controllers.ErrorResponse;
import org.oransc.enrichment.controllers.VoidResponse;
+import org.oransc.enrichment.controllers.producer.ProducerJobInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
private final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
- public static final String JOB_CREATED_URL = "/producer_simulator/job_created";
- public static final String JOB_DELETED_URL = "/producer_simulator/job_deleted";
- public static final String JOB_CREATED_ERROR_URL = "/producer_simulator/job_created_error";
- public static final String JOB_DELETED_ERROR_URL = "/producer_simulator/job_deleted_error";
+ public static final String JOB_URL = "/producer_simulator/ei_job";
+ public static final String JOB_ERROR_URL = "/producer_simulator/ei_job_error";
- public static final String SUPERVISION_URL = "/producer_simulator/supervision";
- public static final String SUPERVISION_ERROR_URL = "/producer_simulator/supervision_error";
+ public static final String SUPERVISION_URL = "/producer_simulator/health_check";
+ public static final String SUPERVISION_ERROR_URL = "/producer_simulator/health_check_error";
public static class TestResults {
public List<ProducerJobInfo> jobsStarted = Collections.synchronizedList(new ArrayList<ProducerJobInfo>());
- public List<ProducerJobInfo> jobsStopped = Collections.synchronizedList(new ArrayList<ProducerJobInfo>());
+ public List<String> jobsStopped = Collections.synchronizedList(new ArrayList<String>());
public int noOfRejectedCreate = 0;
public int noOfRejectedDelete = 0;
public boolean errorFound = false;
@Getter
private TestResults testResults = new TestResults();
- @PostMapping(path = JOB_CREATED_URL, produces = MediaType.APPLICATION_JSON_VALUE)
+ @PostMapping(path = JOB_URL, produces = MediaType.APPLICATION_JSON_VALUE)
@ApiOperation(value = "Callback for EI job creation", notes = "")
@ApiResponses(
value = { //
}
}
- @PostMapping(path = JOB_DELETED_URL, produces = MediaType.APPLICATION_JSON_VALUE)
+ @DeleteMapping(path = "/producer_simulator/ei_job/{eiJobId}", produces = MediaType.APPLICATION_JSON_VALUE)
@ApiOperation(value = "Callback for EI job deletion", notes = "")
@ApiResponses(
value = { //
@ApiResponse(code = 200, message = "OK", response = VoidResponse.class)}//
)
public ResponseEntity<Object> jobDeletedCallback( //
- @RequestBody ProducerJobInfo request) {
+ @PathVariable("eiJobId") String eiJobId) {
try {
- logger.info("Job deleted callback {}", request.id);
- this.testResults.jobsStopped.add(request);
+ logger.info("Job deleted callback {}", eiJobId);
+ this.testResults.jobsStopped.add(eiJobId);
return new ResponseEntity<>(HttpStatus.OK);
} catch (Exception e) {
return ErrorResponse.create(e, HttpStatus.NOT_FOUND);
}
}
- @PostMapping(path = JOB_CREATED_ERROR_URL, produces = MediaType.APPLICATION_JSON_VALUE)
+ @PostMapping(path = JOB_ERROR_URL, produces = MediaType.APPLICATION_JSON_VALUE)
@ApiOperation(value = "Callback for EI job creation, returns error", notes = "", hidden = true)
@ApiResponses(
value = { //
return ErrorResponse.create("Producer returns error on create job", HttpStatus.NOT_FOUND);
}
- @PostMapping(path = JOB_DELETED_ERROR_URL, produces = MediaType.APPLICATION_JSON_VALUE)
+ @DeleteMapping(path = JOB_ERROR_URL + "/{eiJobId}", produces = MediaType.APPLICATION_JSON_VALUE)
@ApiOperation(value = "Callback for EI job creation, returns error", notes = "", hidden = true)
@ApiResponses(
value = { //
<module>policy-agent</module>
<module>sdnc-a1-controller</module>
<module>enrichment-coordinator-service</module>
+ <module>r-app-catalogue</module>
</modules>
<build>
<plugins>
--- /dev/null
+.swagger-codegen-ignore
+.swagger-codegen/
+api/README.md
--- /dev/null
+#
+# ============LICENSE_START=======================================================
+# Copyright (C) 2020 Nordix Foundation.
+# ================================================================================
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# SPDX-License-Identifier: Apache-2.0
+# ============LICENSE_END=========================================================
+#
+FROM openjdk:11-jre-slim
+
+ARG JAR
+
+WORKDIR /opt/app/r-app-catalogue
+RUN mkdir -p /var/log/r-app-catalogue
+
+EXPOSE 8081 8433
+
+ADD /config/application.yaml /opt/app/r-app-catalogue/config/application.yaml
+ADD target/${JAR} /opt/app/r-app-catalogue/r-app-catalogue.jar
+
+
+RUN chmod -R 777 /opt/app/r-app-catalogue/config/
+
+CMD ["java", "-jar", "/opt/app/r-app-catalogue/r-app-catalogue.jar"]
+
+
+
+
--- /dev/null
+# O-RAN-SC Non-RT RIC rAPP Catalogue
+
+The O-RAN Non-RT RIC rApp Catalogue provides an OpenApi 3.0 REST API for services to register themselves and discover
+other services.
+
+**NOTE!** The definition of the REST API is done in the `api/rac-api.json` file. The yaml version of the file is
+generated during compilation.
+
+The application is a SpringBoot application generated using the openapitools openapi-generator-maven-plugin.
+
+To start the application run:
+`mvn spring-boot:run`
+
+## License
+
+Copyright (C) 2020 Nordix Foundation. 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.
--- /dev/null
+{
+ "openapi": "3.0.0",
+ "info": {
+ "title": "rAPP Catalogue API",
+ "description": "The Non RT-RIC Service Catalogue provides a way for services to register themselves for other services to discover.",
+ "version": "1.0.0"
+ },
+ "paths": {
+ "/services": {
+ "get": {
+ "summary": "Service names",
+ "deprecated": false,
+ "operationId": "getServiceNamesUsingGET",
+ "responses": {
+ "200": {
+ "description": "Service names",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "example": [
+ "DroneIdentifier",
+ "Collector"
+ ]
+ }
+ }
+ },
+ "401": {
+ "description": "Unauthorized"
+ },
+ "403": {
+ "description": "Forbidden"
+ },
+ "404": {
+ "description": "Not used"
+ }
+ },
+ "tags": [
+ "rAPP Catalogue API"
+ ]
+ }
+ },
+ "/services/{serviceName}": {
+ "get": {
+ "summary": "Individual Service",
+ "deprecated": false,
+ "operationId": "getIndividualServiceUsingGET",
+ "responses": {
+ "200": {
+ "description": "EI Job",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/service"
+ }
+ }
+ }
+ },
+ "401": {
+ "description": "Unauthorized"
+ },
+ "403": {
+ "description": "Forbidden"
+ },
+ "404": {
+ "description": "Service is not found",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/error_information"
+ }
+ }
+ }
+ }
+ },
+ "parameters": [
+ {
+ "in": "path",
+ "name": "serviceName",
+ "description": "serviceName",
+ "schema": {
+ "type": "string"
+ },
+ "required": true,
+ "example": "DroneIdentifier"
+ }
+ ],
+ "tags": [
+ "rAPP Catalogue API"
+ ]
+ },
+ "put": {
+ "summary": "Create or update a Service",
+ "deprecated": false,
+ "operationId": "putIndividualServiceUsingPUT",
+ "responses": {
+ "200": {
+ "description": "Service updated"
+ },
+ "201": {
+ "description": "Service created"
+ },
+ "401": {
+ "description": "Unauthorized"
+ },
+ "403": {
+ "description": "Forbidden"
+ },
+ "404": {
+ "description": "Provided service is not correct",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/error_information"
+ },
+ "example": {
+ "detail": "Service is missing required property version",
+ "status": 404
+ }
+ }
+ }
+ }
+ },
+ "parameters": [
+ {
+ "name": "serviceName",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ },
+ "example": "DroneIdentifier"
+ }
+ ],
+ "requestBody": {
+ "description": "Service to create/update",
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/service"
+ }
+ }
+ }
+ },
+ "tags": [
+ "rAPP Catalogue API"
+ ]
+ },
+ "delete": {
+ "summary": "Remove a Service from the catalogue",
+ "deprecated": false,
+ "operationId": "deleteIndividualServiceUsingDELETE",
+ "responses": {
+ "200": {
+ "description": "Not used"
+ },
+ "204": {
+ "description": "Job deleted"
+ },
+ "401": {
+ "description": "Unauthorized"
+ },
+ "403": {
+ "description": "Forbidden"
+ },
+ "404": {
+ "description": "Service is not found",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/error_information"
+ }
+ }
+ }
+ }
+ },
+ "parameters": [
+ {
+ "name": "serviceName",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ },
+ "example": "DroneIdentifier"
+ }
+ ],
+ "tags": [
+ "rAPP Catalogue API"
+ ]
+ }
+ }
+ },
+ "components": {
+ "schemas": {
+ "service": {
+ "description": "A Service",
+ "type": "object",
+ "title": "service",
+ "required": [
+ "version"
+ ],
+ "properties": {
+ "version": {
+ "description": "Version of the Service",
+ "type": "string",
+ "example": "1.0.0"
+ },
+ "display_name": {
+ "description": "Display name for the Service",
+ "type": "string",
+ "example": "Drone Identifier"
+ },
+ "description": {
+ "description": "Description of the Service",
+ "type": "string",
+ "example": "Detects if a UE is a drone"
+ }
+ }
+ },
+ "error_information": {
+ "description": "Problem as defined in https://tools.ietf.org/html/rfc7807",
+ "type": "object",
+ "title": "error_information",
+ "properties": {
+ "detail": {
+ "description": "A human-readable explanation specific to this occurrence of the problem.",
+ "type": "string",
+ "example": "Service not found"
+ },
+ "status": {
+ "format": "int32",
+ "description": "The HTTP status code generated by the origin server for this occurrence of the problem.",
+ "type": "integer",
+ "example": 404
+ }
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+openapi: 3.0.0
+info:
+ title: rAPP Catalogue API
+ description: The Non RT-RIC Service Catalogue provides a way for services to register
+ themselves for other services to discover.
+ version: 1.0.0
+servers:
+- url: /
+paths:
+ /services:
+ get:
+ tags:
+ - rAPP Catalogue API
+ summary: Service names
+ operationId: getServiceNamesUsingGET
+ responses:
+ 200:
+ description: Service names
+ content:
+ application/json:
+ schema:
+ type: array
+ items:
+ type: string
+ example:
+ - DroneIdentifier
+ - Collector
+ 401:
+ description: Unauthorized
+ 403:
+ description: Forbidden
+ 404:
+ description: Not used
+ deprecated: false
+ /services/{serviceName}:
+ get:
+ tags:
+ - rAPP Catalogue API
+ summary: Individual Service
+ operationId: getIndividualServiceUsingGET
+ parameters:
+ - name: serviceName
+ in: path
+ description: serviceName
+ required: true
+ style: simple
+ explode: false
+ schema:
+ type: string
+ example: DroneIdentifier
+ responses:
+ 200:
+ description: EI Job
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/service'
+ 401:
+ description: Unauthorized
+ 403:
+ description: Forbidden
+ 404:
+ description: Service is not found
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/error_information'
+ deprecated: false
+ put:
+ tags:
+ - rAPP Catalogue API
+ summary: Create or update a Service
+ operationId: putIndividualServiceUsingPUT
+ parameters:
+ - name: serviceName
+ in: path
+ required: true
+ style: simple
+ explode: false
+ schema:
+ type: string
+ example: DroneIdentifier
+ requestBody:
+ description: Service to create/update
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/service'
+ required: true
+ responses:
+ 200:
+ description: Service updated
+ 201:
+ description: Service created
+ 401:
+ description: Unauthorized
+ 403:
+ description: Forbidden
+ 404:
+ description: Provided service is not correct
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/error_information'
+ example:
+ detail: Service is missing required property version
+ status: 404
+ deprecated: false
+ delete:
+ tags:
+ - rAPP Catalogue API
+ summary: Remove a Service from the catalogue
+ operationId: deleteIndividualServiceUsingDELETE
+ parameters:
+ - name: serviceName
+ in: path
+ required: true
+ style: simple
+ explode: false
+ schema:
+ type: string
+ example: DroneIdentifier
+ responses:
+ 200:
+ description: Not used
+ 204:
+ description: Job deleted
+ 401:
+ description: Unauthorized
+ 403:
+ description: Forbidden
+ 404:
+ description: Service is not found
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/error_information'
+ deprecated: false
+components:
+ schemas:
+ service:
+ title: service
+ required:
+ - version
+ type: object
+ properties:
+ version:
+ type: string
+ description: Version of the Service
+ example: 1.0.0
+ display_name:
+ type: string
+ description: Display name for the Service
+ example: Drone Identifier
+ description:
+ type: string
+ description: Description of the Service
+ example: Detects if a UE is a drone
+ description: A Service
+ error_information:
+ title: error_information
+ type: object
+ properties:
+ detail:
+ type: string
+ description: A human-readable explanation specific to this occurrence of
+ the problem.
+ example: Service not found
+ status:
+ type: integer
+ description: The HTTP status code generated by the origin server for this
+ occurrence of the problem.
+ format: int32
+ example: 404
+ description: Problem as defined in https://tools.ietf.org/html/rfc7807
--- /dev/null
+spring:
+ profiles:
+ active: prod
+
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>\r
+<!--\r
+* ========================LICENSE_START=================================\r
+* O-RAN-SC\r
+* %%\r
+* Copyright (C) 2020 Nordix Foundation\r
+* %%\r
+* Licensed under the Apache License, Version 2.0 (the "License");\r
+* you may not use this file except in compliance with the License.\r
+* You may obtain a copy of the License at\r
+*\r
+* http://www.apache.org/licenses/LICENSE-2.0\r
+*\r
+* Unless required by applicable law or agreed to in writing, software\r
+* distributed under the License is distributed on an "AS IS" BASIS,\r
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+* See the License for the specific language governing permissions and\r
+* limitations under the License.\r
+* ========================LICENSE_END===================================\r
+-->\r
+<project xmlns="http://maven.apache.org/POM/4.0.0"\r
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"\r
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">\r
+ <modelVersion>4.0.0</modelVersion>\r
+\r
+ <parent>\r
+ <groupId>org.springframework.boot</groupId>\r
+ <artifactId>spring-boot-starter-parent</artifactId>\r
+ <version>2.3.4.RELEASE</version>\r
+ <relativePath />\r
+ </parent>\r
+ <groupId>org.o-ran-sc.nonrtric</groupId>\r
+ <artifactId>r-app-catalogue</artifactId>\r
+ <version>1.0.0-SNAPSHOT</version>\r
+ <licenses>\r
+ <license>\r
+ <name>The Apache Software License, Version 2.0</name>\r
+ <url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>\r
+ </license>\r
+ </licenses>\r
+ <properties>\r
+ <java.version>11</java.version>\r
+ <swagger-annotations.version>1.5.22</swagger-annotations.version>\r
+ <springfox.version>2.9.2</springfox.version>\r
+ <jackson-databind-nullable.version>0.2.1</jackson-databind-nullable.version>\r
+ <openapi-generator-maven-plugin.version>4.3.1</openapi-generator-maven-plugin.version>\r
+ <swagger-codegen-maven-plugin.version>3.0.11</swagger-codegen-maven-plugin.version>\r
+ <docker-maven-plugin.version>0.30.0</docker-maven-plugin.version>\r
+ </properties>\r
+\r
+ <dependencies>\r
+ <dependency>\r
+ <groupId>io.swagger</groupId>\r
+ <artifactId>swagger-annotations</artifactId>\r
+ <version>${swagger-annotations.version}</version>\r
+ </dependency>\r
+ <dependency>\r
+ <groupId>com.fasterxml.jackson.core</groupId>\r
+ <artifactId>jackson-annotations</artifactId>\r
+ </dependency>\r
+ <dependency>\r
+ <groupId>org.springframework</groupId>\r
+ <artifactId>spring-beans</artifactId>\r
+ </dependency>\r
+ <dependency>\r
+ <groupId>org.springframework.boot</groupId>\r
+ <artifactId>spring-boot-autoconfigure</artifactId>\r
+ </dependency>\r
+ <dependency>\r
+ <groupId>org.springframework</groupId>\r
+ <artifactId>spring-web</artifactId>\r
+ </dependency>\r
+ <dependency>\r
+ <groupId>org.springframework.boot</groupId>\r
+ <artifactId>spring-boot</artifactId>\r
+ </dependency>\r
+ <dependency>\r
+ <groupId>org.springframework</groupId>\r
+ <artifactId>spring-webmvc</artifactId>\r
+ </dependency>\r
+ <dependency>\r
+ <groupId>org.springframework</groupId>\r
+ <artifactId>spring-context</artifactId>\r
+ </dependency>\r
+ <dependency>\r
+ <groupId>io.springfox</groupId>\r
+ <artifactId>springfox-swagger2</artifactId>\r
+ <version>${springfox.version}</version>\r
+ </dependency>\r
+ <dependency>\r
+ <groupId>io.springfox</groupId>\r
+ <artifactId>springfox-core</artifactId>\r
+ <version>${springfox.version}</version>\r
+ </dependency>\r
+ <dependency>\r
+ <groupId>io.springfox</groupId>\r
+ <artifactId>springfox-spring-web</artifactId>\r
+ <version>${springfox.version}</version>\r
+ </dependency>\r
+ <dependency>\r
+ <groupId>io.springfox</groupId>\r
+ <artifactId>springfox-spi</artifactId>\r
+ <version>${springfox.version}</version>\r
+ </dependency>\r
+ <dependency>\r
+ <groupId>org.assertj</groupId>\r
+ <artifactId>assertj-core</artifactId>\r
+ </dependency>\r
+ <dependency>\r
+ <groupId>org.apache.tomcat.embed</groupId>\r
+ <artifactId>tomcat-embed-core</artifactId>\r
+ </dependency>\r
+ <dependency>\r
+ <groupId>org.openapitools</groupId>\r
+ <artifactId>jackson-databind-nullable</artifactId>\r
+ <version>${jackson-databind-nullable.version}</version>\r
+ </dependency>\r
+ <dependency>\r
+ <groupId>javax.validation</groupId>\r
+ <artifactId>validation-api</artifactId>\r
+ </dependency>\r
+ <dependency>\r
+ <groupId>com.fasterxml.jackson.core</groupId>\r
+ <artifactId>jackson-databind</artifactId>\r
+ </dependency>\r
+ <dependency>\r
+ <groupId>org.yaml</groupId>\r
+ <artifactId>snakeyaml</artifactId>\r
+ <scope>runtime</scope>\r
+ </dependency>\r
+ <!-- TEST -->\r
+ <dependency>\r
+ <groupId>org.springframework</groupId>\r
+ <artifactId>spring-test</artifactId>\r
+ <scope>test</scope>\r
+ </dependency>\r
+ <dependency>\r
+ <groupId>org.junit.jupiter</groupId>\r
+ <artifactId>junit-jupiter-api</artifactId>\r
+ <scope>test</scope>\r
+ </dependency>\r
+ </dependencies>\r
+\r
+ <build>\r
+ <plugins>\r
+ <plugin>\r
+ <groupId>org.openapitools</groupId>\r
+ <artifactId>openapi-generator-maven-plugin</artifactId>\r
+ <version>${openapi-generator-maven-plugin.version}</version>\r
+ <executions>\r
+ <execution>\r
+ <goals>\r
+ <goal>generate</goal>\r
+ </goals>\r
+ <configuration>\r
+ <inputSpec>${project.basedir}/api/rac-api.json</inputSpec>\r
+ <generatorName>spring</generatorName>\r
+ <apiPackage>org.oransc.rappcatalogue.api</apiPackage>\r
+ <modelPackage>org.oransc.rappcatalogue.model</modelPackage>\r
+ <configOptions>\r
+ <delegatePattern>true</delegatePattern>\r
+ </configOptions>\r
+ </configuration>\r
+ </execution>\r
+ </executions>\r
+ </plugin>\r
+ <plugin>\r
+ <groupId>io.swagger.codegen.v3</groupId>\r
+ <artifactId>swagger-codegen-maven-plugin</artifactId>\r
+ <version>${swagger-codegen-maven-plugin.version}</version>\r
+ <executions>\r
+ <execution>\r
+ <goals>\r
+ <goal>generate</goal>\r
+ </goals>\r
+ <configuration>\r
+ <inputSpec>${project.basedir}/api/rac-api.json</inputSpec>\r
+ <language>openapi-yaml</language>\r
+ <output>${project.basedir}/api/</output>\r
+ <configOptions>\r
+ <outputFile>rac-api.yaml</outputFile>\r
+ </configOptions>\r
+ </configuration>\r
+ </execution>\r
+ </executions>\r
+ </plugin>\r
+ <plugin>\r
+ <groupId>io.fabric8</groupId>\r
+ <artifactId>docker-maven-plugin</artifactId>\r
+ <version>${docker-maven-plugin.version}</version>\r
+ <inherited>false</inherited>\r
+ <executions>\r
+ <execution>\r
+ <id>generate-r-app-catalogue-image</id>\r
+ <phase>package</phase>\r
+ <goals>\r
+ <goal>build</goal>\r
+ </goals>\r
+ <configuration>\r
+ <pullRegistry>${env.CONTAINER_PULL_REGISTRY}</pullRegistry>\r
+ <images>\r
+ <image>\r
+ <name>o-ran-sc/nonrtric-r-app-catalogue:${project.version}</name>\r
+ <build>\r
+ <cleanup>try</cleanup>\r
+ <contextDir>${basedir}</contextDir>\r
+ <dockerFile>Dockerfile</dockerFile>\r
+ <args>\r
+ <JAR>${project.build.finalName}.jar</JAR>\r
+ </args>\r
+ <tags>\r
+ <tag>${project.version}</tag>\r
+ </tags>\r
+ </build>\r
+ </image>\r
+ </images>\r
+ </configuration>\r
+ </execution>\r
+ <execution>\r
+ <id>push-r-app-catalogue-image</id>\r
+ <goals>\r
+ <goal>build</goal>\r
+ <goal>push</goal>\r
+ </goals>\r
+ <configuration>\r
+ <pullRegistry>${env.CONTAINER_PULL_REGISTRY}</pullRegistry>\r
+ <pushRegistry>${env.CONTAINER_PUSH_REGISTRY}</pushRegistry>\r
+ <images>\r
+ <image>\r
+ <name>o-ran-sc/nonrtric-r-app-catalogue:${project.version}</name>\r
+ <build>\r
+ <contextDir>${basedir}</contextDir>\r
+ <dockerFile>Dockerfile</dockerFile>\r
+ <args>\r
+ <JAR>${project.build.finalName}.jar</JAR>\r
+ </args>\r
+ <tags>\r
+ <tag>${project.version}</tag>\r
+ <tag>latest</tag>\r
+ </tags>\r
+ </build>\r
+ </image>\r
+ </images>\r
+ </configuration>\r
+ </execution>\r
+ </executions>\r
+ </plugin>\r
+ </plugins>\r
+ </build>\r
+</project>
\ No newline at end of file
--- /dev/null
+package org.oransc.rappcatalogue.api;\r
+\r
+import java.util.Arrays;\r
+import java.util.List;\r
+\r
+import org.springframework.http.HttpStatus;\r
+import org.springframework.http.ResponseEntity;\r
+\r
+@org.springframework.stereotype.Service\r
+public class ServicesApiDelegateImpl implements ServicesApiDelegate {\r
+\r
+ @Override\r
+ public ResponseEntity<Void> deleteIndividualServiceUsingDELETE(String serviceName) {\r
+ return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);\r
+ }\r
+\r
+ // @Override\r
+ // public ResponseEntity<Service> getIndividualServiceUsingGET(String serviceName) {\r
+ // return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);\r
+\r
+ // }\r
+\r
+ @Override\r
+ public ResponseEntity<List<String>> getServiceNamesUsingGET() {\r
+ List<String> services = Arrays.asList("a", "b");\r
+ return ResponseEntity.ok(services);\r
+ }\r
+\r
+ // @Override\r
+ // public ResponseEntity<Void> putIndividualServiceUsingPUT(String serviceName, Service service) {\r
+ // return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);\r
+\r
+ // }\r
+}\r
--- /dev/null
+package org.oransc.rappcatalogue.api;\r
+\r
+import static org.assertj.core.api.Assertions.assertThat;\r
+\r
+import java.util.Arrays;\r
+import java.util.List;\r
+\r
+import org.junit.jupiter.api.Test;\r
+import org.junit.jupiter.api.extension.ExtendWith;\r
+import org.springframework.http.HttpStatus;\r
+import org.springframework.http.ResponseEntity;\r
+import org.springframework.test.context.junit.jupiter.SpringExtension;\r
+\r
+@ExtendWith(SpringExtension.class)\r
+class ServicesApiDelegateImplTest {\r
+\r
+ @Test\r
+ void putValidService_shouldBeOk() {\r
+ ServicesApiDelegateImpl delegateUnderTest = new ServicesApiDelegateImpl();\r
+\r
+ ResponseEntity<List<String>> response = delegateUnderTest.getServiceNamesUsingGET();\r
+ }\r
+\r
+ @Test\r
+ void getServices_shouldProvideArrayOfServices() throws Exception {\r
+ ServicesApiDelegateImpl delegateUnderTest = new ServicesApiDelegateImpl();\r
+\r
+ ResponseEntity<List<String>> response = delegateUnderTest.getServiceNamesUsingGET();\r
+\r
+ assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);\r
+ assertThat(response.getBody()).isEqualTo(Arrays.asList("a", "b"));\r
+ }\r
+}\r
[testenv:docs]
basepython = python3
-deps =
- sphinx
- sphinx-rtd-theme
- sphinxcontrib-httpdomain
- recommonmark
- lfdocs-conf
+deps = -r{toxinidir}/docs/requirements-docs.txt
commands =
- sphinx-build -W -b html -n -d {envtmpdir}/doctrees ./docs/ {toxinidir}/docs/_build/html
+ sphinx-build -W -b html -n -d {envtmpdir}/docs/doctrees ./docs/ {toxinidir}/docs/_build/html
echo "Generated docs available in {toxinidir}/docs/_build/html"
whitelist_externals = echo
[testenv:docs-linkcheck]
basepython = python3
-deps = sphinx
- sphinx-rtd-theme
- sphinxcontrib-httpdomain
- recommonmark
- lfdocs-conf
+deps = -r{toxinidir}/docs/requirements-docs.txt
commands = sphinx-build -W -b linkcheck -d {envtmpdir}/doctrees ./docs/ {toxinidir}/docs/_build/linkcheck