fix(misc-openapi): fix openlineage, platform events & swagger (#12539)

This commit is contained in:
david-leifker 2025-02-04 08:26:12 -06:00 committed by GitHub
parent d1e8a0a145
commit a4f64fd49b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 87 additions and 20 deletions

View File

@ -28,7 +28,7 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@EnableWebMvc @EnableWebMvc
@OpenAPIDefinition( @OpenAPIDefinition(
info = @Info(title = "DataHub OpenAPI", version = "2.0.0"), info = @Info(title = "DataHub OpenAPI", version = "2.0.0"),
servers = {@Server(url = "/openapi/", description = "Default Server URL")}) servers = {@Server(url = "/", description = "Default Server URL")})
@Order(2) @Order(2)
@Configuration @Configuration
public class SpringWebConfig implements WebMvcConfigurer { public class SpringWebConfig implements WebMvcConfigurer {

View File

@ -27,7 +27,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
@RestController @RestController
@RequestMapping("/openlineage/api/v1") @RequestMapping("/openapi/openlineage/api/v1")
@Slf4j @Slf4j
public class LineageApiImpl implements LineageApi { public class LineageApiImpl implements LineageApi {
private static final ObjectMapper OBJECT_MAPPER = OpenLineageClientUtils.newObjectMapper(); private static final ObjectMapper OBJECT_MAPPER = OpenLineageClientUtils.newObjectMapper();

View File

@ -39,7 +39,7 @@ import org.springframework.web.bind.annotation.RestController;
@RestController @RestController
@RequiredArgsConstructor @RequiredArgsConstructor
@RequestMapping("/openapiv2/platform/entities/v1") @RequestMapping("/openapi/v2/platform/entities/v1")
@Slf4j @Slf4j
@Tag( @Tag(
name = "Platform Entities", name = "Platform Entities",

View File

@ -35,6 +35,7 @@ import java.util.stream.Stream;
@SuppressWarnings({"rawtypes", "unchecked"}) @SuppressWarnings({"rawtypes", "unchecked"})
public class OpenAPIV3Generator { public class OpenAPIV3Generator {
private static final String PATH_PREFIX = "/openapi/v3";
private static final String MODEL_VERSION = "_v3"; private static final String MODEL_VERSION = "_v3";
private static final String TYPE_OBJECT = "object"; private static final String TYPE_OBJECT = "object";
private static final String TYPE_BOOLEAN = "boolean"; private static final String TYPE_BOOLEAN = "boolean";
@ -171,18 +172,19 @@ public class OpenAPIV3Generator {
final Paths paths = new Paths(); final Paths paths = new Paths();
// --> Cross-entity Paths // --> Cross-entity Paths
paths.addPathItem("/v3/entity/scroll", buildGenericListEntitiesPath()); paths.addPathItem(PATH_PREFIX + "/entity/scroll", buildGenericListEntitiesPath());
// --> Entity Paths // --> Entity Paths
definedEntitySpecs.forEach( definedEntitySpecs.forEach(
e -> { e -> {
paths.addPathItem( paths.addPathItem(
String.format("/v3/entity/%s", e.getName().toLowerCase()), buildListEntityPath(e)); String.format(PATH_PREFIX + "/entity/%s", e.getName().toLowerCase()),
buildListEntityPath(e));
paths.addPathItem( paths.addPathItem(
String.format("/v3/entity/%s/batchGet", e.getName().toLowerCase()), String.format(PATH_PREFIX + "/entity/%s/batchGet", e.getName().toLowerCase()),
buildBatchGetEntityPath(e)); buildBatchGetEntityPath(e));
paths.addPathItem( paths.addPathItem(
String.format("/v3/entity/%s/{urn}", e.getName().toLowerCase()), String.format(PATH_PREFIX + "/entity/%s/{urn}", e.getName().toLowerCase()),
buildSingleEntityPath(e)); buildSingleEntityPath(e));
}); });
@ -196,8 +198,9 @@ public class OpenAPIV3Generator {
a -> a ->
paths.addPathItem( paths.addPathItem(
String.format( String.format(
"/v3/entity/%s/{urn}/%s", PATH_PREFIX + "/entity/%s/{urn}/%s",
e.getName().toLowerCase(), a.getName().toLowerCase()), e.getName().toLowerCase(),
a.getName().toLowerCase()),
buildSingleEntityAspectPath(e, a)))); buildSingleEntityAspectPath(e, a))));
definedEntitySpecs.forEach( definedEntitySpecs.forEach(
e -> e ->
@ -208,8 +211,9 @@ public class OpenAPIV3Generator {
a -> a ->
paths.addPathItem( paths.addPathItem(
String.format( String.format(
"/v3/entity/%s/{urn}/%s", PATH_PREFIX + "/entity/%s/{urn}/%s",
e.getName().toLowerCase(), a.getName().toLowerCase()), e.getName().toLowerCase(),
a.getName().toLowerCase()),
buildSingleEntityAspectPath(e, a)))); buildSingleEntityAspectPath(e, a))));
// --> Link & Unlink APIs // --> Link & Unlink APIs
@ -219,7 +223,8 @@ public class OpenAPIV3Generator {
.forEach( .forEach(
entitySpec -> { entitySpec -> {
paths.addPathItem( paths.addPathItem(
"/v3/entity/versioning/{versionSetUrn}/relationship/versionOf/{entityUrn}", PATH_PREFIX
+ "/entity/versioning/{versionSetUrn}/relationship/versionOf/{entityUrn}",
buildVersioningRelationshipPath()); buildVersioningRelationshipPath());
}); });
} }

View File

@ -4,9 +4,6 @@ import com.fasterxml.jackson.databind.ObjectMapper;
import com.linkedin.gms.factory.kafka.schemaregistry.InternalSchemaRegistryFactory; import com.linkedin.gms.factory.kafka.schemaregistry.InternalSchemaRegistryFactory;
import com.linkedin.metadata.registry.SchemaRegistryService; import com.linkedin.metadata.registry.SchemaRegistryService;
import io.datahubproject.openapi.schema.registry.SchemaRegistryController; import io.datahubproject.openapi.schema.registry.SchemaRegistryController;
import io.swagger.v3.oas.annotations.OpenAPIDefinition;
import io.swagger.v3.oas.annotations.info.Info;
import io.swagger.v3.oas.annotations.servers.Server;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Qualifier;
@ -19,9 +16,6 @@ import org.springframework.web.servlet.config.annotation.EnableWebMvc;
@Slf4j @Slf4j
@EnableWebMvc @EnableWebMvc
@OpenAPIDefinition(
info = @Info(title = "DataHub OpenAPI", version = "1.0.0"),
servers = {@Server(url = "/schema-registry/", description = "Schema Registry Server URL")})
@Order(3) @Order(3)
@ConditionalOnProperty( @ConditionalOnProperty(
name = "kafka.schemaRegistry.type", name = "kafka.schemaRegistry.type",

View File

@ -0,0 +1,34 @@
[
{
"request": {
"url": "/openapi/openlineage/api/v1/lineage",
"description": "Post openlineage",
"json": {
"eventType": "START",
"eventTime": "2020-12-28T19:52:00.001+10:00",
"run": {
"runId": "d46e465b-d358-4d32-83d4-df660ff614dd"
},
"job": {
"namespace": "workshop",
"name": "process_taxes"
},
"inputs": [
{
"namespace": "postgres://workshop-db:None",
"name": "workshop.public.taxes",
"facets": {
"dataSource": {
"_producer": "https://github.com/OpenLineage/OpenLineage/tree/0.10.0/integration/airflow",
"_schemaURL": "https://raw.githubusercontent.com/OpenLineage/OpenLineage/main/spec/OpenLineage.json#/definitions/DataSourceDatasetFacet",
"name": "postgres://workshop-db:None",
"uri": "workshop-db"
}
}
}
],
"producer": "https://github.com/OpenLineage/OpenLineage/blob/v1-0-0/client"
}
}
}
]

View File

@ -0,0 +1,31 @@
[
{
"request": {
"url": "/openapi/v2/platform/entities/v1/",
"description": "Create data product",
"params": {
"async": "false"
},
"json": [
{
"entityType": "dataProduct",
"entityUrn": "urn:li:dataProduct:4fd5aea7-a15f-4842-a2cc-e6562605ebf7",
"changeType": "UPSERT",
"aspectName": "status",
"aspect": {
"value": {
"__type": "Status",
"removed": false
},
"contentType": "application/json"
}
}
]
},
"response": {
"json": [
"urn:li:dataProduct:4fd5aea7-a15f-4842-a2cc-e6562605ebf7"
]
}
}
]

View File

@ -57,13 +57,14 @@ def evaluate_test(auth_session, test_name, test_data):
continue continue
url = req_resp["request"]["url"] url = req_resp["request"]["url"]
actual_resp = execute_request(auth_session, req_resp["request"]) actual_resp = execute_request(auth_session, req_resp["request"])
diff = None
try: try:
if "response" in req_resp and "status_codes" in req_resp["response"]: if "response" in req_resp and "status_codes" in req_resp["response"]:
assert ( assert (
actual_resp.status_code in req_resp["response"]["status_codes"] actual_resp.status_code in req_resp["response"]["status_codes"]
) )
else: else:
assert actual_resp.status_code in [200, 202, 204] assert actual_resp.status_code in [200, 201, 202, 204]
if "response" in req_resp: if "response" in req_resp:
if "json" in req_resp["response"]: if "json" in req_resp["response"]:
if "exclude_regex_paths" in req_resp["response"]: if "exclude_regex_paths" in req_resp["response"]:
@ -87,7 +88,9 @@ def evaluate_test(auth_session, test_name, test_data):
) )
if description: if description:
logger.error(f"Step {idx} Description: {description}") logger.error(f"Step {idx} Description: {description}")
logger.error(f"Response content: {actual_resp.content}") if diff:
logger.error(f"Unexpected diff: {diff}")
logger.debug(f"Response content: {actual_resp.content}")
raise e raise e
except Exception as e: except Exception as e:
logger.error(f"Error executing test: {test_name}") logger.error(f"Error executing test: {test_name}")