Skip to content

Commit

Permalink
feat: add optional protobuf formatter
Browse files Browse the repository at this point in the history
Protobuf formatter is written in go and based on the buf formatter. It is installed as Extension and introduces go as dependency. If enabled it will run on top and independent of normalization.
  • Loading branch information
keejon committed Sep 26, 2024
1 parent 349ae92 commit b5b10f9
Show file tree
Hide file tree
Showing 52 changed files with 5,292 additions and 137 deletions.
1 change: 1 addition & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,6 @@ include setup.py
include setup.cfg
include LICENSE
include MANIFEST.in
include *.so

recursive-exclude examples *~ *.pyc \.*
3 changes: 3 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -475,6 +475,9 @@ Keys to take special care are the ones needed to configure Kafka and advertised_
- ``true``
- If enabled, kafka errors which can be retried or custom errors specififed for the service will not be raised,
instead, a warning log is emitted. This will denoise issue tracking systems, i.e. sentry
* - ``use_protobuf_formatter``
- ``false``
- If protobuf formatter should be used on protobuf schemas in order to normalize schemas. The formatter is used on top and independent of regular normalization and schemas will be persisted in a formatted state.


Authentication and authorization of Karapace Schema Registry REST API
Expand Down
25 changes: 25 additions & 0 deletions go/protopace/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# If you prefer the allow list template instead of the deny list, see community template:
# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
#
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib

# Test binary, built with `go test -c`
*.test

# Output of the go coverage tool, specifically when used with LiteIDE
*.out

# Dependency directories (remove the comment below to include it)
# vendor/

# Go workspace file
go.work
go.work.sum

# env file
.env
69 changes: 69 additions & 0 deletions go/protopace/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# Change these variables as necessary.
MAIN_PACKAGE_PATH := .
BINARY_NAME := protopace
BUILD_DIR := ../../karapace/protobuf/protopace/bin

# ==================================================================================== #
# HELPERS
# ==================================================================================== #

## help: print this help message
.PHONY: help
help:
@echo 'Usage:'
@sed -n 's/^##//p' ${MAKEFILE_LIST} | column -t -s ':' | sed -e 's/^/ /'

.PHONY: confirm
confirm:
@echo -n 'Are you sure? [y/N] ' && read ans && [ $${ans:-N} = y ]

.PHONY: no-dirty
no-dirty:
git diff --exit-code


# ==================================================================================== #
# QUALITY CONTROL
# ==================================================================================== #

## format: format code
.PHONY: format
format:
go fmt ./...

## tidy: format code and tidy modfile
.PHONY: tidy
tidy:
go fmt ./...
go mod tidy -v

## audit: run quality control checks
.PHONY: audit
audit:
go mod verify
go vet ./...
go run honnef.co/go/tools/cmd/staticcheck@latest -checks=all,-ST1000,-U1000 ./...
go run golang.org/x/vuln/cmd/govulncheck@latest ./...
go test -race -buildvcs -vet=off ./...


# ==================================================================================== #
# DEVELOPMENT
# ==================================================================================== #

## test: run all tests
.PHONY: test
test:
go test -v -race -buildvcs ./...

## test/cover: run all tests and display coverage
.PHONY: test/cover
test/cover:
go test -v -race -buildvcs -coverprofile=/tmp/coverage.out ./...
go tool cover -html=/tmp/coverage.out

## build: build the application
.PHONY: build
build:
# Include additional build steps, like TypeScript, SCSS or Tailwind compilation here...
go build -o=/tmp/bin/${BINARY_NAME} ${MAIN_PACKAGE_PATH}
41 changes: 41 additions & 0 deletions go/protopace/compatibility.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package main

import (
"context"

"github.com/Aiven-Open/karapace/go/protopace/schema"

"github.com/bufbuild/buf/private/bufpkg/bufcheck/bufbreaking"
"github.com/bufbuild/buf/private/bufpkg/bufconfig"
"github.com/bufbuild/buf/private/pkg/tracing"
"go.uber.org/zap"
)

