Skip to content

A simple ER service implemented using OpenAPI. #239

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
## 2.1.X
- OpenAPI used to generate simple ER service used by lookup test.

## 2.1.14
- Made changes to /generate end-point to improve efficiency how result of event generation is handled

Expand Down
107 changes: 107 additions & 0 deletions openapi/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
<?xml version="1.0" encoding="UTF-8" ?>

<project
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.github.eiffel-community</groupId>
<artifactId>eiffel-remrem-generate</artifactId>
<version>${eiffel-remrem-generate.version}</version>
</parent>

<groupId>com.github.eiffel-community</groupId>
<artifactId>openapi</artifactId>
<version>${eiffel-remrem-generate.version}</version>
<name>generate-openapi-test</name>

<dependencies>
<dependency>
<groupId>com.github.eiffel-community</groupId>
<artifactId>eiffel-remrem-semantics</artifactId>
<version>2.2.7</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
<version>2.7.7</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-ui</artifactId>
<version>1.6.14</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>2.7.7</version>
</dependency>
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-annotations</artifactId>
<version>1.6.3</version>
</dependency>
<dependency>
<groupId>org.openapitools</groupId>
<artifactId>jackson-databind-nullable</artifactId>
<version>0.2.4</version>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.openapitools</groupId>
<artifactId>openapi-generator-maven-plugin</artifactId>
<version>5.4.0</version>
<executions>
<execution>
<goals>
<goal>generate</goal>
</goals>
<configuration>
<inputSpec>${project.basedir}/src/main/resources/openapi-spec.yaml</inputSpec>
<generatorName>spring</generatorName>
<apiPackage>com.ericsson.eiffel.remrem.api</apiPackage>
<modelPackage>com.ericsson.eiffel.remrem.model</modelPackage>
<supportingFilesToGenerate>
ApiUtil.java
</supportingFilesToGenerate>
<configOptions>
<useSpringController>true</useSpringController>
<delegatePattern>true</delegatePattern>
</configOptions>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<version>3.2.0</version>
<executions>
<execution>
<id>add-client-sources</id>
<phase>generate-sources</phase>
<goals>
<goal>add-source</goal>
</goals>
<configuration>
<sources>
<source>${project.build.directory}/generated-sources/openapi/src/main/java</source>
</sources>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.ericsson.eiffel.remrem;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.RestController;

