Skip to content
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

Support expression for primitive type in json schema #751

Merged
merged 52 commits into from
Jan 20, 2024
Merged
Show file tree
Hide file tree
Changes from 49 commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
2cef977
Adjust padding to accommodate good enough headers and don't include …
akrambek Oct 25, 2023
d201582
Merge branch 'develop' into feature/consumer-group-cont
akrambek Oct 25, 2023
76bf9de
Merge branch 'feature/consumer-group-cont' into develop
akrambek Oct 26, 2023
29ae79c
Merge branch 'aklivity:develop' into develop
akrambek Oct 30, 2023
ec1b39e
Merge branch 'aklivity:develop' into develop
akrambek Oct 30, 2023
51a9f0e
Merge branch 'aklivity:develop' into develop
akrambek Oct 31, 2023
4394783
Merge branch 'aklivity:develop' into develop
akrambek Oct 31, 2023
e8696ce
Merge branch 'aklivity:develop' into develop
akrambek Nov 2, 2023
51c37b1
Merge branch 'aklivity:develop' into develop
akrambek Nov 2, 2023
5da5f04
Merge branch 'aklivity:develop' into develop
akrambek Nov 2, 2023
db1e17c
Merge branch 'aklivity:develop' into develop
akrambek Nov 4, 2023
40f73dc
Merge branch 'aklivity:develop' into develop
akrambek Nov 6, 2023
d1a0492
Merge branch 'aklivity:develop' into develop
akrambek Nov 23, 2023
45799ce
Merge branch 'aklivity:develop' into develop
akrambek Nov 29, 2023
1e55162
Merge branch 'aklivity:develop' into develop
akrambek Nov 30, 2023
fedc41f
Merge branch 'aklivity:develop' into develop
akrambek Dec 4, 2023
18a8d74
Merge branch 'aklivity:develop' into develop
akrambek Dec 4, 2023
f160aad
Merge branch 'aklivity:develop' into develop
akrambek Dec 4, 2023
e0e7d5a
Merge branch 'aklivity:develop' into develop
akrambek Dec 6, 2023
9f4a8a6
Merge branch 'aklivity:develop' into develop
akrambek Dec 8, 2023
456f111
Merge branch 'aklivity:develop' into develop
akrambek Dec 8, 2023
0d27262
Merge branch 'aklivity:develop' into develop
akrambek Dec 9, 2023
9fe7a91
Merge branch 'aklivity:develop' into develop
akrambek Dec 11, 2023
7e3d237
Merge branch 'aklivity:develop' into develop
akrambek Dec 12, 2023
33c4411
Merge branch 'aklivity:develop' into develop
akrambek Dec 13, 2023
fe9e318
Merge branch 'aklivity:develop' into develop
akrambek Dec 14, 2023
d8b5e5c
Merge branch 'aklivity:develop' into develop
akrambek Dec 14, 2023
ebca7ef
Merge branch 'aklivity:develop' into develop
akrambek Dec 18, 2023
5e3e059
Merge branch 'aklivity:develop' into develop
akrambek Dec 22, 2023
ee71db9
Merge branch 'aklivity:develop' into develop
akrambek Dec 24, 2023
0b7a15a
Merge branch 'aklivity:develop' into develop
akrambek Dec 25, 2023
be13489
Merge branch 'aklivity:develop' into develop
akrambek Dec 26, 2023
95df84c
Merge branch 'aklivity:develop' into develop
akrambek Dec 26, 2023
3ebdbf5
Merge branch 'aklivity:develop' into develop
akrambek Dec 28, 2023
24ad9e1
Merge branch 'aklivity:develop' into develop
akrambek Dec 30, 2023
6d21fec
Merge branch 'aklivity:develop' into develop
akrambek Dec 31, 2023
368a0a6
Merge branch 'aklivity:develop' into develop
akrambek Dec 31, 2023
7069f1a
Merge branch 'aklivity:develop' into develop
akrambek Jan 2, 2024
09b7041
Merge branch 'aklivity:develop' into develop
akrambek Jan 3, 2024
98f1faa
Merge branch 'aklivity:develop' into develop
akrambek Jan 4, 2024
371391a
Merge branch 'aklivity:develop' into develop
akrambek Jan 5, 2024
c6a0882
Merge branch 'aklivity:develop' into develop
akrambek Jan 8, 2024
f99f009
Merge branch 'aklivity:develop' into develop
akrambek Jan 9, 2024
a110b68
Merge branch 'aklivity:develop' into develop
akrambek Jan 11, 2024
80c4625
Merge branch 'aklivity:develop' into develop
akrambek Jan 16, 2024
0fe8153
Support expression for primitive type in json schema
akrambek Jan 19, 2024
6617e20
Merge branch 'aklivity:develop' into develop
akrambek Jan 19, 2024
dd9ef56
Merge branch 'develop' into bug/583
akrambek Jan 19, 2024
876dd76
Change oneOf to anyOf
akrambek Jan 19, 2024
698607b
Remove redundent expression config
akrambek Jan 19, 2024
4f60382
Add test to support expression validation
akrambek Jan 20, 2024
79ea2ad
Add negative test
akrambek Jan 20, 2024
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
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import static java.util.Collections.singletonMap;
import static org.agrona.LangUtil.rethrowUnchecked;

