Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [0.11.1] - 2026-07-27

### Fixed

- `datasets`: Resolve registered protobuf dependencies, including STAC types, when constructing dynamic datapoint descriptors.

## [0.11.0] - 2026-07-23

### Added
Expand Down Expand Up @@ -160,7 +166,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Added support for Tilebox Observability, including logging and tracing helpers.
- Added examples for using the library.

[Unreleased]: https://github.com/tilebox/tilebox-go/compare/v0.11.0...HEAD
[Unreleased]: https://github.com/tilebox/tilebox-go/compare/v0.11.1...HEAD
[0.11.1]: https://github.com/tilebox/tilebox-go/compare/v0.11.0...v0.11.1
[0.11.0]: https://github.com/tilebox/tilebox-go/compare/v0.10.0...v0.11.0
[0.10.0]: https://github.com/tilebox/tilebox-go/compare/v0.9.0...v0.10.0
[0.9.0]: https://github.com/tilebox/tilebox-go/compare/v0.8.0...v0.9.0
Expand Down
5 changes: 2 additions & 3 deletions datasets/v1/datasets.go
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ var idField = datasetsv1.Field_builder{
Annotation: datasetsv1.FieldAnnotation_builder{
Description: "A universally unique identifier (UUID) that uniquely identifies each data point, automatically generated by Tilebox.",
ExampleValue: "4e8a2836-72f8-4ac2-a9e9-cbe3492ef60c",
SourceJsonPointer: new("/properties/tilebox_id"),
SourceJsonPointer: new("/properties/tilebox:id"),
}.Build(),
}.Build()

Expand All @@ -150,8 +150,7 @@ var ingestionTimeField = datasetsv1.Field_builder{
Annotation: datasetsv1.FieldAnnotation_builder{
Description: "The time the data point was ingested into the Tilebox API, automatically generated by Tilebox.",
ExampleValue: "2022-10-17T14:35:28Z",
SourceJsonPointer: new("/properties/created"),
JsonSchemaRef: new("https://schemas.stacspec.org/v1.1.0/item-spec/json-schema/datetime.json#/properties/created"),
SourceJsonPointer: new("/properties/tilebox:ingestion_time"),
}.Build(),
}.Build()