@RestController
@SpringBootApplication
public class OpenApiApplication {
public static void main(String[] args) {
SpringApplication.run(OpenApiApplication.class, args);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
package com.ericsson.eiffel.remrem.api;

import com.ericsson.eiffel.remrem.model.InlineResponse200;
import com.ericsson.eiffel.remrem.model.EiffelEvent;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.jayway.jsonpath.Configuration;
import com.jayway.jsonpath.JsonPath;
import com.jayway.jsonpath.JsonPathException;
import com.jayway.jsonpath.PathNotFoundException;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.Schema;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.*;


import javax.validation.Valid;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.nio.file.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@Service
@RestController
public class EventsApiService implements EventsApiDelegate {
static private List<String> events = new ArrayList();

static {
loadEventsFromFiles();
}

public static void loadEventsFromFiles() {
String dir = "src/test/resources/ER/events";
File directory = new File(dir);
File[] files = directory.listFiles();

for (File file : files) {
if (!file.isFile())
continue;

String filename = file.getAbsolutePath();

try {
String event = loadEventFromFile(filename);
events.add(event);
}
catch (IOException e) {
e.printStackTrace();
}
}
}

static String loadEventFromFile(String filename) throws IOException {
return new String(Files.readAllBytes(Paths.get(filename)));
}

@RequestMapping(
method = RequestMethod.GET,
value = "/events/{id}",
produces = { "application/json" }
)
public ResponseEntity<EiffelEvent> getEventUsingGET(
@Parameter(name = "id", description = "Id of the event.", required = true, schema = @Schema(description = "")) @PathVariable("id") String id
) {

for (String event : events) {
try {
Object document = Configuration.defaultConfiguration().jsonProvider().parse(event);
Object value = JsonPath.read(document, "$.id");
if (value.equals(id)) {
try {
ObjectMapper mapper = new ObjectMapper();
return new ResponseEntity<>(mapper.readValue(event, EiffelEvent.class), HttpStatus.OK);
} catch (JsonProcessingException e) {
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
} catch (PathNotFoundException e) {
// The given property path doesn't exist. No need to continue, the event doesn't match
// given criteria...
continue;
} catch (JsonPathException e) {
// The given path is mangled. Maybe handling of this, more general exception, is sufficient,
// but I split the handling as I don't know if it can be useful in a future...
continue;
}
}

return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}

@Override
@RequestMapping(
method = RequestMethod.GET,
value = "/events",
produces = { "application/json" }
)
public ResponseEntity<InlineResponse200> getEventsUsingGET(
@Parameter(name = "pageSize", description = "The number of events to be displayed per page.", schema = @Schema(description = "", defaultValue = "500")) @Valid @RequestParam(value = "pageSize", required = false, defaultValue = "500") Integer pageSize,
@Parameter(name = "params", description = "", schema = @Schema(description = "")) @Valid @RequestParam(value = "", required = false) Map<String, String> params) {
String keysToIgnore[] = { "pageSize", "shallow" };
ObjectMapper mapper = new ObjectMapper();
List<EiffelEvent> matchedEvents = new ArrayList<>();
for (String event : events) {
boolean matches = true;
try {
Object document = Configuration.defaultConfiguration().jsonProvider().parse(event);
process_keys:
for (String key : params.keySet()) {
for (String keyToIgnore : keysToIgnore) {
if (key.equals(keyToIgnore))
// This isn't a property path; take another key.
continue process_keys;
}

String expected = params.get(key);
Object value = JsonPath.read(document, "$." + key);
if (expected == null || expected.equals(""))
// If value of query parameter is not present, don't compare value, just test
// if the property (given by key) exists. As the flow reached this point it means
// that the property exists (otherwise PathNotFoundException would have been thrown).
continue;

if (!value.equals(expected)) {
matches = false;
// The criteria is not matched. As they're treated as AND, no need to continue.
// Try another event.
break;
}
}
}
catch (PathNotFoundException e) {
// The given property path doesn't exist. No need to continue, the event doesn't match
// given criteria...
continue;
}
catch (JsonPathException e) {
// The given path is mangled. Maybe handling of this, more general exception, is sufficient,
// but I split the handling as I don't know if it can be useful in a future...
continue;
}

if (matches) {
try {
matchedEvents.add(mapper.readValue(event, EiffelEvent.class));
}
catch (JsonProcessingException e) {
// Something went wrong...
// TODO: Should INTERNAL SERVER ERROR be responded?
e.printStackTrace();
}
}
}

InlineResponse200 response = new InlineResponse200();
response.pageSize(1).pageNo(1).items(matchedEvents);
return new ResponseEntity<>(response, HttpStatus.OK);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package com.ericsson.eiffel.remrem.api;

import com.ericsson.eiffel.remrem.model.InlineResponse2001;
import com.ericsson.eiffel.remrem.model.SearchParameters;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.context.request.NativeWebRequest;

import javax.validation.Valid;
import java.util.Optional;

@RestController
public class SearchApiService implements SearchApiDelegate {
@Override
public Optional<NativeWebRequest> getRequest() {
return SearchApiDelegate.super.getRequest();
}

@Override
@Operation(
operationId = "searchUsingPOST",
summary = "To get upstream/downstream events for an event based on the searchParameters passed",
tags = { "API" },
responses = {
@ApiResponse(responseCode = "200", description = "OK", content = @Content(mediaType = "application/json", schema = @Schema(implementation = InlineResponse2001.class))),
@ApiResponse(responseCode = "400", description = "Bad Request"),
@ApiResponse(responseCode = "401", description = "Unauthorized"),
@ApiResponse(responseCode = "403", description = "Forbidden"),
@ApiResponse(responseCode = "404", description = "Not Found"),
@ApiResponse(responseCode = "422", description = "Content Too Large - use the limit flag to limit the amount of events")
}
)
@RequestMapping(
method = RequestMethod.POST,
value = "/search/{id}",
produces = { "application/json" },
consumes = { "application/json" }
)
public ResponseEntity<InlineResponse2001> searchUsingPOST(
@Parameter(name = "id", description = "Id of the event.", required = true, schema = @Schema(description = "")) @PathVariable("id") String id,
@Parameter(name = "limit", description = "Determines the maximum amount of events to be fetched. Use `-1` for maximum amount of the events the server can provide. ", schema = @Schema(description = "", defaultValue = "-1")) @Valid @RequestParam(value = "limit", required = false, defaultValue = "-1") Integer limit,
@Parameter(name = "levels", description = "Determines the maximum amount of levels to search. Use `-1` for maximum amount of levels the server can search ", schema = @Schema(description = "", defaultValue = "-1")) @Valid @RequestParam(value = "levels", required = false, defaultValue = "-1") Integer levels,
@Parameter(name = "searchParameters", description = "Select what types of links you want upstream/downstream search to follow. Examples: * Select `CAUSE` if you only want the search to follow `CAUSE` links disregarding other links. * Select `CONTEXT` and `ACTIVITY_EXECUTION` if you want to follow both `CONTEXT` and `ACTIVITY_EXECUTION` links. Link Types: - CAUSE - CONTEXT - FLOW_CONTEXT - ACTIVITY_EXECUTION - PREVIOUS_ACTIVITY_EXECUTION - PREVIOUS_VERSION - COMPOSITION - ENVIRONMENT - ARTIFACT - SUBJECT - ELEMENT - BASE - CHANGE - TEST_SUITE_EXECUTION - TEST_CASE_EXECUTION - IUT - TERC - MODIFIED_ANNOUNCEMENT - SUB_CONFIDENCE_LEVEL - REUSED_ARTIFACT - VERIFICATION_BASIS - PRECURSOR - ORIGINAL_TRIGGER - CONFIGURATION - ALL **Example** In the following example `dlt` stands for downlink and `ult` stands for uplink ", schema = @Schema(description = "")) @Valid @RequestBody(required = false) SearchParameters searchParameters
) {
return SearchApiDelegate.super.searchUsingPOST(id, limit, levels, searchParameters);
}
}
Loading