Skip to content

Commit 46c8baf

Browse files
trietschivan-p92Bob van den Hoogen
authored
feat(pace-69): setup plugin system and add openai gpt4 based data policy generator (#91)
Co-authored-by: Ivan Plantevin <[email protected]> Co-authored-by: Bob van den Hoogen <[email protected]>
1 parent 780ab01 commit 46c8baf

File tree

97 files changed

+11506
-1983
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

97 files changed

+11506
-1983
lines changed

.github/workflows/publish-release-artifacts.yaml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,17 @@ jobs:
4242
buf beta registry tag create "buf.build/getstrm/pace:${buf_commit_hash}" ${{ env.TAG }}
4343
buf_dependency_version=$(buf alpha sdk maven-version --module=buf.build/getstrm/pace:${{ env.TAG }} --plugin=buf.build/protocolbuffers/java:v24.4 | sed "s/24.4.0.1.//")
4444
echo "BUF_DEPENDENCY_VERSION=${buf_dependency_version}" >> "$GITHUB_ENV"
45+
- name: Generate JSON Schema
46+
env:
47+
BUF_TOKEN: ${{ secrets.BUF_TOKEN }}
48+
run: |
49+
cd protos
50+
buf generate
51+
- name: Copy JSON Schema
52+
run: |
53+
find app/src/main/resources/jsonschema -type d -maxdepth 1 -mindepth 1 | xargs -I{} rm -rf {}
54+
mkdir -p app/src/main/resources/jsonschema
55+
cp -r protos/json-schema/* app/src/main/resources/jsonschema
4556
4657
# JVM artifact
4758
- uses: actions/setup-java@v3

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,6 @@ protos/openapi.yaml
1717
integration-test/pace.log
1818
integration-test/awk
1919
integration-test/cut
20+
app/src/test/resources/openai.properties
21+
22+
rest/envoy-local.yaml

Makefile

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@ SHELL := /bin/bash
66
git_branch := $(shell git rev-parse --abbrev-ref HEAD)
77
descriptor_file := "rest/descriptor.binpb"
88

9-
buf-publish-current-branch:
10-
[[ "$$OSTYPE" == "darwin"* ]] && SED=gsed || SED=sed && \
11-
commit_hash=$$(cd protos > /dev/null && buf push --branch "${git_branch}") && \
12-
[ ! -z "$$commit_hash" ] && commit_hash_short=$$(echo "$$commit_hash" | cut -c1-12) && $$SED -i "s|generatedBufDependencyVersion=.*|generatedBufDependencyVersion=00000000000000.$$commit_hash_short|g" gradle.properties || echo "No changes to protos, gradle.properties not updated"
9+
buf-publish-current-branch: copy-json-schema-to-resources
10+
@ [[ "$$OSTYPE" == "darwin"* ]] && SED=gsed || SED=sed
11+
@ commit_hash=$$(cd protos > /dev/null && buf push --branch "${git_branch}")
12+
@ [ ! -z "$$commit_hash" ] && commit_hash_short=$$(echo "$$commit_hash" | cut -c1-12) && $$SED -i "s|generatedBufDependencyVersion=.*|generatedBufDependencyVersion=00000000000000.$$commit_hash_short|g" gradle.properties || echo "No changes to protos, gradle.properties not updated"
1313

1414
run-docker-local:
1515
./gradlew buildDocker && docker run -p 8080:8080 -p 9090:9090 -p 50051:50051 -e SPRING_PROFILES_ACTIVE=dockerdev pace:latest
@@ -18,7 +18,11 @@ buf-create-descriptor-binpb: # PHONY on purpose, as we want to regenerate every
1818
rm -f ${descriptor_file} && \
1919
buf build protos --config ./protos/buf.yaml -o ${descriptor_file}
2020

21-
run-rest-proxy: buf-create-descriptor-binpb
21+
create-envoy-spec:
22+
@ export GRPC_SERVICES=$$(buf build -o -#format=json | jq -rc '.file | map(select(.name | startswith("getstrm"))) | map(select(.service > 0) | (.package + "." + .service[].name))')
23+
@ envsubst < rest/envoy-local-template.yaml > rest/envoy-local.yaml
24+
25+
run-rest-proxy: buf-create-descriptor-binpb create-envoy-spec
2226
docker run -p 9090:9090 -p 9000:9000 -v $$(pwd)/rest/envoy-local.yaml:/etc/envoy/envoy.yaml -v $$(pwd)/${descriptor_file}:/tmp/envoy/descriptor.binpb envoyproxy/envoy:v1.28-latest
2327

2428
/tmp/envoy.yaml: rest/envoy-local.yaml
@@ -28,4 +32,10 @@ run-rest-proxy-localhost: buf-create-descriptor-binpb /tmp/envoy.yaml
2832
docker run --net=host -p 9090:9090 -p 9000:9000 -v /tmp/envoy.yaml:/etc/envoy/envoy.yaml -v $$(pwd)/${descriptor_file}:/tmp/envoy/descriptor.binpb envoyproxy/envoy:v1.28-latest
2933

3034
json-schema:
31-
(cd protos; buf generate)
35+
@ rm -rf protos/json-schema
36+
@ (cd protos; buf generate)
37+
38+
copy-json-schema-to-resources: json-schema
39+
@ find app/src/main/resources/jsonschema -type d -maxdepth 1 -mindepth 1 | xargs -I{} rm -rf {}
40+
@ mkdir -p app/src/main/resources/jsonschema
41+
@ cp -r protos/json-schema/* app/src/main/resources/jsonschema

app/build.gradle.kts

Lines changed: 36 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import nu.studer.gradle.jooq.JooqGenerate
66
import org.flywaydb.gradle.task.FlywayMigrateTask
77
import org.gradle.api.tasks.testing.logging.TestExceptionFormat
88
import org.springframework.boot.gradle.tasks.bundling.BootJar
9+
import java.io.ByteArrayOutputStream
910
import java.net.InetAddress
1011
import java.net.Socket
1112
import java.time.Instant
@@ -21,6 +22,7 @@ val postgresPort: Int by rootProject.extra
2122
val jooqSchema = rootProject.extra["schema"] as String
2223
val jooqMigrationDir = "$projectDir/src/main/resources/db/migration/postgresql"
2324
val jooqVersion = rootProject.ext["jooqVersion"] as String
25+
val kotestVersion = rootProject.ext["kotestVersion"] as String
2426
val openDataDiscoveryOpenApiDir = layout.buildDirectory.dir("generated/source/odd").get()
2527
project.version = if (gradle.startParameter.taskNames.any { it.lowercase() == "builddocker" }) {
2628
"${project.version}-SNAPSHOT"
@@ -44,7 +46,7 @@ dependencies {
4446
// Dependencies managed by Spring
4547
implementation("org.springframework.boot:spring-boot-starter-jooq")
4648
// TODO remove once we upgrade Spring: override SnakeYAML dependency, as the one managed by Spring is too old and is vulnerable
47-
implementation("org.yaml:snakeyaml:2.0")
49+
implementation("org.yaml:snakeyaml:2.2")
4850
implementation("org.flywaydb:flyway-core")
4951
implementation("org.jetbrains.kotlin:kotlin-reflect")
5052
implementation("org.postgresql:postgresql")
@@ -60,28 +62,35 @@ dependencies {
6062

6163
// Self-managed dependencies
6264
implementation("net.devh:grpc-server-spring-boot-starter:2.15.0.RELEASE")
63-
implementation("com.databricks:databricks-sdk-java:0.10.0")
65+
implementation("com.databricks:databricks-sdk-java:0.13.0")
6466
implementation("com.github.drapostolos:type-parser:0.8.1")
65-
implementation("com.microsoft.sqlserver:mssql-jdbc:12.4.2.jre8")
67+
implementation("com.microsoft.sqlserver:mssql-jdbc:12.4.2.jre11")
6668

67-
implementation("com.nimbusds:nimbus-jose-jwt:9.37")
68-
implementation("org.bouncycastle:bcpkix-jdk18on:1.76")
69+
implementation("com.nimbusds:nimbus-jose-jwt:9.37.1")
70+
implementation("org.bouncycastle:bcpkix-jdk18on:1.77")
6971

70-
implementation(enforcedPlatform("com.google.cloud:libraries-bom:26.24.0"))
72+
implementation(enforcedPlatform("com.google.cloud:libraries-bom:26.27.0"))
7173
implementation("com.google.cloud:google-cloud-bigquery")
7274
implementation("com.google.cloud:google-cloud-datacatalog")
7375

7476
implementation("build.buf.gen:getstrm_pace_grpc_java:1.59.0.2.$generatedBufDependencyVersion")
7577
implementation("build.buf.gen:getstrm_pace_grpc_kotlin:1.4.1.1.$generatedBufDependencyVersion")
76-
implementation("build.buf.gen:getstrm_pace_protocolbuffers_java:24.4.0.1.$generatedBufDependencyVersion")
77-
implementation("build.buf:protovalidate:0.1.6")
78+
implementation("build.buf.gen:getstrm_pace_protocolbuffers_java:25.1.0.1.$generatedBufDependencyVersion")
79+
implementation("build.buf:protovalidate:0.1.8")
7880

7981
implementation("com.apollographql.apollo3:apollo-runtime:3.8.2")
8082

83+
implementation("com.aallam.openai:openai-client:3.6.1")
84+
implementation(platform("io.ktor:ktor-bom:2.3.6"))
85+
runtimeOnly("io.ktor:ktor-client-okhttp")
86+
implementation("io.ktor:ktor-client-logging")
87+
8188
// Test dependencies
82-
testImplementation("org.springframework.boot:spring-boot-starter-test")
83-
testImplementation("io.kotest:kotest-assertions-core-jvm:5.7.2")
84-
testImplementation("io.kotest:kotest-runner-junit5:5.7.2")
89+
testImplementation("org.springframework.boot:spring-boot-starter-test") {
90+
exclude(group = "com.vaadin.external.google", module = "android-json")
91+
}
92+
testImplementation("io.kotest:kotest-assertions-core-jvm:$kotestVersion")
93+
testImplementation("io.kotest:kotest-runner-junit5:$kotestVersion")
8594
testImplementation("io.mockk:mockk:1.13.8")
8695
testImplementation("io.zonky.test:embedded-postgres:2.0.4")
8796
}
@@ -308,11 +317,27 @@ val createProtoDescriptor =
308317
val copyDocker =
309318
tasks.register<Copy>("copyDocker") {
310319
group = "docker"
320+
val grpcServices: String = ByteArrayOutputStream().use { outputStream ->
321+
project.exec {
322+
workingDir("$rootDir/protos")
323+
324+
commandLine(
325+
"bash",
326+
"-c",
327+
"buf build -o -#format=json | jq -rc '.file | map(select(.name | startswith(\"getstrm\"))) | map(select(.service > 0) | (.package + \".\" + .service[].name))'"
328+
)
329+
standardOutput = outputStream
330+
}
331+
332+
outputStream.toString()
333+
}
334+
311335
from("src/main/docker")
312336
include("*")
313337
into("build/docker")
314338
expand(
315339
"version" to project.version,
340+
"grpcServices" to grpcServices,
316341
)
317342
}
318343

app/src/main/docker/envoy.yaml

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -29,15 +29,7 @@ static_resources:
2929
typed_config:
3030
"@type": type.googleapis.com/envoy.extensions.filters.http.grpc_json_transcoder.v3.GrpcJsonTranscoder
3131
proto_descriptor: "/app/descriptor.binpb"
32-
# TODO Leaving this hardcoded for now, as we only have a few service at the moment
33-
# In order to dynamically determine the list of services, we can use the following command
34-
# buf build -o -#format=json | jq -r '.file | map(select(.name | startswith("getstrm"))) | map(select(.service > 0) | (.package + "." + .service[].name))[] | ("- " + .)'
35-
# With the `copyDocker` command in the gradle build file, this could be expanded as a variable in this file
36-
services:
37-
- "getstrm.pace.api.data_catalogs.v1alpha.DataCatalogsService"
38-
- "getstrm.pace.api.data_policies.v1alpha.DataPoliciesService"
39-
- "getstrm.pace.api.processing_platforms.v1alpha.ProcessingPlatformsService"
40-
- "getstrm.pace.api.global_transforms.v1alpha.GlobalTransformsService"
32+
services: ${grpcServices}
4133
auto_mapping: false
4234
convert_grpc_status: true
4335
request_validation_options:
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package com.getstrm.pace.api
2+
3+
import build.buf.gen.getstrm.pace.api.plugins.v1alpha.*
4+
import com.getstrm.pace.service.PluginsService
5+
import net.devh.boot.grpc.server.service.GrpcService
6+
7+
@GrpcService
8+
class PluginsApi(private val pluginsService: PluginsService) : PluginsServiceGrpcKt.PluginsServiceCoroutineImplBase() {
9+
override suspend fun listPlugins(request: ListPluginsRequest): ListPluginsResponse =
10+
ListPluginsResponse.newBuilder()
11+
.addAllPlugins(pluginsService.listPlugins())
12+
.build()
13+
14+
override suspend fun getPayloadJSONSchema(request: GetPayloadJSONSchemaRequest): GetPayloadJSONSchemaResponse =
15+
GetPayloadJSONSchemaResponse.newBuilder()
16+
.setSchema(pluginsService.getPluginPayloadJsonSchema(request.pluginId))
17+
.build()
18+
19+
override suspend fun invokePlugin(request: InvokePluginRequest): InvokePluginResponse =
20+
pluginsService.invokePlugin(request)
21+
}

app/src/main/kotlin/com/getstrm/pace/config/AppConfiguration.kt

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,6 @@ package com.getstrm.pace.config
22

33
import build.buf.gen.getstrm.pace.api.entities.v1alpha.DataCatalog
44
import org.springframework.boot.context.properties.ConfigurationProperties
5-
import org.springframework.boot.context.properties.ConfigurationPropertiesScan
6-
import org.springframework.boot.context.properties.EnableConfigurationProperties
7-
import org.springframework.stereotype.Component
85

96
@ConfigurationProperties(prefix = "app")
107
data class AppConfiguration(
@@ -21,4 +18,3 @@ data class CatalogConfiguration(
2118
val password: String?,
2219
val fetchSize: Int? = 1,
2320
)
24-

app/src/main/kotlin/com/getstrm/pace/config/PaceConfiguration.kt

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,12 @@ package com.getstrm.pace.config
33
import com.getstrm.pace.grpc.ExceptionHandlerInterceptor
44
import com.getstrm.pace.grpc.ValidationInterceptor
55
import net.devh.boot.grpc.server.interceptor.GrpcGlobalServerInterceptor
6+
import org.springframework.boot.context.properties.ConfigurationPropertiesScan
67
import org.springframework.boot.context.properties.EnableConfigurationProperties
78
import org.springframework.context.annotation.Configuration
89

910
@Configuration
10-
@EnableConfigurationProperties(ProcessingPlatformConfiguration::class,
11-
AppConfiguration::class,
12-
GlobalTransformsConfiguration::class,
13-
)
11+
@ConfigurationPropertiesScan("com.getstrm.pace.config")
1412
class PaceConfiguration {
1513
@GrpcGlobalServerInterceptor
1614
fun exceptionInterceptor() = ExceptionHandlerInterceptor(false)
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package com.getstrm.pace.exceptions
2+
3+
import build.buf.protovalidate.ValidationResult
4+
import build.buf.protovalidate.Validator
5+
import build.buf.protovalidate.exceptions.ValidationException
6+
import com.google.protobuf.Message
7+
import com.google.rpc.BadRequest
8+
import com.google.rpc.DebugInfo
9+
import org.slf4j.LoggerFactory
10+
11+
object ProtoValidator {
12+
private val log by lazy { LoggerFactory.getLogger(javaClass) }
13+
private val validator = Validator()
14+
15+
fun validate(message: Message): PaceStatusException? {
16+
return try {
17+
val result: ValidationResult = validator.validate(message)
18+
19+
if (result.violations.isEmpty()) {
20+
null
21+
} else {
22+
val violations = result.violations.map {
23+
BadRequest.FieldViolation.newBuilder()
24+
.setField(if (it.forKey) "${it.fieldPath} (map key)" else it.fieldPath)
25+
.setDescription("${it.message} (constraint id = ${it.constraintId})")
26+
.build()
27+
}
28+
29+
BadRequestException(
30+
BadRequestException.Code.INVALID_ARGUMENT,
31+
BadRequest.newBuilder()
32+
.addAllFieldViolations(violations)
33+
.build(),
34+
errorMessage = "Validation of message ${message.descriptorForType.name} failed, ${violations.size} violations found",
35+
)
36+
}
37+
} catch (e: ValidationException) {
38+
val protoMessageName = message.descriptorForType.name ?: "'Cannot determine Proto Message name'"
39+
log.error("An exception occurred while validating message $protoMessageName", e)
40+
InternalException(
41+
InternalException.Code.INTERNAL,
42+
DebugInfo.newBuilder()
43+
.setDetail("Validation of message $protoMessageName failed, ${e.message}, ${PaceStatusException.BUG_REPORT}")
44+
.addAllStackEntries(e.stackTrace.map { it.toString() })
45+
.build()
46+
)
47+
}
48+
}
49+
}

0 commit comments

Comments
 (0)