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

feat(openapi): support structured properties & forms #9652

Merged
merged 4 commits into from
Jan 20, 2024
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,7 @@ 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();
Expand All @@ -195,11 +196,21 @@ private void generateSchema(final File file) {
} else {
fileBaseName = getBaseName(file.getName());
}
Files.write(Paths.get(jsonDirectory + sep + fileBaseName + ".json"),

final String targetName;
if (schema.has("Aspect") && schema.get("Aspect").has("name") &&
!schema.get("Aspect").get("name").asText().equalsIgnoreCase(fileBaseName)) {
targetName = OpenApiEntities.toUpperFirst(schema.get("Aspect").get("name").asText());
prettySchema = prettySchema.replaceAll(fileBaseName, targetName);
} else {
targetName = fileBaseName;
}

Files.write(Paths.get(jsonDirectory + sep + targetName + ".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())));
aspectType.add(NODE_FACTORY.objectNode().put("$ref", "#/definitions/" + targetName));
}
} catch (IOException | ProcessingException e) {
throw new RuntimeException(e);
Expand Down
30 changes: 24 additions & 6 deletions buildSrc/src/main/java/io/datahubproject/OpenApiEntities.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
import com.fasterxml.jackson.dataformat.yaml.YAMLMapper;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.linkedin.metadata.models.registry.config.Entities;
import com.linkedin.metadata.models.registry.config.Entity;
Expand Down Expand Up @@ -58,8 +59,12 @@ public class OpenApiEntities {
.add("notebookInfo").add("editableNotebookProperties")
.add("dataProductProperties")
.add("institutionalMemory")
.add("forms").add("formInfo").add("dynamicFormAssignment")
.build();

private final static ImmutableSet<String> ENTITY_EXCLUSIONS = ImmutableSet.<String>builder()
.add("structuredProperty")
.build();

public OpenApiEntities(JsonNodeFactory NODE_FACTORY) {
this.NODE_FACTORY = NODE_FACTORY;
Expand Down Expand Up @@ -117,14 +122,27 @@ public ObjectNode entityExtension(List<ObjectNode> nodesList, ObjectNode schemas
return componentsNode;
}

private static String toUpperFirst(String s) {
return s.substring(0, 1).toUpperCase() + s.substring(1);
/**
* Convert the pdl model names to desired class names. Upper case first letter unless the 3rd character is upper case.
* i.e. mlModel -> MLModel
* dataset -> Dataset
* dataProduct -> DataProduct
* @param s input string
* @return class name
*/
public static String toUpperFirst(String s) {
if (s.length() > 2 && s.substring(2, 3).equals(s.substring(2, 3).toUpperCase())) {
david-leifker marked this conversation as resolved.
Show resolved Hide resolved
return s.substring(0, 2).toUpperCase() + s.substring(2);
} else {
return s.substring(0, 1).toUpperCase() + s.substring(1);
}
}

private Set<String> withEntitySchema(ObjectNode schemasNode, Set<String> definitions) {
return entityMap.values().stream()
// Make sure the primary key is defined
.filter(entity -> definitions.contains(toUpperFirst(entity.getKeyAspect())))
.filter(entity -> !ENTITY_EXCLUSIONS.contains(entity.getName()))
.map(entity -> {
final String upperName = toUpperFirst(entity.getName());

Expand Down Expand Up @@ -547,7 +565,7 @@ private ObjectNode buildSingleEntityAspectPath(Entity entity, String aspect) {

ObjectNode getMethod = NODE_FACTORY.objectNode()
.put("summary", String.format("Get %s for %s.", aspect, entity.getName()))
.put("operationId", String.format("get%s", upperFirstAspect, upperFirstEntity));
.put("operationId", String.format("get%s", upperFirstAspect));
getMethod.set("tags", tagsNode);
ArrayNode singlePathParametersNode = NODE_FACTORY.arrayNode();
getMethod.set("parameters", singlePathParametersNode);
Expand Down Expand Up @@ -575,13 +593,13 @@ private ObjectNode buildSingleEntityAspectPath(Entity entity, String aspect) {
.set("application/json", NODE_FACTORY.objectNode())));
ObjectNode headMethod = NODE_FACTORY.objectNode()
.put("summary", String.format("%s on %s existence.", aspect, upperFirstEntity))
.put("operationId", String.format("head%s", upperFirstAspect, upperFirstEntity))
.put("operationId", String.format("head%s", upperFirstAspect))
.set("responses", headResponses);
headMethod.set("tags", tagsNode);

ObjectNode deleteMethod = NODE_FACTORY.objectNode()
.put("summary", String.format("Delete %s on entity %s", aspect, upperFirstEntity))
.put("operationId", String.format("delete%s", upperFirstAspect, upperFirstEntity))
.put("operationId", String.format("delete%s", upperFirstAspect))
.set("responses", NODE_FACTORY.objectNode()
.set("200", NODE_FACTORY.objectNode()
.put("description", String.format("Delete %s on %s entity.", aspect, upperFirstEntity))
Expand All @@ -591,7 +609,7 @@ private ObjectNode buildSingleEntityAspectPath(Entity entity, String aspect) {

ObjectNode postMethod = NODE_FACTORY.objectNode()
.put("summary", String.format("Create aspect %s on %s ", aspect, upperFirstEntity))
.put("operationId", String.format("create%s", upperFirstAspect, upperFirstEntity));
.put("operationId", String.format("create%s", upperFirstAspect));
postMethod.set("requestBody", NODE_FACTORY.objectNode()
.put("description", String.format("Create aspect %s on %s entity.", aspect, upperFirstEntity))
.put("required", true).set("content", NODE_FACTORY.objectNode()
Expand Down
2 changes: 2 additions & 0 deletions docker/profiles/docker-compose.gms.yml
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,8 @@ x-datahub-gms-service: &datahub-gms-service
timeout: 5s
volumes:
- ${HOME}/.datahub/plugins:/etc/datahub/plugins
labels:
io.datahubproject.datahub.component: "gms"

x-datahub-gms-service-dev: &datahub-gms-service-dev
<<: *datahub-gms-service
Expand Down
15 changes: 12 additions & 3 deletions docs-website/sidebars.js
Original file line number Diff line number Diff line change
Expand Up @@ -561,9 +561,18 @@ module.exports = {
],
},
{
type: "doc",
label: "OpenAPI",
id: "docs/api/openapi/openapi-usage-guide",
OpenAPI: [
{
type: "doc",
label: "OpenAPI",
id: "docs/api/openapi/openapi-usage-guide",
},
{
type: "doc",
label: "Structured Properties",
id: "docs/api/openapi/openapi-structured-properties",
},
],
},
"docs/dev-guides/timeline",
{
Expand Down
Loading
Loading