Add Phase 2 artifact repository: types, blob store, gRPC service

Build the complete artifact pillar with five packages:
- artifacts: Artifact, Snapshot, Citation, Publisher types with Get/Store
  DB methods, tag/category management, metadata ops, YAML import
- blob: content-addressable store (SHA256, hierarchical dir layout)
- proto: protobuf definitions (common.proto, artifacts.proto) with buf
  linting and code generation
- server: gRPC ArtifactService implementation (create/get artifacts,
  store/retrieve blobs, manage tags/categories, search by tag)

All FK insertion ordering is correct (parent rows before children).
Full test coverage across artifacts, blob, and server packages.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-21 09:56:34 -07:00
parent bb2c7f7ef3
commit b64177baa8
22 changed files with 5017 additions and 1 deletions

15
proto/buf.gen.yaml Normal file
View File

@@ -0,0 +1,15 @@
version: v2
managed:
enabled: true
override:
- file_option: go_package_prefix
value: git.wntrmute.dev/kyle/exo/proto
plugins:
- local: protoc-gen-go
out: ../proto
opt: paths=source_relative
- local: protoc-gen-go-grpc
out: ../proto
opt: paths=source_relative

12
proto/buf.yaml Normal file
View File

@@ -0,0 +1,12 @@
version: v2
modules:
- path: .
lint:
use:
- STANDARD
breaking:
use:
- FILE

1533
proto/exo/v1/artifacts.pb.go Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,157 @@
syntax = "proto3";
package exo.v1;
option go_package = "git.wntrmute.dev/kyle/exo/proto/exo/v1;exov1";
import "exo/v1/common.proto";
// Publisher represents a publishing entity.
message Publisher {
string id = 1;
string name = 2;
string address = 3;
}
// Citation holds bibliographic information.
message Citation {
string id = 1;
string doi = 2;
string title = 3;
int32 year = 4;
string published = 5; // ISO 8601 UTC
repeated string authors = 6;
Publisher publisher = 7;
string source = 8;
string abstract = 9;
repeated MetadataEntry metadata = 10;
}
// BlobRef is a reference to content in the blob store.
message BlobRef {
string snapshot_id = 1;
string id = 2; // SHA256 hash
string format = 3; // MIME type
}
// Snapshot represents content at a specific point in time.
message Snapshot {
string artifact_id = 1;
string id = 2;
int64 stored_at = 3;
string datetime = 4; // ISO 8601 UTC
Citation citation = 5;
string source = 6;
repeated BlobRef blobs = 7;
repeated MetadataEntry metadata = 8;
}
// Artifact is the top-level container for a knowledge source.
message Artifact {
string id = 1;
string type = 2;
Citation citation = 3;
string latest = 4; // ISO 8601 UTC
repeated Snapshot snapshots = 5;
repeated string tags = 6;
repeated string categories = 7;
repeated MetadataEntry metadata = 8;
}
// --- Service messages ---
message CreateArtifactRequest {
Artifact artifact = 1;
}
message CreateArtifactResponse {
string id = 1;
}
message GetArtifactRequest {
string id = 1;
}
message GetArtifactResponse {
Artifact artifact = 1;
}
message DeleteArtifactRequest {
string id = 1;
}
message DeleteArtifactResponse {}
message StoreBlobRequest {
string snapshot_id = 1;
string format = 2; // MIME type
bytes data = 3;
}
message StoreBlobResponse {
string id = 1; // SHA256 hash
}
message GetBlobRequest {
string id = 1; // SHA256 hash
}
message GetBlobResponse {
bytes data = 1;
string format = 2;
}
message ListTagsRequest {}
message ListTagsResponse {
repeated string tags = 1;
}
message CreateTagRequest {
string tag = 1;
}
message CreateTagResponse {}
message ListCategoriesRequest {}
message ListCategoriesResponse {
repeated string categories = 1;
}
message CreateCategoryRequest {
string category = 1;
}
message CreateCategoryResponse {}
message SearchByTagRequest {
string tag = 1;
}
message SearchByTagResponse {
repeated string artifact_ids = 1;
}
// ArtifactService provides CRUD operations for the artifact repository.
service ArtifactService {
// Artifacts
rpc CreateArtifact(CreateArtifactRequest) returns (CreateArtifactResponse);
rpc GetArtifact(GetArtifactRequest) returns (GetArtifactResponse);
rpc DeleteArtifact(DeleteArtifactRequest) returns (DeleteArtifactResponse);
// Blobs
rpc StoreBlob(StoreBlobRequest) returns (StoreBlobResponse);
rpc GetBlob(GetBlobRequest) returns (GetBlobResponse);
// Tags
rpc ListTags(ListTagsRequest) returns (ListTagsResponse);
rpc CreateTag(CreateTagRequest) returns (CreateTagResponse);
// Categories
rpc ListCategories(ListCategoriesRequest) returns (ListCategoriesResponse);
rpc CreateCategory(CreateCategoryRequest) returns (CreateCategoryResponse);
// Search
rpc SearchByTag(SearchByTagRequest) returns (SearchByTagResponse);
}

