Skip to content

Commit

Permalink
Merge pull request #26 from planetlabs/features
Browse files Browse the repository at this point in the history
Support extensions on collections
  • Loading branch information
tschaub authored Jul 7, 2023
2 parents 14b42b0 + 8cc2433 commit 37e45a3
Show file tree
Hide file tree
Showing 4 changed files with 162 additions and 6 deletions.
1 change: 1 addition & 0 deletions api/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,4 +84,5 @@ type Root struct {
type Extension interface {
URI() string
Encode(map[string]any) error
Decode([]byte) error
}
86 changes: 86 additions & 0 deletions api/features.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,83 @@ import (
"github.com/mitchellh/mapstructure"
)

type Collection struct {
Id string `json:"id"`
Title string `json:"title,omitempty"`
Description string `json:"description,omitempty"`
Links []*Link `json:"links"`
Extent *Extent `json:"extent,omitempty"`
ItemType string `json:"itemType,omitempty"`
Crs []string `json:"crs,omitempty"`
Extensions []Extension `json:"-"`
}

var (
_ json.Marshaler = (*Collection)(nil)
_ json.Unmarshaler = (*Collection)(nil)
)

func (collection Collection) MarshalJSON() ([]byte, error) {
collectionMap := map[string]any{}
decoder, decoderErr := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
TagName: "json",
Result: &collectionMap,
})
if decoderErr != nil {
return nil, decoderErr
}

decodeErr := decoder.Decode(collection)
if decodeErr != nil {
return nil, decodeErr
}

for _, extension := range collection.Extensions {
if err := extension.Encode(collectionMap); err != nil {
return nil, fmt.Errorf("trouble encoding feature JSON with the %q extension: %w", extension.URI(), err)
}
}

return json.Marshal(collectionMap)
}

type decodedCollection Collection

func (collection *Collection) UnmarshalJSON(data []byte) error {
d := &decodedCollection{Extensions: collection.Extensions}
if err := json.Unmarshal(data, d); err != nil {
return err
}
*collection = Collection(*d)

for _, e := range collection.Extensions {
if err := e.Decode(data); err != nil {
return err
}
}
return nil
}

type Extent struct {
Spatial *SpatialExtent `json:"spatial,omitempty"`
Temporal *TemporalExtent `json:"temporal,omitempty"`
}

type SpatialExtent struct {
Bbox [][]float64 `json:"bbox"`
Crs string `json:"crs"`
}

type TemporalExtent struct {
Interval [][]any `json:"interval"`
Trs string `json:"trs"`
}

type CollectionsList struct {
Collections []*Collection `json:"collections"`
Links []*Link `json:"links"`
}

type Feature struct {
Id string `json:"id,omitempty"`
Geometry any `json:"geometry"`
Expand Down Expand Up @@ -68,3 +145,12 @@ func (feature Feature) MarshalJSON() ([]byte, error) {

return json.Marshal(featureMap)
}

type FeatureCollection struct {
Type string `json:"type"`
Features []*Feature `json:"features"`
Links []*Link `json:"links,omitempty"`
TimeStamp string `json:"timeStamp,omitempty"`
NumberMatched int `json:"numberMatched,omitempty"`
NumberReturned int `json:"numberReturned,omitempty"`
}
76 changes: 70 additions & 6 deletions api/features_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package api_test

import (
"encoding/json"
"errors"
"fmt"
"testing"

Expand Down Expand Up @@ -95,14 +96,17 @@ func TestFeatureMarshal(t *testing.T) {
}
}

type TestExtension struct {
var (
_ api.Extension = (*FeatureExtension)(nil)
_ api.Extension = (*CollectionExtension)(nil)
)

type FeatureExtension struct {
RootFoo string
NestedBar string
}

var _ api.Extension = (*TestExtension)(nil)

func (e *TestExtension) Encode(featureMap map[string]any) error {
func (e *FeatureExtension) Encode(featureMap map[string]any) error {
featureMap["test:foo"] = e.RootFoo
propertiesMap, ok := featureMap["properties"].(map[string]any)
if !ok {
Expand All @@ -112,7 +116,28 @@ func (e *TestExtension) Encode(featureMap map[string]any) error {
return nil
}

func (e *TestExtension) URI() string {
func (e *FeatureExtension) Decode(data []byte) error {
return errors.New("not implemented")
}

func (e *FeatureExtension) URI() string {
return "https://example.com/test-extension"
}

type CollectionExtension struct {
Classes []string
}

func (e *CollectionExtension) Encode(collectionMap map[string]any) error {
collectionMap["classes"] = e.Classes
return nil
}

func (e *CollectionExtension) Decode(data []byte) error {
return json.Unmarshal(data, e)
}

func (e *CollectionExtension) URI() string {
return "https://example.com/test-extension"
}

Expand All @@ -123,7 +148,7 @@ func TestFeatureMarshalExtension(t *testing.T) {
"one": "core-property",
},
Extensions: []api.Extension{
&TestExtension{
&FeatureExtension{
RootFoo: "foo-value",
NestedBar: "bar-value",
},
Expand All @@ -147,3 +172,42 @@ func TestFeatureMarshalExtension(t *testing.T) {
require.NoError(t, err)
assert.JSONEq(t, expected, string(actual))
}

func TestCollectionMarshalExtension(t *testing.T) {
collection := &api.Collection{
Id: "test-collection",
Extensions: []api.Extension{
&CollectionExtension{
Classes: []string{"foo", "bar"},
},
},
Links: []*api.Link{},
}

expected := `{
"id": "test-collection",
"classes": ["foo", "bar"],
"links": []
}`

actual, err := json.Marshal(collection)
require.NoError(t, err)
assert.JSONEq(t, expected, string(actual))
}

func TestCollectionUnmarshalExtension(t *testing.T) {
data := `{
"id": "test-collection",
"classes": ["foo", "bar"],
"links": []
}`

extension := &CollectionExtension{}
collection := &api.Collection{
Extensions: []api.Extension{extension},
}

err := json.Unmarshal([]byte(data), collection)
require.NoError(t, err)
assert.Equal(t, []string{"foo", "bar"}, extension.Classes)
}
5 changes: 5 additions & 0 deletions api/records.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,3 +91,8 @@ func (r *RecordCore) Encode(featureMap map[string]any) error {

return nil
}

func (r *RecordCore) Decode(data []byte) error {
// TODO: implement this
return nil
}

0 comments on commit 37e45a3

Please sign in to comment.