<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
- <version>2.6.6</version>
+ <version>2.7.6</version>
<relativePath />
</parent>
<groupId>org.o-ran-sc.nonrtric.plt</groupId>
<artifactId>s3</artifactId>
<version>2.17.292</version>
</dependency>
-
<!-- TEST -->
<dependency>
<groupId>org.springdoc</groupId>
<system>JIRA</system>
<url>https://jira.o-ran-sc.org/</url>
</issueManagement>
-</project>
+</project>
\ No newline at end of file
import org.oransc.ics.repository.InfoType;
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;
private final AsyncRestClient restClient;
private final InfoJobs eiJobs;
- @Autowired
public A1eCallbacks(ApplicationConfig config, InfoJobs eiJobs, SecurityContext securityContext) {
AsyncRestClientFactory restClientFactory =
new AsyncRestClientFactory(config.getWebClientConfig(), securityContext);
@RequestHeader Map<String, String> headers) throws ServiceException {
final boolean isNewJob = this.infoJobs.get(eiJobId) == null;
- InfoType eiType = this.infoTypes.getCompatibleType(eiJobObject.eiTypeId);
-
- return authorization.authorizeDataJob(headers, eiType, eiJobObject.jobDefinition, AccessType.WRITE) //
- .flatMap(x -> validatePutEiJob(eiJobId, eiType, eiJobObject)) //
- .flatMap(job -> startEiJob(job, eiType)) //
- .doOnNext(newEiJob -> this.infoJobs.put(newEiJob)) //
- .map(newEiJob -> new ResponseEntity<>(isNewJob ? HttpStatus.CREATED : HttpStatus.OK)) //
- .onErrorResume(throwable -> Mono.just(ErrorResponse.create(throwable, HttpStatus.INTERNAL_SERVER_ERROR)));
+ try {
+ InfoType eiType = this.infoTypes.getCompatibleType(eiJobObject.eiTypeId);
+
+ return authorization.authorizeDataJob(headers, eiType, eiJobObject.jobDefinition, AccessType.WRITE) //
+ .flatMap(x -> validatePutEiJob(eiJobId, eiType, eiJobObject)) //
+ .flatMap(job -> startEiJob(job, eiType)) //
+ .doOnNext(newEiJob -> this.infoJobs.put(newEiJob)) //
+ .map(newEiJob -> new ResponseEntity<>(isNewJob ? HttpStatus.CREATED : HttpStatus.OK)) //
+ .onErrorResume(
+ throwable -> Mono.just(ErrorResponse.create(throwable, HttpStatus.INTERNAL_SERVER_ERROR)));
+ } catch (Exception e) {
+ return Mono.just(ErrorResponse.create(e, HttpStatus.INTERNAL_SERVER_ERROR));
+ }
}
private Mono<InfoJob> startEiJob(InfoJob newEiJob, InfoType type) {
import org.oransc.ics.repository.InfoType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;
private final AsyncRestClient restClient;
private static Gson gson = new GsonBuilder().disableHtmlEscaping().create();
- @Autowired
public AuthorizationCheck(ApplicationConfig applicationConfig, SecurityContext securityContext) {
this.applicationConfig = applicationConfig;
import org.oransc.ics.configuration.ApplicationConfig;
import org.oransc.ics.repository.InfoType;
import org.oransc.ics.repository.InfoTypeSubscriptions;
-import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;
public static final String API_VERSION = "version_1";
- @Autowired
public ConsumerCallbacks(ApplicationConfig config, InfoTypeSubscriptions infoTypeSubscriptions,
SecurityContext securityContext) {
AsyncRestClientFactory restClientFactory =
import org.oransc.ics.repository.InfoTypes;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
private static Gson gson = new GsonBuilder().disableHtmlEscaping().create();
private final AuthorizationCheck authorization;
- @Autowired
public ConsumerController(InfoJobs jobs, InfoTypes infoTypes, InfoProducers infoProducers,
ProducerCallbacks producerCallbacks, InfoTypeSubscriptions infoTypeSubscriptions,
AuthorizationCheck authorization) {
public Mono<ResponseEntity<Object>> putIndividualInfoJob( //
@PathVariable(ConsumerConsts.INFO_JOB_ID_PATH) String jobId, //
@RequestBody ConsumerJobInfo informationJobObject, //
- @RequestHeader Map<String, String> headers) throws ServiceException {
+ @RequestHeader Map<String, String> headers) {
final boolean isNewJob = this.infoJobs.get(jobId) == null;
logger.debug("PUT info job, id: {}, obj: {}", jobId, informationJobObject);
- InfoType infoType = this.infoTypes.getCompatibleType(informationJobObject.infoTypeId);
-
- return authorization.authorizeDataJob(headers, infoType, informationJobObject.jobDefinition, AccessType.WRITE) //
- .flatMap(x -> validatePutInfoJob(jobId, infoType, informationJobObject)) //
- .flatMap(job -> startInfoSubscriptionJob(job, infoType)) //
- .doOnNext(this.infoJobs::put) //
- .map(newJob -> new ResponseEntity<>(isNewJob ? HttpStatus.CREATED : HttpStatus.OK)) //
- .onErrorResume(ErrorResponse::createMono);
+ try {
+ InfoType infoType = this.infoTypes.getCompatibleType(informationJobObject.infoTypeId);
+
+ return authorization
+ .authorizeDataJob(headers, infoType, informationJobObject.jobDefinition, AccessType.WRITE) //
+ .flatMap(x -> validatePutInfoJob(jobId, infoType, informationJobObject)) //
+ .flatMap(job -> startInfoSubscriptionJob(job, infoType)) //
+ .doOnNext(this.infoJobs::put) //
+ .map(newJob -> new ResponseEntity<>(isNewJob ? HttpStatus.CREATED : HttpStatus.OK)) //
+ .onErrorResume(ErrorResponse::createMono);
+ } catch (Exception e) {
+ return ErrorResponse.createMono(e, HttpStatus.NOT_FOUND);
+ }
}
@GetMapping(path = "/info-type-subscription", produces = MediaType.APPLICATION_JSON_VALUE)
}
public Mono<String> startInfoJob(InfoProducer producer, InfoJob infoJob, Retry retrySpec) {
- ProducerJobInfo request = new ProducerJobInfo(infoJob);
- String body = gson.toJson(request);
-
- return restClient.post(producer.getJobCallbackUrl(), body) //
+ return restClient.post(producer.getJobCallbackUrl(), jobCallbackBody(infoJob)) //
.retryWhen(retrySpec) //
.doOnNext(resp -> logger.debug("Job subscription {} started OK {}", infoJob.getId(), producer.getId())) //
.onErrorResume(throwable -> {
.doOnNext(resp -> producer.setJobEnabled(infoJob));
}
+ private String jobCallbackBody(InfoJob infoJob) {
+ ProducerJobInfo request = new ProducerJobInfo(infoJob);
+ return gson.toJson(request);
+ }
+
private Collection<InfoProducer> getProducersForJob(InfoType type, InfoProducers infoProducers) {
return infoProducers.getProducersSupportingType(type);
}
name = ProducerConsts.INFO_TYPE_ID_PARAM,
required = false,
description = "If given, only the producers for the Info Type is returned.") //
- @RequestParam(name = ProducerConsts.INFO_TYPE_ID_PARAM, required = false) String typeId //
- ) throws ServiceException {
+ @RequestParam(name = ProducerConsts.INFO_TYPE_ID_PARAM, required = false) String typeId) {
logger.debug("GET producer identifiers");
- List<String> result = new ArrayList<>();
- for (InfoProducer infoProducer : getProducers(typeId)) {
- result.add(infoProducer.getId());
- }
+ try {
+ List<String> result = new ArrayList<>();
+ for (InfoProducer infoProducer : getProducers(typeId)) {
+ result.add(infoProducer.getId());
+ }
- return new ResponseEntity<>(gson.toJson(result), HttpStatus.OK);
+ return new ResponseEntity<>(gson.toJson(result), HttpStatus.OK);
+ } catch (Exception e) {
+ return ErrorResponse.create(e, HttpStatus.INTERNAL_SERVER_ERROR);
+ }
}
private Collection<InfoProducer> getProducers(String typeId) throws ServiceException {
private final InfoType type;
@Getter
- private final String owner;
+ private String owner;
@Getter
- private final Object jobData;
+ private Object jobData;
@Getter
- private final String targetUrl;
+ private String targetUrl;
@Getter
- private final String jobStatusUrl;
+ private String jobStatusUrl;
@Getter
@Builder.Default
return this.id.equals(o);
}
+ public synchronized void update(InfoJob job) {
+ this.jobData = job.jobData;
+ this.owner = job.owner;
+ this.jobStatusUrl = job.jobStatusUrl;
+ this.targetUrl = job.targetUrl;
+ }
+
}
}
private void doPut(InfoJob job) {
- allEiJobs.put(job.getId(), job);
- jobsByType.put(job.getType().getId(), job);
- jobsByOwner.put(job.getOwner(), job);
+ InfoJob prevDefinition = this.get(job.getId());
+ if (prevDefinition == null) {
+ allEiJobs.put(job.getId(), job);
+ jobsByType.put(job.getType().getId(), job);
+ jobsByOwner.put(job.getOwner(), job);
+ } else {
+ prevDefinition.update(job);
+ }
}
private void storeJob(InfoJob job) {
import org.oransc.ics.exceptions.ServiceException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
public class InfoProducers {
private final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
private final Map<String, InfoProducer> allInfoProducers = new HashMap<>();
-
- @Autowired
- private ProducerCallbacks producerCallbacks;
-
- @Autowired
- private A1eCallbacks consumerCallbacks;
-
- @Autowired
- private InfoJobs infoJobs;
-
- @Autowired
- private InfoTypes infoTypes;
+ private final ProducerCallbacks producerCallbacks;
+ private final A1eCallbacks consumerCallbacks;
+ private final InfoJobs infoJobs;
+ private final InfoTypes infoTypes;
+
+ public InfoProducers(ProducerCallbacks producerCallbacks, A1eCallbacks consumerCallbacks, InfoJobs infoJobs,
+ InfoTypes infoTypes) {
+ this.producerCallbacks = producerCallbacks;
+ this.consumerCallbacks = consumerCallbacks;
+ this.infoJobs = infoJobs;
+ this.infoTypes = infoTypes;
+ }
public InfoProducer registerProducer(InfoProducerRegistrationInfo producerInfo) {
final String producerId = producerInfo.getId();
package org.oransc.ics.tasks;
-import org.oransc.ics.configuration.ApplicationConfig;
import org.oransc.ics.controllers.a1e.A1eCallbacks;
import org.oransc.ics.controllers.r1producer.ProducerCallbacks;
import org.oransc.ics.repository.InfoJob;
import org.oransc.ics.repository.InfoProducers;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
private final ProducerCallbacks producerCallbacks;
private final A1eCallbacks consumerCallbacks;
- @Autowired
- public ProducerSupervision(ApplicationConfig applicationConfig, InfoProducers infoProducers, InfoJobs infoJobs,
- ProducerCallbacks producerCallbacks, A1eCallbacks consumerCallbacks) {
+ public ProducerSupervision(InfoProducers infoProducers, InfoJobs infoJobs, ProducerCallbacks producerCallbacks,
+ A1eCallbacks consumerCallbacks) {
this.infoProducers = infoProducers;
this.infoJobs = infoJobs;
this.producerCallbacks = producerCallbacks;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.boot.test.context.TestConfiguration;
+import org.springframework.boot.test.web.server.LocalServerPort;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
-import org.springframework.boot.web.server.LocalServerPort;
import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
putInfoProducerWithOneType(REG_TYPE_ID4, REG_TYPE_ID4);
putInfoProducerWithOneType(REG_TYPE_ID5, REG_TYPE_ID5);
- String url = A1eConsts.API_ROOT + "/eijobs/jobId";
+ String url = A1eConsts.API_ROOT + "/eijobs/" + EI_JOB_ID;
String body = gson.toJson(eiJobInfo(PUT_TYPE_ID, EI_JOB_ID));
ResponseEntity<String> resp = restClient().putForEntity(url, body).block();
assertThat(this.infoJobs.size()).isEqualTo(1);
resp = restClient().putForEntity(url, body).block();
assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.OK);
+
+ restClient().delete(url).block();
+
+ }
+
+ @Test
+ void testA1EPutJob_unknownType() throws Exception {
+ final String REG_TYPE_ID1 = "type_1.5.0"; // Compatible
+ putInfoProducerWithOneType(REG_TYPE_ID1, REG_TYPE_ID1);
+
+ String body = gson.toJson(eiJobInfo("junkTypeId", EI_JOB_ID));
+
+ String url = A1eConsts.API_ROOT + "/eijobs/jobId";
+ testErrorCode(restClient().put(url, body), HttpStatus.NOT_FOUND, "not found");
+
+ url = A1eConsts.API_ROOT + "/eijobs/" + EI_JOB_ID;
+ final String PUT_TYPE_ERROR_ID = "type_1.1";
+ body = gson.toJson(eiJobInfo(PUT_TYPE_ERROR_ID, EI_JOB_ID));
+ testErrorCode(restClient().put(url, body), HttpStatus.NOT_FOUND, "not found");
}
@Test
assertThat(job.getOwner()).isEqualTo("owner");
verifyJobStatus(EI_JOB_ID, "ENABLED");
+
+ body = gson.toJson(consumerJobInfo("junkTypeId", "jobId", ""));
+ testErrorCode(restClient().put(url, body), HttpStatus.NOT_FOUND, "not found");
}
@Test
String url = A1eConsts.API_ROOT + "/eijobs/jobId";
// The element with name "property1" is mandatory in the schema
- A1eEiJobInfo jobInfo =
- new A1eEiJobInfo(TYPE_ID, jsonObject("{ \"XXstring\" : \"value\" }"), "owner", "targetUri", "jobStatusUrl");
+ A1eEiJobInfo jobInfo = new A1eEiJobInfo(TYPE_ID, toJsonObject("{ \"XXstring\" : \"value\" }"), "owner",
+ "targetUri", "jobStatusUrl");
String body = gson.toJson(jobInfo);
testErrorCode(restClient().put(url, body), HttpStatus.BAD_REQUEST, "Json validation failure");
String url = ConsumerConsts.API_ROOT + "/info-jobs/jobId";
// The element with name "property1" is mandatory in the schema
ConsumerJobInfo jobInfo =
- new ConsumerJobInfo(TYPE_ID, jsonObject("{ \"XXstring\" : \"value\" }"), "owner", "targetUri", null);
+ new ConsumerJobInfo(TYPE_ID, toJsonObject("{ \"XXstring\" : \"value\" }"), "owner", "targetUri", null);
String body = gson.toJson(jobInfo);
testErrorCode(restClient().put(url, body), HttpStatus.BAD_REQUEST, "Json validation failure");
String url = ConsumerConsts.API_ROOT + "/info-jobs/jobId";
- ConsumerJobInfo jobInfo = new ConsumerJobInfo(TYPE_ID, jsonObject(), "owner", "junk", null);
+ ConsumerJobInfo jobInfo = new ConsumerJobInfo(TYPE_ID, jsonObject(""), "owner", "junk", null);
String body = gson.toJson(jobInfo);
testErrorCode(restClient().put(url, body), HttpStatus.BAD_REQUEST, "URI: junk is not absolute");
ConsumerJobInfo consumerJobInfo(String typeId, String infoJobId, String owner)
throws JsonMappingException, JsonProcessingException {
- return new ConsumerJobInfo(typeId, jsonObject(), owner, "https://junk.com",
+ return new ConsumerJobInfo(typeId, jsonObject(owner), owner, "https://junk.com",
baseUrl() + A1eCallbacksSimulatorController.getJobStatusUrl(infoJobId));
}
}
A1eEiJobInfo eiJobInfo(String typeId, String infoJobId, String owner) throws Exception {
- return new A1eEiJobInfo(typeId, jsonObject(), owner, "https://junk.com",
+ return new A1eEiJobInfo(typeId, jsonObject(owner), owner, "https://junk.com",
baseUrl() + A1eCallbacksSimulatorController.getJobStatusUrl(infoJobId));
}
- private Object jsonObject(String json) {
+ private Object toJsonObject(String json) {
try {
return JsonParser.parseString(json).getAsJsonObject();
} catch (Exception e) {
}
}
+ private Object jsonObject(String aValue) {
+ return toJsonObject("{ " + EI_JOB_PROPERTY + " : \"" + aValue + "\" }");
+ }
+
private Object typeSpecifcInfoObject() {
- return jsonObject("{ \"propertyName\" : \"value\" }");
+ return toJsonObject("{ \"propertyName\" : \"value\" }");
}
private Object jsonSchemaObject() {
+ EI_JOB_PROPERTY //
+ "]" //
+ "}"; //
- return jsonObject(schemaStr);
- }
-
- private Object jsonObject() {
- return jsonObject("{ " + EI_JOB_PROPERTY + " : \"value\" }");
+ return toJsonObject(schemaStr);
}
private InfoJob putEiJob(String infoTypeId, String jobId, String owner) throws Exception {
private InfoType putInfoProducerWithOneType(String producerId, String infoTypeId)
throws JsonMappingException, JsonProcessingException, ServiceException {
- this.putInfoType(infoTypeId);
+ if (this.infoTypes.get(infoTypeId) == null) {
+ this.putInfoType(infoTypeId);
+ }
String url = ProducerConsts.API_ROOT + "/info-producers/" + producerId;
String body = gson.toJson(producerInfoRegistratioInfo(infoTypeId));
import org.slf4j.LoggerFactory;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
-import org.springframework.boot.web.server.LocalServerPort;
+import org.springframework.boot.test.web.server.LocalServerPort;
import org.springframework.test.context.TestPropertySource;
@SpringBootTest(webEnvironment = WebEnvironment.DEFINED_PORT)