View File

@@ -0,0 +1,477 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.6.1
// - protoc (unknown)
// source: exo/v1/artifacts.proto
package exov1
import (
context "context"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
)
// 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.64.0 or later.
const _ = grpc.SupportPackageIsVersion9
const (
ArtifactService_CreateArtifact_FullMethodName = "/exo.v1.ArtifactService/CreateArtifact"
ArtifactService_GetArtifact_FullMethodName = "/exo.v1.ArtifactService/GetArtifact"
ArtifactService_DeleteArtifact_FullMethodName = "/exo.v1.ArtifactService/DeleteArtifact"
ArtifactService_StoreBlob_FullMethodName = "/exo.v1.ArtifactService/StoreBlob"
ArtifactService_GetBlob_FullMethodName = "/exo.v1.ArtifactService/GetBlob"
ArtifactService_ListTags_FullMethodName = "/exo.v1.ArtifactService/ListTags"
ArtifactService_CreateTag_FullMethodName = "/exo.v1.ArtifactService/CreateTag"
ArtifactService_ListCategories_FullMethodName = "/exo.v1.ArtifactService/ListCategories"
ArtifactService_CreateCategory_FullMethodName = "/exo.v1.ArtifactService/CreateCategory"
ArtifactService_SearchByTag_FullMethodName = "/exo.v1.ArtifactService/SearchByTag"
)
// ArtifactServiceClient is the client API for ArtifactService 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.
//
// ArtifactService provides CRUD operations for the artifact repository.
type ArtifactServiceClient interface {
// Artifacts
CreateArtifact(ctx context.Context, in *CreateArtifactRequest, opts ...grpc.CallOption) (*CreateArtifactResponse, error)
GetArtifact(ctx context.Context, in *GetArtifactRequest, opts ...grpc.CallOption) (*GetArtifactResponse, error)
DeleteArtifact(ctx context.Context, in *DeleteArtifactRequest, opts ...grpc.CallOption) (*DeleteArtifactResponse, error)
// Blobs
StoreBlob(ctx context.Context, in *StoreBlobRequest, opts ...grpc.CallOption) (*StoreBlobResponse, error)
GetBlob(ctx context.Context, in *GetBlobRequest, opts ...grpc.CallOption) (*GetBlobResponse, error)
// Tags
ListTags(ctx context.Context, in *ListTagsRequest, opts ...grpc.CallOption) (*ListTagsResponse, error)
CreateTag(ctx context.Context, in *CreateTagRequest, opts ...grpc.CallOption) (*CreateTagResponse, error)
// Categories
ListCategories(ctx context.Context, in *ListCategoriesRequest, opts ...grpc.CallOption) (*ListCategoriesResponse, error)
CreateCategory(ctx context.Context, in *CreateCategoryRequest, opts ...grpc.CallOption) (*CreateCategoryResponse, error)
// Search
SearchByTag(ctx context.Context, in *SearchByTagRequest, opts ...grpc.CallOption) (*SearchByTagResponse, error)
}
type artifactServiceClient struct {
cc grpc.ClientConnInterface
}
func NewArtifactServiceClient(cc grpc.ClientConnInterface) ArtifactServiceClient {
return &artifactServiceClient{cc}
}
func (c *artifactServiceClient) CreateArtifact(ctx context.Context, in *CreateArtifactRequest, opts ...grpc.CallOption) (*CreateArtifactResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(CreateArtifactResponse)
err := c.cc.Invoke(ctx, ArtifactService_CreateArtifact_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *artifactServiceClient) GetArtifact(ctx context.Context, in *GetArtifactRequest, opts ...grpc.CallOption) (*GetArtifactResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(GetArtifactResponse)
err := c.cc.Invoke(ctx, ArtifactService_GetArtifact_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *artifactServiceClient) DeleteArtifact(ctx context.Context, in *DeleteArtifactRequest, opts ...grpc.CallOption) (*DeleteArtifactResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(DeleteArtifactResponse)
err := c.cc.Invoke(ctx, ArtifactService_DeleteArtifact_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *artifactServiceClient) StoreBlob(ctx context.Context, in *StoreBlobRequest, opts ...grpc.CallOption) (*StoreBlobResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(StoreBlobResponse)
err := c.cc.Invoke(ctx, ArtifactService_StoreBlob_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *artifactServiceClient) GetBlob(ctx context.Context, in *GetBlobRequest, opts ...grpc.CallOption) (*GetBlobResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(GetBlobResponse)
err := c.cc.Invoke(ctx, ArtifactService_GetBlob_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *artifactServiceClient) ListTags(ctx context.Context, in *ListTagsRequest, opts ...grpc.CallOption) (*ListTagsResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(ListTagsResponse)
err := c.cc.Invoke(ctx, ArtifactService_ListTags_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *artifactServiceClient) CreateTag(ctx context.Context, in *CreateTagRequest, opts ...grpc.CallOption) (*CreateTagResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(CreateTagResponse)
err := c.cc.Invoke(ctx, ArtifactService_CreateTag_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *artifactServiceClient) ListCategories(ctx context.Context, in *ListCategoriesRequest, opts ...grpc.CallOption) (*ListCategoriesResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(ListCategoriesResponse)
err := c.cc.Invoke(ctx, ArtifactService_ListCategories_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *artifactServiceClient) CreateCategory(ctx context.Context, in *CreateCategoryRequest, opts ...grpc.CallOption) (*CreateCategoryResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(CreateCategoryResponse)
err := c.cc.Invoke(ctx, ArtifactService_CreateCategory_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *artifactServiceClient) SearchByTag(ctx context.Context, in *SearchByTagRequest, opts ...grpc.CallOption) (*SearchByTagResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(SearchByTagResponse)
err := c.cc.Invoke(ctx, ArtifactService_SearchByTag_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
// ArtifactServiceServer is the server API for ArtifactService service.
// All implementations must embed UnimplementedArtifactServiceServer
// for forward compatibility.
//
// ArtifactService provides CRUD operations for the artifact repository.
type ArtifactServiceServer interface {
// Artifacts
CreateArtifact(context.Context, *CreateArtifactRequest) (*CreateArtifactResponse, error)
GetArtifact(context.Context, *GetArtifactRequest) (*GetArtifactResponse, error)
DeleteArtifact(context.Context, *DeleteArtifactRequest) (*DeleteArtifactResponse, error)
// Blobs
StoreBlob(context.Context, *StoreBlobRequest) (*StoreBlobResponse, error)
GetBlob(context.Context, *GetBlobRequest) (*GetBlobResponse, error)
// Tags
ListTags(context.Context, *ListTagsRequest) (*ListTagsResponse, error)
CreateTag(context.Context, *CreateTagRequest) (*CreateTagResponse, error)
// Categories
ListCategories(context.Context, *ListCategoriesRequest) (*ListCategoriesResponse, error)
CreateCategory(context.Context, *CreateCategoryRequest) (*CreateCategoryResponse, error)
// Search
SearchByTag(context.Context, *SearchByTagRequest) (*SearchByTagResponse, error)
mustEmbedUnimplementedArtifactServiceServer()
}
// UnimplementedArtifactServiceServer must be embedded to have
// forward compatible implementations.
//
// NOTE: this should be embedded by value instead of pointer to avoid a nil
// pointer dereference when methods are called.
type UnimplementedArtifactServiceServer struct{}
func (UnimplementedArtifactServiceServer) CreateArtifact(context.Context, *CreateArtifactRequest) (*CreateArtifactResponse, error) {
return nil, status.Error(codes.Unimplemented, "method CreateArtifact not implemented")
}
func (UnimplementedArtifactServiceServer) GetArtifact(context.Context, *GetArtifactRequest) (*GetArtifactResponse, error) {
return nil, status.Error(codes.Unimplemented, "method GetArtifact not implemented")
}
func (UnimplementedArtifactServiceServer) DeleteArtifact(context.Context, *DeleteArtifactRequest) (*DeleteArtifactResponse, error) {
return nil, status.Error(codes.Unimplemented, "method DeleteArtifact not implemented")
}
func (UnimplementedArtifactServiceServer) StoreBlob(context.Context, *StoreBlobRequest) (*StoreBlobResponse, error) {
return nil, status.Error(codes.Unimplemented, "method StoreBlob not implemented")
}
func (UnimplementedArtifactServiceServer) GetBlob(context.Context, *GetBlobRequest) (*GetBlobResponse, error) {
return nil, status.Error(codes.Unimplemented, "method GetBlob not implemented")
}
func (UnimplementedArtifactServiceServer) ListTags(context.Context, *ListTagsRequest) (*ListTagsResponse, error) {
return nil, status.Error(codes.Unimplemented, "method ListTags not implemented")
}
func (UnimplementedArtifactServiceServer) CreateTag(context.Context, *CreateTagRequest) (*CreateTagResponse, error) {
return nil, status.Error(codes.Unimplemented, "method CreateTag not implemented")
}
func (UnimplementedArtifactServiceServer) ListCategories(context.Context, *ListCategoriesRequest) (*ListCategoriesResponse, error) {
return nil, status.Error(codes.Unimplemented, "method ListCategories not implemented")
}
func (UnimplementedArtifactServiceServer) CreateCategory(context.Context, *CreateCategoryRequest) (*CreateCategoryResponse, error) {
return nil, status.Error(codes.Unimplemented, "method CreateCategory not implemented")
}
func (UnimplementedArtifactServiceServer) SearchByTag(context.Context, *SearchByTagRequest) (*SearchByTagResponse, error) {
return nil, status.Error(codes.Unimplemented, "method SearchByTag not implemented")
}
func (UnimplementedArtifactServiceServer) mustEmbedUnimplementedArtifactServiceServer() {}
func (UnimplementedArtifactServiceServer) testEmbeddedByValue() {}
// UnsafeArtifactServiceServer may be embedded to opt out of forward compatibility for this service.
// Use of this interface is not recommended, as added methods to ArtifactServiceServer will
// result in compilation errors.
type UnsafeArtifactServiceServer interface {
mustEmbedUnimplementedArtifactServiceServer()
}
func RegisterArtifactServiceServer(s grpc.ServiceRegistrar, srv ArtifactServiceServer) {
// If the following call panics, it indicates UnimplementedArtifactServiceServer was
// embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O.
if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
t.testEmbeddedByValue()
}
s.RegisterService(&ArtifactService_ServiceDesc, srv)
}
func _ArtifactService_CreateArtifact_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(CreateArtifactRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ArtifactServiceServer).CreateArtifact(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: ArtifactService_CreateArtifact_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ArtifactServiceServer).CreateArtifact(ctx, req.(*CreateArtifactRequest))
}
return interceptor(ctx, in, info, handler)
}
func _ArtifactService_GetArtifact_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(GetArtifactRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ArtifactServiceServer).GetArtifact(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: ArtifactService_GetArtifact_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ArtifactServiceServer).GetArtifact(ctx, req.(*GetArtifactRequest))
}
return interceptor(ctx, in, info, handler)
}
func _ArtifactService_DeleteArtifact_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(DeleteArtifactRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ArtifactServiceServer).DeleteArtifact(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: ArtifactService_DeleteArtifact_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ArtifactServiceServer).DeleteArtifact(ctx, req.(*DeleteArtifactRequest))
}
return interceptor(ctx, in, info, handler)
}
func _ArtifactService_StoreBlob_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(StoreBlobRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ArtifactServiceServer).StoreBlob(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: ArtifactService_StoreBlob_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ArtifactServiceServer).StoreBlob(ctx, req.(*StoreBlobRequest))
}
return interceptor(ctx, in, info, handler)
}
func _ArtifactService_GetBlob_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(GetBlobRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ArtifactServiceServer).GetBlob(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: ArtifactService_GetBlob_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ArtifactServiceServer).GetBlob(ctx, req.(*GetBlobRequest))
}
return interceptor(ctx, in, info, handler)
}
func _ArtifactService_ListTags_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(ListTagsRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ArtifactServiceServer).ListTags(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: ArtifactService_ListTags_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ArtifactServiceServer).ListTags(ctx, req.(*ListTagsRequest))
}
return interceptor(ctx, in, info, handler)
}
func _ArtifactService_CreateTag_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(CreateTagRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ArtifactServiceServer).CreateTag(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: ArtifactService_CreateTag_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ArtifactServiceServer).CreateTag(ctx, req.(*CreateTagRequest))
}
return interceptor(ctx, in, info, handler)
}
func _ArtifactService_ListCategories_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(ListCategoriesRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ArtifactServiceServer).ListCategories(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: ArtifactService_ListCategories_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ArtifactServiceServer).ListCategories(ctx, req.(*ListCategoriesRequest))
}
return interceptor(ctx, in, info, handler)
}
func _ArtifactService_CreateCategory_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(CreateCategoryRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ArtifactServiceServer).CreateCategory(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: ArtifactService_CreateCategory_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ArtifactServiceServer).CreateCategory(ctx, req.(*CreateCategoryRequest))
}
return interceptor(ctx, in, info, handler)
}
func _ArtifactService_SearchByTag_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(SearchByTagRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ArtifactServiceServer).SearchByTag(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: ArtifactService_SearchByTag_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ArtifactServiceServer).SearchByTag(ctx, req.(*SearchByTagRequest))
}
return interceptor(ctx, in, info, handler)
}
// ArtifactService_ServiceDesc is the grpc.ServiceDesc for ArtifactService service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
var ArtifactService_ServiceDesc = grpc.ServiceDesc{
ServiceName: "exo.v1.ArtifactService",
HandlerType: (*ArtifactServiceServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "CreateArtifact",
Handler: _ArtifactService_CreateArtifact_Handler,
},
{
MethodName: "GetArtifact",
Handler: _ArtifactService_GetArtifact_Handler,
},
{
MethodName: "DeleteArtifact",
Handler: _ArtifactService_DeleteArtifact_Handler,
},
{
MethodName: "StoreBlob",
Handler: _ArtifactService_StoreBlob_Handler,
},
{
MethodName: "GetBlob",
Handler: _ArtifactService_GetBlob_Handler,
},
{
MethodName: "ListTags",
Handler: _ArtifactService_ListTags_Handler,
},
{
MethodName: "CreateTag",
Handler: _ArtifactService_CreateTag_Handler,
},
{
MethodName: "ListCategories",
Handler: _ArtifactService_ListCategories_Handler,
},
{
MethodName: "CreateCategory",
Handler: _ArtifactService_CreateCategory_Handler,
},
{
MethodName: "SearchByTag",
Handler: _ArtifactService_SearchByTag_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "exo/v1/artifacts.proto",
}

297
proto/exo/v1/common.pb.go Normal file
View File

@@ -0,0 +1,297 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.11
// protoc (unknown)
// source: exo/v1/common.proto
package exov1
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
unsafe "unsafe"
)
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)
)
// Value is a typed key-value entry.
type Value struct {
state protoimpl.MessageState `protogen:"open.v1"`
Contents string `protobuf:"bytes,1,opt,name=contents,proto3" json:"contents,omitempty"`
Type string `protobuf:"bytes,2,opt,name=type,proto3" json:"type,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *Value) Reset() {
*x = Value{}
mi := &file_exo_v1_common_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *Value) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Value) ProtoMessage() {}
func (x *Value) ProtoReflect() protoreflect.Message {
mi := &file_exo_v1_common_proto_msgTypes[0]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use Value.ProtoReflect.Descriptor instead.
func (*Value) Descriptor() ([]byte, []int) {
return file_exo_v1_common_proto_rawDescGZIP(), []int{0}
}
func (x *Value) GetContents() string {
if x != nil {
return x.Contents
}
return ""
}
func (x *Value) GetType() string {
if x != nil {
return x.Type
}
return ""
}
// MetadataEntry is a single key-value metadata pair.
type MetadataEntry struct {
state protoimpl.MessageState `protogen:"open.v1"`
Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"`
Value *Value `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *MetadataEntry) Reset() {
*x = MetadataEntry{}
mi := &file_exo_v1_common_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *MetadataEntry) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*MetadataEntry) ProtoMessage() {}
func (x *MetadataEntry) ProtoReflect() protoreflect.Message {
mi := &file_exo_v1_common_proto_msgTypes[1]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use MetadataEntry.ProtoReflect.Descriptor instead.
func (*MetadataEntry) Descriptor() ([]byte, []int) {
return file_exo_v1_common_proto_rawDescGZIP(), []int{1}
}
func (x *MetadataEntry) GetKey() string {
if x != nil {
return x.Key
}
return ""
}
func (x *MetadataEntry) GetValue() *Value {
if x != nil {
return x.Value
}
return nil
}
// Header is attached to every persistent object.
type Header struct {
state protoimpl.MessageState `protogen:"open.v1"`
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
Type string `protobuf:"bytes,2,opt,name=type,proto3" json:"type,omitempty"`
Created int64 `protobuf:"varint,3,opt,name=created,proto3" json:"created,omitempty"`
Modified int64 `protobuf:"varint,4,opt,name=modified,proto3" json:"modified,omitempty"`
Categories []string `protobuf:"bytes,5,rep,name=categories,proto3" json:"categories,omitempty"`
Tags []string `protobuf:"bytes,6,rep,name=tags,proto3" json:"tags,omitempty"`
Metadata []*MetadataEntry `protobuf:"bytes,7,rep,name=metadata,proto3" json:"metadata,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *Header) Reset() {
*x = Header{}
mi := &file_exo_v1_common_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *Header) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Header) ProtoMessage() {}
func (x *Header) ProtoReflect() protoreflect.Message {
mi := &file_exo_v1_common_proto_msgTypes[2]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use Header.ProtoReflect.Descriptor instead.
func (*Header) Descriptor() ([]byte, []int) {
return file_exo_v1_common_proto_rawDescGZIP(), []int{2}
}
func (x *Header) GetId() string {
if x != nil {
return x.Id
}
return ""
}
func (x *Header) GetType() string {
if x != nil {
return x.Type
}
return ""
}
func (x *Header) GetCreated() int64 {
if x != nil {
return x.Created
}
return 0
}
func (x *Header) GetModified() int64 {
if x != nil {
return x.Modified
}
return 0
}
func (x *Header) GetCategories() []string {
if x != nil {
return x.Categories
}
return nil
}
func (x *Header) GetTags() []string {
if x != nil {
return x.Tags
}
return nil
}
func (x *Header) GetMetadata() []*MetadataEntry {
if x != nil {
return x.Metadata
}
return nil
}
var File_exo_v1_common_proto protoreflect.FileDescriptor
const file_exo_v1_common_proto_rawDesc = "" +
"\n" +
"\x13exo/v1/common.proto\x12\x06exo.v1\"7\n" +
"\x05Value\x12\x1a\n" +
"\bcontents\x18\x01 \x01(\tR\bcontents\x12\x12\n" +
"\x04type\x18\x02 \x01(\tR\x04type\"F\n" +
"\rMetadataEntry\x12\x10\n" +
"\x03key\x18\x01 \x01(\tR\x03key\x12#\n" +
"\x05value\x18\x02 \x01(\v2\r.exo.v1.ValueR\x05value\"\xc9\x01\n" +
"\x06Header\x12\x0e\n" +
"\x02id\x18\x01 \x01(\tR\x02id\x12\x12\n" +
"\x04type\x18\x02 \x01(\tR\x04type\x12\x18\n" +
"\acreated\x18\x03 \x01(\x03R\acreated\x12\x1a\n" +
"\bmodified\x18\x04 \x01(\x03R\bmodified\x12\x1e\n" +
"\n" +
"categories\x18\x05 \x03(\tR\n" +
"categories\x12\x12\n" +
"\x04tags\x18\x06 \x03(\tR\x04tags\x121\n" +
"\bmetadata\x18\a \x03(\v2\x15.exo.v1.MetadataEntryR\bmetadataB\x80\x01\n" +
"\n" +
"com.exo.v1B\vCommonProtoP\x01Z,git.wntrmute.dev/kyle/exo/proto/exo/v1;exov1\xa2\x02\x03EXX\xaa\x02\x06Exo.V1\xca\x02\x06Exo\\V1\xe2\x02\x12Exo\\V1\\GPBMetadata\xea\x02\aExo::V1b\x06proto3"
var (
file_exo_v1_common_proto_rawDescOnce sync.Once
file_exo_v1_common_proto_rawDescData []byte
)
func file_exo_v1_common_proto_rawDescGZIP() []byte {
file_exo_v1_common_proto_rawDescOnce.Do(func() {
file_exo_v1_common_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_exo_v1_common_proto_rawDesc), len(file_exo_v1_common_proto_rawDesc)))
})
return file_exo_v1_common_proto_rawDescData
}
var file_exo_v1_common_proto_msgTypes = make([]protoimpl.MessageInfo, 3)
var file_exo_v1_common_proto_goTypes = []any{
(*Value)(nil), // 0: exo.v1.Value
(*MetadataEntry)(nil), // 1: exo.v1.MetadataEntry
(*Header)(nil), // 2: exo.v1.Header
}
var file_exo_v1_common_proto_depIdxs = []int32{
0, // 0: exo.v1.MetadataEntry.value:type_name -> exo.v1.Value
1, // 1: exo.v1.Header.metadata:type_name -> exo.v1.MetadataEntry
2, // [2:2] is the sub-list for method output_type
2, // [2:2] is the sub-list for method input_type
2, // [2:2] is the sub-list for extension type_name
2, // [2:2] is the sub-list for extension extendee
0, // [0:2] is the sub-list for field type_name
}
func init() { file_exo_v1_common_proto_init() }
func file_exo_v1_common_proto_init() {
if File_exo_v1_common_proto != nil {
return
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_exo_v1_common_proto_rawDesc), len(file_exo_v1_common_proto_rawDesc)),
NumEnums: 0,
NumMessages: 3,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_exo_v1_common_proto_goTypes,
DependencyIndexes: file_exo_v1_common_proto_depIdxs,
MessageInfos: file_exo_v1_common_proto_msgTypes,
}.Build()
File_exo_v1_common_proto = out.File
file_exo_v1_common_proto_goTypes = nil
file_exo_v1_common_proto_depIdxs = nil
}

28
proto/exo/v1/common.proto Normal file
View File

@@ -0,0 +1,28 @@
syntax = "proto3";
package exo.v1;
option go_package = "git.wntrmute.dev/kyle/exo/proto/exo/v1;exov1";
// Value is a typed key-value entry.
message Value {
string contents = 1;
string type = 2;
}
// MetadataEntry is a single key-value metadata pair.
message MetadataEntry {
string key = 1;
Value value = 2;
}
// Header is attached to every persistent object.
message Header {
string id = 1;
string type = 2;
int64 created = 3;
int64 modified = 4;
repeated string categories = 5;
repeated string tags = 6;
repeated MetadataEntry metadata = 7;
}