Skip to content

Commit

Permalink
Version 1.2 differences:
Browse files Browse the repository at this point in the history
- added new parameter to point to the root product schemas directory (default to ".")
- modified naming generation strategy for uplifted properties:
  - in case the uplifted property is a composed schema we try first to compute name from aggregated references (allOf and oneOf are supported)
  - if above fails name is based on property
- modified behavior of uplifting schemas in case of composed schemas -> schema reuse if possible
- discriminator mapping feature:
  - using x-discriminator-value
  - using $id value if it is MEF urn
  - using name of the type
- discriminator discovery feature:
  - in case discriminator is not provided x-discriminator-name value is used
  - if above fails autodiscovery strategy of discriminator property is applied trying names from list (@type, mapType)

Signed-off-by: Bartosz Michalik <[email protected]>
  • Loading branch information
bartoszm committed Dec 14, 2020
1 parent 3c30da3 commit 2eb57a6
Show file tree
Hide file tree
Showing 40 changed files with 1,765 additions and 110 deletions.
82 changes: 76 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,20 @@ mvn clean package
java -jar blender-1.0.jar <commang> [args]
```

## Tool generate command synopis
## Tool `generate` command synopsis

```shell script
NAME
sonata-blending-tool-cli generate - Generate code using configuration.

SYNOPSIS
sonata-blending-tool-cli generate
(-c <configuration file> | --config <configuration file>)
(-c <configuration file> | --config <configuration file>) [(-d <product specifications root directory> | --product-spec-root-dir <product specifications root directory>)]
[(-e <files encoding> | -encoding <files encoding>)]
(-i <spec file> | --input-spec <spec file>)
[(-m <model to be augmented> | -model-name <model to be augmented>)]
[(-m <model to be augmented> | --model-name <model to be augmented>)]
[(-p <product specifications> | --product-spec <product specifications>)...]
[--strict-mode]

OPTIONS
-c <configuration file>, --config <configuration file>
Expand All @@ -38,20 +39,89 @@ OPTIONS
config-help -g {generator name} command for language specific config
options.

-d <product specifications root directory>, --product-spec-root-dir
<product specifications root directory>
sets of product specification root directory for specifications you
would like to integrate

-e <files encoding>, -encoding <files encoding>
encoding used to read API and product definitions. By default system
encoding is used

-i <spec file>, --input-spec <spec file>
location of the OpenAPI spec, as URL or file (required)

-m <model to be augmented>, --model-name <model to be augmented>
Model which will be hosting product specific extensions (e.g.
MEFProductConfiguration)

-p <product specifications>, --product-spec <product specifications>
sets of product specification you would like to integrate

--strict-mode
Verify that model to be augmented allows for extension (contains
discriminator definition). If strict-mode is `false` tool will add a
discriminator on the fly if possible.

```
### Usage example
Assuming you have a valid spring generator configuration
(as explained [here](https://openapi-generator.tech/docs/generators/spring)) in `configurations/spring`
```shell script
java -jar blender-1.2.jar generate -d ./schemas/POQ/ -p Access_E_Line_OVC.yaml -p Carrier_Ethernet_Operator_UNI.yaml \
-c ./configurations/spring/spring-server.yaml
-i ./api/serviceability/offeringQualification/productOfferingQualificationManagement.api.yaml
```
## Tool `blend` command synopsis
Blend command generates an OAS 3 definition of combined API and product specifications
```shell script
NAME
sonata-blending-tool-cli blend - Blend Product Specifications into
OpenAPI.

SYNOPSIS
sonata-blending-tool-cli blend [(-d <product specifications root directory> | --product-spec-root-dir <product specifications root directory>)]
[(-e <files encoding> | -encoding <files encoding>)]
(-i <spec file> | --input-spec <spec file>)
[(-m <model to be augmented> | --model-name <model to be augmented>)]
[(-p <product specifications> | --product-spec <product specifications>)...]
[--strict-mode]

OPTIONS
-d <product specifications root directory>, --product-spec-root-dir
<product specifications root directory>
sets of product specification root directory for specifications you
would like to integrate

-e <files encoding>, -encoding <files encoding>
encoding used to read API and product definitions. By default system
encoding is used

-i <spec file>, --input-spec <spec file>
location of the OpenAPI spec, as URL or file (required)

-m <model to be augmented>, -model-name <model to be augmented>
Model which will be hosting product specific extensions (E.g.
ProductCharacteristics)
-m <model to be augmented>, --model-name <model to be augmented>
Model which will be hosting product specific extensions (e.g.
MEFProductConfiguration)