func Check(schema schema.Schema, previousSchema schema.Schema) error {
handler := bufbreaking.NewHandler(zap.NewNop(), tracing.NopTracer)
ctx := context.Background()
image, err := schema.CompileBufImage()
if err != nil {
return err
}
previousImage, err := previousSchema.CompileBufImage()
if err != nil {
return err
}
checkConfig, _ := bufconfig.NewEnabledCheckConfig(
bufconfig.FileVersionV2,
nil,
[]string{
"FIELD_NO_DELETE",
"FILE_SAME_PACKAGE",
"FIELD_SAME_NAME",
"FIELD_SAME_JSON_NAME",
"FILE_NO_DELETE",
"ENUM_NO_DELETE",
},
nil,
nil,
)
config := bufconfig.NewBreakingConfig(checkConfig, false)
return handler.Check(ctx, config, previousImage, image)
}
34 changes: 34 additions & 0 deletions go/protopace/compatibility_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package main

import (
"os"
"testing"

s "github.com/Aiven-Open/karapace/go/protopace/schema"
"github.com/stretchr/testify/assert"
)

func TestCompatibility(t *testing.T) {
assert := assert.New(t)

data, _ := os.ReadFile("./fixtures/dependency.proto")
dependencySchema, err := s.FromString("my/awesome/customer/v1/nested_value.proto", string(data), nil)
assert.NoError(err)
assert.NotNil(dependencySchema)

data, _ = os.ReadFile("./fixtures/test.proto")
testSchema, err := s.FromString("test.proto", string(data), []s.Schema{*dependencySchema})
assert.NoError(err)
assert.NotNil(testSchema)

data, _ = os.ReadFile("./fixtures/test_previous.proto")
previousSchema, err := s.FromString("test.proto", string(data), []s.Schema{*dependencySchema})
assert.NoError(err)
assert.NotNil(previousSchema)

err = Check(*testSchema, *testSchema)
assert.NoError(err)

err = Check(*testSchema, *previousSchema)
assert.ErrorContains(err, "Field \"5\" with name \"foo\" on message \"EventValue\" changed type from \"string\" to \"int32\".")
}
12 changes: 12 additions & 0 deletions go/protopace/fixtures/dependency.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
syntax = "proto3";
package my.awesome.customer.v1;

message NestedValue {
string value = 1;
}

enum Status {
UNKNOWN = 0;
ACTIVE = 1;
INACTIVE = 2;
}
30 changes: 30 additions & 0 deletions go/protopace/fixtures/test.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
syntax = "proto3";

package my.awesome.customer.v1;

import "my/awesome/customer/v1/nested_value.proto";
import "google/protobuf/timestamp.proto";

option ruby_package = "My::Awesome::Customer::V1";
option csharp_namespace = "my.awesome.customer.V1";
option go_package = "github.com/customer/api/my/awesome/customer/v1;dspv1";
option java_multiple_files = true;
option java_outer_classname = "EventValueProto";
option java_package = "com.my.awesome.customer.v1";
option objc_class_prefix = "TDD";
option php_metadata_namespace = "My\\Awesome\\Customer\\V1";
option php_namespace = "My\\Awesome\\Customer\\V1";

message Local {
message NestedValue {
string foo = 1;
}
}

message EventValue {
NestedValue nested_value = 1;
google.protobuf.Timestamp created_at = 2;
Status status = 3;
Local.NestedValue local_nested_value = 4;
int32 foo = 5;
}
30 changes: 30 additions & 0 deletions go/protopace/fixtures/test_previous.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
syntax = "proto3";

package my.awesome.customer.v1;

import "my/awesome/customer/v1/nested_value.proto";
import "google/protobuf/timestamp.proto";

option ruby_package = "My::Awesome::Customer::V1";
option csharp_namespace = "my.awesome.customer.V1";
option go_package = "github.com/customer/api/my/awesome/customer/v1;dspv1";
option java_multiple_files = true;
option java_outer_classname = "EventValueProto";
option java_package = "com.my.awesome.customer.v1";
option objc_class_prefix = "TDD";
option php_metadata_namespace = "My\\Awesome\\Customer\\V1";
option php_namespace = "My\\Awesome\\Customer\\V1";

message Local {
message NestedValue {
string foo = 1;
}
}

message EventValue {
NestedValue nested_value = 1;
google.protobuf.Timestamp created_at = 2;
Status status = 3;
Local.NestedValue local_nested_value = 4;
string foo = 5;
}
Loading

0 comments on commit b5b10f9

Please sign in to comment.