From 72934813ea110ad1229404fa15a74addfcb83098 Mon Sep 17 00:00:00 2001 From: Damien Whitten Date: Tue, 8 Apr 2025 12:44:12 +1000 Subject: [PATCH 1/2] Replay Queries and parsers --- ext/db/00002_events.sql | 6 +- ext/db/00003_replay.sql | 19 ++ go.mod | 29 ++- go.sum | 66 ++++-- .../gen/gestest/v1/gestest_pb/foo.j5s.pb.go | 140 ++++++------ .../gestest/v1/gestest_spb/foo.p.j5s.pb.go | 2 +- .../v1/gestest_spb/foo.p.j5s_grpc.pb.go | 2 +- .../gestest/v1/gestest_tpb/foo.p.j5s.pb.go | 2 +- .../v1/gestest_tpb/foo.p.j5s_grpc.pb.go | 2 +- .../gen/o5/ges/v1/ges_pb/events.j5s.pb.go | 214 +++++++++++------- .../gen/o5/ges/v1/ges_spb/events.p.j5s.pb.go | 2 +- .../o5/ges/v1/ges_spb/events.p.j5s_grpc.pb.go | 2 +- internal/integration/universe.go | 33 +-- internal/replay/listener.go | 182 +++++++++++++++ internal/replay/publish.go | 101 +++++++++ internal/replay/queries.go | 1 + internal/replay/query.go | 85 +++++++ internal/service/event.go | 196 ++++++++++++++++ internal/service/event_test.go | 94 ++++++++ internal/service/query.go | 4 +- internal/service/upsert.go | 79 +++++++ internal/service/worker.go | 68 +----- schema/ges/o5/ges/v1/events.j5s | 15 +- schema/ges/o5/ges/v1/events.j5s.proto | 49 ++-- .../ges/o5/ges/v1/service/events.p.j5s.proto | 2 +- schema/test/gestest/v1/foo.j5s.proto | 9 +- .../test/gestest/v1/service/foo.p.j5s.proto | 2 +- schema/test/gestest/v1/topic/foo.p.j5s.proto | 2 +- 28 files changed, 1110 insertions(+), 298 deletions(-) create mode 100644 ext/db/00003_replay.sql create mode 100644 internal/replay/listener.go create mode 100644 internal/replay/publish.go create mode 100644 internal/replay/queries.go create mode 100644 internal/replay/query.go create mode 100644 internal/service/event.go create mode 100644 internal/service/event_test.go create mode 100644 internal/service/upsert.go diff --git a/ext/db/00002_events.sql b/ext/db/00002_events.sql index f9673a5..3ecabb2 100644 --- a/ext/db/00002_events.sql +++ b/ext/db/00002_events.sql @@ -4,7 +4,8 @@ CREATE TABLE event ( timestamp timestamptz NOT NULL, entity_name text NOT NULL, - data jsonb NOT NULL + data jsonb NOT NULL -- ges.v1.Event + ); CREATE TABLE upsert ( @@ -12,7 +13,8 @@ CREATE TABLE upsert ( entity_id text NOT NULL, last_event_id text NOT NULL, last_event_timestamp timestamptz NOT NULL, - data jsonb NOT NULL, + + data jsonb NOT NULL, -- ges.v1.Upsert PRIMARY KEY (entity_name, entity_id) ); diff --git a/ext/db/00003_replay.sql b/ext/db/00003_replay.sql new file mode 100644 index 0000000..2e1a04b --- /dev/null +++ b/ext/db/00003_replay.sql @@ -0,0 +1,19 @@ +-- +goose Up + +CREATE TABLE replay_event ( + replay_id text PRIMARY KEY, -- {queue_url}/{event_id} + event_id text REFERENCES event(id) NOT NULL, + queue_url text NOT NULL +); + +CREATE TABLE replay_upsert ( + replay_id text PRIMARY KEY, -- {queue_url}/{entity_name}/{entity_id} + entity_name text NOT NULL, + entity_id text NOT NULL, + queue_url text NOT NULL +); + +-- +goose Down + +DROP TABLE replay_event; +DROP TABLE replay_upsert; diff --git a/go.mod b/go.mod index 1723286..08af993 100644 --- a/go.mod +++ b/go.mod @@ -5,28 +5,34 @@ go 1.24.0 toolchain go1.24.1 require ( - buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.5-20250307204501-0409229c3780.1 + buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.6-20250307204501-0409229c3780.1 + github.com/aws/aws-sdk-go-v2/service/sqs v1.38.4 github.com/elgris/sqrl v0.0.0-20210727210741-7e0198b30236 github.com/google/uuid v1.6.0 - github.com/pentops/flowtest v0.0.0-20241110231021-42663ac00b63 - github.com/pentops/golib v0.0.0-20250107012216-1b5307b3bfe0 + github.com/jackc/pgx/v5 v5.7.4 + github.com/pentops/flowtest v0.0.0-20250403234635-311159fa1e81 + github.com/pentops/golib v0.0.0-20250326060930-8c83d58ddb63 github.com/pentops/grpc.go v0.0.0-20250326042738-bcdfc2b43fa9 - github.com/pentops/j5 v0.0.0-20250326000307-24e2adf77e8e + github.com/pentops/j5 v0.0.0-20250407052915-b2fc017d8ac2 github.com/pentops/log.go v0.0.15 - github.com/pentops/o5-messaging v0.0.0-20250317182016-de51c0e702a3 + github.com/pentops/o5-messaging v0.0.0-20250401052720-1fac32f8ed9c github.com/pentops/pgtest.go v0.0.0-20241223222214-7638cc50e15b - github.com/pentops/protostate v0.0.0-20250324023023-a72be074893a + github.com/pentops/protostate v0.0.0-20250403011625-3c2baa2e4af4 github.com/pentops/runner v0.0.0-20250116202335-8635b2a42547 github.com/pentops/sqrlx.go v0.0.0-20250324231942-5f3ef6c21f8e github.com/pressly/goose v2.7.0+incompatible - google.golang.org/genproto/googleapis/api v0.0.0-20250313205543-e70fdf4c4cb4 + google.golang.org/genproto/googleapis/api v0.0.0-20250324211829-b45e905df463 google.golang.org/grpc v1.71.0 - google.golang.org/protobuf v1.36.5 + google.golang.org/protobuf v1.36.6 ) require ( cel.dev/expr v0.22.0 // indirect github.com/antlr4-go/antlr/v4 v4.13.1 // indirect + github.com/aws/aws-sdk-go-v2 v1.36.3 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34 // indirect + github.com/aws/smithy-go v1.22.3 // indirect github.com/bufbuild/protocompile v0.14.1 // indirect github.com/bufbuild/protovalidate-go v0.9.2 // indirect github.com/fatih/color v1.18.0 // indirect @@ -36,6 +42,8 @@ require ( github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 // indirect github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.1 // indirect github.com/iancoleman/strcase v0.3.0 // indirect + github.com/jackc/pgpassfile v1.0.0 // indirect + github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect github.com/jhump/protoreflect v1.17.0 // indirect github.com/lib/pq v1.10.9 // indirect github.com/mattn/go-colorable v0.1.14 // indirect @@ -43,10 +51,13 @@ require ( github.com/pkg/errors v0.9.1 // indirect github.com/shopspring/decimal v1.4.0 // indirect github.com/stoewer/go-strcase v1.3.0 // indirect + golang.org/x/crypto v0.36.0 // indirect golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 // indirect golang.org/x/net v0.37.0 // indirect golang.org/x/sync v0.12.0 // indirect golang.org/x/sys v0.31.0 // indirect golang.org/x/text v0.23.0 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20250313205543-e70fdf4c4cb4 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250324211829-b45e905df463 // indirect ) + +replace github.com/pentops/o5-messaging => /Users/daemonl/pentops/o5-messaging diff --git a/go.sum b/go.sum index 342761f..e59a455 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.5-20250307204501-0409229c3780.1 h1:j+l4+E1EEo83GVIxuqinfFOTyImSQUH90WfufE86xaI= -buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.5-20250307204501-0409229c3780.1/go.mod h1:eOqrCVUfhh7SLo00urDe/XhJHljj0dWMZirS0aX7cmc= +buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.6-20250307204501-0409229c3780.1 h1:zgJPqo17m28+Lf5BW4xv3PvU20BnrmTcGYrog22lLIU= +buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.6-20250307204501-0409229c3780.1/go.mod h1:avRlCjnFzl98VPaeCtJ24RrV/wwHFzB8sWXhj26+n/U= cel.dev/expr v0.22.0 h1:+hFFhLPmquBImfs1BiN2PZmkr5ASse2ZOuaxIs9e4R8= cel.dev/expr v0.22.0/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= @@ -8,6 +8,16 @@ github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20O github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= github.com/antlr4-go/antlr/v4 v4.13.1 h1:SqQKkuVZ+zWkMMNkjy5FZe5mr5WURWnlpmOuzYWrPrQ= github.com/antlr4-go/antlr/v4 v4.13.1/go.mod h1:GKmUxMtwp6ZgGwZSva4eWPC5mS6vUAmOABFgjdkM7Nw= +github.com/aws/aws-sdk-go-v2 v1.36.3 h1:mJoei2CxPutQVxaATCzDUjcZEjVRdpsiiXi2o38yqWM= +github.com/aws/aws-sdk-go-v2 v1.36.3/go.mod h1:LLXuLpgzEbD766Z5ECcRmi8AzSwfZItDtmABVkRLGzg= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34 h1:ZK5jHhnrioRkUNOc+hOgQKlUL5JeC3S6JgLxtQ+Rm0Q= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34/go.mod h1:p4VfIceZokChbA9FzMbRGz5OV+lekcVtHlPKEO0gSZY= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34 h1:SZwFm17ZUNNg5Np0ioo/gq8Mn6u9w19Mri8DnJ15Jf0= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34/go.mod h1:dFZsC0BLo346mvKQLWmoJxT+Sjp+qcVR1tRVHQGOH9Q= +github.com/aws/aws-sdk-go-v2/service/sqs v1.38.4 h1:rxG8LzVTNCOUppzbQAWfEEDJg4knmnH7zZGEnf7QOrs= +github.com/aws/aws-sdk-go-v2/service/sqs v1.38.4/go.mod h1:Bar4MrRxeqdn6XIh8JGfiXuFRmyrrsZNTJotxEJmWW0= +github.com/aws/smithy-go v1.22.3 h1:Z//5NuZCSW6R4PhQ93hShNbyBbn8BWCmCVCt+Q8Io5k= +github.com/aws/smithy-go v1.22.3/go.mod h1:t1ufH5HMublsJYulve2RKmHDC15xu1f26kHCp/HgceI= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/bufbuild/protocompile v0.14.1 h1:iA73zAf/fyljNjQKwYzUHD6AD4R8KMasmwa/FBatYVw= github.com/bufbuild/protocompile v0.14.1/go.mod h1:ppVdAIhbr2H8asPk6k4pY7t9zB1OU5DoEw9xY/FUi1c= @@ -57,6 +67,14 @@ github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.1 h1:KcFzXwzM/kGhIRHvc8jdix github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.1/go.mod h1:qOchhhIlmRcqk/O9uCo/puJlyo07YINaIqdZfZG3Jkc= github.com/iancoleman/strcase v0.3.0 h1:nTXanmYxhfFAMjZL34Ov6gkzEsSJZ5DbhxWjvSASxEI= github.com/iancoleman/strcase v0.3.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= +github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= +github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= +github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo= +github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= +github.com/jackc/pgx/v5 v5.7.4 h1:9wKznZrhWa2QiHL+NjTSPP6yjl3451BX3imWDnokYlg= +github.com/jackc/pgx/v5 v5.7.4/go.mod h1:ncY89UGWxg82EykZUwSpUKEfccBGGYq1xjrOpsbsfGQ= +github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo= +github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= github.com/jhump/protoreflect v1.17.0 h1:qOEr613fac2lOuTgWN4tPAtLL7fUSbuJL5X5XumQh94= github.com/jhump/protoreflect v1.17.0/go.mod h1:h9+vUUL38jiBzck8ck+6G/aeMX8Z4QUY/NiJPwPNi+8= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= @@ -72,22 +90,20 @@ github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stg github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= -github.com/pentops/flowtest v0.0.0-20241110231021-42663ac00b63 h1:c/Km4mfvR35s9uPVfSXTaZswD5T93vaeMRdTSJ2SGyY= -github.com/pentops/flowtest v0.0.0-20241110231021-42663ac00b63/go.mod h1:vNp8crAKcH0f/sZU9frkmQLUeDsTIgMqV14kQtkAqC0= -github.com/pentops/golib v0.0.0-20250107012216-1b5307b3bfe0 h1:GA2C2velp/AoojR+fsf4kFXhJ1MLg0y3H84Z2yJ6/+Q= -github.com/pentops/golib v0.0.0-20250107012216-1b5307b3bfe0/go.mod h1:I58JIVvL1/nP4CEHGKGbBhvWIEA9mVkGeoviemaqanU= +github.com/pentops/flowtest v0.0.0-20250403234635-311159fa1e81 h1:qCxKnV8bGDR0WR6lFRvIP4jGw7S2TaTptKIBqnhutbU= +github.com/pentops/flowtest v0.0.0-20250403234635-311159fa1e81/go.mod h1:vNp8crAKcH0f/sZU9frkmQLUeDsTIgMqV14kQtkAqC0= +github.com/pentops/golib v0.0.0-20250326060930-8c83d58ddb63 h1:s5qtWT2/s79gy/wm3/bwvKYLK6u2AkW05JiLPqxraP0= +github.com/pentops/golib v0.0.0-20250326060930-8c83d58ddb63/go.mod h1:I58JIVvL1/nP4CEHGKGbBhvWIEA9mVkGeoviemaqanU= github.com/pentops/grpc.go v0.0.0-20250326042738-bcdfc2b43fa9 h1:Vsx71KpYbOoL53/J5ogLjPcKFWhs0Oiz0BFuVSLmVOo= github.com/pentops/grpc.go v0.0.0-20250326042738-bcdfc2b43fa9/go.mod h1:kUYGdH/6jh6hHZzaevmPnDiRewzCHZXw3CTGk/4u5nM= -github.com/pentops/j5 v0.0.0-20250326000307-24e2adf77e8e h1:BjQgpNDsyR3jnL0ZefrKCCMQu6kM2qP9H6sOZs3NU28= -github.com/pentops/j5 v0.0.0-20250326000307-24e2adf77e8e/go.mod h1:iEHeUub/dC1C2hoNbl9gvYVcNU6sq8WWBz5TIyqAJW0= +github.com/pentops/j5 v0.0.0-20250407052915-b2fc017d8ac2 h1:LMItMPngL2GP+KUSSJKg2SJbKeIDpHj0kf+AJoTu2GE= +github.com/pentops/j5 v0.0.0-20250407052915-b2fc017d8ac2/go.mod h1:57/DD8Uxf24hQnL/bZqPBAqt2n4eiAPewRJPhcz33us= github.com/pentops/log.go v0.0.15 h1:yoyu8/2LTvJ4daAkRuKcDyLe1eICwJdiCkWgZwgtq3A= github.com/pentops/log.go v0.0.15/go.mod h1:yR34x8aMlvhdGvqgIU4+0MiLjJTKt0vpcgUnVN2nZV4= -github.com/pentops/o5-messaging v0.0.0-20250317182016-de51c0e702a3 h1:xlKXoYV+A7xFxg1O4HyBSb4/k6hVc+exlEVKeoGXzCc= -github.com/pentops/o5-messaging v0.0.0-20250317182016-de51c0e702a3/go.mod h1:RuFTVPd7iejyBP+Pj3+Udf8gbv5SYtPFlXCrR0BPgqE= github.com/pentops/pgtest.go v0.0.0-20241223222214-7638cc50e15b h1:UfL+A9/Nwi0QWrW7K06L8vYA+LsFRowWFtVYZICil+s= github.com/pentops/pgtest.go v0.0.0-20241223222214-7638cc50e15b/go.mod h1:U4AOJK8uL3IkJx1t5MBsOXudjZ111il31WE9UxL8Wz8= -github.com/pentops/protostate v0.0.0-20250324023023-a72be074893a h1:McYzPZe27mgXVcn6MdKpGwv9pVVNs48DITa5nWOMLFA= -github.com/pentops/protostate v0.0.0-20250324023023-a72be074893a/go.mod h1:PlsKWhiFeu2wWWnF5prWIThY6eNNL3ggvS1XSACrSEQ= +github.com/pentops/protostate v0.0.0-20250403011625-3c2baa2e4af4 h1:F73JY9+Lwh2DZXTuNhV34tNnB1IfJFXBBhJfZuJxdQ4= +github.com/pentops/protostate v0.0.0-20250403011625-3c2baa2e4af4/go.mod h1:SAs8Cb+9jvulrkWirNq38gsq8hheddmjfsHbPRb+B10= github.com/pentops/runner v0.0.0-20250116202335-8635b2a42547 h1:PFKl3Fo73gT208bXmeoIsF58QotFhwNSSOK/vGa8b2s= github.com/pentops/runner v0.0.0-20250116202335-8635b2a42547/go.mod h1:hzPLosxy6O/1qjLUyt9lzE2M1Re/ASxmrErHbImXgNk= github.com/pentops/sqrlx.go v0.0.0-20250324231942-5f3ef6c21f8e h1:1CZQsGPyYJTGS7uNsT6H3Yy69oi4T2QbkbEaUiNQ9js= @@ -128,16 +144,16 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= -go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY= -go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI= -go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ= -go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE= +go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ= +go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y= +go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M= +go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE= go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A= go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU= go.opentelemetry.io/otel/sdk/metric v1.34.0 h1:5CeK9ujjbFVL5c1PhLuStg1wxA7vQv7ce1EK0Gyvahk= go.opentelemetry.io/otel/sdk/metric v1.34.0/go.mod h1:jQ/r8Ze28zRKoNRdkjCZxfs6YvBTG1+YIqyFVFYec5w= -go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k= -go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE= +go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs= +go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= @@ -145,6 +161,8 @@ go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34= +golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 h1:nDVHiLt8aIbd/VzvPWN6kSOPE7+F/fNFDSXLVYkE/Iw= golang.org/x/exp v0.0.0-20250305212735-054e65f0b394/go.mod h1:sIifuuw/Yco/y6yb6+bDNfyeQ/MdPUy/hKEMYQV17cM= @@ -203,10 +221,10 @@ google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7 google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto/googleapis/api v0.0.0-20250313205543-e70fdf4c4cb4 h1:IFnXJq3UPB3oBREOodn1v1aGQeZYQclEmvWRMN0PSsY= -google.golang.org/genproto/googleapis/api v0.0.0-20250313205543-e70fdf4c4cb4/go.mod h1:c8q6Z6OCqnfVIqUFJkCzKcrj8eCvUrz+K4KRzSTuANg= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250313205543-e70fdf4c4cb4 h1:iK2jbkWL86DXjEx0qiHcRE9dE4/Ahua5k6V8OWFb//c= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250313205543-e70fdf4c4cb4/go.mod h1:LuRYeWDFV6WOn90g357N17oMCaxpgCnbi/44qJvDn2I= +google.golang.org/genproto/googleapis/api v0.0.0-20250324211829-b45e905df463 h1:hE3bRWtU6uceqlh4fhrSnUyjKHMKB9KrTLLG+bc0ddM= +google.golang.org/genproto/googleapis/api v0.0.0-20250324211829-b45e905df463/go.mod h1:U90ffi8eUL9MwPcrJylN5+Mk2v3vuPDptd5yyNUiRR8= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250324211829-b45e905df463 h1:e0AIkUUhxyBKh6ssZNrAMeqhA7RKUj42346d1y02i2g= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250324211829-b45e905df463/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= @@ -214,8 +232,8 @@ google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8 google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= google.golang.org/grpc v1.71.0 h1:kF77BGdPTQ4/JZWMlb9VpJ5pa25aqvVqogsxNHHdeBg= google.golang.org/grpc v1.71.0/go.mod h1:H0GRtasmQOh9LkFoCPDu3ZrwUtD1YGE+b2vYBYd/8Ec= -google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM= -google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= +google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/internal/gen/gestest/v1/gestest_pb/foo.j5s.pb.go b/internal/gen/gestest/v1/gestest_pb/foo.j5s.pb.go index 79bc3ab..f6c1cd0 100644 --- a/internal/gen/gestest/v1/gestest_pb/foo.j5s.pb.go +++ b/internal/gen/gestest/v1/gestest_pb/foo.j5s.pb.go @@ -1,4 +1,4 @@ -// Code generated by j5convert. DO NOT EDIT. +// Generated by j5build v0.0.0-20250403212908-de7c3c2e6cce. DO NOT EDIT // Code generated by protoc-gen-go. DO NOT EDIT. // versions: @@ -11,6 +11,7 @@ package gestest_pb import ( _ "buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go/buf/validate" _ "github.com/pentops/j5/gen/j5/ext/v1/ext_j5pb" + _ "github.com/pentops/j5/gen/j5/list/v1/list_j5pb" psm_j5pb "github.com/pentops/j5/gen/j5/state/v1/psm_j5pb" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" @@ -477,75 +478,78 @@ var file_gestest_v1_foo_j5s_proto_rawDesc = []byte{ 0x64, 0x61, 0x74, 0x65, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1b, 0x6a, 0x35, 0x2f, 0x65, 0x78, 0x74, 0x2f, 0x76, 0x31, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x1a, 0x1a, 0x6a, 0x35, 0x2f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x2f, 0x76, 0x31, 0x2f, 0x6d, 0x65, - 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x57, 0x0a, 0x07, - 0x46, 0x6f, 0x6f, 0x4b, 0x65, 0x79, 0x73, 0x12, 0x37, 0x0a, 0x06, 0x66, 0x6f, 0x6f, 0x5f, 0x69, - 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x20, 0xba, 0x48, 0x15, 0x72, 0x13, 0x32, 0x11, - 0x5e, 0x5b, 0x30, 0x2d, 0x39, 0x41, 0x2d, 0x5a, 0x61, 0x2d, 0x7a, 0x5d, 0x7b, 0x32, 0x32, 0x7d, - 0x24, 0xc2, 0xff, 0x8e, 0x02, 0x03, 0xb2, 0x02, 0x00, 0x52, 0x05, 0x66, 0x6f, 0x6f, 0x49, 0x64, - 0x3a, 0x13, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0xea, 0x85, 0x8f, 0x02, 0x07, 0x0a, 0x03, - 0x66, 0x6f, 0x6f, 0x10, 0x01, 0x22, 0x3c, 0x0a, 0x07, 0x46, 0x6f, 0x6f, 0x44, 0x61, 0x74, 0x61, - 0x12, 0x1c, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x08, - 0xc2, 0xff, 0x8e, 0x02, 0x03, 0xf2, 0x01, 0x00, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x3a, 0x13, + 0x1a, 0x1c, 0x6a, 0x35, 0x2f, 0x6c, 0x69, 0x73, 0x74, 0x2f, 0x76, 0x31, 0x2f, 0x61, 0x6e, 0x6e, + 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1a, + 0x6a, 0x35, 0x2f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x2f, 0x76, 0x31, 0x2f, 0x6d, 0x65, 0x74, 0x61, + 0x64, 0x61, 0x74, 0x61, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x57, 0x0a, 0x07, 0x46, 0x6f, + 0x6f, 0x4b, 0x65, 0x79, 0x73, 0x12, 0x37, 0x0a, 0x06, 0x66, 0x6f, 0x6f, 0x5f, 0x69, 0x64, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x20, 0xba, 0x48, 0x15, 0x72, 0x13, 0x32, 0x11, 0x5e, 0x5b, + 0x30, 0x2d, 0x39, 0x41, 0x2d, 0x5a, 0x61, 0x2d, 0x7a, 0x5d, 0x7b, 0x32, 0x32, 0x7d, 0x24, 0xc2, + 0xff, 0x8e, 0x02, 0x03, 0xb2, 0x02, 0x00, 0x52, 0x05, 0x66, 0x6f, 0x6f, 0x49, 0x64, 0x3a, 0x13, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0xea, 0x85, 0x8f, 0x02, 0x07, 0x0a, 0x03, 0x66, 0x6f, - 0x6f, 0x10, 0x04, 0x22, 0x9b, 0x02, 0x0a, 0x08, 0x46, 0x6f, 0x6f, 0x53, 0x74, 0x61, 0x74, 0x65, - 0x12, 0x45, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6a, 0x35, 0x2e, 0x73, 0x74, 0x61, 0x74, 0x65, 0x2e, 0x76, 0x31, - 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x42, 0x0d, - 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0x52, 0x08, 0x6d, - 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x38, 0x0a, 0x04, 0x6b, 0x65, 0x79, 0x73, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x67, 0x65, 0x73, 0x74, 0x65, 0x73, 0x74, 0x2e, - 0x76, 0x31, 0x2e, 0x46, 0x6f, 0x6f, 0x4b, 0x65, 0x79, 0x73, 0x42, 0x0f, 0xba, 0x48, 0x03, 0xc8, - 0x01, 0x01, 0xc2, 0xff, 0x8e, 0x02, 0x04, 0x52, 0x02, 0x08, 0x01, 0x52, 0x04, 0x6b, 0x65, 0x79, - 0x73, 0x12, 0x36, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x13, 0x2e, 0x67, 0x65, 0x73, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x6f, 0x6f, - 0x44, 0x61, 0x74, 0x61, 0x42, 0x0d, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0xc2, 0xff, 0x8e, 0x02, - 0x02, 0x52, 0x00, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, 0x41, 0x0a, 0x06, 0x73, 0x74, 0x61, - 0x74, 0x75, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x15, 0x2e, 0x67, 0x65, 0x73, 0x74, - 0x65, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x6f, 0x6f, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, - 0x42, 0x12, 0xba, 0x48, 0x08, 0xc8, 0x01, 0x01, 0x82, 0x01, 0x02, 0x10, 0x01, 0xc2, 0xff, 0x8e, - 0x02, 0x02, 0x5a, 0x00, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x3a, 0x13, 0xc2, 0xff, + 0x6f, 0x10, 0x01, 0x22, 0x3c, 0x0a, 0x07, 0x46, 0x6f, 0x6f, 0x44, 0x61, 0x74, 0x61, 0x12, 0x1c, + 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x08, 0xc2, 0xff, + 0x8e, 0x02, 0x03, 0xf2, 0x01, 0x00, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x3a, 0x13, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0xea, 0x85, 0x8f, 0x02, 0x07, 0x0a, 0x03, 0x66, 0x6f, 0x6f, 0x10, - 0x02, 0x22, 0xef, 0x01, 0x0a, 0x0c, 0x46, 0x6f, 0x6f, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, - 0x70, 0x65, 0x12, 0x42, 0x0a, 0x06, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x67, 0x65, 0x73, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, - 0x46, 0x6f, 0x6f, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x2e, 0x43, 0x72, 0x65, - 0x61, 0x74, 0x65, 0x42, 0x07, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0x48, 0x00, 0x52, 0x06, - 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x12, 0x45, 0x0a, 0x07, 0x61, 0x72, 0x63, 0x68, 0x69, 0x76, - 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x67, 0x65, 0x73, 0x74, 0x65, 0x73, - 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x6f, 0x6f, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, - 0x65, 0x2e, 0x41, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x42, 0x07, 0xc2, 0xff, 0x8e, 0x02, 0x02, - 0x52, 0x00, 0x48, 0x00, 0x52, 0x07, 0x61, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x1a, 0x2f, 0x0a, - 0x06, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x12, 0x1c, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x08, 0xc2, 0xff, 0x8e, 0x02, 0x03, 0xf2, 0x01, 0x00, 0x52, - 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x3a, 0x07, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0x1a, 0x12, - 0x0a, 0x07, 0x41, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x3a, 0x07, 0xc2, 0xff, 0x8e, 0x02, 0x02, - 0x52, 0x00, 0x3a, 0x07, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x5a, 0x00, 0x42, 0x06, 0x0a, 0x04, 0x74, - 0x79, 0x70, 0x65, 0x22, 0xdf, 0x01, 0x0a, 0x08, 0x46, 0x6f, 0x6f, 0x45, 0x76, 0x65, 0x6e, 0x74, - 0x12, 0x45, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6a, 0x35, 0x2e, 0x73, 0x74, 0x61, 0x74, 0x65, 0x2e, 0x76, 0x31, - 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x42, 0x0d, - 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0x52, 0x08, 0x6d, - 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x38, 0x0a, 0x04, 0x6b, 0x65, 0x79, 0x73, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x67, 0x65, 0x73, 0x74, 0x65, 0x73, 0x74, 0x2e, - 0x76, 0x31, 0x2e, 0x46, 0x6f, 0x6f, 0x4b, 0x65, 0x79, 0x73, 0x42, 0x0f, 0xba, 0x48, 0x03, 0xc8, - 0x01, 0x01, 0xc2, 0xff, 0x8e, 0x02, 0x04, 0x52, 0x02, 0x08, 0x01, 0x52, 0x04, 0x6b, 0x65, 0x79, - 0x73, 0x12, 0x3d, 0x0a, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x18, 0x2e, 0x67, 0x65, 0x73, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x6f, - 0x6f, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x42, 0x0d, 0xba, 0x48, 0x03, 0xc8, - 0x01, 0x01, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x62, 0x00, 0x52, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, - 0x3a, 0x13, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0xea, 0x85, 0x8f, 0x02, 0x07, 0x0a, 0x03, - 0x66, 0x6f, 0x6f, 0x10, 0x03, 0x2a, 0x57, 0x0a, 0x09, 0x46, 0x6f, 0x6f, 0x53, 0x74, 0x61, 0x74, - 0x75, 0x73, 0x12, 0x1a, 0x0a, 0x16, 0x46, 0x4f, 0x4f, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, - 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x15, - 0x0a, 0x11, 0x46, 0x4f, 0x4f, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x41, 0x43, 0x54, - 0x49, 0x56, 0x45, 0x10, 0x01, 0x12, 0x17, 0x0a, 0x13, 0x46, 0x4f, 0x4f, 0x5f, 0x53, 0x54, 0x41, - 0x54, 0x55, 0x53, 0x5f, 0x49, 0x4e, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x10, 0x02, 0x42, 0x3b, - 0x5a, 0x39, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x65, 0x6e, - 0x74, 0x6f, 0x70, 0x73, 0x2f, 0x67, 0x65, 0x73, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, - 0x6c, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x67, 0x65, 0x73, 0x74, 0x65, 0x73, 0x74, 0x2f, 0x76, 0x31, - 0x2f, 0x67, 0x65, 0x73, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x33, + 0x04, 0x22, 0xa8, 0x02, 0x0a, 0x08, 0x46, 0x6f, 0x6f, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x45, + 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x1a, 0x2e, 0x6a, 0x35, 0x2e, 0x73, 0x74, 0x61, 0x74, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, + 0x74, 0x61, 0x74, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x42, 0x0d, 0xba, 0x48, + 0x03, 0xc8, 0x01, 0x01, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0x52, 0x08, 0x6d, 0x65, 0x74, + 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x38, 0x0a, 0x04, 0x6b, 0x65, 0x79, 0x73, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x67, 0x65, 0x73, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x76, 0x31, + 0x2e, 0x46, 0x6f, 0x6f, 0x4b, 0x65, 0x79, 0x73, 0x42, 0x0f, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, + 0xc2, 0xff, 0x8e, 0x02, 0x04, 0x52, 0x02, 0x08, 0x01, 0x52, 0x04, 0x6b, 0x65, 0x79, 0x73, 0x12, + 0x36, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, + 0x67, 0x65, 0x73, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x6f, 0x6f, 0x44, 0x61, + 0x74, 0x61, 0x42, 0x0d, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, + 0x00, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, 0x4e, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, + 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x15, 0x2e, 0x67, 0x65, 0x73, 0x74, 0x65, 0x73, + 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x6f, 0x6f, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x42, 0x1f, + 0xba, 0x48, 0x08, 0xc8, 0x01, 0x01, 0x82, 0x01, 0x02, 0x10, 0x01, 0xc2, 0xff, 0x8e, 0x02, 0x02, + 0x5a, 0x00, 0x8a, 0xf7, 0x98, 0xc6, 0x02, 0x07, 0xa2, 0x01, 0x04, 0x52, 0x02, 0x08, 0x01, 0x52, + 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x3a, 0x13, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, + 0xea, 0x85, 0x8f, 0x02, 0x07, 0x0a, 0x03, 0x66, 0x6f, 0x6f, 0x10, 0x02, 0x22, 0xef, 0x01, 0x0a, + 0x0c, 0x46, 0x6f, 0x6f, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x42, 0x0a, + 0x06, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, + 0x67, 0x65, 0x73, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x6f, 0x6f, 0x45, 0x76, + 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x42, 0x07, + 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0x48, 0x00, 0x52, 0x06, 0x63, 0x72, 0x65, 0x61, 0x74, + 0x65, 0x12, 0x45, 0x0a, 0x07, 0x61, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x67, 0x65, 0x73, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, + 0x46, 0x6f, 0x6f, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x2e, 0x41, 0x72, 0x63, + 0x68, 0x69, 0x76, 0x65, 0x42, 0x07, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0x48, 0x00, 0x52, + 0x07, 0x61, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x1a, 0x2f, 0x0a, 0x06, 0x43, 0x72, 0x65, 0x61, + 0x74, 0x65, 0x12, 0x1c, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x42, 0x08, 0xc2, 0xff, 0x8e, 0x02, 0x03, 0xf2, 0x01, 0x00, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, + 0x3a, 0x07, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0x1a, 0x12, 0x0a, 0x07, 0x41, 0x72, 0x63, + 0x68, 0x69, 0x76, 0x65, 0x3a, 0x07, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0x3a, 0x07, 0xc2, + 0xff, 0x8e, 0x02, 0x02, 0x5a, 0x00, 0x42, 0x06, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x22, 0xec, + 0x01, 0x0a, 0x08, 0x46, 0x6f, 0x6f, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x45, 0x0a, 0x08, 0x6d, + 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, + 0x6a, 0x35, 0x2e, 0x73, 0x74, 0x61, 0x74, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x76, 0x65, 0x6e, + 0x74, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x42, 0x0d, 0xba, 0x48, 0x03, 0xc8, 0x01, + 0x01, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, + 0x74, 0x61, 0x12, 0x38, 0x0a, 0x04, 0x6b, 0x65, 0x79, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x13, 0x2e, 0x67, 0x65, 0x73, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x6f, + 0x6f, 0x4b, 0x65, 0x79, 0x73, 0x42, 0x0f, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0xc2, 0xff, 0x8e, + 0x02, 0x04, 0x52, 0x02, 0x08, 0x01, 0x52, 0x04, 0x6b, 0x65, 0x79, 0x73, 0x12, 0x4a, 0x0a, 0x05, + 0x65, 0x76, 0x65, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x67, 0x65, + 0x73, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x6f, 0x6f, 0x45, 0x76, 0x65, 0x6e, + 0x74, 0x54, 0x79, 0x70, 0x65, 0x42, 0x1a, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0xc2, 0xff, 0x8e, + 0x02, 0x02, 0x62, 0x00, 0x8a, 0xf7, 0x98, 0xc6, 0x02, 0x07, 0xaa, 0x01, 0x04, 0x52, 0x02, 0x08, + 0x01, 0x52, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x3a, 0x13, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, + 0x00, 0xea, 0x85, 0x8f, 0x02, 0x07, 0x0a, 0x03, 0x66, 0x6f, 0x6f, 0x10, 0x03, 0x2a, 0x57, 0x0a, + 0x09, 0x46, 0x6f, 0x6f, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1a, 0x0a, 0x16, 0x46, 0x4f, + 0x4f, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, + 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x15, 0x0a, 0x11, 0x46, 0x4f, 0x4f, 0x5f, 0x53, 0x54, + 0x41, 0x54, 0x55, 0x53, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x10, 0x01, 0x12, 0x17, 0x0a, + 0x13, 0x46, 0x4f, 0x4f, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x49, 0x4e, 0x41, 0x43, + 0x54, 0x49, 0x56, 0x45, 0x10, 0x02, 0x42, 0x3b, 0x5a, 0x39, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, + 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x65, 0x6e, 0x74, 0x6f, 0x70, 0x73, 0x2f, 0x67, 0x65, 0x73, + 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x67, 0x65, + 0x73, 0x74, 0x65, 0x73, 0x74, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x73, 0x74, 0x65, 0x73, 0x74, + 0x5f, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/internal/gen/gestest/v1/gestest_spb/foo.p.j5s.pb.go b/internal/gen/gestest/v1/gestest_spb/foo.p.j5s.pb.go index b2026a2..baf7bdf 100644 --- a/internal/gen/gestest/v1/gestest_spb/foo.p.j5s.pb.go +++ b/internal/gen/gestest/v1/gestest_spb/foo.p.j5s.pb.go @@ -1,4 +1,4 @@ -// Code generated by j5convert. DO NOT EDIT. +// Generated by j5build v0.0.0-20250403212908-de7c3c2e6cce. DO NOT EDIT // Code generated by protoc-gen-go. DO NOT EDIT. // versions: diff --git a/internal/gen/gestest/v1/gestest_spb/foo.p.j5s_grpc.pb.go b/internal/gen/gestest/v1/gestest_spb/foo.p.j5s_grpc.pb.go index 78c9864..35a98c1 100644 --- a/internal/gen/gestest/v1/gestest_spb/foo.p.j5s_grpc.pb.go +++ b/internal/gen/gestest/v1/gestest_spb/foo.p.j5s_grpc.pb.go @@ -1,4 +1,4 @@ -// Code generated by j5convert. DO NOT EDIT. +// Generated by j5build v0.0.0-20250403212908-de7c3c2e6cce. DO NOT EDIT // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: diff --git a/internal/gen/gestest/v1/gestest_tpb/foo.p.j5s.pb.go b/internal/gen/gestest/v1/gestest_tpb/foo.p.j5s.pb.go index e385013..f87dd7e 100644 --- a/internal/gen/gestest/v1/gestest_tpb/foo.p.j5s.pb.go +++ b/internal/gen/gestest/v1/gestest_tpb/foo.p.j5s.pb.go @@ -1,4 +1,4 @@ -// Code generated by j5convert. DO NOT EDIT. +// Generated by j5build v0.0.0-20250403212908-de7c3c2e6cce. DO NOT EDIT // Code generated by protoc-gen-go. DO NOT EDIT. // versions: diff --git a/internal/gen/gestest/v1/gestest_tpb/foo.p.j5s_grpc.pb.go b/internal/gen/gestest/v1/gestest_tpb/foo.p.j5s_grpc.pb.go index a705a7c..f7da597 100644 --- a/internal/gen/gestest/v1/gestest_tpb/foo.p.j5s_grpc.pb.go +++ b/internal/gen/gestest/v1/gestest_tpb/foo.p.j5s_grpc.pb.go @@ -1,4 +1,4 @@ -// Code generated by j5convert. DO NOT EDIT. +// Generated by j5build v0.0.0-20250403212908-de7c3c2e6cce. DO NOT EDIT // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: diff --git a/internal/gen/o5/ges/v1/ges_pb/events.j5s.pb.go b/internal/gen/o5/ges/v1/ges_pb/events.j5s.pb.go index fe1f238..bd98a72 100644 --- a/internal/gen/o5/ges/v1/ges_pb/events.j5s.pb.go +++ b/internal/gen/o5/ges/v1/ges_pb/events.j5s.pb.go @@ -1,4 +1,4 @@ -// Code generated by j5convert. DO NOT EDIT. +// Generated by j5build v0.0.0-20250403212908-de7c3c2e6cce. DO NOT EDIT // Code generated by protoc-gen-go. DO NOT EDIT. // versions: @@ -11,6 +11,7 @@ package ges_pb import ( _ "buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go/buf/validate" _ "github.com/pentops/j5/gen/j5/ext/v1/ext_j5pb" + psm_j5pb "github.com/pentops/j5/gen/j5/state/v1/psm_j5pb" any_j5t "github.com/pentops/j5/j5types/any_j5t" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" @@ -31,14 +32,16 @@ type Event struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` - EntityName string `protobuf:"bytes,2,opt,name=entity_name,json=entityName,proto3" json:"entity_name,omitempty"` - Sequence uint64 `protobuf:"varint,3,opt,name=sequence,proto3" json:"sequence,omitempty"` - Timestamp *timestamppb.Timestamp `protobuf:"bytes,4,opt,name=timestamp,proto3" json:"timestamp,omitempty"` - EventType string `protobuf:"bytes,5,opt,name=event_type,json=eventType,proto3" json:"event_type,omitempty"` - EntityKeys *any_j5t.Any `protobuf:"bytes,6,opt,name=entity_keys,json=entityKeys,proto3" json:"entity_keys,omitempty"` - EventData *any_j5t.Any `protobuf:"bytes,7,opt,name=event_data,json=eventData,proto3" json:"event_data,omitempty"` - EntityState *any_j5t.Any `protobuf:"bytes,8,opt,name=entity_state,json=entityState,proto3" json:"entity_state,omitempty"` + EntityName string `protobuf:"bytes,1,opt,name=entity_name,json=entityName,proto3" json:"entity_name,omitempty"` + Metadata *psm_j5pb.EventPublishMetadata `protobuf:"bytes,2,opt,name=metadata,proto3" json:"metadata,omitempty"` + GrpcMethod string `protobuf:"bytes,3,opt,name=grpc_method,json=grpcMethod,proto3" json:"grpc_method,omitempty"` + GrpcService string `protobuf:"bytes,4,opt,name=grpc_service,json=grpcService,proto3" json:"grpc_service,omitempty"` + BodyType string `protobuf:"bytes,5,opt,name=body_type,json=bodyType,proto3" json:"body_type,omitempty"` + EventType string `protobuf:"bytes,6,opt,name=event_type,json=eventType,proto3" json:"event_type,omitempty"` + EntityKeys *any_j5t.Any `protobuf:"bytes,7,opt,name=entity_keys,json=entityKeys,proto3" json:"entity_keys,omitempty"` + EventData *any_j5t.Any `protobuf:"bytes,8,opt,name=event_data,json=eventData,proto3" json:"event_data,omitempty"` + EntityState *any_j5t.Any `protobuf:"bytes,9,opt,name=entity_state,json=entityState,proto3" json:"entity_state,omitempty"` + EntityStatus string `protobuf:"bytes,10,opt,name=entity_status,json=entityStatus,proto3" json:"entity_status,omitempty"` } func (x *Event) Reset() { @@ -73,32 +76,39 @@ func (*Event) Descriptor() ([]byte, []int) { return file_o5_ges_v1_events_j5s_proto_rawDescGZIP(), []int{0} } -func (x *Event) GetId() string { +func (x *Event) GetEntityName() string { if x != nil { - return x.Id + return x.EntityName } return "" } -func (x *Event) GetEntityName() string { +func (x *Event) GetMetadata() *psm_j5pb.EventPublishMetadata { if x != nil { - return x.EntityName + return x.Metadata + } + return nil +} + +func (x *Event) GetGrpcMethod() string { + if x != nil { + return x.GrpcMethod } return "" } -func (x *Event) GetSequence() uint64 { +func (x *Event) GetGrpcService() string { if x != nil { - return x.Sequence + return x.GrpcService } - return 0 + return "" } -func (x *Event) GetTimestamp() *timestamppb.Timestamp { +func (x *Event) GetBodyType() string { if x != nil { - return x.Timestamp + return x.BodyType } - return nil + return "" } func (x *Event) GetEventType() string { @@ -129,6 +139,13 @@ func (x *Event) GetEntityState() *any_j5t.Any { return nil } +func (x *Event) GetEntityStatus() string { + if x != nil { + return x.EntityStatus + } + return "" +} + type Upsert struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -136,9 +153,11 @@ type Upsert struct { EntityName string `protobuf:"bytes,1,opt,name=entity_name,json=entityName,proto3" json:"entity_name,omitempty"` EntityId string `protobuf:"bytes,2,opt,name=entity_id,json=entityId,proto3" json:"entity_id,omitempty"` - LastEventId string `protobuf:"bytes,3,opt,name=last_event_id,json=lastEventId,proto3" json:"last_event_id,omitempty"` - LastEventTimestamp *timestamppb.Timestamp `protobuf:"bytes,4,opt,name=last_event_timestamp,json=lastEventTimestamp,proto3" json:"last_event_timestamp,omitempty"` - Data *any_j5t.Any `protobuf:"bytes,5,opt,name=data,proto3" json:"data,omitempty"` + GrpcMethod string `protobuf:"bytes,3,opt,name=grpc_method,json=grpcMethod,proto3" json:"grpc_method,omitempty"` + GrpcService string `protobuf:"bytes,4,opt,name=grpc_service,json=grpcService,proto3" json:"grpc_service,omitempty"` + LastEventId string `protobuf:"bytes,5,opt,name=last_event_id,json=lastEventId,proto3" json:"last_event_id,omitempty"` + LastEventTimestamp *timestamppb.Timestamp `protobuf:"bytes,6,opt,name=last_event_timestamp,json=lastEventTimestamp,proto3" json:"last_event_timestamp,omitempty"` + Data *any_j5t.Any `protobuf:"bytes,7,opt,name=data,proto3" json:"data,omitempty"` } func (x *Upsert) Reset() { @@ -187,6 +206,20 @@ func (x *Upsert) GetEntityId() string { return "" } +func (x *Upsert) GetGrpcMethod() string { + if x != nil { + return x.GrpcMethod + } + return "" +} + +func (x *Upsert) GetGrpcService() string { + if x != nil { + return x.GrpcService + } + return "" +} + func (x *Upsert) GetLastEventId() string { if x != nil { return x.LastEventId @@ -219,62 +252,78 @@ var file_o5_ges_v1_events_j5s_proto_rawDesc = []byte{ 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1b, 0x6a, 0x35, 0x2f, 0x65, 0x78, 0x74, 0x2f, 0x76, 0x31, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x1a, 0x19, 0x6a, 0x35, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x61, 0x6e, 0x79, - 0x2f, 0x76, 0x31, 0x2f, 0x61, 0x6e, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xd8, 0x03, - 0x0a, 0x05, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x1e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x42, 0x0e, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0xc2, 0xff, 0x8e, 0x02, 0x03, - 0xf2, 0x01, 0x00, 0x52, 0x02, 0x69, 0x64, 0x12, 0x2f, 0x0a, 0x0b, 0x65, 0x6e, 0x74, 0x69, 0x74, - 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x0e, 0xba, 0x48, - 0x03, 0xc8, 0x01, 0x01, 0xc2, 0xff, 0x8e, 0x02, 0x03, 0xf2, 0x01, 0x00, 0x52, 0x0a, 0x65, 0x6e, - 0x74, 0x69, 0x74, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x2a, 0x0a, 0x08, 0x73, 0x65, 0x71, 0x75, - 0x65, 0x6e, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x42, 0x0e, 0xba, 0x48, 0x03, 0xc8, - 0x01, 0x01, 0xc2, 0xff, 0x8e, 0x02, 0x03, 0xfa, 0x01, 0x00, 0x52, 0x08, 0x73, 0x65, 0x71, 0x75, - 0x65, 0x6e, 0x63, 0x65, 0x12, 0x48, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, - 0x70, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, - 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, - 0x61, 0x6d, 0x70, 0x42, 0x0e, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0xc2, 0xff, 0x8e, 0x02, 0x03, - 0xaa, 0x02, 0x00, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x2d, - 0x0a, 0x0a, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x05, 0x20, 0x01, - 0x28, 0x09, 0x42, 0x0e, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0xc2, 0xff, 0x8e, 0x02, 0x03, 0xf2, - 0x01, 0x00, 0x52, 0x09, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x44, 0x0a, - 0x0b, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x5f, 0x6b, 0x65, 0x79, 0x73, 0x18, 0x06, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6a, 0x35, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x61, 0x6e, - 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x6e, 0x79, 0x42, 0x0d, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, - 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x4a, 0x00, 0x52, 0x0a, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x4b, - 0x65, 0x79, 0x73, 0x12, 0x42, 0x0a, 0x0a, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x64, 0x61, 0x74, - 0x61, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6a, 0x35, 0x2e, 0x74, 0x79, 0x70, - 0x65, 0x73, 0x2e, 0x61, 0x6e, 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x6e, 0x79, 0x42, 0x0d, 0xba, - 0x48, 0x03, 0xc8, 0x01, 0x01, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x4a, 0x00, 0x52, 0x09, 0x65, 0x76, - 0x65, 0x6e, 0x74, 0x44, 0x61, 0x74, 0x61, 0x12, 0x46, 0x0a, 0x0c, 0x65, 0x6e, 0x74, 0x69, 0x74, - 0x79, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, - 0x6a, 0x35, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x61, 0x6e, 0x79, 0x2e, 0x76, 0x31, 0x2e, - 0x41, 0x6e, 0x79, 0x42, 0x0d, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0xc2, 0xff, 0x8e, 0x02, 0x02, - 0x4a, 0x00, 0x52, 0x0b, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x3a, - 0x07, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0x22, 0xba, 0x02, 0x0a, 0x06, 0x55, 0x70, 0x73, - 0x65, 0x72, 0x74, 0x12, 0x2f, 0x0a, 0x0b, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x5f, 0x6e, 0x61, + 0x74, 0x6f, 0x1a, 0x1a, 0x6a, 0x35, 0x2f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x2f, 0x76, 0x31, 0x2f, + 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x19, + 0x6a, 0x35, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x61, 0x6e, 0x79, 0x2f, 0x76, 0x31, 0x2f, + 0x61, 0x6e, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xd6, 0x04, 0x0a, 0x05, 0x45, 0x76, + 0x65, 0x6e, 0x74, 0x12, 0x2f, 0x0a, 0x0b, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x0e, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0xc2, 0xff, 0x8e, 0x02, 0x03, 0xf2, 0x01, 0x00, 0x52, 0x0a, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, - 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x2b, 0x0a, 0x09, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x5f, 0x69, - 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x0e, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0xc2, - 0xff, 0x8e, 0x02, 0x03, 0xf2, 0x01, 0x00, 0x52, 0x08, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x49, - 0x64, 0x12, 0x32, 0x0a, 0x0d, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, - 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x0e, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, - 0xc2, 0xff, 0x8e, 0x02, 0x03, 0xf2, 0x01, 0x00, 0x52, 0x0b, 0x6c, 0x61, 0x73, 0x74, 0x45, 0x76, - 0x65, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x5c, 0x0a, 0x14, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x65, 0x76, - 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x42, - 0x0e, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0xc2, 0xff, 0x8e, 0x02, 0x03, 0xaa, 0x02, 0x00, 0x52, - 0x12, 0x6c, 0x61, 0x73, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, - 0x61, 0x6d, 0x70, 0x12, 0x37, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x05, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x14, 0x2e, 0x6a, 0x35, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x61, 0x6e, 0x79, - 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x6e, 0x79, 0x42, 0x0d, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0xc2, - 0xff, 0x8e, 0x02, 0x02, 0x4a, 0x00, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x3a, 0x07, 0xc2, 0xff, - 0x8e, 0x02, 0x02, 0x52, 0x00, 0x42, 0x36, 0x5a, 0x34, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, - 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x65, 0x6e, 0x74, 0x6f, 0x70, 0x73, 0x2f, 0x67, 0x65, 0x73, 0x2f, - 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x6f, 0x35, 0x2f, - 0x67, 0x65, 0x73, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x73, 0x5f, 0x70, 0x62, 0x62, 0x06, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x4c, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x6a, 0x35, 0x2e, 0x73, 0x74, 0x61, 0x74, + 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, + 0x68, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x42, 0x0d, 0xba, 0x48, 0x03, 0xc8, 0x01, + 0x01, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, + 0x74, 0x61, 0x12, 0x2f, 0x0a, 0x0b, 0x67, 0x72, 0x70, 0x63, 0x5f, 0x6d, 0x65, 0x74, 0x68, 0x6f, + 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x0e, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0xc2, + 0xff, 0x8e, 0x02, 0x03, 0xf2, 0x01, 0x00, 0x52, 0x0a, 0x67, 0x72, 0x70, 0x63, 0x4d, 0x65, 0x74, + 0x68, 0x6f, 0x64, 0x12, 0x31, 0x0a, 0x0c, 0x67, 0x72, 0x70, 0x63, 0x5f, 0x73, 0x65, 0x72, 0x76, + 0x69, 0x63, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x42, 0x0e, 0xba, 0x48, 0x03, 0xc8, 0x01, + 0x01, 0xc2, 0xff, 0x8e, 0x02, 0x03, 0xf2, 0x01, 0x00, 0x52, 0x0b, 0x67, 0x72, 0x70, 0x63, 0x53, + 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x2b, 0x0a, 0x09, 0x62, 0x6f, 0x64, 0x79, 0x5f, 0x74, + 0x79, 0x70, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x42, 0x0e, 0xba, 0x48, 0x03, 0xc8, 0x01, + 0x01, 0xc2, 0xff, 0x8e, 0x02, 0x03, 0xf2, 0x01, 0x00, 0x52, 0x08, 0x62, 0x6f, 0x64, 0x79, 0x54, + 0x79, 0x70, 0x65, 0x12, 0x2d, 0x0a, 0x0a, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x79, 0x70, + 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x42, 0x0e, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0xc2, + 0xff, 0x8e, 0x02, 0x03, 0xf2, 0x01, 0x00, 0x52, 0x09, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, + 0x70, 0x65, 0x12, 0x44, 0x0a, 0x0b, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x5f, 0x6b, 0x65, 0x79, + 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6a, 0x35, 0x2e, 0x74, 0x79, 0x70, + 0x65, 0x73, 0x2e, 0x61, 0x6e, 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x6e, 0x79, 0x42, 0x0d, 0xba, + 0x48, 0x03, 0xc8, 0x01, 0x01, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x4a, 0x00, 0x52, 0x0a, 0x65, 0x6e, + 0x74, 0x69, 0x74, 0x79, 0x4b, 0x65, 0x79, 0x73, 0x12, 0x42, 0x0a, 0x0a, 0x65, 0x76, 0x65, 0x6e, + 0x74, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6a, + 0x35, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x61, 0x6e, 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x41, + 0x6e, 0x79, 0x42, 0x0d, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x4a, + 0x00, 0x52, 0x09, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x44, 0x61, 0x74, 0x61, 0x12, 0x46, 0x0a, 0x0c, + 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x09, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x6a, 0x35, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x61, 0x6e, + 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x6e, 0x79, 0x42, 0x0d, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, + 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x4a, 0x00, 0x52, 0x0b, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x53, + 0x74, 0x61, 0x74, 0x65, 0x12, 0x33, 0x0a, 0x0d, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x5f, 0x73, + 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x42, 0x0e, 0xba, 0x48, 0x03, + 0xc8, 0x01, 0x01, 0xc2, 0xff, 0x8e, 0x02, 0x03, 0xf2, 0x01, 0x00, 0x52, 0x0c, 0x65, 0x6e, 0x74, + 0x69, 0x74, 0x79, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x3a, 0x07, 0xc2, 0xff, 0x8e, 0x02, 0x02, + 0x52, 0x00, 0x22, 0x9e, 0x03, 0x0a, 0x06, 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x12, 0x2f, 0x0a, + 0x0b, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x42, 0x0e, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0xc2, 0xff, 0x8e, 0x02, 0x03, 0xf2, + 0x01, 0x00, 0x52, 0x0a, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x2b, + 0x0a, 0x09, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x42, 0x0e, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0xc2, 0xff, 0x8e, 0x02, 0x03, 0xf2, 0x01, + 0x00, 0x52, 0x08, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x49, 0x64, 0x12, 0x2f, 0x0a, 0x0b, 0x67, + 0x72, 0x70, 0x63, 0x5f, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, + 0x42, 0x0e, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0xc2, 0xff, 0x8e, 0x02, 0x03, 0xf2, 0x01, 0x00, + 0x52, 0x0a, 0x67, 0x72, 0x70, 0x63, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x12, 0x31, 0x0a, 0x0c, + 0x67, 0x72, 0x70, 0x63, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x09, 0x42, 0x0e, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0xc2, 0xff, 0x8e, 0x02, 0x03, 0xf2, + 0x01, 0x00, 0x52, 0x0b, 0x67, 0x72, 0x70, 0x63, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, + 0x32, 0x0a, 0x0d, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x42, 0x0e, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0xc2, 0xff, + 0x8e, 0x02, 0x03, 0xf2, 0x01, 0x00, 0x52, 0x0b, 0x6c, 0x61, 0x73, 0x74, 0x45, 0x76, 0x65, 0x6e, + 0x74, 0x49, 0x64, 0x12, 0x5c, 0x0a, 0x14, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x65, 0x76, 0x65, 0x6e, + 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x06, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x42, 0x0e, 0xba, + 0x48, 0x03, 0xc8, 0x01, 0x01, 0xc2, 0xff, 0x8e, 0x02, 0x03, 0xaa, 0x02, 0x00, 0x52, 0x12, 0x6c, + 0x61, 0x73, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, + 0x70, 0x12, 0x37, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x14, 0x2e, 0x6a, 0x35, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x61, 0x6e, 0x79, 0x2e, 0x76, + 0x31, 0x2e, 0x41, 0x6e, 0x79, 0x42, 0x0d, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0xc2, 0xff, 0x8e, + 0x02, 0x02, 0x4a, 0x00, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x3a, 0x07, 0xc2, 0xff, 0x8e, 0x02, + 0x02, 0x52, 0x00, 0x42, 0x36, 0x5a, 0x34, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, + 0x6d, 0x2f, 0x70, 0x65, 0x6e, 0x74, 0x6f, 0x70, 0x73, 0x2f, 0x67, 0x65, 0x73, 0x2f, 0x69, 0x6e, + 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x6f, 0x35, 0x2f, 0x67, 0x65, + 0x73, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x73, 0x5f, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x33, } var ( @@ -291,17 +340,18 @@ func file_o5_ges_v1_events_j5s_proto_rawDescGZIP() []byte { var file_o5_ges_v1_events_j5s_proto_msgTypes = make([]protoimpl.MessageInfo, 2) var file_o5_ges_v1_events_j5s_proto_goTypes = []interface{}{ - (*Event)(nil), // 0: o5.ges.v1.Event - (*Upsert)(nil), // 1: o5.ges.v1.Upsert - (*timestamppb.Timestamp)(nil), // 2: google.protobuf.Timestamp - (*any_j5t.Any)(nil), // 3: j5.types.any.v1.Any + (*Event)(nil), // 0: o5.ges.v1.Event + (*Upsert)(nil), // 1: o5.ges.v1.Upsert + (*psm_j5pb.EventPublishMetadata)(nil), // 2: j5.state.v1.EventPublishMetadata + (*any_j5t.Any)(nil), // 3: j5.types.any.v1.Any + (*timestamppb.Timestamp)(nil), // 4: google.protobuf.Timestamp } var file_o5_ges_v1_events_j5s_proto_depIdxs = []int32{ - 2, // 0: o5.ges.v1.Event.timestamp:type_name -> google.protobuf.Timestamp + 2, // 0: o5.ges.v1.Event.metadata:type_name -> j5.state.v1.EventPublishMetadata 3, // 1: o5.ges.v1.Event.entity_keys:type_name -> j5.types.any.v1.Any 3, // 2: o5.ges.v1.Event.event_data:type_name -> j5.types.any.v1.Any 3, // 3: o5.ges.v1.Event.entity_state:type_name -> j5.types.any.v1.Any - 2, // 4: o5.ges.v1.Upsert.last_event_timestamp:type_name -> google.protobuf.Timestamp + 4, // 4: o5.ges.v1.Upsert.last_event_timestamp:type_name -> google.protobuf.Timestamp 3, // 5: o5.ges.v1.Upsert.data:type_name -> j5.types.any.v1.Any 6, // [6:6] is the sub-list for method output_type 6, // [6:6] is the sub-list for method input_type diff --git a/internal/gen/o5/ges/v1/ges_spb/events.p.j5s.pb.go b/internal/gen/o5/ges/v1/ges_spb/events.p.j5s.pb.go index 25d98d7..a43ea09 100644 --- a/internal/gen/o5/ges/v1/ges_spb/events.p.j5s.pb.go +++ b/internal/gen/o5/ges/v1/ges_spb/events.p.j5s.pb.go @@ -1,4 +1,4 @@ -// Code generated by j5convert. DO NOT EDIT. +// Generated by j5build v0.0.0-20250403212908-de7c3c2e6cce. DO NOT EDIT // Code generated by protoc-gen-go. DO NOT EDIT. // versions: diff --git a/internal/gen/o5/ges/v1/ges_spb/events.p.j5s_grpc.pb.go b/internal/gen/o5/ges/v1/ges_spb/events.p.j5s_grpc.pb.go index b8f1798..1329698 100644 --- a/internal/gen/o5/ges/v1/ges_spb/events.p.j5s_grpc.pb.go +++ b/internal/gen/o5/ges/v1/ges_spb/events.p.j5s_grpc.pb.go @@ -1,4 +1,4 @@ -// Code generated by j5convert. DO NOT EDIT. +// Generated by j5build v0.0.0-20250403212908-de7c3c2e6cce. DO NOT EDIT // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: diff --git a/internal/integration/universe.go b/internal/integration/universe.go index e30db48..f63d59e 100644 --- a/internal/integration/universe.go +++ b/internal/integration/universe.go @@ -5,7 +5,6 @@ import ( "fmt" "testing" - "github.com/google/uuid" "github.com/pentops/flowtest" "github.com/pentops/ges/internal/gen/o5/ges/v1/ges_spb" "github.com/pentops/ges/internal/service" @@ -19,7 +18,6 @@ import ( "github.com/pentops/pgtest.go/pgtest" "github.com/pentops/sqrlx.go/sqrlx" "google.golang.org/protobuf/proto" - "google.golang.org/protobuf/types/known/timestamppb" ) type Universe struct { @@ -30,17 +28,13 @@ type Universe struct { DB sqrlx.Transactor GRPCPair *flowtest.GRPCPair Outbox *outboxtest.OutboxAsserter - - Codec *j5codec.Codec } func NewUniverse(ctx context.Context, t *testing.T) (*flowtest.Stepper[*testing.T], *Universe) { name := t.Name() stepper := flowtest.NewStepper[*testing.T](name) - uu := &Universe{ - Codec: j5codec.NewCodec(), - } + uu := &Universe{} stepper.Setup(func(ctx context.Context, t flowtest.Asserter) error { log.DefaultLogger = log.NewCallbackLogger(stepper.LevelLog) //log.DefaultLogger.SetLevel(slog.LevelDebug) @@ -88,27 +82,16 @@ func (uu *Universe) Run(ctx context.Context, t flowtest.TB) { func (uu *Universe) HandleGeneric(ctx context.Context, t flowtest.TB, msg o5msg.Message) { - bodyData, err := uu.Codec.EncodeAny(msg.ProtoReflect()) + bodyData, err := j5codec.Global.EncodeAny(msg.ProtoReflect()) if err != nil { t.Fatalf("failed to encode message: %v", err) } - header := msg.O5MessageHeader() - - wrapper := &messaging_pb.Message{ - MessageId: uuid.New().String(), - Timestamp: timestamppb.Now(), - GrpcService: header.GrpcService, - GrpcMethod: header.GrpcMethod, - Body: &messaging_pb.Any{ - TypeUrl: fmt.Sprintf("type.googleapis.com/%s", msg.ProtoReflect().Descriptor().FullName()), - Value: bodyData.J5Json, - Encoding: messaging_pb.WireEncoding_J5_JSON, - }, - DelaySeconds: 0, - DestinationTopic: header.DestinationTopic, - Headers: header.Headers, - Extension: header.Extension, + wrapper := o5msg.MessageWrapper(msg) + wrapper.Body = &messaging_pb.Any{ + TypeUrl: fmt.Sprintf("type.googleapis.com/%s", msg.ProtoReflect().Descriptor().FullName()), + Value: bodyData.J5Json, + Encoding: messaging_pb.WireEncoding_J5_JSON, } _, err = uu.GenericTopic.Generic(ctx, &messaging_tpb.GenericMessage{ @@ -124,7 +107,7 @@ func (uu *Universe) DecodeAnyTo(t flowtest.TB, input *any_j5t.Any, output proto. if input == nil { t.Fatalf("input any is nil") } - err := uu.Codec.DecodeAnyTo(input, output) + err := j5codec.Global.DecodeAnyTo(input, output) if err != nil { t.Fatalf("failed to decode any: %v", err) } diff --git a/internal/replay/listener.go b/internal/replay/listener.go new file mode 100644 index 0000000..7993bc2 --- /dev/null +++ b/internal/replay/listener.go @@ -0,0 +1,182 @@ +package replay + +import ( + "context" + "fmt" + "time" + + "github.com/jackc/pgx/v5" + "github.com/jackc/pgx/v5/pgconn" + "github.com/pentops/log.go/log" +) + +type Topic struct { + Name string + + // Callback runs at the beginning of each connection, and on each + // notification. Return 'true' to re-run the callback (e.g. for draining the + // a queue) + Callback func(context.Context, pgx.Tx) (more bool, err error) +} + +// Listener listens for Postgres notifications on a loop, and handles dropped +// connections. +type Listener struct { + postgresDSN string + conn *pgx.Conn + topics []Topic +} + +func NewListener(dsn string, topics []Topic) (*Listener, error) { + return &Listener{ + postgresDSN: dsn, + topics: topics, + }, nil +} + +func (ll *Listener) connection(ctx context.Context) (*pgx.Conn, error) { + if ll.conn != nil { + return ll.conn, nil + } + + dialContext, cancel := context.WithTimeout(ctx, time.Second*5) + defer cancel() + + conn, err := pgx.Connect(dialContext, ll.postgresDSN) + if err != nil { + return nil, fmt.Errorf("connecting to PG: %w", err) + } + + for { + if err := conn.Ping(dialContext); err != nil { + log.WithError(ctx, err).Warn("pinging Listener PG") + time.Sleep(time.Second) + continue + } + + log.Info(ctx, "pinging Listener PG OK") + break + } + + for _, topic := range ll.topics { + if _, err := conn.Exec(ctx, "LISTEN ?", topic.Name); err != nil { + return nil, fmt.Errorf("error listening to topic %s: %w", topic.Name, err) + } + } + + return conn, nil +} + +func (ll *Listener) closeConnection(ctx context.Context) { + if err := ll.conn.Close(ctx); err != nil { + log.WithError(ctx, err).Error("error closing PG connection") + // but ignore + } + ll.conn = nil +} + +func (ll *Listener) waitForNotification(ctx context.Context) (*pgconn.Notification, error) { + conn, err := ll.connection(ctx) + if err != nil { + return nil, err + } + noti, err := conn.WaitForNotification(ctx) + if err == nil { // Short Happy Path + return noti, nil + } + + log.WithError(ctx, err).Warn("listener error, drop connection") + ll.closeConnection(ctx) + return ll.waitForNotification(ctx) +} + +// Listen first calls each callback to handle any messages while the connection +// was down, then runs the callbacks on each notification. +// Any callback error will exit the listener loop. +func (ll *Listener) Listen(ctx context.Context) error { + var err error + + conn, err := ll.connection(ctx) + if err != nil { + return fmt.Errorf("error getting connection: %w", err) + } + + for _, topic := range ll.topics { + err := ll.runCallback(ctx, topic, conn) + if err != nil { + return err + } + } + + for { + log.Debug(ctx, "waiting for notification") + + noti, err := ll.waitForNotification(ctx) + if err != nil { + return err + } + + found := false + for _, topic := range ll.topics { + if topic.Name == noti.Channel { + err := ll.runCallback(ctx, topic, conn) + if err != nil { + return err + } + found = true + break + } + } + if !found { + return fmt.Errorf("unknown notification channel: %s", noti.Channel) + } + } +} + +func (ll *Listener) runCallback(ctx context.Context, topic Topic, conn *pgx.Conn) error { + log.WithField(ctx, "topic", topic.Name).Debug("running callback") + hasMore := true + var err error + for hasMore { + err = ll.transact(ctx, func(ctx context.Context, tx pgx.Tx) error { + hasMore, err = topic.Callback(ctx, tx) + if err != nil { + return fmt.Errorf("error running callback: %w", err) + } + return nil + }) + if err != nil { + return err + } + } + return nil +} + +func (ll *Listener) transact(ctx context.Context, callback func(context.Context, pgx.Tx) error) error { + conn, err := ll.connection(ctx) + if err != nil { + return fmt.Errorf("error getting connection: %w", err) + } + tx, err := conn.BeginTx(ctx, pgx.TxOptions{ + IsoLevel: pgx.ReadCommitted, + }) + if err != nil { + return fmt.Errorf("error beginning transaction: %w", err) + } + + err = callback(ctx, tx) + if err != nil { + rollbackErr := tx.Rollback(ctx) + if rollbackErr != nil { + log.WithError(ctx, rollbackErr).Error("error rolling back transaction") + } + return err + } + + err = tx.Commit(ctx) + if err != nil { + return fmt.Errorf("error committing transaction: %w", err) + } + + return nil +} diff --git a/internal/replay/publish.go b/internal/replay/publish.go new file mode 100644 index 0000000..0f46d60 --- /dev/null +++ b/internal/replay/publish.go @@ -0,0 +1,101 @@ +package replay + +import ( + "context" + "fmt" + "strconv" + + "github.com/aws/aws-sdk-go-v2/service/sqs" + "github.com/aws/aws-sdk-go-v2/service/sqs/types" + "github.com/pentops/golib/gl" + "github.com/pentops/log.go/log" + "github.com/pentops/o5-messaging/gen/o5/messaging/v1/messaging_pb" + "google.golang.org/protobuf/encoding/protojson" +) + +const ( + ReplayEventPGChannel = "replay_event_notification" + ReplayUpsertPGChannel = "replay_upsert_notification" +) + +type SQS interface { + PublishBatch(context.Context, sqs.SendMessageBatchInput) (sqs.SendMessageBatchOutput, error) +} + +type Message interface { + Message() (*messaging_pb.Message, error) + Destination() string +} + +type messageBatch[T Message] struct { + queueUrl string + messages []T + messageBodies []string +} + +type messageBatches[T Message] map[string]messageBatch[T] + +func (mb messageBatches[T]) addRow(row T) error { + destination := row.Destination() + batch, ok := mb[destination] + if !ok { + batch = messageBatch[T]{queueUrl: destination} + } + msg, err := row.Message() + if err != nil { + log.WithError(context.Background(), err).Error("failed to get message") + + } + messageBody, err := protojson.Marshal(msg) + if err != nil { + return fmt.Errorf("failed to marshal message: %w", err) + } + batch.messages = append(batch.messages, row) + batch.messageBodies = append(batch.messageBodies, string(messageBody)) + mb[destination] = batch + return nil + +} + +// publishBatch publishes a batch of messages to SQS, returning the successfully +// published messages. +func publishBatch[T Message](ctx context.Context, sqsClient SQS, batch messageBatch[T]) ([]T, error) { + log.WithField(ctx, "queueUrl", batch.queueUrl).Debug("publishing batch") + + input := sqs.SendMessageBatchInput{ + Entries: make([]types.SendMessageBatchRequestEntry, len(batch.messageBodies)), + QueueUrl: &batch.queueUrl, + } + + for idx, body := range batch.messageBodies { + input.Entries[idx] = types.SendMessageBatchRequestEntry{ + Id: gl.Ptr(strconv.Itoa(idx)), + MessageBody: &body, + } + } + + output, err := sqsClient.PublishBatch(ctx, input) + if err != nil { + + // From the docs: The result of sending each message is reported individually in the response. Because the batch request can result in a combination of successful and unsuccessful actions, you should check for batch errors even when the call returns an HTTP status code of 200 . + + return nil, fmt.Errorf("error publishing batch: %w", err) + } + + success := make([]T, 0, len(output.Successful)) + for _, msg := range output.Successful { + if msg.Id == nil { + return nil, fmt.Errorf("message ID is nil") + } + idx, err := strconv.Atoi(*msg.Id) + if err != nil { + return nil, fmt.Errorf("error converting message ID to int: %w", err) + } + if idx < 0 || idx >= len(batch.messages) { + return nil, fmt.Errorf("message ID out of range: %d", idx) + } + success = append(success, batch.messages[idx]) + } + + return success, nil +} diff --git a/internal/replay/queries.go b/internal/replay/queries.go new file mode 100644 index 0000000..c36cb6c --- /dev/null +++ b/internal/replay/queries.go @@ -0,0 +1 @@ +package replay diff --git a/internal/replay/query.go b/internal/replay/query.go new file mode 100644 index 0000000..5a74225 --- /dev/null +++ b/internal/replay/query.go @@ -0,0 +1,85 @@ +package replay + +import ( + "context" + "fmt" + + "github.com/jackc/pgx/v5" + "github.com/pentops/log.go/log" +) + +type Row interface { + Scan(dest ...any) error +} + +type MessageQuery[T Message] interface { + // SelectQuery returns the SQL query to select messages from the table. + SelectQuery() string + + // DeleteQuery returns the SQL query to delete successfully published messages from the table. + DeleteQuery([]T) (string, []interface{}, error) + + // ScanRow scans a row from the DB result into a message wrapper + ScanRow(row Row) (T, error) +} + +func doPage[T Message](ctx context.Context, tx pgx.Tx, sqs SQS, qq MessageQuery[T]) (bool, error) { + var count int + + selectQuery := qq.SelectQuery() + + var sendError error + + batches := messageBatches[T]{} + count = 0 + rows, err := tx.Query(ctx, selectQuery) + if err != nil { + return false, fmt.Errorf("error selecting outbox messages: %w", err) + } + + if err := func() error { + defer rows.Close() + for rows.Next() { + count++ + msg, err := qq.ScanRow(rows) + if err != nil { + return fmt.Errorf("error scanning outbox row: %w", err) + } + if err := batches.addRow(msg); err != nil { + return err + } + } + return rows.Err() + }(); err != nil { + return false, err + } + + log.WithField(ctx, "count", count).Debug("got messages") + + for _, batch := range batches { + successMessages, err := publishBatch(ctx, sqs, batch) + if err != nil { + log.WithError(ctx, err).Error("error publishing batch") + sendError = err + // NOTE: sendError is handled at the end, the transaction should still be + // committed for any prior successful sends + break + } + + log.WithField(ctx, "successCount", len(successMessages)).Debug("published outbox messages") + deleteQuery, args, err := qq.DeleteQuery(successMessages) + if err != nil { + return false, fmt.Errorf("error creating delete query: %w", err) + } + _, err = tx.Exec(ctx, deleteQuery, args...) + if err != nil { + return false, fmt.Errorf("error deleting outbox messages: %w", err) + } + } + + if sendError != nil { + return true, fmt.Errorf("error sending batch of replay messages: %w", sendError) + } + + return count > 0, nil +} diff --git a/internal/service/event.go b/internal/service/event.go new file mode 100644 index 0000000..322637b --- /dev/null +++ b/internal/service/event.go @@ -0,0 +1,196 @@ +package service + +import ( + "encoding/json" + "fmt" + "strings" + + "github.com/google/uuid" + "github.com/pentops/ges/internal/gen/o5/ges/v1/ges_pb" + "github.com/pentops/ges/internal/replay" + "github.com/pentops/j5/gen/j5/state/v1/psm_j5pb" + "github.com/pentops/j5/j5types/any_j5t" + "github.com/pentops/j5/lib/j5codec" + "github.com/pentops/o5-messaging/gen/o5/messaging/v1/messaging_pb" + "google.golang.org/protobuf/encoding/protojson" + "google.golang.org/protobuf/types/known/timestamppb" +) + +func reconstructEvent(event *ges_pb.Event) (*messaging_pb.Message, error) { + + type ReconstructedEvent struct { + Metadata json.RawMessage `json:"metadata"` + Keys json.RawMessage `json:"keys"` + Event map[string]interface{} `json:"event"` + Data json.RawMessage `json:"data"` + Status string `json:"status"` + } + + metadata, err := j5codec.Global.ProtoToJSON(event.Metadata.ProtoReflect()) + if err != nil { + return nil, fmt.Errorf("error converting metadata to JSON: %w", err) + } + + shell := &ReconstructedEvent{ + Metadata: json.RawMessage(metadata), + Keys: event.EntityKeys.J5Json, + Data: event.EntityState.J5Json, + Status: event.EntityStatus, + Event: map[string]interface{}{ + event.EventType: json.RawMessage(event.EventData.J5Json), + "!type": event.EventType, + }, + } + + // shell holds the j5-json structure generically as a native go json object + bodyBytes, err := json.Marshal(shell) + if err != nil { + return nil, fmt.Errorf("error marshalling event shell: %w", err) + } + + return &messaging_pb.Message{ + MessageId: uuid.NewString(), + GrpcService: event.GrpcService, + GrpcMethod: event.GrpcMethod, + Timestamp: timestamppb.Now(), + Extension: &messaging_pb.Message_Event_{ + Event: &messaging_pb.Message_Event{ + EntityName: event.EntityName, + }, + }, + Body: &messaging_pb.Any{ + TypeUrl: fmt.Sprintf("type.googleapis.com/%s", event.BodyType), + Encoding: messaging_pb.WireEncoding_J5_JSON, + Value: bodyBytes, + }, + }, nil +} + +func parseEvent(msg *messaging_pb.Message) (*ges_pb.Event, error) { + + type EventShell struct { + Metadata json.RawMessage `json:"metadata"` + Keys json.RawMessage `json:"keys"` + Event map[string]json.RawMessage `json:"event"` + Data json.RawMessage `json:"data"` + Status string `json:"status"` + } + + ext := msg.GetEvent() + if ext == nil { + return nil, fmt.Errorf("message does not contain event extension") + } + + shell := &EventShell{} + + err := json.Unmarshal(msg.Body.Value, &shell) + if err != nil { + return nil, fmt.Errorf("failed to unmarshal body: %w", err) + } + + metadata := &psm_j5pb.EventPublishMetadata{} + + err = j5codec.Global.JSONToProto(shell.Metadata, metadata.ProtoReflect()) + if err != nil { + return nil, fmt.Errorf("failed to unmarshal metadata: %w", err) + } + + event := &ges_pb.Event{ + Metadata: metadata, + EntityName: ext.EntityName, + EntityState: &any_j5t.Any{ + TypeName: fmt.Sprintf("%sState", ext.EntityName), + J5Json: shell.Data, + }, + EntityKeys: &any_j5t.Any{ + TypeName: fmt.Sprintf("%sKeys", ext.EntityName), + J5Json: shell.Keys, + }, + EntityStatus: shell.Status, + GrpcService: msg.GrpcService, + GrpcMethod: msg.GrpcMethod, + BodyType: strings.TrimPrefix(msg.Body.TypeUrl, googleTypePrefix), + } + + for k, v := range shell.Event { + if k == "!type" { + // !type should equal the other key, but not required. + continue + } + if event.EventType != "" { + return nil, fmt.Errorf("unexpected key in event oneof wrapper %s", k) + } + event.EventType = k + event.EventData = &any_j5t.Any{ + TypeName: fmt.Sprintf("%sEventType", ext.EntityName), + J5Json: v, + } + break + } + + return event, nil +} + +type eventRow struct { + id string + message *ges_pb.Event + destination string +} + +func (er *eventRow) Message() (*messaging_pb.Message, error) { + + return reconstructEvent(er.message) +} + +func wrapAny(val *any_j5t.Any) *JSONAny { + return &JSONAny{ + TypeName: val.TypeName, + J5Json: val.J5Json, + } +} + +type JSONAny struct { + TypeName string `json:"typeName"` + J5Json []byte `json:"j5json"` +} + +func (er *eventRow) Destination() string { + return er.destination +} + +type eventQueryer struct{} + +var _ replay.MessageQuery[*eventRow] = (*eventQueryer)(nil) + +func (eq *eventQueryer) SelectQuery() string { + return "SELECT " + + "replay_event.replay_id, " + + "replay_event.queue_url, " + + "event.data " + + "FROM replay_event " + + "LEFT JOIN event ON event.id = replay_event.event_id " + + "LIMIT 10 FOR UPDATE SKIP LOCKED" +} + +func (eq *eventQueryer) ScanRow(row replay.Row) (*eventRow, error) { + var outboxRow eventRow + var dataBytes []byte + err := row.Scan(&outboxRow.id, &outboxRow.destination, &dataBytes) + if err != nil { + return nil, fmt.Errorf("error scanning outbox row: %w", err) + } + outboxRow.message = &ges_pb.Event{} + err = protojson.Unmarshal(dataBytes, outboxRow.message) + if err != nil { + return nil, fmt.Errorf("error unmarshalling event data: %w", err) + } + return &outboxRow, nil +} + +func (eq *eventQueryer) DeleteQuery(rows []*eventRow) (string, []interface{}, error) { + ids := make([]string, len(rows)) + for i, row := range rows { + ids[i] = row.id + } + return "DELETE FROM replay_event WHERE replay_id = ANY($1)", []interface{}{ids}, nil +} diff --git a/internal/service/event_test.go b/internal/service/event_test.go new file mode 100644 index 0000000..9dc78e6 --- /dev/null +++ b/internal/service/event_test.go @@ -0,0 +1,94 @@ +package service + +import ( + "bytes" + "encoding/json" + "fmt" + "testing" + + "github.com/pentops/flowtest/prototest" + "github.com/pentops/ges/internal/gen/gestest/v1/gestest_pb" + "github.com/pentops/ges/internal/gen/gestest/v1/gestest_tpb" + "github.com/pentops/j5/gen/j5/state/v1/psm_j5pb" + "github.com/pentops/j5/lib/id62" + "github.com/pentops/j5/lib/j5codec" + "github.com/pentops/o5-messaging/gen/o5/messaging/v1/messaging_pb" + "github.com/pentops/o5-messaging/o5msg" + "google.golang.org/protobuf/types/known/timestamppb" +) + +func TestParseReconstruct(t *testing.T) { + fooMsg := &gestest_tpb.FooEventMessage{ + Metadata: &psm_j5pb.EventPublishMetadata{ + EventId: id62.NewString(), + Timestamp: timestamppb.Now(), + Sequence: 1, + }, + Keys: &gestest_pb.FooKeys{ + FooId: id62.NewString(), + }, + Event: &gestest_pb.FooEventType{ + Type: &gestest_pb.FooEventType_Create_{ + Create: &gestest_pb.FooEventType_Create{ + Name: "Foo", + }, + }, + }, + Data: &gestest_pb.FooData{ + Name: "Foo", + }, + Status: gestest_pb.FooStatus_ACTIVE, + } + + wrapped := wrapMessage(t, fooMsg) + printJSON(t, "input", wrapped.Body.Value) + + parsed, err := parseEvent(wrapped) + if err != nil { + t.Fatalf("failed to parse event: %v", err) + } + + reconstructed, err := reconstructEvent(parsed) + if err != nil { + t.Fatalf("failed to reconstruct event: %v", err) + } + + printJSON(t, "reconstructed", reconstructed.Body.Value) + + if reconstructed.Body.Encoding != messaging_pb.WireEncoding_J5_JSON { + t.Fatalf("expected J5_JSON encoding, got %v", reconstructed.Body.Encoding) + } + + parsedReconstructed := &gestest_tpb.FooEventMessage{} + err = j5codec.Global.JSONToProto(reconstructed.Body.Value, parsedReconstructed.ProtoReflect()) + if err != nil { + t.Fatalf("failed to decode reconstructed event: %v", err) + } + + prototest.AssertEqualProto(t, fooMsg, parsedReconstructed) + +} + +func printJSON(t testing.TB, label string, input []byte) { + buffer := &bytes.Buffer{} + err := json.Indent(buffer, input, "", " ") + if err != nil { + t.Fatalf("failed to indent JSON: %v", err) + } + t.Logf("%s:\n%s", label, buffer.String()) +} + +func wrapMessage(t testing.TB, msg o5msg.Message) *messaging_pb.Message { + bodyData, err := j5codec.Global.EncodeAny(msg.ProtoReflect()) + if err != nil { + t.Fatalf("failed to encode message: %v", err) + } + + wrapper := o5msg.MessageWrapper(msg) + wrapper.Body = &messaging_pb.Any{ + TypeUrl: fmt.Sprintf("type.googleapis.com/%s", msg.ProtoReflect().Descriptor().FullName()), + Value: bodyData.J5Json, + Encoding: messaging_pb.WireEncoding_J5_JSON, + } + return wrapper +} diff --git a/internal/service/query.go b/internal/service/query.go index 2beec40..d127216 100644 --- a/internal/service/query.go +++ b/internal/service/query.go @@ -29,8 +29,8 @@ func NewQueryService(db sqrlx.Transactor) (*QueryService, error) { TableName: "event", DataColumn: "data", FallbackSortColumns: []pquery.ProtoField{ - pquery.NewProtoField("timestamp", gl.Ptr("timestamp")), - pquery.NewProtoField("id", gl.Ptr("id")), + pquery.NewProtoField("metadata.timestamp", gl.Ptr("timestamp")), + pquery.NewProtoField("metadata.event_id", gl.Ptr("id")), }, //Auth: //AuthJoin: diff --git a/internal/service/upsert.go b/internal/service/upsert.go new file mode 100644 index 0000000..a7fd7e3 --- /dev/null +++ b/internal/service/upsert.go @@ -0,0 +1,79 @@ +package service + +import ( + "fmt" + + "github.com/pentops/ges/internal/gen/o5/ges/v1/ges_pb" + "github.com/pentops/ges/internal/replay" + "github.com/pentops/o5-messaging/gen/o5/messaging/v1/messaging_pb" + "google.golang.org/protobuf/encoding/protojson" + "google.golang.org/protobuf/types/known/timestamppb" +) + +type upsertRow struct { + id string + message *ges_pb.Upsert + destination string +} + +func (ur *upsertRow) Message() (*messaging_pb.Message, error) { + return &messaging_pb.Message{ + MessageId: ur.id, + GrpcService: ur.message.GrpcService, + GrpcMethod: ur.message.GrpcMethod, + Timestamp: timestamppb.Now(), + Extension: &messaging_pb.Message_Upsert_{ + Upsert: &messaging_pb.Message_Upsert{ + EntityName: ur.message.EntityName, + }, + }, + Body: &messaging_pb.Any{ + TypeUrl: fmt.Sprintf("type.googleapis.com/%s", ur.message.Data.TypeName), + Encoding: messaging_pb.WireEncoding_J5_JSON, + Value: ur.message.Data.J5Json, + }, + }, nil + +} + +func (ur *upsertRow) Destination() string { + return ur.destination +} + +type upsertQueryer struct{} + +var _ replay.MessageQuery[*upsertRow] = (*upsertQueryer)(nil) + +func (uq *upsertQueryer) SelectQuery() string { + return "SELECT " + + "replay_upsert.replay_id, " + + "replay_upsert.queue_url, " + + "upsert.data " + + "FROM replay_upsert " + + "LEFT JOIN upsert ON upsert.entity_name = replay_upsert.entity_name " + + "AND upsert.entity_id = replay_upsert.entity_id " + + "LIMIT 10 FOR UPDATE SKIP LOCKED" +} + +func (uq *upsertQueryer) ScanRow(row replay.Row) (*upsertRow, error) { + var outboxRow upsertRow + var dataBytes []byte + err := row.Scan(&outboxRow.id, &outboxRow.destination, &dataBytes) + if err != nil { + return nil, fmt.Errorf("error scanning outbox row: %w", err) + } + outboxRow.message = &ges_pb.Upsert{} + err = protojson.Unmarshal(dataBytes, outboxRow.message) + if err != nil { + return nil, fmt.Errorf("error unmarshalling upsert data: %w", err) + } + return &outboxRow, nil +} + +func (uq *upsertQueryer) DeleteQuery(rows []*upsertRow) (string, []interface{}, error) { + ids := make([]string, len(rows)) + for i, row := range rows { + ids[i] = row.id + } + return "DELETE FROM replay_upsert WHERE replay_id = ANY($1)", []interface{}{ids}, nil +} diff --git a/internal/service/worker.go b/internal/service/worker.go index eb1b8a7..7c48805 100644 --- a/internal/service/worker.go +++ b/internal/service/worker.go @@ -21,6 +21,10 @@ import ( "google.golang.org/protobuf/types/known/timestamppb" ) +const ( + googleTypePrefix = "type.googleapis.com/" +) + type EventWorker struct { db sqrlx.Transactor @@ -65,44 +69,10 @@ func (ww *EventWorker) storeGeneric(ctx context.Context, req *messaging_tpb.Gene } func (ww *EventWorker) storeEvent(ctx context.Context, msg *messaging_pb.Message) error { - ext := msg.GetEvent() - - shell := &EventShell{} - err := json.Unmarshal(msg.Body.Value, &shell) + event, err := parseEvent(msg) if err != nil { - return fmt.Errorf("failed to unmarshal body: %w", err) - } - - event := &ges_pb.Event{ - Id: shell.Metadata.EventID, - Sequence: shell.Metadata.Sequence, - Timestamp: timestamppb.New(shell.Metadata.Timestamp), - EntityName: ext.EntityName, - EntityState: &any_j5t.Any{ - TypeName: fmt.Sprintf("%sState", ext.EntityName), - J5Json: shell.Data, - }, - EntityKeys: &any_j5t.Any{ - TypeName: fmt.Sprintf("%sKeys", ext.EntityName), - J5Json: shell.Keys, - }, - } - - for k, v := range shell.Event { - if k == "!type" { - // !type should equal the other key, but not required. - continue - } - if event.EventType != "" { - return fmt.Errorf("unexpected key in event oneof wrapper %s", k) - } - event.EventType = k - event.EventData = &any_j5t.Any{ - TypeName: fmt.Sprintf("%sEventType", ext.EntityName), - J5Json: v, - } - break + return fmt.Errorf("failed to parse event: %w", err) } eventData, err := protojson.Marshal(event) @@ -111,11 +81,9 @@ func (ww *EventWorker) storeEvent(ctx context.Context, msg *messaging_pb.Message } log.WithFields(ctx, map[string]interface{}{ - "eventId": event.Id, - "eventTimestamp": event.Timestamp.AsTime(), + "eventId": event.Metadata.EventId, + "eventTimestamp": event.Metadata.Timestamp.AsTime(), "entityName": event.EntityName, - "eventType": event.EventType, - "entityKeys": event.EntityKeys, }).Info("Event") return ww.db.Transact(ctx, &sqrlx.TxOptions{ @@ -131,8 +99,8 @@ func (ww *EventWorker) storeEvent(ctx context.Context, msg *messaging_pb.Message "data", ). Values( - event.Id, - event.Timestamp.AsTime(), + event.Metadata.EventId, + event.Metadata.Timestamp.AsTime(), event.EntityName, eventData, ), @@ -143,20 +111,6 @@ func (ww *EventWorker) storeEvent(ctx context.Context, msg *messaging_pb.Message return nil }) - -} - -type EventShell struct { - Metadata EventMetadata `json:"metadata"` - Keys json.RawMessage `json:"keys"` - Event map[string]json.RawMessage `json:"event"` - Data json.RawMessage `json:"data"` -} - -type EventMetadata struct { - EventID string `json:"eventId"` - Timestamp time.Time `json:"timestamp"` - Sequence uint64 `json:"sequence,string"` } func (ww *EventWorker) storeUpsert(ctx context.Context, msg *messaging_pb.Message) error { @@ -175,7 +129,7 @@ func (ww *EventWorker) storeUpsert(ctx context.Context, msg *messaging_pb.Messag LastEventId: shell.Metadata.EventID, LastEventTimestamp: timestamppb.New(shell.Metadata.Timestamp), Data: &any_j5t.Any{ - TypeName: strings.TrimPrefix(msg.Body.TypeUrl, "type.googleapis.com/"), + TypeName: strings.TrimPrefix(msg.Body.TypeUrl, googleTypePrefix), J5Json: msg.Body.Value, }, } diff --git a/schema/ges/o5/ges/v1/events.j5s b/schema/ges/o5/ges/v1/events.j5s index 82bec33..2cc90f4 100644 --- a/schema/ges/o5/ges/v1/events.j5s +++ b/schema/ges/o5/ges/v1/events.j5s @@ -1,5 +1,7 @@ package o5.ges.v1 +import j5.state.v1:psm + service Query { basePath = "/ges/v1" @@ -37,21 +39,28 @@ service Query { } object Event { - field id ! string field entity_name ! string + field metadata ! object:psm.EventPublishMetadata + + field grpc_method ! string + field grpc_service ! string + field body_type ! string - field sequence ! integer:UINT64 - field timestamp ! timestamp field event_type ! string field entity_keys ! any field event_data ! any field entity_state ! any + field entity_status ! string + } object Upsert { field entity_name ! string field entity_id ! string + + field grpc_method ! string + field grpc_service ! string field last_event_id ! string field last_event_timestamp ! timestamp diff --git a/schema/ges/o5/ges/v1/events.j5s.proto b/schema/ges/o5/ges/v1/events.j5s.proto index d9a43f9..0214b16 100644 --- a/schema/ges/o5/ges/v1/events.j5s.proto +++ b/schema/ges/o5/ges/v1/events.j5s.proto @@ -1,4 +1,4 @@ -// Code generated by j5convert. DO NOT EDIT. +// Generated by j5build v0.0.0-20250403212908-de7c3c2e6cce. DO NOT EDIT syntax = "proto3"; @@ -7,50 +7,61 @@ package o5.ges.v1; import "buf/validate/validate.proto"; import "google/protobuf/timestamp.proto"; import "j5/ext/v1/annotations.proto"; +import "j5/state/v1/metadata.proto"; import "j5/types/any/v1/any.proto"; message Event { option (j5.ext.v1.message).object = {}; - string id = 1 [ + string entity_name = 1 [ (buf.validate.field).required = true, (j5.ext.v1.field).string = {} ]; - string entity_name = 2 [ + j5.state.v1.EventPublishMetadata metadata = 2 [ + (buf.validate.field).required = true, + (j5.ext.v1.field).object = {} + ]; + + string grpc_method = 3 [ (buf.validate.field).required = true, (j5.ext.v1.field).string = {} ]; - uint64 sequence = 3 [ + string grpc_service = 4 [ (buf.validate.field).required = true, - (j5.ext.v1.field).integer = {} + (j5.ext.v1.field).string = {} ]; - google.protobuf.Timestamp timestamp = 4 [ + string body_type = 5 [ (buf.validate.field).required = true, - (j5.ext.v1.field).timestamp = {} + (j5.ext.v1.field).string = {} ]; - string event_type = 5 [ + string event_type = 6 [ (buf.validate.field).required = true, (j5.ext.v1.field).string = {} ]; - j5.types.any.v1.Any entity_keys = 6 [ + j5.types.any.v1.Any entity_keys = 7 [ (buf.validate.field).required = true, (j5.ext.v1.field).any = {} ]; - j5.types.any.v1.Any event_data = 7 [ + j5.types.any.v1.Any event_data = 8 [ (buf.validate.field).required = true, (j5.ext.v1.field).any = {} ]; - j5.types.any.v1.Any entity_state = 8 [ + j5.types.any.v1.Any entity_state = 9 [ (buf.validate.field).required = true, (j5.ext.v1.field).any = {} ]; + + string entity_status = 10 [ + (buf.validate.field).required = true, + (j5.ext.v1.field).string = {} + ]; } message Upsert { @@ -66,17 +77,27 @@ message Upsert { (j5.ext.v1.field).string = {} ]; - string last_event_id = 3 [ + string grpc_method = 3 [ + (buf.validate.field).required = true, + (j5.ext.v1.field).string = {} + ]; + + string grpc_service = 4 [ + (buf.validate.field).required = true, + (j5.ext.v1.field).string = {} + ]; + + string last_event_id = 5 [ (buf.validate.field).required = true, (j5.ext.v1.field).string = {} ]; - google.protobuf.Timestamp last_event_timestamp = 4 [ + google.protobuf.Timestamp last_event_timestamp = 6 [ (buf.validate.field).required = true, (j5.ext.v1.field).timestamp = {} ]; - j5.types.any.v1.Any data = 5 [ + j5.types.any.v1.Any data = 7 [ (buf.validate.field).required = true, (j5.ext.v1.field).any = {} ]; diff --git a/schema/ges/o5/ges/v1/service/events.p.j5s.proto b/schema/ges/o5/ges/v1/service/events.p.j5s.proto index 88e7406..f671b13 100644 --- a/schema/ges/o5/ges/v1/service/events.p.j5s.proto +++ b/schema/ges/o5/ges/v1/service/events.p.j5s.proto @@ -1,4 +1,4 @@ -// Code generated by j5convert. DO NOT EDIT. +// Generated by j5build v0.0.0-20250403212908-de7c3c2e6cce. DO NOT EDIT syntax = "proto3"; diff --git a/schema/test/gestest/v1/foo.j5s.proto b/schema/test/gestest/v1/foo.j5s.proto index 6c13446..5de5bb5 100644 --- a/schema/test/gestest/v1/foo.j5s.proto +++ b/schema/test/gestest/v1/foo.j5s.proto @@ -1,4 +1,4 @@ -// Code generated by j5convert. DO NOT EDIT. +// Generated by j5build v0.0.0-20250403212908-de7c3c2e6cce. DO NOT EDIT syntax = "proto3"; @@ -6,6 +6,7 @@ package gestest.v1; import "buf/validate/validate.proto"; import "j5/ext/v1/annotations.proto"; +import "j5/list/v1/annotations.proto"; import "j5/state/v1/metadata.proto"; message FooKeys { @@ -63,7 +64,8 @@ message FooState { defined_only: true } }, - (j5.ext.v1.field).enum = {} + (j5.ext.v1.field).enum = {}, + (j5.list.v1.field).enum.filtering.filterable = true ]; } @@ -107,7 +109,8 @@ message FooEvent { FooEventType event = 3 [ (buf.validate.field).required = true, - (j5.ext.v1.field).oneof = {} + (j5.ext.v1.field).oneof = {}, + (j5.list.v1.field).oneof.filtering.filterable = true ]; } diff --git a/schema/test/gestest/v1/service/foo.p.j5s.proto b/schema/test/gestest/v1/service/foo.p.j5s.proto index 3c7e4da..701e04b 100644 --- a/schema/test/gestest/v1/service/foo.p.j5s.proto +++ b/schema/test/gestest/v1/service/foo.p.j5s.proto @@ -1,4 +1,4 @@ -// Code generated by j5convert. DO NOT EDIT. +// Generated by j5build v0.0.0-20250403212908-de7c3c2e6cce. DO NOT EDIT syntax = "proto3"; diff --git a/schema/test/gestest/v1/topic/foo.p.j5s.proto b/schema/test/gestest/v1/topic/foo.p.j5s.proto index 168def8..bb62a81 100644 --- a/schema/test/gestest/v1/topic/foo.p.j5s.proto +++ b/schema/test/gestest/v1/topic/foo.p.j5s.proto @@ -1,4 +1,4 @@ -// Code generated by j5convert. DO NOT EDIT. +// Generated by j5build v0.0.0-20250403212908-de7c3c2e6cce. DO NOT EDIT syntax = "proto3"; From 36d387a52d7bbcb52b18fbdad35fedf4a635c512 Mon Sep 17 00:00:00 2001 From: Damien Whitten Date: Tue, 8 Apr 2025 16:36:08 +1000 Subject: [PATCH 2/2] Replay Topic --- cmd/ges/main.go | 38 ++- ext/db/00002_events.sql | 8 +- ext/db/00003_replay.sql | 3 +- ext/o5/app.yaml | 1 + go.mod | 17 +- go.sum | 20 ++ .../gen/o5/ges/v1/ges_tpb/events.p.j5s.pb.go | 287 ++++++++++++++++++ .../o5/ges/v1/ges_tpb/events.p.j5s_grpc.pb.go | 149 +++++++++ .../ges_tpb/events.p.j5s_o5_messaging.pb.go | 127 ++++++++ .../ges/v1/ges_tpb/events.p.j5s_sugar.pb.go | 3 + internal/integration/event_test.go | 28 ++ internal/integration/universe.go | 70 ++++- internal/integration/upsert_test.go | 26 ++ internal/replay/listener.go | 13 +- internal/replay/publish.go | 11 +- internal/replay/queries.go | 1 - internal/replay/query.go | 76 ++++- internal/service/app.go | 6 + internal/service/event.go | 106 +++++-- internal/service/replay.go | 58 ++++ internal/service/upsert.go | 136 ++++++++- internal/service/worker.go | 125 +------- schema/ges/o5/ges/v1/events.j5s | 46 ++- schema/ges/o5/ges/v1/topic/events.p.j5s.proto | 60 ++++ schema/test/gestest/v1/topic/foo.p.j5s.proto | 12 +- 25 files changed, 1212 insertions(+), 215 deletions(-) create mode 100644 internal/gen/o5/ges/v1/ges_tpb/events.p.j5s.pb.go create mode 100644 internal/gen/o5/ges/v1/ges_tpb/events.p.j5s_grpc.pb.go create mode 100644 internal/gen/o5/ges/v1/ges_tpb/events.p.j5s_o5_messaging.pb.go create mode 100644 internal/gen/o5/ges/v1/ges_tpb/events.p.j5s_sugar.pb.go delete mode 100644 internal/replay/queries.go create mode 100644 internal/service/replay.go create mode 100644 schema/ges/o5/ges/v1/topic/events.p.j5s.proto diff --git a/cmd/ges/main.go b/cmd/ges/main.go index 1feb046..ffd543b 100644 --- a/cmd/ges/main.go +++ b/cmd/ges/main.go @@ -2,12 +2,16 @@ package main import ( "context" + "fmt" + "github.com/aws/aws-sdk-go-v2/config" + "github.com/aws/aws-sdk-go-v2/service/sqs" "github.com/pentops/ges/internal/service" "github.com/pentops/grpc.go/grpcbind" "github.com/pentops/runner/commander" "github.com/pentops/sqrlx.go/pgenv" "github.com/pressly/goose" + "golang.org/x/sync/errgroup" "google.golang.org/grpc" "google.golang.org/grpc/reflection" ) @@ -51,11 +55,33 @@ func runServe(ctx context.Context, cfg struct { return err } - grpcServer := grpc.NewServer(grpc.ChainUnaryInterceptor( - service.GRPCMiddleware()..., - )) - app.RegisterGRPC(grpcServer) - reflection.Register(grpcServer) + awsConfig, err := config.LoadDefaultConfig(ctx) + if err != nil { + return fmt.Errorf("failed to load configuration: %w", err) + } + + sqsClient := sqs.NewFromConfig(awsConfig) + + listener, err := service.ReplayListener(cfg.DatabaseConfig.URL, sqsClient) + if err != nil { + return err + } + + eg, ctx := errgroup.WithContext(ctx) + eg.Go(func() error { + + grpcServer := grpc.NewServer(grpc.ChainUnaryInterceptor( + service.GRPCMiddleware()..., + )) + app.RegisterGRPC(grpcServer) + reflection.Register(grpcServer) + + return cfg.ListenAndServe(ctx, grpcServer) + }) + + eg.Go(func() error { + return listener.Listen(ctx) + }) - return cfg.ListenAndServe(ctx, grpcServer) + return eg.Wait() } diff --git a/ext/db/00002_events.sql b/ext/db/00002_events.sql index 3ecabb2..4fecaf7 100644 --- a/ext/db/00002_events.sql +++ b/ext/db/00002_events.sql @@ -1,5 +1,8 @@ -- +goose Up CREATE TABLE event ( + grpc_service text NOT NULL, + grpc_method text NOT NULL, + id text PRIMARY KEY, timestamp timestamptz NOT NULL, entity_name text NOT NULL, @@ -9,6 +12,9 @@ CREATE TABLE event ( ); CREATE TABLE upsert ( + grpc_service text NOT NULL, + grpc_method text NOT NULL, + entity_name text NOT NULL, entity_id text NOT NULL, last_event_id text NOT NULL, @@ -16,7 +22,7 @@ CREATE TABLE upsert ( data jsonb NOT NULL, -- ges.v1.Upsert - PRIMARY KEY (entity_name, entity_id) + PRIMARY KEY (grpc_service, grpc_method, entity_id) ); diff --git a/ext/db/00003_replay.sql b/ext/db/00003_replay.sql index 2e1a04b..8d088a9 100644 --- a/ext/db/00003_replay.sql +++ b/ext/db/00003_replay.sql @@ -8,7 +8,8 @@ CREATE TABLE replay_event ( CREATE TABLE replay_upsert ( replay_id text PRIMARY KEY, -- {queue_url}/{entity_name}/{entity_id} - entity_name text NOT NULL, + grpc_service text NOT NULL, + grpc_method text NOT NULL, entity_id text NOT NULL, queue_url text NOT NULL ); diff --git a/ext/o5/app.yaml b/ext/o5/app.yaml index 745b4c0..4165719 100644 --- a/ext/o5/app.yaml +++ b/ext/o5/app.yaml @@ -33,6 +33,7 @@ runtimes: envName: "*" - name: "global/upsert" envName: "*" + - name: "/ges.v1.topic.ReplayTopic" containers: - name: main diff --git a/go.mod b/go.mod index 08af993..23806e4 100644 --- a/go.mod +++ b/go.mod @@ -6,21 +6,24 @@ toolchain go1.24.1 require ( buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.6-20250307204501-0409229c3780.1 + github.com/aws/aws-sdk-go-v2/config v1.29.9 github.com/aws/aws-sdk-go-v2/service/sqs v1.38.4 github.com/elgris/sqrl v0.0.0-20210727210741-7e0198b30236 github.com/google/uuid v1.6.0 github.com/jackc/pgx/v5 v5.7.4 + github.com/lib/pq v1.10.9 github.com/pentops/flowtest v0.0.0-20250403234635-311159fa1e81 github.com/pentops/golib v0.0.0-20250326060930-8c83d58ddb63 github.com/pentops/grpc.go v0.0.0-20250326042738-bcdfc2b43fa9 github.com/pentops/j5 v0.0.0-20250407052915-b2fc017d8ac2 github.com/pentops/log.go v0.0.15 - github.com/pentops/o5-messaging v0.0.0-20250401052720-1fac32f8ed9c + github.com/pentops/o5-messaging v0.0.0-20250408063726-cf9d6419c7cd github.com/pentops/pgtest.go v0.0.0-20241223222214-7638cc50e15b github.com/pentops/protostate v0.0.0-20250403011625-3c2baa2e4af4 github.com/pentops/runner v0.0.0-20250116202335-8635b2a42547 github.com/pentops/sqrlx.go v0.0.0-20250324231942-5f3ef6c21f8e github.com/pressly/goose v2.7.0+incompatible + golang.org/x/sync v0.12.0 google.golang.org/genproto/googleapis/api v0.0.0-20250324211829-b45e905df463 google.golang.org/grpc v1.71.0 google.golang.org/protobuf v1.36.6 @@ -30,8 +33,16 @@ require ( cel.dev/expr v0.22.0 // indirect github.com/antlr4-go/antlr/v4 v4.13.1 // indirect github.com/aws/aws-sdk-go-v2 v1.36.3 // indirect + github.com/aws/aws-sdk-go-v2/credentials v1.17.62 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.30 // indirect github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34 // indirect github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34 // indirect + github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.25.1 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.29.1 // indirect + github.com/aws/aws-sdk-go-v2/service/sts v1.33.17 // indirect github.com/aws/smithy-go v1.22.3 // indirect github.com/bufbuild/protocompile v0.14.1 // indirect github.com/bufbuild/protovalidate-go v0.9.2 // indirect @@ -45,7 +56,6 @@ require ( github.com/jackc/pgpassfile v1.0.0 // indirect github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect github.com/jhump/protoreflect v1.17.0 // indirect - github.com/lib/pq v1.10.9 // indirect github.com/mattn/go-colorable v0.1.14 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/pkg/errors v0.9.1 // indirect @@ -54,10 +64,7 @@ require ( golang.org/x/crypto v0.36.0 // indirect golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 // indirect golang.org/x/net v0.37.0 // indirect - golang.org/x/sync v0.12.0 // indirect golang.org/x/sys v0.31.0 // indirect golang.org/x/text v0.23.0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20250324211829-b45e905df463 // indirect ) - -replace github.com/pentops/o5-messaging => /Users/daemonl/pentops/o5-messaging diff --git a/go.sum b/go.sum index e59a455..81a627d 100644 --- a/go.sum +++ b/go.sum @@ -10,12 +10,30 @@ github.com/antlr4-go/antlr/v4 v4.13.1 h1:SqQKkuVZ+zWkMMNkjy5FZe5mr5WURWnlpmOuzYW github.com/antlr4-go/antlr/v4 v4.13.1/go.mod h1:GKmUxMtwp6ZgGwZSva4eWPC5mS6vUAmOABFgjdkM7Nw= github.com/aws/aws-sdk-go-v2 v1.36.3 h1:mJoei2CxPutQVxaATCzDUjcZEjVRdpsiiXi2o38yqWM= github.com/aws/aws-sdk-go-v2 v1.36.3/go.mod h1:LLXuLpgzEbD766Z5ECcRmi8AzSwfZItDtmABVkRLGzg= +github.com/aws/aws-sdk-go-v2/config v1.29.9 h1:Kg+fAYNaJeGXp1vmjtidss8O2uXIsXwaRqsQJKXVr+0= +github.com/aws/aws-sdk-go-v2/config v1.29.9/go.mod h1:oU3jj2O53kgOU4TXq/yipt6ryiooYjlkqqVaZk7gY/U= +github.com/aws/aws-sdk-go-v2/credentials v1.17.62 h1:fvtQY3zFzYJ9CfixuAQ96IxDrBajbBWGqjNTCa79ocU= +github.com/aws/aws-sdk-go-v2/credentials v1.17.62/go.mod h1:ElETBxIQqcxej++Cs8GyPBbgMys5DgQPTwo7cUPDKt8= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.30 h1:x793wxmUWVDhshP8WW2mlnXuFrO4cOd3HLBroh1paFw= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.30/go.mod h1:Jpne2tDnYiFascUEs2AWHJL9Yp7A5ZVy3TNyxaAjD6M= github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34 h1:ZK5jHhnrioRkUNOc+hOgQKlUL5JeC3S6JgLxtQ+Rm0Q= github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34/go.mod h1:p4VfIceZokChbA9FzMbRGz5OV+lekcVtHlPKEO0gSZY= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34 h1:SZwFm17ZUNNg5Np0ioo/gq8Mn6u9w19Mri8DnJ15Jf0= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34/go.mod h1:dFZsC0BLo346mvKQLWmoJxT+Sjp+qcVR1tRVHQGOH9Q= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 h1:bIqFDwgGXXN1Kpp99pDOdKMTTb5d2KyU5X/BZxjOkRo= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3/go.mod h1:H5O/EsxDWyU+LP/V8i5sm8cxoZgc2fdNR9bxlOFrQTo= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3 h1:eAh2A4b5IzM/lum78bZ590jy36+d/aFLgKF/4Vd1xPE= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3/go.mod h1:0yKJC/kb8sAnmlYa6Zs3QVYqaC8ug2AbnNChv5Ox3uA= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15 h1:dM9/92u2F1JbDaGooxTq18wmmFzbJRfXfVfy96/1CXM= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15/go.mod h1:SwFBy2vjtA0vZbjjaFtfN045boopadnoVPhu4Fv66vY= github.com/aws/aws-sdk-go-v2/service/sqs v1.38.4 h1:rxG8LzVTNCOUppzbQAWfEEDJg4knmnH7zZGEnf7QOrs= github.com/aws/aws-sdk-go-v2/service/sqs v1.38.4/go.mod h1:Bar4MrRxeqdn6XIh8JGfiXuFRmyrrsZNTJotxEJmWW0= +github.com/aws/aws-sdk-go-v2/service/sso v1.25.1 h1:8JdC7Gr9NROg1Rusk25IcZeTO59zLxsKgE0gkh5O6h0= +github.com/aws/aws-sdk-go-v2/service/sso v1.25.1/go.mod h1:qs4a9T5EMLl/Cajiw2TcbNt2UNo/Hqlyp+GiuG4CFDI= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.29.1 h1:KwuLovgQPcdjNMfFt9OhUd9a2OwcOKhxfvF4glTzLuA= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.29.1/go.mod h1:MlYRNmYu/fGPoxBQVvBYr9nyr948aY/WLUvwBMBJubs= +github.com/aws/aws-sdk-go-v2/service/sts v1.33.17 h1:PZV5W8yk4OtH1JAuhV2PXwwO9v5G5Aoj+eMCn4T+1Kc= +github.com/aws/aws-sdk-go-v2/service/sts v1.33.17/go.mod h1:cQnB8CUnxbMU82JvlqjKR2HBOm3fe9pWorWBza6MBJ4= github.com/aws/smithy-go v1.22.3 h1:Z//5NuZCSW6R4PhQ93hShNbyBbn8BWCmCVCt+Q8Io5k= github.com/aws/smithy-go v1.22.3/go.mod h1:t1ufH5HMublsJYulve2RKmHDC15xu1f26kHCp/HgceI= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= @@ -100,6 +118,8 @@ github.com/pentops/j5 v0.0.0-20250407052915-b2fc017d8ac2 h1:LMItMPngL2GP+KUSSJKg github.com/pentops/j5 v0.0.0-20250407052915-b2fc017d8ac2/go.mod h1:57/DD8Uxf24hQnL/bZqPBAqt2n4eiAPewRJPhcz33us= github.com/pentops/log.go v0.0.15 h1:yoyu8/2LTvJ4daAkRuKcDyLe1eICwJdiCkWgZwgtq3A= github.com/pentops/log.go v0.0.15/go.mod h1:yR34x8aMlvhdGvqgIU4+0MiLjJTKt0vpcgUnVN2nZV4= +github.com/pentops/o5-messaging v0.0.0-20250408063726-cf9d6419c7cd h1:m3pSELKmnQxSuLBWqYD0iXTUKBlVlgZ52PwBHxxP1aw= +github.com/pentops/o5-messaging v0.0.0-20250408063726-cf9d6419c7cd/go.mod h1:RuFTVPd7iejyBP+Pj3+Udf8gbv5SYtPFlXCrR0BPgqE= github.com/pentops/pgtest.go v0.0.0-20241223222214-7638cc50e15b h1:UfL+A9/Nwi0QWrW7K06L8vYA+LsFRowWFtVYZICil+s= github.com/pentops/pgtest.go v0.0.0-20241223222214-7638cc50e15b/go.mod h1:U4AOJK8uL3IkJx1t5MBsOXudjZ111il31WE9UxL8Wz8= github.com/pentops/protostate v0.0.0-20250403011625-3c2baa2e4af4 h1:F73JY9+Lwh2DZXTuNhV34tNnB1IfJFXBBhJfZuJxdQ4= diff --git a/internal/gen/o5/ges/v1/ges_tpb/events.p.j5s.pb.go b/internal/gen/o5/ges/v1/ges_tpb/events.p.j5s.pb.go new file mode 100644 index 0000000..24c208f --- /dev/null +++ b/internal/gen/o5/ges/v1/ges_tpb/events.p.j5s.pb.go @@ -0,0 +1,287 @@ +// Generated by j5build v0.0.0-20250403212908-de7c3c2e6cce. DO NOT EDIT + +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.32.0 +// protoc (unknown) +// source: o5/ges/v1/topic/events.p.j5s.proto + +package ges_tpb + +import ( + _ "buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go/buf/validate" + _ "github.com/pentops/j5/gen/j5/ext/v1/ext_j5pb" + _ "github.com/pentops/j5/gen/j5/messaging/v1/messaging_j5pb" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + emptypb "google.golang.org/protobuf/types/known/emptypb" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type EventsMessage struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + QueueUrl string `protobuf:"bytes,1,opt,name=queue_url,json=queueUrl,proto3" json:"queue_url,omitempty"` + GrpcService string `protobuf:"bytes,2,opt,name=grpc_service,json=grpcService,proto3" json:"grpc_service,omitempty"` + GrpcMethod string `protobuf:"bytes,3,opt,name=grpc_method,json=grpcMethod,proto3" json:"grpc_method,omitempty"` +} + +func (x *EventsMessage) Reset() { + *x = EventsMessage{} + if protoimpl.UnsafeEnabled { + mi := &file_o5_ges_v1_topic_events_p_j5s_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *EventsMessage) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*EventsMessage) ProtoMessage() {} + +func (x *EventsMessage) ProtoReflect() protoreflect.Message { + mi := &file_o5_ges_v1_topic_events_p_j5s_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use EventsMessage.ProtoReflect.Descriptor instead. +func (*EventsMessage) Descriptor() ([]byte, []int) { + return file_o5_ges_v1_topic_events_p_j5s_proto_rawDescGZIP(), []int{0} +} + +func (x *EventsMessage) GetQueueUrl() string { + if x != nil { + return x.QueueUrl + } + return "" +} + +func (x *EventsMessage) GetGrpcService() string { + if x != nil { + return x.GrpcService + } + return "" +} + +func (x *EventsMessage) GetGrpcMethod() string { + if x != nil { + return x.GrpcMethod + } + return "" +} + +type UpsertsMessage struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + QueueUrl string `protobuf:"bytes,1,opt,name=queue_url,json=queueUrl,proto3" json:"queue_url,omitempty"` + GrpcService string `protobuf:"bytes,2,opt,name=grpc_service,json=grpcService,proto3" json:"grpc_service,omitempty"` + GrpcMethod string `protobuf:"bytes,3,opt,name=grpc_method,json=grpcMethod,proto3" json:"grpc_method,omitempty"` +} + +func (x *UpsertsMessage) Reset() { + *x = UpsertsMessage{} + if protoimpl.UnsafeEnabled { + mi := &file_o5_ges_v1_topic_events_p_j5s_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *UpsertsMessage) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UpsertsMessage) ProtoMessage() {} + +func (x *UpsertsMessage) ProtoReflect() protoreflect.Message { + mi := &file_o5_ges_v1_topic_events_p_j5s_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UpsertsMessage.ProtoReflect.Descriptor instead. +func (*UpsertsMessage) Descriptor() ([]byte, []int) { + return file_o5_ges_v1_topic_events_p_j5s_proto_rawDescGZIP(), []int{1} +} + +func (x *UpsertsMessage) GetQueueUrl() string { + if x != nil { + return x.QueueUrl + } + return "" +} + +func (x *UpsertsMessage) GetGrpcService() string { + if x != nil { + return x.GrpcService + } + return "" +} + +func (x *UpsertsMessage) GetGrpcMethod() string { + if x != nil { + return x.GrpcMethod + } + return "" +} + +var File_o5_ges_v1_topic_events_p_j5s_proto protoreflect.FileDescriptor + +var file_o5_ges_v1_topic_events_p_j5s_proto_rawDesc = []byte{ + 0x0a, 0x22, 0x6f, 0x35, 0x2f, 0x67, 0x65, 0x73, 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x6f, 0x70, 0x69, + 0x63, 0x2f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x70, 0x2e, 0x6a, 0x35, 0x73, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0f, 0x6f, 0x35, 0x2e, 0x67, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, + 0x74, 0x6f, 0x70, 0x69, 0x63, 0x1a, 0x1b, 0x62, 0x75, 0x66, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, + 0x61, 0x74, 0x65, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x1a, 0x1b, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x62, 0x75, 0x66, 0x2f, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, + 0x1b, 0x6a, 0x35, 0x2f, 0x65, 0x78, 0x74, 0x2f, 0x76, 0x31, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x21, 0x6a, 0x35, + 0x2f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x69, 0x6e, 0x67, 0x2f, 0x76, 0x31, 0x2f, 0x61, 0x6e, + 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, + 0xa9, 0x01, 0x0a, 0x0d, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x12, 0x2b, 0x0a, 0x09, 0x71, 0x75, 0x65, 0x75, 0x65, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x42, 0x0e, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0xc2, 0xff, 0x8e, 0x02, + 0x03, 0xf2, 0x01, 0x00, 0x52, 0x08, 0x71, 0x75, 0x65, 0x75, 0x65, 0x55, 0x72, 0x6c, 0x12, 0x31, + 0x0a, 0x0c, 0x67, 0x72, 0x70, 0x63, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x42, 0x0e, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0xc2, 0xff, 0x8e, 0x02, + 0x03, 0xf2, 0x01, 0x00, 0x52, 0x0b, 0x67, 0x72, 0x70, 0x63, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, + 0x65, 0x12, 0x2f, 0x0a, 0x0b, 0x67, 0x72, 0x70, 0x63, 0x5f, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x0e, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0xc2, 0xff, + 0x8e, 0x02, 0x03, 0xf2, 0x01, 0x00, 0x52, 0x0a, 0x67, 0x72, 0x70, 0x63, 0x4d, 0x65, 0x74, 0x68, + 0x6f, 0x64, 0x3a, 0x07, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0x22, 0xaa, 0x01, 0x0a, 0x0e, + 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x73, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x2b, + 0x0a, 0x09, 0x71, 0x75, 0x65, 0x75, 0x65, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x42, 0x0e, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0xc2, 0xff, 0x8e, 0x02, 0x03, 0xf2, 0x01, + 0x00, 0x52, 0x08, 0x71, 0x75, 0x65, 0x75, 0x65, 0x55, 0x72, 0x6c, 0x12, 0x31, 0x0a, 0x0c, 0x67, + 0x72, 0x70, 0x63, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x42, 0x0e, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0xc2, 0xff, 0x8e, 0x02, 0x03, 0xf2, 0x01, + 0x00, 0x52, 0x0b, 0x67, 0x72, 0x70, 0x63, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x2f, + 0x0a, 0x0b, 0x67, 0x72, 0x70, 0x63, 0x5f, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x09, 0x42, 0x0e, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0xc2, 0xff, 0x8e, 0x02, 0x03, + 0xf2, 0x01, 0x00, 0x52, 0x0a, 0x67, 0x72, 0x70, 0x63, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x3a, + 0x07, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0x32, 0xa9, 0x01, 0x0a, 0x0b, 0x52, 0x65, 0x70, + 0x6c, 0x61, 0x79, 0x54, 0x6f, 0x70, 0x69, 0x63, 0x12, 0x42, 0x0a, 0x06, 0x45, 0x76, 0x65, 0x6e, + 0x74, 0x73, 0x12, 0x1e, 0x2e, 0x6f, 0x35, 0x2e, 0x67, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x74, + 0x6f, 0x70, 0x69, 0x63, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x4d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x44, 0x0a, 0x07, + 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x73, 0x12, 0x1f, 0x2e, 0x6f, 0x35, 0x2e, 0x67, 0x65, 0x73, + 0x2e, 0x76, 0x31, 0x2e, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x2e, 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, + 0x73, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, + 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, + 0x22, 0x00, 0x1a, 0x10, 0xda, 0xa2, 0xf5, 0xe4, 0x02, 0x0a, 0x0a, 0x06, 0x72, 0x65, 0x70, 0x6c, + 0x61, 0x79, 0x52, 0x00, 0x42, 0x37, 0x5a, 0x35, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, + 0x6f, 0x6d, 0x2f, 0x70, 0x65, 0x6e, 0x74, 0x6f, 0x70, 0x73, 0x2f, 0x67, 0x65, 0x73, 0x2f, 0x69, + 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x6f, 0x35, 0x2f, 0x67, + 0x65, 0x73, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x65, 0x73, 0x5f, 0x74, 0x70, 0x62, 0x62, 0x06, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_o5_ges_v1_topic_events_p_j5s_proto_rawDescOnce sync.Once + file_o5_ges_v1_topic_events_p_j5s_proto_rawDescData = file_o5_ges_v1_topic_events_p_j5s_proto_rawDesc +) + +func file_o5_ges_v1_topic_events_p_j5s_proto_rawDescGZIP() []byte { + file_o5_ges_v1_topic_events_p_j5s_proto_rawDescOnce.Do(func() { + file_o5_ges_v1_topic_events_p_j5s_proto_rawDescData = protoimpl.X.CompressGZIP(file_o5_ges_v1_topic_events_p_j5s_proto_rawDescData) + }) + return file_o5_ges_v1_topic_events_p_j5s_proto_rawDescData +} + +var file_o5_ges_v1_topic_events_p_j5s_proto_msgTypes = make([]protoimpl.MessageInfo, 2) +var file_o5_ges_v1_topic_events_p_j5s_proto_goTypes = []interface{}{ + (*EventsMessage)(nil), // 0: o5.ges.v1.topic.EventsMessage + (*UpsertsMessage)(nil), // 1: o5.ges.v1.topic.UpsertsMessage + (*emptypb.Empty)(nil), // 2: google.protobuf.Empty +} +var file_o5_ges_v1_topic_events_p_j5s_proto_depIdxs = []int32{ + 0, // 0: o5.ges.v1.topic.ReplayTopic.Events:input_type -> o5.ges.v1.topic.EventsMessage + 1, // 1: o5.ges.v1.topic.ReplayTopic.Upserts:input_type -> o5.ges.v1.topic.UpsertsMessage + 2, // 2: o5.ges.v1.topic.ReplayTopic.Events:output_type -> google.protobuf.Empty + 2, // 3: o5.ges.v1.topic.ReplayTopic.Upserts:output_type -> google.protobuf.Empty + 2, // [2:4] is the sub-list for method output_type + 0, // [0:2] is the sub-list for method input_type + 0, // [0:0] is the sub-list for extension type_name + 0, // [0:0] is the sub-list for extension extendee + 0, // [0:0] is the sub-list for field type_name +} + +func init() { file_o5_ges_v1_topic_events_p_j5s_proto_init() } +func file_o5_ges_v1_topic_events_p_j5s_proto_init() { + if File_o5_ges_v1_topic_events_p_j5s_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_o5_ges_v1_topic_events_p_j5s_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*EventsMessage); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_o5_ges_v1_topic_events_p_j5s_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*UpsertsMessage); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_o5_ges_v1_topic_events_p_j5s_proto_rawDesc, + NumEnums: 0, + NumMessages: 2, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_o5_ges_v1_topic_events_p_j5s_proto_goTypes, + DependencyIndexes: file_o5_ges_v1_topic_events_p_j5s_proto_depIdxs, + MessageInfos: file_o5_ges_v1_topic_events_p_j5s_proto_msgTypes, + }.Build() + File_o5_ges_v1_topic_events_p_j5s_proto = out.File + file_o5_ges_v1_topic_events_p_j5s_proto_rawDesc = nil + file_o5_ges_v1_topic_events_p_j5s_proto_goTypes = nil + file_o5_ges_v1_topic_events_p_j5s_proto_depIdxs = nil +} diff --git a/internal/gen/o5/ges/v1/ges_tpb/events.p.j5s_grpc.pb.go b/internal/gen/o5/ges/v1/ges_tpb/events.p.j5s_grpc.pb.go new file mode 100644 index 0000000..939a616 --- /dev/null +++ b/internal/gen/o5/ges/v1/ges_tpb/events.p.j5s_grpc.pb.go @@ -0,0 +1,149 @@ +// Generated by j5build v0.0.0-20250403212908-de7c3c2e6cce. DO NOT EDIT + +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.3.0 +// - protoc (unknown) +// source: o5/ges/v1/topic/events.p.j5s.proto + +package ges_tpb + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" + emptypb "google.golang.org/protobuf/types/known/emptypb" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.32.0 or later. +const _ = grpc.SupportPackageIsVersion7 + +const ( + ReplayTopic_Events_FullMethodName = "/o5.ges.v1.topic.ReplayTopic/Events" + ReplayTopic_Upserts_FullMethodName = "/o5.ges.v1.topic.ReplayTopic/Upserts" +) + +// ReplayTopicClient is the client API for ReplayTopic service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type ReplayTopicClient interface { + Events(ctx context.Context, in *EventsMessage, opts ...grpc.CallOption) (*emptypb.Empty, error) + Upserts(ctx context.Context, in *UpsertsMessage, opts ...grpc.CallOption) (*emptypb.Empty, error) +} + +type replayTopicClient struct { + cc grpc.ClientConnInterface +} + +func NewReplayTopicClient(cc grpc.ClientConnInterface) ReplayTopicClient { + return &replayTopicClient{cc} +} + +func (c *replayTopicClient) Events(ctx context.Context, in *EventsMessage, opts ...grpc.CallOption) (*emptypb.Empty, error) { + out := new(emptypb.Empty) + err := c.cc.Invoke(ctx, ReplayTopic_Events_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *replayTopicClient) Upserts(ctx context.Context, in *UpsertsMessage, opts ...grpc.CallOption) (*emptypb.Empty, error) { + out := new(emptypb.Empty) + err := c.cc.Invoke(ctx, ReplayTopic_Upserts_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// ReplayTopicServer is the server API for ReplayTopic service. +// All implementations must embed UnimplementedReplayTopicServer +// for forward compatibility +type ReplayTopicServer interface { + Events(context.Context, *EventsMessage) (*emptypb.Empty, error) + Upserts(context.Context, *UpsertsMessage) (*emptypb.Empty, error) + mustEmbedUnimplementedReplayTopicServer() +} + +// UnimplementedReplayTopicServer must be embedded to have forward compatible implementations. +type UnimplementedReplayTopicServer struct { +} + +func (UnimplementedReplayTopicServer) Events(context.Context, *EventsMessage) (*emptypb.Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method Events not implemented") +} +func (UnimplementedReplayTopicServer) Upserts(context.Context, *UpsertsMessage) (*emptypb.Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method Upserts not implemented") +} +func (UnimplementedReplayTopicServer) mustEmbedUnimplementedReplayTopicServer() {} + +// UnsafeReplayTopicServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to ReplayTopicServer will +// result in compilation errors. +type UnsafeReplayTopicServer interface { + mustEmbedUnimplementedReplayTopicServer() +} + +func RegisterReplayTopicServer(s grpc.ServiceRegistrar, srv ReplayTopicServer) { + s.RegisterService(&ReplayTopic_ServiceDesc, srv) +} + +func _ReplayTopic_Events_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(EventsMessage) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ReplayTopicServer).Events(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: ReplayTopic_Events_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ReplayTopicServer).Events(ctx, req.(*EventsMessage)) + } + return interceptor(ctx, in, info, handler) +} + +func _ReplayTopic_Upserts_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(UpsertsMessage) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ReplayTopicServer).Upserts(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: ReplayTopic_Upserts_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ReplayTopicServer).Upserts(ctx, req.(*UpsertsMessage)) + } + return interceptor(ctx, in, info, handler) +} + +// ReplayTopic_ServiceDesc is the grpc.ServiceDesc for ReplayTopic service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var ReplayTopic_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "o5.ges.v1.topic.ReplayTopic", + HandlerType: (*ReplayTopicServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "Events", + Handler: _ReplayTopic_Events_Handler, + }, + { + MethodName: "Upserts", + Handler: _ReplayTopic_Upserts_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "o5/ges/v1/topic/events.p.j5s.proto", +} diff --git a/internal/gen/o5/ges/v1/ges_tpb/events.p.j5s_o5_messaging.pb.go b/internal/gen/o5/ges/v1/ges_tpb/events.p.j5s_o5_messaging.pb.go new file mode 100644 index 0000000..ebb936b --- /dev/null +++ b/internal/gen/o5/ges/v1/ges_tpb/events.p.j5s_o5_messaging.pb.go @@ -0,0 +1,127 @@ +// Code generated by protoc-gen-go-o5-messaging. DO NOT EDIT. +// versions: +// - protoc-gen-go-o5-messaging 0.0.0 +// source: o5/ges/v1/topic/events.p.j5s.proto + +package ges_tpb + +import ( + context "context" + o5msg "github.com/pentops/o5-messaging/o5msg" +) + +// Service: ReplayTopic +// Method: Events + +func (msg *EventsMessage) O5MessageHeader() o5msg.Header { + header := o5msg.Header{ + GrpcService: "o5.ges.v1.topic.ReplayTopic", + GrpcMethod: "Events", + Headers: map[string]string{}, + DestinationTopic: "replay", + } + return header +} + +// Method: Upserts + +func (msg *UpsertsMessage) O5MessageHeader() o5msg.Header { + header := o5msg.Header{ + GrpcService: "o5.ges.v1.topic.ReplayTopic", + GrpcMethod: "Upserts", + Headers: map[string]string{}, + DestinationTopic: "replay", + } + return header +} + +type ReplayTopicTxSender[C any] struct { + sender o5msg.TxSender[C] +} + +func NewReplayTopicTxSender[C any](sender o5msg.TxSender[C]) *ReplayTopicTxSender[C] { + sender.Register(o5msg.TopicDescriptor{ + Service: "o5.ges.v1.topic.ReplayTopic", + Methods: []o5msg.MethodDescriptor{ + { + Name: "Events", + Message: (*EventsMessage).ProtoReflect(nil).Descriptor(), + }, + { + Name: "Upserts", + Message: (*UpsertsMessage).ProtoReflect(nil).Descriptor(), + }, + }, + }) + return &ReplayTopicTxSender[C]{sender: sender} +} + +type ReplayTopicCollector[C any] struct { + collector o5msg.Collector[C] +} + +func NewReplayTopicCollector[C any](collector o5msg.Collector[C]) *ReplayTopicCollector[C] { + collector.Register(o5msg.TopicDescriptor{ + Service: "o5.ges.v1.topic.ReplayTopic", + Methods: []o5msg.MethodDescriptor{ + { + Name: "Events", + Message: (*EventsMessage).ProtoReflect(nil).Descriptor(), + }, + { + Name: "Upserts", + Message: (*UpsertsMessage).ProtoReflect(nil).Descriptor(), + }, + }, + }) + return &ReplayTopicCollector[C]{collector: collector} +} + +type ReplayTopicPublisher struct { + publisher o5msg.Publisher +} + +func NewReplayTopicPublisher(publisher o5msg.Publisher) *ReplayTopicPublisher { + publisher.Register(o5msg.TopicDescriptor{ + Service: "o5.ges.v1.topic.ReplayTopic", + Methods: []o5msg.MethodDescriptor{ + { + Name: "Events", + Message: (*EventsMessage).ProtoReflect(nil).Descriptor(), + }, + { + Name: "Upserts", + Message: (*UpsertsMessage).ProtoReflect(nil).Descriptor(), + }, + }, + }) + return &ReplayTopicPublisher{publisher: publisher} +} + +// Method: Events + +func (send ReplayTopicTxSender[C]) Events(ctx context.Context, sendContext C, msg *EventsMessage) error { + return send.sender.Send(ctx, sendContext, msg) +} + +func (collect ReplayTopicCollector[C]) Events(sendContext C, msg *EventsMessage) { + collect.collector.Collect(sendContext, msg) +} + +func (publish ReplayTopicPublisher) Events(ctx context.Context, msg *EventsMessage) error { + return publish.publisher.Publish(ctx, msg) +} + +// Method: Upserts + +func (send ReplayTopicTxSender[C]) Upserts(ctx context.Context, sendContext C, msg *UpsertsMessage) error { + return send.sender.Send(ctx, sendContext, msg) +} + +func (collect ReplayTopicCollector[C]) Upserts(sendContext C, msg *UpsertsMessage) { + collect.collector.Collect(sendContext, msg) +} + +func (publish ReplayTopicPublisher) Upserts(ctx context.Context, msg *UpsertsMessage) error { + return publish.publisher.Publish(ctx, msg) +} diff --git a/internal/gen/o5/ges/v1/ges_tpb/events.p.j5s_sugar.pb.go b/internal/gen/o5/ges/v1/ges_tpb/events.p.j5s_sugar.pb.go new file mode 100644 index 0000000..b22b385 --- /dev/null +++ b/internal/gen/o5/ges/v1/ges_tpb/events.p.j5s_sugar.pb.go @@ -0,0 +1,3 @@ +// Code generated by protoc-gen-go-sugar. DO NOT EDIT. + +package ges_tpb diff --git a/internal/integration/event_test.go b/internal/integration/event_test.go index fb655f7..de65222 100644 --- a/internal/integration/event_test.go +++ b/internal/integration/event_test.go @@ -8,6 +8,7 @@ import ( "github.com/pentops/ges/internal/gen/gestest/v1/gestest_pb" "github.com/pentops/ges/internal/gen/gestest/v1/gestest_tpb" "github.com/pentops/ges/internal/gen/o5/ges/v1/ges_spb" + "github.com/pentops/ges/internal/gen/o5/ges/v1/ges_tpb" "github.com/pentops/j5/gen/j5/state/v1/psm_j5pb" "github.com/pentops/j5/lib/id62" "google.golang.org/protobuf/types/known/timestamppb" @@ -62,7 +63,34 @@ func TestEventCycle(t *testing.T) { fooKeys := &gestest_pb.FooKeys{} uu.DecodeAnyTo(t, evt.EntityKeys, fooKeys) t.Equal(fooMsg.Keys.FooId, fooKeys.FooId) + t.Equal("gestest.v1.Foo", evt.EntityName) }) + flow.Step("Replay", func(ctx context.Context, t flowtest.Asserter) { + t.MustMessage(uu.ReplayTopic.Events(ctx, &ges_tpb.EventsMessage{ + QueueUrl: "test-queue", + + GrpcService: "gestest.v1.topic.FooPublishTopic", + GrpcMethod: "FooEvent", + })) + + out := uu.CaptureReplayEvents(ctx, t) + + if len(out) != 1 { + t.Fatalf("expected 1 event, got %d", len(out)) + } + + evt := out[0] + + t.Log(evt) + }) + + flow.Step("Empty Replay", func(ctx context.Context, t flowtest.Asserter) { + out := uu.CaptureReplayEvents(ctx, t) + if len(out) != 0 { + t.Fatalf("expected event to be consumed, %d in queue", len(out)) + } + }) + } diff --git a/internal/integration/universe.go b/internal/integration/universe.go index f63d59e..2492053 100644 --- a/internal/integration/universe.go +++ b/internal/integration/universe.go @@ -5,8 +5,12 @@ import ( "fmt" "testing" + "github.com/aws/aws-sdk-go-v2/service/sqs" + "github.com/aws/aws-sdk-go-v2/service/sqs/types" "github.com/pentops/flowtest" "github.com/pentops/ges/internal/gen/o5/ges/v1/ges_spb" + "github.com/pentops/ges/internal/gen/o5/ges/v1/ges_tpb" + "github.com/pentops/ges/internal/replay" "github.com/pentops/ges/internal/service" "github.com/pentops/j5/j5types/any_j5t" "github.com/pentops/j5/lib/j5codec" @@ -17,11 +21,13 @@ import ( "github.com/pentops/o5-messaging/outbox/outboxtest" "github.com/pentops/pgtest.go/pgtest" "github.com/pentops/sqrlx.go/sqrlx" + "google.golang.org/protobuf/encoding/protojson" "google.golang.org/protobuf/proto" ) type Universe struct { GenericTopic messaging_tpb.GenericMessageTopicClient + ReplayTopic ges_tpb.ReplayTopicClient Query ges_spb.QueryServiceClient @@ -52,7 +58,7 @@ func NewUniverse(ctx context.Context, t *testing.T) (*flowtest.Stepper[*testing. func setupUniverse(ctx context.Context, t flowtest.Asserter, uu *Universe) { t.Helper() - conn := pgtest.GetTestDB(t, pgtest.WithDir("../../ext/db"), pgtest.WithSchemaName("testing_oms")) + conn := pgtest.GetTestDB(t, pgtest.WithDir("../../ext/db")) db := sqrlx.NewPostgres(conn) uu.DB = db @@ -71,6 +77,7 @@ func setupUniverse(ctx context.Context, t flowtest.Asserter, uu *Universe) { appSet.RegisterGRPC(uu.GRPCPair.Server) uu.GenericTopic = messaging_tpb.NewGenericMessageTopicClient(uu.GRPCPair.Client) + uu.ReplayTopic = ges_tpb.NewReplayTopicClient(uu.GRPCPair.Client) uu.Query = ges_spb.NewQueryServiceClient(uu.GRPCPair.Client) uu.Run(ctx, t) @@ -80,6 +87,67 @@ func (uu *Universe) Run(ctx context.Context, t flowtest.TB) { uu.GRPCPair.ServeUntilDone(t, ctx) } +type mockSQS struct { + sends []sqs.SendMessageBatchInput +} + +func (m *mockSQS) SendMessageBatch(ctx context.Context, input *sqs.SendMessageBatchInput, opts ...func(*sqs.Options)) (*sqs.SendMessageBatchOutput, error) { + m.sends = append(m.sends, *input) + success := make([]types.SendMessageBatchResultEntry, len(input.Entries)) + for i := range input.Entries { + success[i] = types.SendMessageBatchResultEntry{ + Id: input.Entries[i].Id, + } + } + return &sqs.SendMessageBatchOutput{ + Failed: []types.BatchResultErrorEntry{}, + Successful: success, + }, nil +} + +func (uu *Universe) CaptureReplayEvents(ctx context.Context, t flowtest.TB) []*messaging_pb.Message { + t.Helper() + return captureReplay(ctx, t, uu.DB, &service.EventReplay{}) +} + +func (uu *Universe) CaptureReplayUpserts(ctx context.Context, t flowtest.TB) []*messaging_pb.Message { + t.Helper() + return captureReplay(ctx, t, uu.DB, &service.UpsertReplay{}) +} + +func captureReplay[T replay.Message](ctx context.Context, t flowtest.TB, db sqrlx.Transactor, query replay.MessageQuery[T]) []*messaging_pb.Message { + t.Helper() + + capture := &mockSQS{} + + err := db.Transact(ctx, &sqrlx.TxOptions{ + ReadOnly: false, + Retryable: false, + }, func(ctx context.Context, tx sqrlx.Transaction) error { + wrapped := replay.WrapSqrlx(tx) + _, err := replay.DoPage(ctx, wrapped, capture, query) + return err + }) + + if err != nil { + t.Fatalf("failed to capture replay events: %v", err) + } + + var messages []*messaging_pb.Message + for _, send := range capture.sends { + for _, entry := range send.Entries { + msg := &messaging_pb.Message{} + err := protojson.Unmarshal([]byte(*entry.MessageBody), msg) + if err != nil { + t.Fatalf("failed to unmarshal message: %v", err) + } + messages = append(messages, msg) + } + } + + return messages +} + func (uu *Universe) HandleGeneric(ctx context.Context, t flowtest.TB, msg o5msg.Message) { bodyData, err := j5codec.Global.EncodeAny(msg.ProtoReflect()) diff --git a/internal/integration/upsert_test.go b/internal/integration/upsert_test.go index b23afc2..39542cc 100644 --- a/internal/integration/upsert_test.go +++ b/internal/integration/upsert_test.go @@ -9,6 +9,7 @@ import ( "github.com/pentops/flowtest/prototest" "github.com/pentops/ges/internal/gen/gestest/v1/gestest_tpb" "github.com/pentops/ges/internal/gen/o5/ges/v1/ges_spb" + "github.com/pentops/ges/internal/gen/o5/ges/v1/ges_tpb" "github.com/pentops/j5/gen/j5/messaging/v1/messaging_j5pb" "github.com/pentops/j5/lib/id62" "google.golang.org/protobuf/types/known/timestamppb" @@ -88,4 +89,29 @@ func TestUpsertCycle(t *testing.T) { }) + flow.Step("Replay", func(ctx context.Context, t flowtest.Asserter) { + t.MustMessage(uu.ReplayTopic.Upserts(ctx, &ges_tpb.UpsertsMessage{ + QueueUrl: "test-queue", + GrpcService: "gestest.v1.topic.FooSummaryTopic", + GrpcMethod: "FooSummary", + })) + + out := uu.CaptureReplayUpserts(ctx, t) + + if len(out) != 1 { + t.Fatalf("expected 1 event, got %d", len(out)) + } + + evt := out[0] + + t.Log(evt) + }) + + flow.Step("Empty Replay", func(ctx context.Context, t flowtest.Asserter) { + out := uu.CaptureReplayEvents(ctx, t) + if len(out) != 0 { + t.Fatalf("expected event to be consumed, %d in queue", len(out)) + } + }) + } diff --git a/internal/replay/listener.go b/internal/replay/listener.go index 7993bc2..04af3ed 100644 --- a/internal/replay/listener.go +++ b/internal/replay/listener.go @@ -16,7 +16,7 @@ type Topic struct { // Callback runs at the beginning of each connection, and on each // notification. Return 'true' to re-run the callback (e.g. for draining the // a queue) - Callback func(context.Context, pgx.Tx) (more bool, err error) + Callback func(context.Context, Transaction) (more bool, err error) } // Listener listens for Postgres notifications on a loop, and handles dropped @@ -138,7 +138,7 @@ func (ll *Listener) runCallback(ctx context.Context, topic Topic, conn *pgx.Conn hasMore := true var err error for hasMore { - err = ll.transact(ctx, func(ctx context.Context, tx pgx.Tx) error { + err = transact(ctx, conn, func(ctx context.Context, tx Transaction) error { hasMore, err = topic.Callback(ctx, tx) if err != nil { return fmt.Errorf("error running callback: %w", err) @@ -152,11 +152,7 @@ func (ll *Listener) runCallback(ctx context.Context, topic Topic, conn *pgx.Conn return nil } -func (ll *Listener) transact(ctx context.Context, callback func(context.Context, pgx.Tx) error) error { - conn, err := ll.connection(ctx) - if err != nil { - return fmt.Errorf("error getting connection: %w", err) - } +func transact(ctx context.Context, conn *pgx.Conn, callback func(context.Context, Transaction) error) error { tx, err := conn.BeginTx(ctx, pgx.TxOptions{ IsoLevel: pgx.ReadCommitted, }) @@ -164,7 +160,8 @@ func (ll *Listener) transact(ctx context.Context, callback func(context.Context, return fmt.Errorf("error beginning transaction: %w", err) } - err = callback(ctx, tx) + wrapped := &pgxTx{wrapped: tx} + err = callback(ctx, wrapped) if err != nil { rollbackErr := tx.Rollback(ctx) if rollbackErr != nil { diff --git a/internal/replay/publish.go b/internal/replay/publish.go index 0f46d60..e620a8e 100644 --- a/internal/replay/publish.go +++ b/internal/replay/publish.go @@ -13,13 +13,8 @@ import ( "google.golang.org/protobuf/encoding/protojson" ) -const ( - ReplayEventPGChannel = "replay_event_notification" - ReplayUpsertPGChannel = "replay_upsert_notification" -) - type SQS interface { - PublishBatch(context.Context, sqs.SendMessageBatchInput) (sqs.SendMessageBatchOutput, error) + SendMessageBatch(context.Context, *sqs.SendMessageBatchInput, ...func(*sqs.Options)) (*sqs.SendMessageBatchOutput, error) } type Message interface { @@ -62,7 +57,7 @@ func (mb messageBatches[T]) addRow(row T) error { func publishBatch[T Message](ctx context.Context, sqsClient SQS, batch messageBatch[T]) ([]T, error) { log.WithField(ctx, "queueUrl", batch.queueUrl).Debug("publishing batch") - input := sqs.SendMessageBatchInput{ + input := &sqs.SendMessageBatchInput{ Entries: make([]types.SendMessageBatchRequestEntry, len(batch.messageBodies)), QueueUrl: &batch.queueUrl, } @@ -74,7 +69,7 @@ func publishBatch[T Message](ctx context.Context, sqsClient SQS, batch messageBa } } - output, err := sqsClient.PublishBatch(ctx, input) + output, err := sqsClient.SendMessageBatch(ctx, input) if err != nil { // From the docs: The result of sending each message is reported individually in the response. Because the batch request can result in a combination of successful and unsuccessful actions, you should check for batch errors even when the call returns an HTTP status code of 200 . diff --git a/internal/replay/queries.go b/internal/replay/queries.go deleted file mode 100644 index c36cb6c..0000000 --- a/internal/replay/queries.go +++ /dev/null @@ -1 +0,0 @@ -package replay diff --git a/internal/replay/query.go b/internal/replay/query.go index 5a74225..6f07edc 100644 --- a/internal/replay/query.go +++ b/internal/replay/query.go @@ -6,12 +6,20 @@ import ( "github.com/jackc/pgx/v5" "github.com/pentops/log.go/log" + "github.com/pentops/sqrlx.go/sqrlx" ) type Row interface { Scan(dest ...any) error } +type Rows interface { + Row + Next() bool + Close() error + Err() error +} + type MessageQuery[T Message] interface { // SelectQuery returns the SQL query to select messages from the table. SelectQuery() string @@ -23,7 +31,61 @@ type MessageQuery[T Message] interface { ScanRow(row Row) (T, error) } -func doPage[T Message](ctx context.Context, tx pgx.Tx, sqs SQS, qq MessageQuery[T]) (bool, error) { +func WrapPublisher[T Message](sqs SQS, mq MessageQuery[T]) func(context.Context, Transaction) (bool, error) { + return func(ctx context.Context, tx Transaction) (bool, error) { + return DoPage(ctx, tx, sqs, mq) + } +} + +type Transaction interface { + Query(ctx context.Context, query string, args ...any) (Rows, error) + Exec(ctx context.Context, query string, args ...any) error +} + +type pgxTx struct { + wrapped pgx.Tx +} + +func (tx *pgxTx) Query(ctx context.Context, query string, args ...any) (Rows, error) { + rows, err := tx.wrapped.Query(ctx, query, args...) + if err != nil { + return nil, fmt.Errorf("error querying messages: %w", err) + } + return pgxRows{rows}, nil +} + +type pgxRows struct { + pgx.Rows +} + +func (r pgxRows) Close() error { + r.Rows.Close() + return nil +} + +func (tx *pgxTx) Exec(ctx context.Context, query string, args ...any) error { + _, err := tx.wrapped.Exec(ctx, query, args...) + return err +} + +type sqrlxTx struct { + wrapped sqrlx.Transaction +} + +func WrapSqrlx(tx sqrlx.Transaction) Transaction { + return &sqrlxTx{wrapped: tx} +} + +func (tx *sqrlxTx) Query(ctx context.Context, query string, args ...any) (Rows, error) { + return tx.wrapped.QueryRaw(ctx, query, args...) +} + +func (tx *sqrlxTx) Exec(ctx context.Context, query string, args ...any) error { + _, err := tx.wrapped.ExecRaw(ctx, query, args...) + return err +} + +func DoPage[T Message](ctx context.Context, tx Transaction, sqs SQS, qq MessageQuery[T]) (bool, error) { var count int selectQuery := qq.SelectQuery() @@ -34,7 +96,8 @@ func doPage[T Message](ctx context.Context, tx pgx.Tx, sqs SQS, qq MessageQuery[ count = 0 rows, err := tx.Query(ctx, selectQuery) if err != nil { - return false, fmt.Errorf("error selecting outbox messages: %w", err) + log.WithError(ctx, err).Error("error selecting messages") + return false, fmt.Errorf("error selecting messages: %w", err) } if err := func() error { @@ -43,7 +106,7 @@ func doPage[T Message](ctx context.Context, tx pgx.Tx, sqs SQS, qq MessageQuery[ count++ msg, err := qq.ScanRow(rows) if err != nil { - return fmt.Errorf("error scanning outbox row: %w", err) + return fmt.Errorf("error scanning row: %w", err) } if err := batches.addRow(msg); err != nil { return err @@ -66,14 +129,15 @@ func doPage[T Message](ctx context.Context, tx pgx.Tx, sqs SQS, qq MessageQuery[ break } - log.WithField(ctx, "successCount", len(successMessages)).Debug("published outbox messages") + log.WithField(ctx, "successCount", len(successMessages)).Debug("published messages") deleteQuery, args, err := qq.DeleteQuery(successMessages) if err != nil { return false, fmt.Errorf("error creating delete query: %w", err) } - _, err = tx.Exec(ctx, deleteQuery, args...) + err = tx.Exec(ctx, deleteQuery, args...) if err != nil { - return false, fmt.Errorf("error deleting outbox messages: %w", err) + log.WithError(ctx, err).Error("error deleting sent messages") + return false, fmt.Errorf("error deleting messages: %w", err) } } diff --git a/internal/service/app.go b/internal/service/app.go index 74ca90d..9105c68 100644 --- a/internal/service/app.go +++ b/internal/service/app.go @@ -13,6 +13,7 @@ import ( type App struct { QueryService *QueryService EventWorker *EventWorker + ReplayWorker *ReplayWorker } func NewApp(db sqrlx.Transactor) (*App, error) { @@ -20,9 +21,13 @@ func NewApp(db sqrlx.Transactor) (*App, error) { if err != nil { return nil, fmt.Errorf("failed to create query service: %w", err) } + + replayWorker := NewReplayWorker(db) + app := &App{ QueryService: qs, EventWorker: NewEventWorker(db), + ReplayWorker: replayWorker, } return app, nil } @@ -30,6 +35,7 @@ func NewApp(db sqrlx.Transactor) (*App, error) { func (a *App) RegisterGRPC(server grpc.ServiceRegistrar) { a.QueryService.RegisterGRPC(server) a.EventWorker.RegisterGRPC(server) + a.ReplayWorker.RegisterGRPC(server) } func GRPCMiddleware() []grpc.UnaryServerInterceptor { diff --git a/internal/service/event.go b/internal/service/event.go index 322637b..e38d64e 100644 --- a/internal/service/event.go +++ b/internal/service/event.go @@ -1,17 +1,24 @@ package service import ( + "context" + "database/sql" "encoding/json" "fmt" "strings" + sq "github.com/elgris/sqrl" "github.com/google/uuid" + "github.com/lib/pq" "github.com/pentops/ges/internal/gen/o5/ges/v1/ges_pb" + "github.com/pentops/ges/internal/gen/o5/ges/v1/ges_tpb" "github.com/pentops/ges/internal/replay" "github.com/pentops/j5/gen/j5/state/v1/psm_j5pb" "github.com/pentops/j5/j5types/any_j5t" "github.com/pentops/j5/lib/j5codec" + "github.com/pentops/log.go/log" "github.com/pentops/o5-messaging/gen/o5/messaging/v1/messaging_pb" + "github.com/pentops/sqrlx.go/sqrlx" "google.golang.org/protobuf/encoding/protojson" "google.golang.org/protobuf/types/known/timestamppb" ) @@ -142,37 +149,96 @@ func (er *eventRow) Message() (*messaging_pb.Message, error) { return reconstructEvent(er.message) } -func wrapAny(val *any_j5t.Any) *JSONAny { - return &JSONAny{ - TypeName: val.TypeName, - J5Json: val.J5Json, - } -} - -type JSONAny struct { - TypeName string `json:"typeName"` - J5Json []byte `json:"j5json"` -} - func (er *eventRow) Destination() string { return er.destination } -type eventQueryer struct{} +type EventReplay struct{} -var _ replay.MessageQuery[*eventRow] = (*eventQueryer)(nil) +var _ replay.MessageQuery[*eventRow] = (*EventReplay)(nil) -func (eq *eventQueryer) SelectQuery() string { +func (eq *EventReplay) SelectQuery() string { return "SELECT " + "replay_event.replay_id, " + "replay_event.queue_url, " + "event.data " + "FROM replay_event " + - "LEFT JOIN event ON event.id = replay_event.event_id " + + "INNER JOIN event ON event.id = replay_event.event_id " + "LIMIT 10 FOR UPDATE SKIP LOCKED" } -func (eq *eventQueryer) ScanRow(row replay.Row) (*eventRow, error) { +func storeEvent(ctx context.Context, db sqrlx.Transactor, msg *messaging_pb.Message) error { + + event, err := parseEvent(msg) + if err != nil { + return fmt.Errorf("failed to parse event: %w", err) + } + + eventData, err := protojson.Marshal(event) + if err != nil { + return fmt.Errorf("failed to marshal event: %w", err) + } + + log.WithFields(ctx, map[string]interface{}{ + "eventId": event.Metadata.EventId, + "eventTimestamp": event.Metadata.Timestamp.AsTime(), + "entityName": event.EntityName, + }).Info("Event") + + return db.Transact(ctx, &sqrlx.TxOptions{ + ReadOnly: false, + Retryable: true, + Isolation: sql.LevelReadCommitted, + }, func(ctx context.Context, tx sqrlx.Transaction) error { + _, err := tx.Exec(ctx, sq.Insert("event"). + Columns( + "id", + "timestamp", + "grpc_service", + "grpc_method", + "entity_name", + "data", + ). + Values( + event.Metadata.EventId, + event.Metadata.Timestamp.AsTime(), + event.GrpcService, + event.GrpcMethod, + event.EntityName, + eventData, + ), + ) + if err != nil { + return fmt.Errorf("failed to insert event: %w", err) + } + + return nil + }) +} + +func queueReplayEvents(ctx context.Context, db sqrlx.Transactor, req *ges_tpb.EventsMessage) error { + sel := sq.Select(). + Column("CONCAT(?::text, '/', id)", req.QueueUrl). + Column("id"). + Column("?", req.QueueUrl). + From("event"). + Where("grpc_method = ?", req.GrpcMethod). + Where("grpc_service = ?", req.GrpcService) + + ins := sq.Insert("replay_event"). + Columns("replay_id", "event_id", "queue_url"). + Select(sel) + + return db.Transact(ctx, &sqrlx.TxOptions{ + ReadOnly: false, + Retryable: true, + }, func(ctx context.Context, tx sqrlx.Transaction) error { + _, err := tx.Exec(ctx, ins) + return err + }) +} + +func (eq *EventReplay) ScanRow(row replay.Row) (*eventRow, error) { var outboxRow eventRow var dataBytes []byte err := row.Scan(&outboxRow.id, &outboxRow.destination, &dataBytes) @@ -187,10 +253,12 @@ func (eq *eventQueryer) ScanRow(row replay.Row) (*eventRow, error) { return &outboxRow, nil } -func (eq *eventQueryer) DeleteQuery(rows []*eventRow) (string, []interface{}, error) { +func (eq *EventReplay) DeleteQuery(rows []*eventRow) (string, []interface{}, error) { ids := make([]string, len(rows)) for i, row := range rows { ids[i] = row.id } - return "DELETE FROM replay_event WHERE replay_id = ANY($1)", []interface{}{ids}, nil + return "DELETE FROM replay_event WHERE replay_id = ANY($1)", []interface{}{ + pq.StringArray(ids), + }, nil } diff --git a/internal/service/replay.go b/internal/service/replay.go new file mode 100644 index 0000000..19bfacb --- /dev/null +++ b/internal/service/replay.go @@ -0,0 +1,58 @@ +package service + +import ( + "context" + + "github.com/pentops/ges/internal/gen/o5/ges/v1/ges_tpb" + "github.com/pentops/ges/internal/replay" + "github.com/pentops/sqrlx.go/sqrlx" + "google.golang.org/grpc" + "google.golang.org/protobuf/types/known/emptypb" +) + +const ( + ReplayEventPGChannel = "replay_event_notification" + ReplayUpsertPGChannel = "replay_upsert_notification" +) + +func ReplayListener(dsn string, sqs replay.SQS) (*replay.Listener, error) { + return replay.NewListener(dsn, []replay.Topic{{ + Name: ReplayEventPGChannel, + Callback: replay.WrapPublisher(sqs, &EventReplay{}), + }, { + Name: ReplayUpsertPGChannel, + Callback: replay.WrapPublisher(sqs, &UpsertReplay{}), + }}) +} + +type ReplayWorker struct { + db sqrlx.Transactor + + ges_tpb.UnsafeReplayTopicServer +} + +var _ ges_tpb.ReplayTopicServer = &ReplayWorker{} + +func NewReplayWorker(db sqrlx.Transactor) *ReplayWorker { + return &ReplayWorker{ + db: db, + } +} + +func (rr *ReplayWorker) RegisterGRPC(server grpc.ServiceRegistrar) { + ges_tpb.RegisterReplayTopicServer(server, rr) +} + +func (rr *ReplayWorker) Events(ctx context.Context, req *ges_tpb.EventsMessage) (*emptypb.Empty, error) { + if err := queueReplayEvents(ctx, rr.db, req); err != nil { + return nil, err + } + return &emptypb.Empty{}, nil +} + +func (rr *ReplayWorker) Upserts(ctx context.Context, req *ges_tpb.UpsertsMessage) (*emptypb.Empty, error) { + if err := queueUpsertEvents(ctx, rr.db, req); err != nil { + return nil, err + } + return &emptypb.Empty{}, nil +} diff --git a/internal/service/upsert.go b/internal/service/upsert.go index a7fd7e3..3ae9923 100644 --- a/internal/service/upsert.go +++ b/internal/service/upsert.go @@ -1,11 +1,22 @@ package service import ( + "context" + "database/sql" + "encoding/json" "fmt" + "strings" + "time" + sq "github.com/elgris/sqrl" + "github.com/lib/pq" "github.com/pentops/ges/internal/gen/o5/ges/v1/ges_pb" + "github.com/pentops/ges/internal/gen/o5/ges/v1/ges_tpb" "github.com/pentops/ges/internal/replay" + "github.com/pentops/j5/j5types/any_j5t" + "github.com/pentops/log.go/log" "github.com/pentops/o5-messaging/gen/o5/messaging/v1/messaging_pb" + "github.com/pentops/sqrlx.go/sqrlx" "google.golang.org/protobuf/encoding/protojson" "google.golang.org/protobuf/types/known/timestamppb" ) @@ -40,22 +51,123 @@ func (ur *upsertRow) Destination() string { return ur.destination } -type upsertQueryer struct{} +type UpsertReplay struct{} -var _ replay.MessageQuery[*upsertRow] = (*upsertQueryer)(nil) +var _ replay.MessageQuery[*upsertRow] = (*UpsertReplay)(nil) -func (uq *upsertQueryer) SelectQuery() string { +func storeUpsert(ctx context.Context, db sqrlx.Transactor, msg *messaging_pb.Message) error { + ext := msg.GetUpsert() + + shell := &UpsertShell{} + + err := json.Unmarshal(msg.Body.Value, &shell) + if err != nil { + return fmt.Errorf("failed to unmarshal body: %w", err) + } + + upsert := &ges_pb.Upsert{ + EntityName: ext.EntityName, + EntityId: shell.Metadata.EntityID, + LastEventId: shell.Metadata.EventID, + GrpcMethod: msg.GrpcMethod, + GrpcService: msg.GrpcService, + LastEventTimestamp: timestamppb.New(shell.Metadata.Timestamp), + Data: &any_j5t.Any{ + TypeName: strings.TrimPrefix(msg.Body.TypeUrl, googleTypePrefix), + J5Json: msg.Body.Value, + }, + } + + upsertData, err := protojson.Marshal(upsert) + if err != nil { + return fmt.Errorf("failed to marshal event: %w", err) + } + log.WithFields(ctx, map[string]interface{}{ + "entityName": upsert.EntityName, + "entityId": upsert.EntityId, + "lastEventId": upsert.LastEventId, + "lastEventTimestamp": upsert.LastEventTimestamp.AsTime(), + }).Info("Upsert") + + qq := sqrlx. + Upsert("upsert"). + Key("grpc_service", upsert.GrpcService). + Key("grpc_method", upsert.GrpcMethod). + Key("entity_id", upsert.EntityId). + Set("entity_name", upsert.EntityName). + Set("last_event_id", upsert.LastEventId). + Set("last_event_timestamp", upsert.LastEventTimestamp.AsTime()). + Set("data", upsertData). + Where("EXCLUDED.last_event_timestamp > upsert.last_event_timestamp") + + return db.Transact(ctx, &sqrlx.TxOptions{ + ReadOnly: false, + Retryable: true, + Isolation: sql.LevelReadCommitted, + }, func(ctx context.Context, tx sqrlx.Transaction) error { + _, err := tx.Exec(ctx, qq) + if err != nil { + return fmt.Errorf("failed to insert event: %w", err) + } + + return nil + }) +} + +type UpsertShell struct { + Metadata UpsertMetadata `json:"upsert"` +} + +type UpsertMetadata struct { + EntityID string `json:"entityId"` + EventID string `json:"eventId"` + Timestamp time.Time `json:"timestamp"` +} + +func (uq *UpsertReplay) SelectQuery() string { return "SELECT " + - "replay_upsert.replay_id, " + - "replay_upsert.queue_url, " + - "upsert.data " + + " replay_upsert.replay_id, " + + " replay_upsert.queue_url, " + + " upsert.data " + "FROM replay_upsert " + - "LEFT JOIN upsert ON upsert.entity_name = replay_upsert.entity_name " + - "AND upsert.entity_id = replay_upsert.entity_id " + + "INNER JOIN upsert " + + " ON upsert.grpc_service = replay_upsert.grpc_service " + + " AND upsert.grpc_method = replay_upsert.grpc_method " + + " AND upsert.entity_id = replay_upsert.entity_id " + "LIMIT 10 FOR UPDATE SKIP LOCKED" } -func (uq *upsertQueryer) ScanRow(row replay.Row) (*upsertRow, error) { +func queueUpsertEvents(ctx context.Context, db sqrlx.Transactor, req *ges_tpb.UpsertsMessage) error { + sel := sq.Select(). + Column("CONCAT(?::text, '/', grpc_service, '/', grpc_method, '/', entity_id)", req.QueueUrl). + Column("grpc_service"). + Column("grpc_method"). + Column("entity_id"). + Column("?", req.QueueUrl). + From("upsert"). + Where("grpc_service = ?", req.GrpcService). + Where("grpc_method = ?", req.GrpcMethod) + + ins := sq.Insert("replay_upsert"). + Columns( + "replay_id", + "grpc_service", + "grpc_method", + "entity_id", + "queue_url", + ). + Select(sel) + + return db.Transact(ctx, &sqrlx.TxOptions{ + ReadOnly: false, + Retryable: true, + }, func(ctx context.Context, tx sqrlx.Transaction) error { + _, err := tx.Exec(ctx, ins) + return err + }) +} + +func (uq *UpsertReplay) ScanRow(row replay.Row) (*upsertRow, error) { var outboxRow upsertRow var dataBytes []byte err := row.Scan(&outboxRow.id, &outboxRow.destination, &dataBytes) @@ -70,10 +182,12 @@ func (uq *upsertQueryer) ScanRow(row replay.Row) (*upsertRow, error) { return &outboxRow, nil } -func (uq *upsertQueryer) DeleteQuery(rows []*upsertRow) (string, []interface{}, error) { +func (uq *UpsertReplay) DeleteQuery(rows []*upsertRow) (string, []interface{}, error) { ids := make([]string, len(rows)) for i, row := range rows { ids[i] = row.id } - return "DELETE FROM replay_upsert WHERE replay_id = ANY($1)", []interface{}{ids}, nil + return "DELETE FROM replay_upsert WHERE replay_id = ANY($1)", []interface{}{ + pq.StringArray(ids), + }, nil } diff --git a/internal/service/worker.go b/internal/service/worker.go index 7c48805..13ff959 100644 --- a/internal/service/worker.go +++ b/internal/service/worker.go @@ -2,23 +2,13 @@ package service import ( "context" - "database/sql" - "encoding/json" "fmt" - "strings" - "time" - sq "github.com/elgris/sqrl" - "github.com/pentops/ges/internal/gen/o5/ges/v1/ges_pb" - "github.com/pentops/j5/j5types/any_j5t" - "github.com/pentops/log.go/log" "github.com/pentops/o5-messaging/gen/o5/messaging/v1/messaging_pb" "github.com/pentops/o5-messaging/gen/o5/messaging/v1/messaging_tpb" "github.com/pentops/sqrlx.go/sqrlx" "google.golang.org/grpc" - "google.golang.org/protobuf/encoding/protojson" "google.golang.org/protobuf/types/known/emptypb" - "google.golang.org/protobuf/types/known/timestamppb" ) const ( @@ -58,123 +48,12 @@ func (ww *EventWorker) storeGeneric(ctx context.Context, req *messaging_tpb.Gene switch ext := req.Message.Extension.(type) { case *messaging_pb.Message_Event_: - return ww.storeEvent(ctx, req.Message) + return storeEvent(ctx, ww.db, req.Message) case *messaging_pb.Message_Upsert_: - return ww.storeUpsert(ctx, req.Message) + return storeUpsert(ctx, ww.db, req.Message) default: return fmt.Errorf("unexpected message extension: %T", ext) } } - -func (ww *EventWorker) storeEvent(ctx context.Context, msg *messaging_pb.Message) error { - - event, err := parseEvent(msg) - if err != nil { - return fmt.Errorf("failed to parse event: %w", err) - } - - eventData, err := protojson.Marshal(event) - if err != nil { - return fmt.Errorf("failed to marshal event: %w", err) - } - - log.WithFields(ctx, map[string]interface{}{ - "eventId": event.Metadata.EventId, - "eventTimestamp": event.Metadata.Timestamp.AsTime(), - "entityName": event.EntityName, - }).Info("Event") - - return ww.db.Transact(ctx, &sqrlx.TxOptions{ - ReadOnly: false, - Retryable: true, - Isolation: sql.LevelReadCommitted, - }, func(ctx context.Context, tx sqrlx.Transaction) error { - _, err := tx.Exec(ctx, sq.Insert("event"). - Columns( - "id", - "timestamp", - "entity_name", - "data", - ). - Values( - event.Metadata.EventId, - event.Metadata.Timestamp.AsTime(), - event.EntityName, - eventData, - ), - ) - if err != nil { - return fmt.Errorf("failed to insert event: %w", err) - } - - return nil - }) -} - -func (ww *EventWorker) storeUpsert(ctx context.Context, msg *messaging_pb.Message) error { - ext := msg.GetUpsert() - - shell := &UpsertShell{} - - err := json.Unmarshal(msg.Body.Value, &shell) - if err != nil { - return fmt.Errorf("failed to unmarshal body: %w", err) - } - - upsert := &ges_pb.Upsert{ - EntityName: ext.EntityName, - EntityId: shell.Metadata.EntityID, - LastEventId: shell.Metadata.EventID, - LastEventTimestamp: timestamppb.New(shell.Metadata.Timestamp), - Data: &any_j5t.Any{ - TypeName: strings.TrimPrefix(msg.Body.TypeUrl, googleTypePrefix), - J5Json: msg.Body.Value, - }, - } - - upsertData, err := protojson.Marshal(upsert) - if err != nil { - return fmt.Errorf("failed to marshal event: %w", err) - } - log.WithFields(ctx, map[string]interface{}{ - "entityName": upsert.EntityName, - "entityId": upsert.EntityId, - "lastEventId": upsert.LastEventId, - "lastEventTimestamp": upsert.LastEventTimestamp.AsTime(), - }).Info("Upsert") - - qq := sqrlx. - Upsert("upsert"). - Key("entity_name", upsert.EntityName). - Key("entity_id", upsert.EntityId). - Set("last_event_id", upsert.LastEventId). - Set("last_event_timestamp", upsert.LastEventTimestamp.AsTime()). - Set("data", upsertData). - Where("EXCLUDED.last_event_timestamp > upsert.last_event_timestamp") - - return ww.db.Transact(ctx, &sqrlx.TxOptions{ - ReadOnly: false, - Retryable: true, - Isolation: sql.LevelReadCommitted, - }, func(ctx context.Context, tx sqrlx.Transaction) error { - _, err := tx.Exec(ctx, qq) - if err != nil { - return fmt.Errorf("failed to insert event: %w", err) - } - - return nil - }) - -} - -type UpsertShell struct { - Metadata UpsertMetadata `json:"upsert"` -} - -type UpsertMetadata struct { - EntityID string `json:"entityId"` - EventID string `json:"eventId"` - Timestamp time.Time `json:"timestamp"` -} diff --git a/schema/ges/o5/ges/v1/events.j5s b/schema/ges/o5/ges/v1/events.j5s index 2cc90f4..a9dab09 100644 --- a/schema/ges/o5/ges/v1/events.j5s +++ b/schema/ges/o5/ges/v1/events.j5s @@ -38,31 +38,45 @@ service Query { } +topic Replay publish { + message Events { + field queueURL ! string + field grpcService ! string + field grpcMethod ! string + } + + message Upserts { + field queueURL ! string + field grpcService ! string + field grpcMethod ! string + } + +} + object Event { - field entity_name ! string + field entityName ! string field metadata ! object:psm.EventPublishMetadata - field grpc_method ! string - field grpc_service ! string - field body_type ! string + field grpcMethod ! string + field grpcService ! string + field bodyType ! string - field event_type ! string + field eventType ! string - field entity_keys ! any - field event_data ! any - field entity_state ! any - field entity_status ! string - + field entityKeys ! any + field eventData ! any + field entityState ! any + field entityStatus ! string } object Upsert { - field entity_name ! string - field entity_id ! string + field entityName ! string + field entityId ! string - field grpc_method ! string - field grpc_service ! string + field grpcMethod ! string + field grpcService ! string - field last_event_id ! string - field last_event_timestamp ! timestamp + field lastEventId ! string + field lastEventTimestamp ! timestamp field data ! any } diff --git a/schema/ges/o5/ges/v1/topic/events.p.j5s.proto b/schema/ges/o5/ges/v1/topic/events.p.j5s.proto new file mode 100644 index 0000000..dce0f2b --- /dev/null +++ b/schema/ges/o5/ges/v1/topic/events.p.j5s.proto @@ -0,0 +1,60 @@ +// Generated by j5build v0.0.0-20250403212908-de7c3c2e6cce. DO NOT EDIT + +syntax = "proto3"; + +package o5.ges.v1.topic; + +import "buf/validate/validate.proto"; +import "google/protobuf/empty.proto"; +import "j5/ext/v1/annotations.proto"; +import "j5/messaging/v1/annotations.proto"; + +service ReplayTopic { + option (j5.messaging.v1.service) = { + topic_name: "replay" + publish: { + } + }; + + rpc Events(EventsMessage) returns (google.protobuf.Empty) {} + + rpc Upserts(UpsertsMessage) returns (google.protobuf.Empty) {} +} + +message EventsMessage { + option (j5.ext.v1.message).object = {}; + + string queue_url = 1 [ + (buf.validate.field).required = true, + (j5.ext.v1.field).string = {} + ]; + + string grpc_service = 2 [ + (buf.validate.field).required = true, + (j5.ext.v1.field).string = {} + ]; + + string grpc_method = 3 [ + (buf.validate.field).required = true, + (j5.ext.v1.field).string = {} + ]; +} + +message UpsertsMessage { + option (j5.ext.v1.message).object = {}; + + string queue_url = 1 [ + (buf.validate.field).required = true, + (j5.ext.v1.field).string = {} + ]; + + string grpc_service = 2 [ + (buf.validate.field).required = true, + (j5.ext.v1.field).string = {} + ]; + + string grpc_method = 3 [ + (buf.validate.field).required = true, + (j5.ext.v1.field).string = {} + ]; +} diff --git a/schema/test/gestest/v1/topic/foo.p.j5s.proto b/schema/test/gestest/v1/topic/foo.p.j5s.proto index bb62a81..a26a621 100644 --- a/schema/test/gestest/v1/topic/foo.p.j5s.proto +++ b/schema/test/gestest/v1/topic/foo.p.j5s.proto @@ -15,9 +15,7 @@ import "j5/state/v1/metadata.proto"; service FooPublishTopic { option (j5.messaging.v1.service) = { topic_name: "foo_publish" - event: { - entity_name: "gestest.v1.Foo" - } + event: {entity_name: "gestest.v1.Foo"} }; rpc FooEvent(FooEventMessage) returns (google.protobuf.Empty) {} @@ -26,9 +24,7 @@ service FooPublishTopic { service FooSummaryTopic { option (j5.messaging.v1.service) = { topic_name: "foo_summary" - upsert: { - entity_name: "gestest.v1.Foo" - } + upsert: {entity_name: "gestest.v1.Foo"} }; rpc FooSummary(FooSummaryMessage) returns (google.protobuf.Empty) {} @@ -60,9 +56,7 @@ message FooEventMessage { gestest.v1.FooStatus status = 5 [ (buf.validate.field) = { required: true - enum: { - defined_only: true - } + enum: {defined_only: true} }, (j5.ext.v1.field).enum = {} ];