import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.io.StringReader;
Expand All @@ -29,10 +30,14 @@
import java.util.List;
import java.util.function.Consumer;

import jakarta.json.Json;
import jakarta.json.JsonArray;
import jakarta.json.JsonArrayBuilder;
import jakarta.json.JsonObject;
import jakarta.json.JsonObjectBuilder;
import jakarta.json.JsonPatch;
import jakarta.json.JsonReader;
import jakarta.json.JsonValue;
import jakarta.json.bind.Jsonb;
import jakarta.json.bind.JsonbBuilder;
import jakarta.json.bind.JsonbConfig;
Expand All @@ -46,21 +51,25 @@
import org.leadpony.justify.api.ProblemHandler;

import io.aklivity.zilla.runtime.engine.Engine;
import io.aklivity.zilla.runtime.engine.expression.ExpressionResolver;
import io.aklivity.zilla.runtime.engine.internal.config.NamespaceAdapter;
import io.aklivity.zilla.runtime.engine.internal.config.schema.UniquePropertyKeysSchema;

public final class EngineConfigReader
{
private final ConfigAdapterContext context;
private final ExpressionResolver expressions;
private final Collection<URL> schemaTypes;
private final Consumer<String> logger;

public EngineConfigReader(
ConfigAdapterContext context,
ExpressionResolver expressions,
Collection<URL> schemaTypes,
Consumer<String> logger)
{
this.context = context;
this.expressions = expressions;
this.schemaTypes = schemaTypes;
this.logger = logger;
}
Expand Down Expand Up @@ -91,18 +100,13 @@ public EngineConfig read(
schemaObject = schemaPatch.apply(schemaObject);
}

if (logger != null)
if (!validateAnnotatedSchema(schemaObject, schemaProvider, errors, configText))
{
final StringWriter out = new StringWriter();
schemaProvider.createGeneratorFactory(singletonMap(PRETTY_PRINTING, true))
.createGenerator(out)
.write(schemaObject)
.close();

final String schemaText = out.getBuffer().toString();
logger.accept(schemaText);
break read;
}

configText = expressions.resolve(configText);

JsonParser schemaParser = schemaProvider.createParserFactory(null)
.createParser(new StringReader(schemaObject.toString()));

Expand Down Expand Up @@ -139,11 +143,11 @@ public EngineConfig read(
}

JsonbConfig config = new JsonbConfig()
.withAdapters(new NamespaceAdapter(context));
.withAdapters(new NamespaceAdapter(context));
Jsonb jsonb = JsonbBuilder.newBuilder()
.withProvider(provider)
.withConfig(config)
.build();
.withProvider(provider)
.withConfig(config)
.build();

Reader reader = new StringReader(readable);
EngineConfigBuilder<EngineConfig> builder = EngineConfig.builder();
Expand Down Expand Up @@ -174,4 +178,158 @@ public EngineConfig read(

return engine;
}

private boolean validateAnnotatedSchema(
JsonObject schemaObject,
JsonProvider schemaProvider,
List<Exception> errors,
String configText)
{
boolean valid = false;

validate:
try
{
final JsonObject annotatedSchemaObject = (JsonObject) annotateJsonObject(schemaObject);

if (logger != null)
{
final StringWriter out = new StringWriter();
schemaProvider.createGeneratorFactory(singletonMap(PRETTY_PRINTING, true))
.createGenerator(out)
.write(annotatedSchemaObject)
.close();

final String schemaText = out.getBuffer().toString();
logger.accept(schemaText);
}

final JsonParser schemaParser = schemaProvider.createParserFactory(null)
.createParser(new StringReader(schemaObject.toString()));

final JsonValidationService service = JsonValidationService.newInstance();
ProblemHandler handler = service.createProblemPrinter(msg -> errors.add(new ConfigException(msg)));
final JsonSchemaReader validator = service.createSchemaReader(schemaParser);
final JsonSchema schema = new UniquePropertyKeysSchema(validator.read());

String readable = configText.stripTrailing();

IntArrayList configsAt = new IntArrayList();
for (int configAt = 0; configAt < readable.length(); )
{
configsAt.addInt(configAt);

Reader reader = new StringReader(readable);
reader.skip(configAt);

try (JsonParser parser = service.createParser(reader, schema, handler))
{
while (parser.hasNext())
{
parser.next();
}

configAt += (int) parser.getLocation().getStreamOffset();
}

if (!errors.isEmpty())
{
break validate;
}
}

valid = true;
}
catch (IOException ex)
{
errors.add(ex);
}

return valid;

}

private JsonValue annotateJsonObject(
JsonObject jsonObject)
{
JsonObjectBuilder builder = Json.createObjectBuilder();

jsonObject.forEach((key, value) ->
{
if ("expression".equals(key))
{
builder.add(key, value);
}
else if (value.getValueType() == JsonValue.ValueType.OBJECT)
{
builder.add(key, annotateJsonObject(value.asJsonObject()));
}
else if (value.getValueType() == JsonValue.ValueType.ARRAY)
{
builder.add(key, annotateJsonArray(value.asJsonArray()));
}
else if (key.equals("type") &&
isPrimitiveType(value.toString().replaceAll("\"", "")))
{
JsonValue pattern = jsonObject.get("pattern");
builder.add(key, value);
builder.add("anyOf", createOneOfTypes(value.toString().replaceAll("\"", ""), pattern));
}
else if (!"pattern".equals(key))
{
builder.add(key, value);
}
});

return builder.build();
}

private JsonValue annotateJsonArray(
JsonArray jsonArray)
{
JsonArrayBuilder arrayBuilder = Json.createArrayBuilder();

jsonArray.forEach(item ->
{
if (item.getValueType() == JsonValue.ValueType.OBJECT)
{
arrayBuilder.add(annotateJsonObject(item.asJsonObject()));
}
else
{
arrayBuilder.add(item);
}
});

return arrayBuilder.build();
}

private boolean isPrimitiveType(
String type)
{
return "string".equals(type) ||
"integer".equals(type) ||
"boolean".equals(type) ||
"number".equals(type);
}

private JsonArray createOneOfTypes(
String originalType,
JsonValue pattern)
{
JsonArrayBuilder oneOfArrayBuilder = Json.createArrayBuilder();
JsonObjectBuilder objectBuilder = Json.createObjectBuilder();
objectBuilder.add("type", originalType);
if (pattern != null)
{
objectBuilder.add("pattern", pattern);
}
oneOfArrayBuilder.add(objectBuilder);

oneOfArrayBuilder.add(Json.createObjectBuilder()
.add("$ref", "#/$defs/expression")
);

return oneOfArrayBuilder.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -171,17 +171,13 @@ private EngineConfig parse(

logger.accept(configText);

if (config.configResolveExpressions())
{
configText = expressions.resolve(configText);
}

try
{
final Function<String, String> namespaceReadURL = l -> readURL.apply(configURL, l);

EngineConfigReader reader = new EngineConfigReader(
new NamespaceConfigAdapterContext(namespaceReadURL),
expressions,
schemaTypes,
config.verboseSchema() ? logger : null);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,10 @@

"$defs":
{
"expression":{
"type": "string",
"pattern": "\\$\\{\\{\\s*([^\\s\\}]*)\\.([^\\s\\}]*)\\s*\\}\\}"
},
"vault":
{
"type": "object",
Expand Down
Loading