-p <product specifications>, --product-spec <product specifications>
sets of product specification you would like to integrate

--strict-mode
Verify that model to be augmented allows for extension (contains
discriminator definition). If strict-mode is `false` tool will add a
discriminator on the fly if possible.

```
### Usage example
```shell script
java -jar blender-1.2.jar blend -d ./schemas -p POQ/Access_E_Line_OVC.yaml -p POQ/Carrier_Ethernet_Operator_UNI.yaml \
-i ./api/serviceability/offeringQualification/productOfferingQualificationManagement.api.yaml
```
3 changes: 2 additions & 1 deletion configurations/open-api-example.yaml
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
generatorName: openapi-yaml
outputDir: out
outputDir: out/oap
legacyDiscriminatorBehavior: false
43 changes: 41 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
@@ -1,4 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~
~ Copyright 2020 Amartus
~
~ 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.
~
-->

<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
Expand All @@ -7,13 +25,17 @@
<groupId>com.amartus.sonata</groupId>
<artifactId>blender</artifactId>
<description>SonataBlendingTool</description>
<version>1.1</version>
<version>1.2</version>
<properties>
<!-- <openapi.generator.version>4.3.1</openapi.generator.version>-->
<openapi.generator.version>5.0.0-beta3</openapi.generator.version>
</properties>
<dependencies>
<!-- https://mvnrepository.com/artifact/org.openapitools/openapi-generator -->
<dependency>
<groupId>org.openapitools</groupId>
<artifactId>openapi-generator</artifactId>
<version>4.3.1</version>
<version>${openapi.generator.version}</version>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
Expand All @@ -36,6 +58,23 @@
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
<dependency>
<groupId>io.swagger.parser.v3</groupId>
<artifactId>swagger-parser-v3</artifactId>
<version>2.0.24</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.6.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<version>5.6.2</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
Expand Down
4 changes: 3 additions & 1 deletion src/main/java/com/amartus/sonata/blender/Blender.java
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
/*
*
* Copyright 2020 Amartus
*
* Licensed under the Apache License, Version 2.0 (the "License");
Expand All @@ -12,6 +13,7 @@
* 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.
*
*/
package com.amartus.sonata.blender;

Expand All @@ -30,7 +32,7 @@
public class Blender {
private static final Logger log = LoggerFactory.getLogger(Blender.class);
public static void main(String[] args) {
String version = "1.0";
String version = "1.2";
Cli.CliBuilder<Runnable> builder =
Cli.<Runnable>builder("sonata-blending-tool-cli")
.withDescription(
Expand Down
65 changes: 43 additions & 22 deletions src/main/java/com/amartus/sonata/blender/cmd/AbstractCmd.java
Original file line number Diff line number Diff line change
@@ -1,18 +1,38 @@
/*
*
* Copyright 2020 Amartus
*
* 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.
*
*/

package com.amartus.sonata.blender.cmd;

import com.amartus.sonata.blender.impl.ProductSpecReader;
import com.amartus.sonata.blender.impl.util.PathUtils;
import io.airlift.airline.Option;
import io.swagger.v3.oas.models.media.Schema;
import io.swagger.v3.parser.core.models.ParseOptions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;

public abstract class AbstractCmd {
Expand All @@ -23,6 +43,12 @@ public abstract class AbstractCmd {
description = "sets of product specification you would like to integrate")
protected List<String> productSpecifications = new ArrayList<>();

@Option(
name = {"-d", "--product-spec-root-dir"},
title = "product specifications root directory",
description = "sets of product specification root directory for specifications you would like to integrate")
protected String productsRootDir = ".";

@Option(name = {"-i", "--input-spec"}, title = "spec file", required = true,
description = "location of the OpenAPI spec, as URL or file (required)")
protected String spec;
Expand All @@ -44,13 +70,17 @@ public abstract class AbstractCmd {
"\n If strict-mode is `false` tool will add a discriminator on the fly if possible."
)
protected boolean strict = false;
private Function<String, Path> toPath = p -> Path.of(productsRootDir, PathUtils.toFileName(p));
private Predicate<String> exists = (String p) -> {
var path = toPath.apply(p);
log.debug("{} -> {}", p, path);
return Files.exists(path);
};

protected Map<String, Schema> toProductSpecifications() {

ParseOptions opt = new ParseOptions();
opt.setResolve(true);
var root = Path.of(productsRootDir);
return productSpecifications.stream()
.flatMap(file -> new ProductSpecReader(modelToAugment, file).readSchemas().entrySet().stream())
.flatMap(schema -> new ProductSpecReader(modelToAugment, root, schema).readSchemas().entrySet().stream())
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (a, b) -> {
if (a.equals(b)) return a;
throw new IllegalArgumentException(String.format("Object for the same key does not match %s %s", a, b));
Expand All @@ -59,30 +89,21 @@ protected Map<String, Schema> toProductSpecifications() {
}

protected void validateProductSpecs(List<String> productSpecifications) {
Optional<String> absolute = productSpecifications.stream()
.filter(this::isAbsolute)
Optional<String> incorrect = productSpecifications.stream()
.filter(ps -> exists.negate().test(ps))
.findFirst();
absolute.ifPresent(p -> {
String current = Paths.get("").toAbsolutePath().toString();

log.warn("{} is an absolute current. it should be relative wrt. {}", p, current);
throw new IllegalArgumentException("All product specifications has to be expressed as relative paths.");
incorrect.ifPresent(p -> {
log.warn("{} cannot be found at {}", p, toPath.apply(p));
throw new IllegalArgumentException("Resource for " + p + " does not exists");
});

boolean incorrectFilesExists = productSpecifications.stream()
.filter(this::notAfile)
.map(toPath)
.filter(p -> !Files.isRegularFile(p))
.peek(p -> log.warn("{} is not a file", p))
.count() > 0;
if (incorrectFilesExists) {
throw new IllegalArgumentException("All product specifications has to exist");
}
}

private boolean notAfile(String p) {
return !Files.isRegularFile(Paths.get(p));
}

private boolean isAbsolute(String p) {
return Paths.get(p).isAbsolute();
}
}
34 changes: 28 additions & 6 deletions src/main/java/com/amartus/sonata/blender/cmd/Blend.java
Original file line number Diff line number Diff line change
@@ -1,10 +1,29 @@
/*
*
* Copyright 2020 Amartus
*
* 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.
*
*/

package com.amartus.sonata.blender.cmd;

import com.amartus.sonata.blender.impl.MergeSchemasAction;
import com.amartus.sonata.blender.impl.postprocess.AlignTypeCompositionWithOasTools;
import com.amartus.sonata.blender.impl.postprocess.ConvertOneOfToAllOffInheritence;
import com.amartus.sonata.blender.impl.postprocess.PropertyCompositionToType;
import com.amartus.sonata.blender.impl.postprocess.PropertyEnumExternalize;
import com.amartus.sonata.blender.impl.postprocess.RemoveSuperflousTypeDeclarations;
import com.amartus.sonata.blender.impl.postprocess.UpdateDiscriminatorMapping;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.ObjectMapper;
Expand All @@ -31,6 +50,8 @@ public class Blend extends AbstractCmd implements Runnable {

@Override
public void run() {
validateProductSpecs(productSpecifications);

SwaggerParseResult result = new OpenAPIParser().readLocation(this.spec, List.of(), new ParseOptions());
OpenAPI openAPI = result.getOpenAPI();
Map<String, Schema> schemasToInject = this.toProductSpecifications();
Expand All @@ -46,17 +67,17 @@ public void run() {
new RemoveSuperflousTypeDeclarations().accept(openAPI);
new PropertyCompositionToType().accept(openAPI);
new PropertyEnumExternalize().accept(openAPI);

new AlignTypeCompositionWithOasTools().accept(openAPI);
new ConvertOneOfToAllOffInheritence().accept(openAPI);
new UpdateDiscriminatorMapping().accept(openAPI);

ObjectMapper mapper = new ObjectMapper(new YAMLFactory()
.enable(YAMLGenerator.Feature.LITERAL_BLOCK_STYLE)
.enable(YAMLGenerator.Feature.SPLIT_LINES)
.enable(YAMLGenerator.Feature.SPLIT_LINES)

)
.addMixIn(Object.class, IgnorePropertyMixin.class)
.setSerializationInclusion(JsonInclude.Include.NON_NULL);
.setSerializationInclusion(JsonInclude.Include.NON_EMPTY);

try {
File output = new File(this.spec + ".modified");
log.info("Writing to {}", output);
Expand All @@ -70,5 +91,6 @@ public void run() {

abstract class IgnorePropertyMixin {
@JsonIgnore
public abstract Map<String, Object> getExtensions();
public abstract boolean getExampleSetFlag();
}

Loading

0 comments on commit 2eb57a6

Please sign in to comment.