Skip to content

Commit

Permalink
better support for null/unset geometry, added Location header on crea…
Browse files Browse the repository at this point in the history
…te feature.
  • Loading branch information
fredmorin committed Nov 28, 2023
1 parent c711a4d commit a831516
Show file tree
Hide file tree
Showing 5 changed files with 60 additions and 51 deletions.
2 changes: 1 addition & 1 deletion internal/data/catalog.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ type Catalog interface {
ReplaceTableFeature(ctx context.Context, name string, id string, feature Feature) error

// CreateTableFeature creates a feature
CreateTableFeature(ctx context.Context, name string, feature Feature) error
CreateTableFeature(ctx context.Context, name string, feature Feature) (string, error)

// DeleteTableFeature deletes a feature
DeleteTableFeature(ctx context.Context, name string, id string) error
Expand Down
18 changes: 9 additions & 9 deletions internal/data/catalog_db.go
Original file line number Diff line number Diff line change
Expand Up @@ -234,22 +234,22 @@ func (cat *catalogDB) TableFeature(ctx context.Context, name string, id string,
return features[0], nil
}

func (cat *catalogDB) CreateTableFeature(ctx context.Context, name string, feature Feature) error {
func (cat *catalogDB) CreateTableFeature(ctx context.Context, name string, feature Feature) (string, error) {
tbl, err := cat.TableByName(name)
if err != nil {
return err
return "", err
}
sql, argValues, err := sqlCreateFeature(tbl, feature)
log.Debug("Create feature query: " + sql)
result, err := cat.dbconn.Exec(ctx, sql, argValues...)
row := cat.dbconn.QueryRow(ctx, sql, argValues...)
var featureId string

err = row.Scan(&featureId)

if err != nil {
return err
}
rows := result.RowsAffected()
if rows != 1 {
return fmt.Errorf("expected to affect 1 row, affected %d", rows)
return "", err
}
return nil
return featureId, err
}

func (cat *catalogDB) ReplaceTableFeature(ctx context.Context, name string, id string, feature Feature) error {
Expand Down
4 changes: 2 additions & 2 deletions internal/data/catalog_mock.go
Original file line number Diff line number Diff line change
Expand Up @@ -242,8 +242,8 @@ func (cat *CatalogMock) TableFeature(ctx context.Context, name string, id string
return features[index].toJSON(propNames), nil
}

func (cat *CatalogMock) CreateTableFeature(ctx context.Context, name string, feature Feature) error {
return nil
func (cat *CatalogMock) CreateTableFeature(ctx context.Context, name string, feature Feature) (string, error) {
return "", nil
}

func (cat *CatalogMock) ReplaceTableFeature(ctx context.Context, name string, id string, feature Feature) error {
Expand Down
71 changes: 35 additions & 36 deletions internal/data/db_sql.go
Original file line number Diff line number Diff line change
Expand Up @@ -192,11 +192,31 @@ func sqlFeature(tbl *Table, param *QueryParam) string {
return sql
}

func getColumnValues(tbl *Table, feature Feature, includeOnlySetProperties bool) ([]string, []string, []interface{}) {
func getColumnValues(tbl *Table, feature Feature, includeOnlySetProperties bool) ([]string, []string, []interface{}, error) {
var columnNames, columnIndex []string
var columnValues []interface{}
var i = 2
var i = 1

var geometryStr []byte
if feature.Geometry != nil {
var err error
geometryStr, err = json.Marshal(feature.Geometry)
if err != nil {
return nil, nil, nil, err
}
}

if feature.Geometry != nil {
columnNames = append(columnNames, tbl.GeometryColumn)
columnIndex = append(columnIndex, fmt.Sprintf("ST_Transform(ST_GeomFromGeoJSON($%v),%v)", i, tbl.Srid))
columnValues = append(columnValues, geometryStr)
i++
} else if !includeOnlySetProperties {
columnNames = append(columnNames, tbl.GeometryColumn)
columnIndex = append(columnIndex, fmt.Sprintf("$%v", i))
columnValues = append(columnValues, feature.Geometry)
i++
}
for _, column := range tbl.Columns {
val, ok := feature.Properties[column]

Expand All @@ -208,14 +228,7 @@ func getColumnValues(tbl *Table, feature Feature, includeOnlySetProperties bool)
}
}

return columnNames, columnIndex, columnValues
}

func buildGeometrySQL(tbl *Table) string {
if len(tbl.Columns) > 0 {
return fmt.Sprintf("ST_Transform(ST_GeomFromGeoJSON($1),%v)", tbl.Srid)
}
return fmt.Sprintf("ST_Transform(ST_GeomFromGeoJSON($1),%v)", tbl.Srid)
return columnNames, columnIndex, columnValues, nil
}

func buildUpdateSetClause(columnNames []string, columnIndex []string) string {
Expand All @@ -227,45 +240,31 @@ func buildUpdateSetClause(columnNames []string, columnIndex []string) string {
}

func sqlCreateFeature(tbl *Table, feature Feature) (string, []interface{}, error) {
columnNames, columnIndex, columnValues := getColumnValues(tbl, feature, true)

columnNamesStr := strings.Join(columnNames, ",")
columnIndexStr := strings.Join(columnIndex, ",")
geomSQL := buildGeometrySQL(tbl)

sql := fmt.Sprintf("INSERT INTO \"%s\".\"%s\" (%s, %s) VALUES (%s, %v);", tbl.Schema, tbl.Table, columnNamesStr, tbl.GeometryColumn, columnIndexStr, geomSQL)

var err error
argValues := make([]interface{}, len(columnValues)+1)
argValues[0], err = json.Marshal(feature.Geometry)
columnNames, columnIndex, columnValues, err := getColumnValues(tbl, feature, true)
if err != nil {
return "", nil, err
}

copy(argValues[1:], columnValues)
columnNamesStr := strings.Join(columnNames, ",")
columnIndexStr := strings.Join(columnIndex, ",")

sql := fmt.Sprintf("INSERT INTO \"%s\".\"%s\" (%s) VALUES (%s) RETURNING %s::varchar;", tbl.Schema, tbl.Table, columnNamesStr, columnIndexStr, tbl.IDColumn)

return sql, argValues, nil
return sql, columnValues, nil
}

func sqlReplaceFeature(tbl *Table, id string, feature Feature) (string, []interface{}, error) {
columnNames, columnIndex, columnValues := getColumnValues(tbl, feature, false)

geomSQL := buildGeometrySQL(tbl)
setClause := buildUpdateSetClause(columnNames, columnIndex)

sql := fmt.Sprintf("UPDATE \"%s\".\"%s\" SET %s=%v%s WHERE \"%v\" = $%v;", tbl.Schema, tbl.Table, tbl.GeometryColumn, geomSQL, setClause, tbl.IDColumn, len(tbl.Columns)+2)

var err error
argValues := make([]interface{}, len(columnValues)+2)
argValues[0], err = json.Marshal(feature.Geometry)
columnNames, columnIndex, columnValues, err := getColumnValues(tbl, feature, true)
if err != nil {
return "", nil, err
}
setClause := buildUpdateSetClause(columnNames, columnIndex)

sql := fmt.Sprintf("UPDATE \"%s\".\"%s\" SET %s WHERE \"%v\" = $%v;", tbl.Schema, tbl.Table, setClause, tbl.IDColumn, len(columnNames))

copy(argValues[1:], columnValues)
argValues[len(columnValues)+1] = id
columnValues = append(columnValues, (id))

return sql, argValues, nil
return sql, columnValues, nil
}

func sqlDeleteFeature(tbl *Table, id string) (string, []interface{}) {
Expand Down
16 changes: 13 additions & 3 deletions internal/service/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -293,17 +293,25 @@ func handleCollectionItems(w http.ResponseWriter, r *http.Request) *appError {
}
return nil
case http.MethodPost:
if format != "json" {
return appErrorInternalFmt(nil, api.ErrMsgInvalidQuery)
}
body, err := ioutil.ReadAll(r.Body)
if err != nil {
return appErrorInternalFmt(err, api.ErrMsgInvalidQuery)
}
print(body)
var feature data.Feature
err = json.Unmarshal([]byte(body), &feature)
if err != nil {
return appErrorInternalFmt(err, api.ErrMsgInvalidQuery)
}
catalogInstance.CreateTableFeature(ctx, name, feature)
var featureId string
featureId, err = catalogInstance.CreateTableFeature(ctx, name, feature)
if err != nil {
return appErrorInternalFmt(err, api.ErrMsgInvalidQuery)
}
urlBase := serveURLBase(r)
w.Header().Set("Location", urlBase+"collections/"+name+"/items/"+featureId+".json")
w.WriteHeader(http.StatusCreated)
return nil

Expand Down Expand Up @@ -406,11 +414,13 @@ func handleItem(w http.ResponseWriter, r *http.Request) *appError {
return appErrorInternalFmt(errQuery, api.ErrMsgInvalidQuery)
}
case http.MethodPut:
if format != "json" {
return appErrorInternalFmt(nil, api.ErrMsgInvalidQuery)
}
body, err := ioutil.ReadAll(r.Body)
if err != nil {
return appErrorInternalFmt(err, api.ErrMsgInvalidQuery)
}
print(body)
var inputFeature data.Feature
err = json.Unmarshal([]byte(body), &inputFeature)
if err != nil {
Expand Down

0 comments on commit a831516

Please sign in to comment.