Expand Down
35 changes: 27 additions & 8 deletions datasets/v1/dynamic.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ import (
"unicode"

"github.com/google/uuid"
// These generated STAC, datasets/v1, Duration, and Timestamp imports register
// supported dataset field type descriptors in protoregistry.GlobalFiles.
_ "github.com/tilebox/tilebox-go/protogen/datasets/stac/v1"
datasetsv1 "github.com/tilebox/tilebox-go/protogen/datasets/v1"
"google.golang.org/protobuf/encoding/protojson"
"google.golang.org/protobuf/proto"
Expand All @@ -18,8 +21,8 @@ import (
"google.golang.org/protobuf/reflect/protoregistry"
"google.golang.org/protobuf/types/descriptorpb"
"google.golang.org/protobuf/types/dynamicpb"
"google.golang.org/protobuf/types/known/durationpb"
"google.golang.org/protobuf/types/known/timestamppb"
_ "google.golang.org/protobuf/types/known/durationpb"
_ "google.golang.org/protobuf/types/known/timestamppb"
)

// DatapointDescriptor contains the resolved protobuf descriptor needed to decode raw datapoint bytes.
Expand All @@ -35,7 +38,7 @@ func NewDatapointDescriptor(dataset *Dataset) (*DatapointDescriptor, error) {
return nil, errors.New("dataset does not include a protobuf descriptor")
}

descriptorSet := descriptorSetWithWellKnownTypes(dataset.Type.GetDescriptorSet())
descriptorSet := descriptorSetWithDependencies(dataset.Type.GetDescriptorSet())
files, err := protodesc.NewFiles(descriptorSet)
if err != nil {
return nil, fmt.Errorf("failed to build dataset protobuf descriptors: %w", err)
Expand Down Expand Up @@ -129,20 +132,36 @@ func (d DatapointDecoder) resolver(descriptor *DatapointDescriptor) interface {
return descriptor.resolver
}

func descriptorSetWithWellKnownTypes(datasetDescriptorSet *descriptorpb.FileDescriptorSet) *descriptorpb.FileDescriptorSet {
// descriptorSetWithDependencies adds imports needed by the dataset descriptor.
// SDK-supported dependencies are registered in protoregistry.GlobalFiles by the
// generated protobuf imports above; application-registered dependencies can be resolved too.
func descriptorSetWithDependencies(datasetDescriptorSet *descriptorpb.FileDescriptorSet) *descriptorpb.FileDescriptorSet {
descriptorSet := &descriptorpb.FileDescriptorSet{}
seen := map[string]bool{}
addFile := func(file *descriptorpb.FileDescriptorProto) {
datasetFiles := make(map[string]*descriptorpb.FileDescriptorProto, len(datasetDescriptorSet.GetFile()))
for _, file := range datasetDescriptorSet.GetFile() {
datasetFiles[file.GetName()] = file
}

var addFile func(file *descriptorpb.FileDescriptorProto)
addFile = func(file *descriptorpb.FileDescriptorProto) {
if file == nil || seen[file.GetName()] {
return
}
seen[file.GetName()] = true
for _, dependency := range file.GetDependency() {
if datasetFile := datasetFiles[dependency]; datasetFile != nil {
addFile(datasetFile)
continue
}
registeredFile, err := protoregistry.GlobalFiles.FindFileByPath(dependency)
if err == nil {
addFile(protodesc.ToFileDescriptorProto(registeredFile))
}
}
descriptorSet.File = append(descriptorSet.File, file)
}

addFile(protodesc.ToFileDescriptorProto(durationpb.File_google_protobuf_duration_proto))
addFile(protodesc.ToFileDescriptorProto(timestamppb.File_google_protobuf_timestamp_proto))
addFile(protodesc.ToFileDescriptorProto(datasetsv1.File_datasets_v1_well_known_types_proto))
for _, file := range datasetDescriptorSet.GetFile() {
addFile(file)
}
Expand Down
39 changes: 39 additions & 0 deletions datasets/v1/dynamic_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"github.com/paulmach/orb"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
stacv1 "github.com/tilebox/tilebox-go/protogen/datasets/stac/v1"
datasetsv1 "github.com/tilebox/tilebox-go/protogen/datasets/v1"
examplesv1 "github.com/tilebox/tilebox-go/protogen/examples/v1"
"google.golang.org/protobuf/proto"
Expand All @@ -27,6 +28,16 @@ func TestNewDatapointDescriptor(t *testing.T) {
assert.Equal(t, "tilebox.v1.Sentinel2Msi", string(descriptor.MessageDescriptor.FullName()))
}

func TestNewDatapointDescriptorResolvesSTACImports(t *testing.T) {
descriptor, err := NewDatapointDescriptor(stacDataset())

require.NoError(t, err)
require.NotNil(t, descriptor)
assets := descriptor.MessageDescriptor.Fields().ByName("assets")
require.NotNil(t, assets)
assert.Equal(t, protoreflect.FullName("datasets.stac.v1.Assets"), assets.Message().FullName())
}

func TestUnmarshalDatapoint(t *testing.T) {
descriptor, err := NewDatapointDescriptor(exampleDataset())
require.NoError(t, err)
Expand Down Expand Up @@ -122,3 +133,31 @@ func durationDataset() *Dataset {
}.Build(),
}
}

func stacDataset() *Dataset {
return &Dataset{
Type: datasetsv1.AnnotatedType_builder{
DescriptorSet: &descriptorpb.FileDescriptorSet{File: []*descriptorpb.FileDescriptorProto{
{
Name: new("tilebox/v1/STACDatapoint.proto"),
Package: new("tilebox.v1"),
Dependency: []string{stacv1.File_datasets_stac_v1_asset_proto.Path()},
MessageType: []*descriptorpb.DescriptorProto{
{
Name: new("STACDatapoint"),
Field: []*descriptorpb.FieldDescriptorProto{
{
Name: new("assets"),
Number: proto.Int32(1),
Label: descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
Type: descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
TypeName: new(".datasets.stac.v1.Assets"),
},
},
},
},
},
}},
}.Build(),
}
}
8 changes: 4 additions & 4 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,8 @@ require (
go.opentelemetry.io/otel/sdk v1.44.0
go.opentelemetry.io/otel/sdk/log v0.20.0
go.opentelemetry.io/otel/trace v1.44.0
go.opentelemetry.io/proto/otlp v1.10.0
golang.org/x/net v0.57.0
google.golang.org/genproto/googleapis/api v0.0.0-20260720211330-0afa2a65878a
go.opentelemetry.io/proto/otlp v1.11.0
google.golang.org/genproto/googleapis/api v0.0.0-20260724162435-b2f20204f0df
google.golang.org/grpc v1.82.1
google.golang.org/protobuf v1.36.11
pgregory.net/rapid v1.3.0
Expand All @@ -45,8 +44,9 @@ require (
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.44.0 // indirect
go.opentelemetry.io/otel/log v0.20.0 // indirect
golang.org/x/net v0.57.0 // indirect
golang.org/x/sys v0.47.0 // indirect
golang.org/x/text v0.40.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20260720211330-0afa2a65878a // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20260724162435-b2f20204f0df // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
12 changes: 6 additions & 6 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,8 @@ go.opentelemetry.io/otel/sdk/metric v1.44.0 h1:3LlKgI+VjbVsjNRFZJZAJ30WjXC5VkNRk
go.opentelemetry.io/otel/sdk/metric v1.44.0/go.mod h1:5B5pMARnXxKhltooO4xUuCBorl65a4EpnTalObqOigA=
go.opentelemetry.io/otel/trace v1.44.0 h1:jxF5CsGYCe74MCRx2X4g7WsY/VBKRqqpNvXlX/6gtIk=
go.opentelemetry.io/otel/trace v1.44.0/go.mod h1:oLl1jrMQAVo6v3GAggN+1VH9VIz9iUSvW53sW1Q8PIE=
go.opentelemetry.io/proto/otlp v1.10.0 h1:IQRWgT5srOCYfiWnpqUYz9CVmbO8bFmKcwYxpuCSL2g=
go.opentelemetry.io/proto/otlp v1.10.0/go.mod h1:/CV4QoCR/S9yaPj8utp3lvQPoqMtxXdzn7ozvvozVqk=
go.opentelemetry.io/proto/otlp v1.11.0 h1:5rrYs0Ykyj50sdU/JU0x8etU+LubXWb+gED6TbEdMIk=
go.opentelemetry.io/proto/otlp v1.11.0/go.mod h1:SmVizdCOAm3XBtG1g1NnOdhW6jtddT72hLMhv8VwA8E=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
golang.org/x/net v0.57.0 h1:K5+3DljvIuDG9/Jv9rvyMywYNFCQ9RSUY6OOTTkT+tE=
Expand All @@ -95,10 +95,10 @@ golang.org/x/text v0.40.0 h1:Ub2Z6/xjgF1WrYQz2nuITOEegKFtiIy+rieRJ5lHZKs=
golang.org/x/text v0.40.0/go.mod h1:hpnzDAfGV753zIKo+wk3u1bVKCGPbrnF7+7LBF/UHVY=
gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4=
gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E=
google.golang.org/genproto/googleapis/api v0.0.0-20260720211330-0afa2a65878a h1:97PfJ4tCxY5C7NzzgGqQEMZmXbISdvSArNNEOoUGKBg=
google.golang.org/genproto/googleapis/api v0.0.0-20260720211330-0afa2a65878a/go.mod h1:1brfde68Npq6+WA75c1EHWPijZEG1kMus61ygPZfn4A=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260720211330-0afa2a65878a h1:qI/YMH1ep2qQtqcp00gMQyoU7mjvbhg88GJKCvfoLj0=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260720211330-0afa2a65878a/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
google.golang.org/genproto/googleapis/api v0.0.0-20260724162435-b2f20204f0df h1:NsJx+hCSwIBI6+C4BuJIkb8xOG1M+nfQDsqIrQHT92k=
google.golang.org/genproto/googleapis/api v0.0.0-20260724162435-b2f20204f0df/go.mod h1:1brfde68Npq6+WA75c1EHWPijZEG1kMus61ygPZfn4A=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260724162435-b2f20204f0df h1:O3ig1i5WDDzsVzRp+cCdgelT9vXnlnOFdlEeFtL4HCc=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260724162435-b2f20204f0df/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
google.golang.org/grpc v1.82.1 h1:NnAxzGRA0677vCa4BUkOAnO5+FfQqVl9iUXeD0IqcGE=
google.golang.org/grpc v1.82.1/go.mod h1:yzTZ1TB1Z3SG+LIYaI+WiE8D5+PZ3ArnrSp8zF3+/ZA=
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
Expand Down
Loading