From eeeaad8b92f3a04e19c18f19b4cd9a94af3090d9 Mon Sep 17 00:00:00 2001 From: Richard Kettelerij Date: Mon, 15 Jul 2024 13:34:33 +0200 Subject: [PATCH 1/2] fix(relations): Check for exact collection match first --- internal/ogc/features/domain/profile.go | 19 +++++++++++++++---- internal/ogc/features/domain/profile_test.go | 11 ++++++++++- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/internal/ogc/features/domain/profile.go b/internal/ogc/features/domain/profile.go index 691bf6fd..e5cc523c 100644 --- a/internal/ogc/features/domain/profile.go +++ b/internal/ogc/features/domain/profile.go @@ -4,6 +4,7 @@ import ( "fmt" "net/url" "regexp" + "sort" "strings" ) @@ -27,6 +28,9 @@ type Profile struct { } func NewProfile(profileName ProfileName, baseURL url.URL, collectionNames []string) Profile { + sort.Slice(collectionNames, func(i, j int) bool { + return len(collectionNames[i]) > len(collectionNames[j]) + }) return Profile{ profileName: profileName, baseURL: baseURL.String(), @@ -39,7 +43,7 @@ func (p *Profile) MapRelationUsingProfile(columnName string, columnValue any, ex switch p.profileName { case RelAsLink: newColumnName = regex.ReplaceAllString(columnName, "") - collectionName := p.findCollectionWithPrefix(newColumnName) + collectionName := p.findCollection(newColumnName) newColumnName += ".href" if columnValue != nil && collectionName != "" { newColumnValue = fmt.Sprintf(featurePath, p.baseURL, collectionName, columnValue) @@ -50,7 +54,7 @@ func (p *Profile) MapRelationUsingProfile(columnName string, columnValue any, ex case RelAsURI: // almost identical to rel-as-link except that there's no ".href" suffix (and potentially a title in the future) newColumnName = regex.ReplaceAllString(columnName, "") - collectionName := p.findCollectionWithPrefix(newColumnName) + collectionName := p.findCollection(newColumnName) if columnValue != nil { newColumnValue = fmt.Sprintf(featurePath, p.baseURL, collectionName, columnValue) } @@ -58,9 +62,16 @@ func (p *Profile) MapRelationUsingProfile(columnName string, columnValue any, ex return } -func (p *Profile) findCollectionWithPrefix(prefix string) string { +func (p *Profile) findCollection(name string) string { + // prefer exact matches first for _, collName := range p.collectionNames { - if strings.HasPrefix(prefix, collName) { + if name == collName { + return collName + } + } + // then prefer fuzzy match (to support infix) + for _, collName := range p.collectionNames { + if strings.HasPrefix(name, collName) { return collName } } diff --git a/internal/ogc/features/domain/profile_test.go b/internal/ogc/features/domain/profile_test.go index 64d1991d..21da54d8 100644 --- a/internal/ogc/features/domain/profile_test.go +++ b/internal/ogc/features/domain/profile_test.go @@ -44,6 +44,15 @@ func TestMapRelationUsingProfile(t *testing.T) { expectedColName: "another_collection_some_infix.href", expectedColVal: "http://example.com/collections/another_collection/items/123", }, + { + name: "RelAsLink with similar collection name (make sure exact match is selected)", + profile: RelAsLink, + columnName: "baz_bazoo_boo_external_fid", + columnValue: "123", + externalFidCol: "external_fid", + expectedColName: "baz_bazoo_boo.href", + expectedColVal: "http://example.com/collections/baz_bazoo_boo/items/123", + }, { name: "RelAsKey", profile: RelAsKey, @@ -86,7 +95,7 @@ func TestMapRelationUsingProfile(t *testing.T) { t.Run(tt.name, func(t *testing.T) { url, err := neturl.Parse("http://example.com") assert.NoError(t, err) - profile := NewProfile(tt.profile, *url, []string{"some_collection", "another_collection", "foo", "bar"}) + profile := NewProfile(tt.profile, *url, []string{"some_collection", "another_collection", "foo", "bar", "baz_bazoo", "baz_bazoo_boo", "baz_bazoo_boo_foo"}) newColName, newColVal := profile.MapRelationUsingProfile(tt.columnName, tt.columnValue, tt.externalFidCol) assert.Equal(t, tt.expectedColName, newColName) assert.Equal(t, tt.expectedColVal, newColVal) From 6c2cd488eb131df7a3af47fed1b2304514b147c4 Mon Sep 17 00:00:00 2001 From: Richard Kettelerij Date: Mon, 15 Jul 2024 13:34:53 +0200 Subject: [PATCH 2/2] chore(mapsheets): Add JSON-FG test for features as mapsheets --- internal/ogc/features/main_test.go | 14 +++ .../testdata/expected_mapsheets_jsonfg.json | 111 ++++++++++++++++++ 2 files changed, 125 insertions(+) create mode 100644 internal/ogc/features/testdata/expected_mapsheets_jsonfg.json diff --git a/internal/ogc/features/main_test.go b/internal/ogc/features/main_test.go index 2f834c2b..51cca5af 100644 --- a/internal/ogc/features/main_test.go +++ b/internal/ogc/features/main_test.go @@ -475,6 +475,20 @@ func TestFeatures(t *testing.T) { statusCode: http.StatusOK, }, }, + { + name: "Request mapsheets as JSON-FG", + fields: fields{ + configFile: "internal/ogc/features/testdata/config_mapsheets.yaml", + url: "http://localhost:8080/collections/:collectionId/items?limit=2&f=jsonfg", + collectionID: "example_mapsheets", + contentCrs: "<" + domain.WGS84CrsURI + ">", + format: "json", + }, + want: want{ + body: "internal/ogc/features/testdata/expected_mapsheets_jsonfg.json", + statusCode: http.StatusOK, + }, + }, { name: "Request mapsheets as HTML", fields: fields{ diff --git a/internal/ogc/features/testdata/expected_mapsheets_jsonfg.json b/internal/ogc/features/testdata/expected_mapsheets_jsonfg.json new file mode 100644 index 00000000..d6db88ca --- /dev/null +++ b/internal/ogc/features/testdata/expected_mapsheets_jsonfg.json @@ -0,0 +1,111 @@ +{ + "type": "FeatureCollection", + "timeStamp": "2000-01-01T00:00:00Z", + "coordRefSys": "http://www.opengis.net/def/crs/OGC/1.3/CRS84", + "links": [ + { + "rel": "self", + "title": "This document as JSON-FG", + "type": "application/vnd.ogc.fg+json", + "href": "http://localhost:8080/collections/example_mapsheets/items?f=jsonfg&limit=2" + }, + { + "rel": "alternate", + "title": "This document as GeoJSON", + "type": "application/geo+json", + "href": "http://localhost:8080/collections/example_mapsheets/items?f=json&limit=2" + }, + { + "rel": "alternate", + "title": "This document as HTML", + "type": "text/html", + "href": "http://localhost:8080/collections/example_mapsheets/items?f=html&limit=2" + }, + { + "rel": "next", + "title": "Next page", + "type": "application/vnd.ogc.fg+json", + "href": "http://localhost:8080/collections/example_mapsheets/items?cursor=Dv4%7CNwyr1Q&f=jsonfg&limit=2" + } + ], + "conformsTo": [ + "http://www.opengis.net/spec/json-fg-1/0.2/conf/core" + ], + "features": [ + { + "id": "3542", + "type": "Feature", + "time": null, + "place": null, + "geometry": { + "type": "Point", + "coordinates": [ + 120919.942, + 489320.199 + ] + }, + "properties": { + "datum_doc": "1900-01-01", + "datum_eind": null, + "datum_strt": "1900-01-01", + "document": "GV00000402", + "huisletter": null, + "huisnummer": 14, + "nummer_id": "0363200000454013", + "postcode": "1013CR", + "rdf_seealso": "http://bag.basisregistraties.overheid.nl/bag/id/nummeraanduiding/0363200000454013", + "status": "Naamgeving uitgegeven", + "straatnaam": "Van Diemenkade", + "toevoeging": null, + "type": "Ligplaats", + "woonplaats": "Amsterdam" + }, + "links": [ + { + "rel": "enclosure", + "title": "Download feature", + "type": "application/octet-stream", + "href": "http://bag.basisregistraties.overheid.nl/bag/id/nummeraanduiding/0363200000454013" + } + ] + }, + { + "id": "3837", + "type": "Feature", + "time": null, + "place": null, + "geometry": { + "type": "Point", + "coordinates": [ + 121108.424, + 488930.925 + ] + }, + "properties": { + "datum_doc": "1900-01-01", + "datum_eind": null, + "datum_strt": "1900-01-01", + "document": "GV00000402", + "huisletter": null, + "huisnummer": 9, + "nummer_id": "0363200000398886", + "postcode": "1013KW", + "rdf_seealso": "http://bag.basisregistraties.overheid.nl/bag/id/nummeraanduiding/0363200000398886", + "status": "Naamgeving uitgegeven", + "straatnaam": "Realengracht", + "toevoeging": null, + "type": "Ligplaats", + "woonplaats": "Amsterdam" + }, + "links": [ + { + "rel": "enclosure", + "title": "Download feature", + "type": "application/octet-stream", + "href": "http://bag.basisregistraties.overheid.nl/bag/id/nummeraanduiding/0363200000398886" + } + ] + } + ], + "numberReturned": 2 +}