Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
c84340c
feat(openAPI): support initial openapi endpoints for get, delete, post
RyanHolstien Apr 13, 2022
c0d6029
feat(openAPI): checkstyle violations
RyanHolstien Apr 13, 2022
2d70d9f
feat(openAPI): test
RyanHolstien Apr 13, 2022
b1c3eae
feat(openAPI): test 2
RyanHolstien Apr 14, 2022
ac70a6b
feat(openAPI): test 3
RyanHolstien Apr 14, 2022
e0c61ca
feat(openAPI): update mocks to match interface updates
RyanHolstien Apr 14, 2022
94633b1
feat(openAPI): change file extension discovery for EnvelopedAspect to…
RyanHolstien Apr 14, 2022
e7a3766
feat(openAPI): check if EnvelopedAspect is picking up other file
RyanHolstien Apr 14, 2022
cfec5a0
feat(openAPI): test fixes
RyanHolstien Apr 14, 2022
09cc2e4
feat(openAPI): more test fixes
RyanHolstien Apr 14, 2022
088ba2c
feat(openAPI): test out logging
RyanHolstien Apr 14, 2022
b885b8f
feat(openAPI): apply fix
RyanHolstien Apr 14, 2022
e947107
feat(openAPI): fix test for urn validation
RyanHolstien Apr 14, 2022
eabe76b
feat(openApi): entities endpoints - update response values to only re…
RyanHolstien Apr 26, 2022
5f30cb9
feat(openapi): update test for master changes
RyanHolstien Apr 26, 2022
c603695
feat(openAPI): add initial docs
RyanHolstien Apr 26, 2022
3aaa3ec
feat(openapi): fix checkstyle warning
RyanHolstien Apr 26, 2022
a4bd391
feat(openapi): fix yarnlint
RyanHolstien Apr 26, 2022
8271a9f
feat(openapi): remove need to send aspect name in api
RyanHolstien Apr 27, 2022
3348ed1
feat(openapi): fix typing issues
RyanHolstien Apr 27, 2022
4cf394d
feat(openapi): update test and docs
RyanHolstien Apr 28, 2022
c85a2b4
lintfix
RyanHolstien Apr 29, 2022
55201ab
add methods to mock service
RyanHolstien May 2, 2022
e3c1140
fix test
RyanHolstien May 2, 2022
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
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,11 @@
*gma-create-all.sql
*gma-drop-all.sql

# Pegasus & Avro
# Pegasus, Avro, & other schemas
**/src/mainGenerated*
**/src/generatedJsonSchema
**/metadata-io/generated
**/metadata-integration/java/datahub-client/generated
**/src/testGenerated*
metadata-events/mxe-registration/src/main/resources/**/*.avsc

Expand Down
3 changes: 3 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ buildscript {
}
classpath "io.codearte.gradle.nexus:gradle-nexus-staging-plugin:0.30.0"
classpath "com.palantir.gradle.gitversion:gradle-git-version:0.12.3"
classpath "gradle.plugin.org.hidetake:gradle-swagger-generator-plugin:2.18.1"
}
}

