Skip to content

Commit

Permalink
[incubator-kie-issues#1100] Fix oas-generation. Add tests (apache#5858)
Browse files Browse the repository at this point in the history
Co-authored-by: Gabriele-Cardosi <[email protected]>
  • Loading branch information
2 people authored and rgdoliveira committed Apr 23, 2024
1 parent 9b4402a commit f3af948
Show file tree
Hide file tree
Showing 4 changed files with 269 additions and 83 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,10 @@
*/
package org.kie.dmn.openapi.impl;

import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
Expand Down Expand Up @@ -72,8 +74,7 @@ private Schema refOrBuiltinSchema(DMNType t) {
return FEELBuiltinTypeSchemas.from(t);
}
if (typesIndex.contains(t)) {
Schema schema = OASFactory.createObject(Schema.class).ref(namingPolicy.getRef(t));
return schema;
return OASFactory.createObject(Schema.class).ref(namingPolicy.getRef(t));
}
throw new UnsupportedOperationException();
}
Expand Down Expand Up @@ -131,7 +132,7 @@ private Schema schemaFromSimpleType(SimpleTypeImpl t) {
parseSimpleType(DMNOASConstants.X_DMN_ALLOWED_VALUES, t, schema, t.getAllowedValuesFEEL(), t.getAllowedValues());
}
if (t.getTypeConstraint() != null && !t.getTypeConstraint().isEmpty()) {
parseSimpleType(DMNOASConstants.X_DMN_TYPE_CONSTRAINTS, t, schema, t.getTypeConstraintFEEL(), t.getAllowedValues());
parseSimpleType(DMNOASConstants.X_DMN_TYPE_CONSTRAINTS, t, schema, t.getTypeConstraintFEEL(), t.getTypeConstraint());
}
schema = nestAsItemIfCollection(schema, t);
schema.addExtension(X_DMN_TYPE, getDMNTypeSchemaXDMNTYPEdescr(t));
Expand All @@ -142,7 +143,9 @@ private Schema schemaFromSimpleType(SimpleTypeImpl t) {
private void parseSimpleType(String schemaString, SimpleTypeImpl t, Schema schema, List<UnaryTest> feelUnaryTests, List<DMNUnaryTest> dmnUnaryTests) {
schema.addExtension(schemaString, feelUnaryTests.stream().map(UnaryTest::toString).collect(Collectors.joining(", ")));
if (DMNTypeUtils.getFEELBuiltInType(ancestor(t)) == BuiltInType.NUMBER) {
FEELSchemaEnum.parseNumbersIntoSchema(schema, dmnUnaryTests);
FEELSchemaEnum.parseRangeableValuesIntoSchema(schema, dmnUnaryTests, Number.class);
} else if (DMNTypeUtils.getFEELBuiltInType(ancestor(t)) == BuiltInType.DATE) {
FEELSchemaEnum.parseRangeableValuesIntoSchema(schema, dmnUnaryTests, LocalDate.class);
} else {
FEELSchemaEnum.parseValuesIntoSchema(schema, dmnUnaryTests);
}
Expand All @@ -154,7 +157,7 @@ private Schema schemaFromCompositeType(CompositeTypeImpl ct) {
for (Entry<String, DMNType> fkv : ct.getFields().entrySet()) {
schema.addProperty(fkv.getKey(), refOrBuiltinSchema(fkv.getValue()));
}
if (isIOSetForInputScope(ct) && ct.getFields().size() > 0) {
if (isIOSetForInputScope(ct) && !ct.getFields().isEmpty()) {
schema.required(new ArrayList<>(ct.getFields().keySet()));
}
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,6 @@
*/
package org.kie.dmn.openapi.impl;

import java.math.BigDecimal;
import java.util.List;
import java.util.stream.Collectors;

import org.eclipse.microprofile.openapi.models.media.Schema;
import org.kie.dmn.api.core.DMNUnaryTest;
import org.kie.dmn.feel.FEEL;
Expand All @@ -32,24 +28,35 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.math.BigDecimal;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

public class FEELSchemaEnum {

private static final Logger LOG = LoggerFactory.getLogger(FEELSchemaEnum.class);

public static void parseValuesIntoSchema(Schema schema, List<DMNUnaryTest> list) {
List<Object> expectLiterals = evaluateUnaryTests(list);
public static void parseValuesIntoSchema(Schema schema, List<DMNUnaryTest> unaryTests) {
List<Object> expectLiterals = evaluateUnaryTests(unaryTests);
try {
checkEvaluatedUnaryTestsForTypeConsistency(expectLiterals);
} catch (IllegalArgumentException e) {
LOG.warn("Unable to parse generic value into the JSON Schema for enumeration");
return;
}
if (expectLiterals.contains(null)) {
schema.setNullable(true);
}
boolean allLiterals = expectLiterals.stream().allMatch(o -> o == null || o instanceof String || o instanceof Number || o instanceof Boolean);
boolean allLiterals = !expectLiterals.isEmpty() && expectLiterals.stream().allMatch(o -> o == null || o instanceof String || o instanceof Number || o instanceof Boolean);
if (allLiterals) {
schema.enumeration(expectLiterals);
} else {
LOG.warn("Unable to parse generic value into the JSON Schema for enumeration");
}
}
public static void parseNumbersIntoSchema(Schema schema, List<DMNUnaryTest> list) {

public static void parseRangeableValuesIntoSchema(Schema schema, List<DMNUnaryTest> list, Class<?> expectedType) {
List<Object> uts = evaluateUnaryTests(list); // we leverage the property of the *base* FEEL grammar(non visited by ASTVisitor, only the ParseTree->AST Visitor) that `>x` is a Range
boolean allowNull = uts.remove(null);
if (allowNull) {
Expand All @@ -67,13 +74,13 @@ public static void parseNumbersIntoSchema(Schema schema, List<DMNUnaryTest> list
schema.exclusiveMaximum(range.getHighBoundary() == RangeBoundary.OPEN);
}
}
} else if (uts.stream().allMatch(o -> o instanceof Number)) {
} else if (uts.stream().allMatch(expectedType::isInstance)) {
if (allowNull) {
uts.add(null);
}
schema.enumeration(uts);
} else {
LOG.warn("Unable to parse number value into the JSON Schema for enumeration");
LOG.warn("Unable to parse {} value into the JSON Schema for enumeration", expectedType);
}
}

Expand All @@ -99,13 +106,43 @@ public static Range consolidateRanges(List<Range> ranges) {
return consistent ? result : null;
}

private static List<Object> evaluateUnaryTests(List<DMNUnaryTest> list) {
FEEL SimpleFEEL = FEEL.newInstance();
List<Object> utEvaluated = list.stream().map(UnaryTestImpl.class::cast)
.map(UnaryTestImpl::toString)
.map(SimpleFEEL::evaluate)
.collect(Collectors.toList());
return utEvaluated;
static List<Object> evaluateUnaryTests(List<DMNUnaryTest> list) {
FEEL feelInstance = FEEL.newInstance();
List<Object> toReturn = list.stream()
.map(UnaryTestImpl.class::cast)
.map(UnaryTestImpl::toString)
.filter(str -> !str.contains("?")) // We have to exclude "TEST" expressions, because they can't be evaluated without a context and their return is meaningless
.map(feelInstance::evaluate)
.collect(Collectors.toList());
checkEvaluatedUnaryTestsForNull(toReturn);
return toReturn;
}

/**
* Method used to verify if the given <code>List</code> contains at most one <code>null</code>,
* since those should be put in the "enum" attribute
*
* @param toCheck
*/
static void checkEvaluatedUnaryTestsForNull(List<Object> toCheck) {
if (toCheck.stream().filter(Objects::isNull).toList().size() > 1) {
throw new IllegalArgumentException("More then one object is null, only one allowed at maximum");
}
}

/**
* Method used to verify if the given <code>List</code> contains the same type of <code>Object</code>s,
* since those should be put in the "enum" attribute
*
* @param toCheck
*/
static void checkEvaluatedUnaryTestsForTypeConsistency(List<Object> toCheck) {
if (toCheck.stream().filter(Objects::nonNull)
.map(Object::getClass)
.collect(Collectors.toUnmodifiableSet())
.size() > 1) {
throw new IllegalArgumentException("Different types of objects, only one allowed");
}
}

private FEELSchemaEnum() {
Expand Down

This file was deleted.

Loading

0 comments on commit f3af948

Please sign in to comment.