Expand Down Expand Up @@ -80,10 +81,12 @@ project.ext.externalDependency = [
'jacksonDataFormatYaml': 'com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.9.10',
'javatuples': 'org.javatuples:javatuples:1.2',
'javaxInject' : 'javax.inject:javax.inject:1',
'javaxValidation' : 'javax.validation:validation-api:2.0.1.Final',
'jerseyCore': 'org.glassfish.jersey.core:jersey-client:2.25.1',
'jerseyGuava': 'org.glassfish.jersey.bundles.repackaged:jersey-guava:2.25.1',
'jettyJaas': 'org.eclipse.jetty:jetty-jaas:9.4.32.v20200930',
'jgrapht': 'org.jgrapht:jgrapht-core:1.5.1',
'jsonSchemaAvro': 'com.github.fge:json-schema-avro:0.1.4',
'jsonSimple': 'com.googlecode.json-simple:json-simple:1.1.1',
'junitJupiterApi': "org.junit.jupiter:junit-jupiter-api:$junitJupiterVersion",
'junitJupiterParams': "org.junit.jupiter:junit-jupiter-params:$junitJupiterVersion",
Expand Down
16 changes: 16 additions & 0 deletions buildSrc/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
apply plugin: 'java'

buildscript {
apply from: '../repositories.gradle'
}

dependencies {
compile('io.acryl:json-schema-avro:0.1.5') {
exclude group: 'com.fasterxml.jackson.core', module: 'jackson-databind'
exclude group: 'com.google.guava', module: 'guava'
}
compile 'com.google.guava:guava:27.0.1-jre'
compile 'com.fasterxml.jackson.core:jackson-databind:2.9.10.7'
compile 'com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.8.11'
compile 'commons-io:commons-io:2.11.0'
}
189 changes: 189 additions & 0 deletions buildSrc/src/main/java/io/datahubproject/GenerateJsonSchemaTask.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
package io.datahubproject;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.dataformat.yaml.YAMLMapper;
import com.github.fge.jackson.JacksonUtils;
import com.github.fge.jackson.JsonLoader;
import com.github.fge.jsonschema.core.exceptions.ProcessingException;
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.FileAlreadyExistsException;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.HashSet;
import java.util.List;
import java.util.stream.Collectors;
import org.gradle.api.DefaultTask;
import org.gradle.api.tasks.CacheableTask;
import org.gradle.api.tasks.InputDirectory;
import org.gradle.api.tasks.OutputDirectory;
import org.gradle.api.tasks.TaskAction;

import static com.github.fge.processing.ProcessingUtil.*;
import static org.apache.commons.io.FilenameUtils.*;


@CacheableTask
public class GenerateJsonSchemaTask extends DefaultTask {
private String inputDirectory;
private String outputDirectory;
private ArrayNode aspectType;
private Path combinedDirectory;
private Path jsonDirectory;
public static final String sep = FileSystems.getDefault().getSeparator();

private static final JsonNodeFactory NODE_FACTORY = JacksonUtils.nodeFactory();

public void setInputDirectory(String inputDirectory) {
this.inputDirectory = inputDirectory;
}

@InputDirectory
public String getInputDirectory() {
return inputDirectory;
}

@OutputDirectory
public String getOutputDirectory() {
return outputDirectory;
}

public void setOutputDirectory(String outputDirectory) {
this.outputDirectory = outputDirectory;
}

@TaskAction
public void generate() throws IOException {
Path baseDir = Paths.get(inputDirectory);
aspectType = NODE_FACTORY.arrayNode();

Path schemaDirectory = Paths.get(outputDirectory);
jsonDirectory = Paths.get(schemaDirectory + sep + "json");
try {
Files.createDirectory(schemaDirectory);
} catch (FileAlreadyExistsException fae) {
// No-op
}
try {
Files.createDirectory(jsonDirectory);
} catch (FileAlreadyExistsException fae) {
// No-op
}
Files.walk(baseDir)
.filter(Files::isRegularFile)
.map(Path::toFile)
.forEach(this::generateSchema);
List<ObjectNode> nodesList = Files.walk(jsonDirectory)
.filter(Files::isRegularFile)
.filter(path -> {
String fileName = path.toFile().getName();
return !getBaseName(fileName).contains("ChangeEvent") && !getBaseName(fileName).contains("AuditEvent");
})
.map(Path::toFile)
.map(file -> {
try {
return (ObjectNode) JsonLoader.fromFile(file);
} catch (IOException e) {
throw new RuntimeException(e);
}
})
.collect(Collectors.toList());

ObjectNode schemasNode = NODE_FACTORY.objectNode();
nodesList.forEach(objectNode -> {
ObjectNode definitions = (ObjectNode) objectNode.get("definitions");
// Modify GenericAspect and EnvelopedAspect to have all Aspect subtypes
if (definitions.has("GenericAspect")) {
ObjectNode genericAspect = (ObjectNode) definitions.get("GenericAspect");
((ObjectNode) genericAspect.get("properties")).replace("value", NODE_FACTORY.objectNode().set("oneOf", aspectType));
}
if (definitions.has("EnvelopedAspect")) {
ObjectNode envelopedAspect = (ObjectNode) definitions.get("EnvelopedAspect");
((ObjectNode) envelopedAspect.get("properties")).replace("value", NODE_FACTORY.objectNode().set("oneOf", aspectType));
}
schemasNode.setAll(definitions);
});
/*
Minimal OpenAPI header
openapi: 3.0.1
info:
title: OpenAPI definition
version: v0
servers:
- url: http://localhost:8080/openapi
description: Generated server url
paths:
/path:
get:
tags:
- path
*/
ObjectNode yamlHeader = (ObjectNode) ((ObjectNode) NODE_FACTORY.objectNode()
.put("openapi", "3.0.1")
.set("info", NODE_FACTORY.objectNode()
.put("title", "OpenAPI Definition")
.put("version", "v0")))
.set("paths", NODE_FACTORY.objectNode()
.set("/path", NODE_FACTORY.objectNode()
.set("get", NODE_FACTORY.objectNode().set("tags", NODE_FACTORY.arrayNode().add("path")))));
JsonNode combinedSchemaDefinitionsYaml = ((ObjectNode) NODE_FACTORY.objectNode().set("components",
NODE_FACTORY.objectNode().set("schemas", schemasNode))).setAll(yamlHeader);

final String yaml = new YAMLMapper().writeValueAsString(combinedSchemaDefinitionsYaml)
.replaceAll("definitions", "components/schemas")
.replaceAll("\n\\s+- type: \"null\"", "");

combinedDirectory = Paths.get(outputDirectory + sep + "combined");
try {
Files.createDirectory(combinedDirectory);
} catch (FileAlreadyExistsException fae) {
// No-op
}
Files.write(Paths.get(combinedDirectory + sep + "open-api.yaml"),
yaml.getBytes(StandardCharsets.UTF_8), StandardOpenOption.WRITE, StandardOpenOption.CREATE,
StandardOpenOption.TRUNCATE_EXISTING);

JsonNode combinedSchemaDefinitionsJson = NODE_FACTORY.objectNode().set("definitions",schemasNode);
String prettySchema = JacksonUtils.prettyPrint(combinedSchemaDefinitionsJson);
Files.write(Paths.get(Paths.get(outputDirectory) + sep + "combined" + sep + "schema.json"),
prettySchema.getBytes(StandardCharsets.UTF_8), StandardOpenOption.WRITE, StandardOpenOption.CREATE,
StandardOpenOption.TRUNCATE_EXISTING);

}

private final HashSet<String> filenames = new HashSet<>();
private void generateSchema(final File file) {
final String fileBaseName;
try {
final JsonNode schema = JsonLoader.fromFile(file);
final JsonNode result = buildResult(schema.toString());
String prettySchema = JacksonUtils.prettyPrint(result);
Path absolutePath = file.getAbsoluteFile().toPath();
if (absolutePath.endsWith(Paths.get("com", "linkedin", "metadata", "aspect", "EnvelopedAspect.avsc"))) {
fileBaseName = "EnvelopedTimeseriesAspect";
prettySchema = prettySchema.replaceAll("EnvelopedAspect", "EnvelopedTimeseriesAspect");
} else if (!filenames.add(file.getName())) {
System.out.println("Not processing legacy schema " + absolutePath);
return;
} else {
fileBaseName = getBaseName(file.getName());
}
Files.write(Paths.get(jsonDirectory + sep + fileBaseName + ".json"),
prettySchema.getBytes(StandardCharsets.UTF_8), StandardOpenOption.WRITE, StandardOpenOption.CREATE,
StandardOpenOption.TRUNCATE_EXISTING);
if (schema.has("Aspect")) {
aspectType.add(NODE_FACTORY.objectNode().put("$ref", "#/definitions/" + getBaseName(file.getName())));
}
} catch (IOException | ProcessingException e) {
throw new RuntimeException(e);
}
}

}
7 changes: 7 additions & 0 deletions docs-website/sidebars.js
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,13 @@ module.exports = {
],
},
],
OpenAPI: [
{
label: "Usage Guide",
type: "doc",
id: "docs/api/openapi/openapi-usage-guide",
},
],
"Usage Guides": [
"docs/policies",
"docs/domains",
Expand Down
Loading