Skip to content

Commit

Permalink
Add upsert entity command
Browse files Browse the repository at this point in the history
  • Loading branch information
fisuda committed Dec 6, 2020
1 parent 6429549 commit e4b799e
Show file tree
Hide file tree
Showing 5 changed files with 305 additions and 5 deletions.
35 changes: 35 additions & 0 deletions docs/ngsi/upsert.md
Expand Up @@ -2,6 +2,7 @@

This command upserts entities.

- [Upsert an entity](#upsert-an-entity)
- [Upsert multiple entities](#upsert-multiple-entities)

### Common Options
Expand All @@ -14,6 +15,40 @@ This command upserts entities.
| --path value, -p value | specify FIWARE ServicePath |
| --help | show help (default: false) |

<a name="upsert-an-entity"/>

## Upset an entity

This command upserts an entity.

```console
ngsi upsert [common options] entity [options]
```

### Options

| Options | Description |
| ------------------------- | ---------------------------------- |
| --data value, -d value | specify data |
| --keyValues, -k | specify keyValues (default: false) |
| --link value, -L value | specify @context |
| --help | show help (default: false) |

### Example

#### Request:

```console
ngsi upsert entity \
--data ' {
"id":"urn:ngsi-ld:Product:010",
"type":"Product",
"name":{"type":"Text", "value":"Lemonade"},
"size":{"type":"Text", "value": "S"},
"price":{"type":"Integer", "value": 99}
}'
```

<a name="upsert-multiple-entities"/>

## Upsert multiple entities
Expand Down
48 changes: 48 additions & 0 deletions internal/ngsicmd/entity.go
Expand Up @@ -32,6 +32,7 @@ package ngsicmd
import (
"fmt"
"net/http"
"net/url"

"github.com/lets-fiware/ngsi-go/internal/ngsilib"
"github.com/urfave/cli/v2"
Expand Down Expand Up @@ -120,6 +121,53 @@ func entityRead(c *cli.Context) error {

return nil
}
func entityUpsert(c *cli.Context) error {
const funcName = "entityUpsert"

ngsi, err := initCmd(c, funcName, true)
if err != nil {
return &ngsiCmdError{funcName, 1, err.Error(), err}
}

client, err := newClient(ngsi, c, false)
if err != nil {
return &ngsiCmdError{funcName, 2, err.Error(), err}
}

client.SetPath("/entities")

v := url.Values{}
options := "upsert"
if c.IsSet("keyValues") {
options = options + ",keyValues"
}
v.Set("options", options)
client.SetQuery(&v)

client.SetContentType()

b, err := readAll(c, ngsi)
if err != nil {
return &ngsiCmdError{funcName, 3, err.Error(), err}
}

if client.IsSafeString() {
b, err = ngsilib.JSONSafeStringEncode(b)
if err != nil {
return &ngsiCmdError{funcName, 4, err.Error(), err}
}
}

res, body, err := client.HTTPPost(b)
if err != nil {
return &ngsiCmdError{funcName, 5, err.Error(), err}
}
if res.StatusCode != http.StatusCreated && res.StatusCode != http.StatusNoContent {
return &ngsiCmdError{funcName, 6, fmt.Sprintf("%s %s", res.Status, string(body)), nil}
}

return nil
}

func entityDelete(c *cli.Context) error {

Expand Down
203 changes: 203 additions & 0 deletions internal/ngsicmd/entity_test.go
Expand Up @@ -436,6 +436,209 @@ func TestEntityReadV2ErrorSafeString(t *testing.T) {
}
}

func TestEntityUpsertV2(t *testing.T) {
ngsi, set, app, _ := setupTest()

setupAddBroker(t, ngsi, "orion", "https://orion", "v2")

reqRes := MockHTTPReqRes{}
reqRes.Res.StatusCode = http.StatusNoContent
reqRes.Path = "/v2/entities"
mock := NewMockHTTP()
mock.ReqRes = append(mock.ReqRes, reqRes)
ngsi.HTTP = mock
setupFlagString(set, "host,data")
setupFlagBool(set, "keyValues")

c := cli.NewContext(app, set, nil)
_ = set.Parse([]string{"--host=orion", "--keyValues", "--data={\"id\":\"urn:ngsi-ld:Product:010\",\"type\":\"Product\",\"name\":{\"type\":\"Text\",\"value\":\"Lemonade\"},\"size\":{\"type\":\"Text\",\"value\":\"S\"},\"price\":{\"type\":\"Integer\",\"value\":99}}"})
err := entityUpsert(c)

assert.NoError(t, err)
}

func TestEntityUpsertV2SafeString(t *testing.T) {
ngsi, set, app, _ := setupTest()

setupAddBroker(t, ngsi, "orion", "https://orion", "v2")

reqRes := MockHTTPReqRes{}
reqRes.Res.StatusCode = http.StatusNoContent
reqRes.Path = "/v2/entities"
mock := NewMockHTTP()
mock.ReqRes = append(mock.ReqRes, reqRes)
ngsi.HTTP = mock
setupFlagString(set, "host,data,safeString")

c := cli.NewContext(app, set, nil)
_ = set.Parse([]string{"--host=orion", "--safeString=on", "--data={\"id\":\"urn:ngsi-ld:Product:010\",\"type\":\"Product\",\"name\":{\"type\":\"Text\",\"value\":\"Lemonade\"},\"size\":{\"type\":\"Text\",\"value\":\"S\"},\"price\":{\"type\":\"Integer\",\"value\":99}}"})
err := entityUpsert(c)

assert.NoError(t, err)
}

func TestEntityUpsertLd(t *testing.T) {
ngsi, set, app, _ := setupTest()

setupAddBroker(t, ngsi, "orion", "https://orion", "ld")

reqRes := MockHTTPReqRes{}
reqRes.Res.StatusCode = http.StatusNoContent
reqRes.Path = "/ngsi-ld/v1/entities"
mock := NewMockHTTP()
mock.ReqRes = append(mock.ReqRes, reqRes)
ngsi.HTTP = mock
setupFlagString(set, "host,data")

c := cli.NewContext(app, set, nil)
_ = set.Parse([]string{"--host=orion", "--data={\"id\":\"urn:ngsi-ld:Product:010\",\"type\":\"Product\",\"name\":{\"type\":\"Text\",\"value\":\"Lemonade\"},\"size\":{\"type\":\"Text\",\"value\":\"S\"},\"price\":{\"type\":\"Integer\",\"value\":99}}"})
err := entityUpsert(c)

assert.NoError(t, err)
}

func TestEntityUpsertErrorInitCmd(t *testing.T) {
_, set, app, _ := setupTest()

c := cli.NewContext(app, set, nil)
err := entityUpsert(c)

if assert.Error(t, err) {
ngsiErr := err.(*ngsiCmdError)
assert.Equal(t, 1, ngsiErr.ErrNo)
assert.Equal(t, "Required host not found", ngsiErr.Message)
} else {
t.FailNow()
}
}

func TestEntityUpsertErrorNewClient(t *testing.T) {
ngsi, set, app, _ := setupTest()

setupAddBroker(t, ngsi, "orion", "https://orion", "v2")

setupFlagString(set, "host,link")

c := cli.NewContext(app, set, nil)
_ = set.Parse([]string{"--host=orion", "--link=abc"})
err := entityUpsert(c)

if assert.Error(t, err) {
ngsiErr := err.(*ngsiCmdError)
assert.Equal(t, 2, ngsiErr.ErrNo)
assert.Equal(t, "abc not found", ngsiErr.Message)
} else {
t.FailNow()
}
}

func TestEntityUpsertErrorReadAll(t *testing.T) {
ngsi, set, app, _ := setupTest()

setupAddBroker(t, ngsi, "orion", "https://orion", "v2")

reqRes := MockHTTPReqRes{}
reqRes.Res.StatusCode = http.StatusOK
reqRes.Path = "/ngsi-ld/v1/entities"
reqRes.ResHeader = http.Header{"Ngsild-Results-Count": []string{"8"}}
mock := NewMockHTTP()
mock.ReqRes = append(mock.ReqRes, reqRes)
ngsi.HTTP = mock
setupFlagString(set, "host")

c := cli.NewContext(app, set, nil)
_ = set.Parse([]string{"--host=orion"})
err := entityUpsert(c)

if assert.Error(t, err) {
ngsiErr := err.(*ngsiCmdError)
assert.Equal(t, 3, ngsiErr.ErrNo)
assert.Equal(t, "data is empty", ngsiErr.Message)
} else {
t.FailNow()
}
}

func TestEntityUpsertErrorSafeString(t *testing.T) {
ngsi, set, app, _ := setupTest()

setupAddBroker(t, ngsi, "orion", "https://orion", "v2")

reqRes := MockHTTPReqRes{}
reqRes.Res.StatusCode = http.StatusOK
reqRes.Path = "/ngsi-ld/v1/entities"
reqRes.ResHeader = http.Header{"Ngsild-Results-Count": []string{"8"}}
mock := NewMockHTTP()
mock.ReqRes = append(mock.ReqRes, reqRes)
ngsi.HTTP = mock
setupFlagString(set, "host,data,safeString")
ngsi.JSONConverter = &MockJSONLib{EncodeErr: errors.New("json error"), DecodeErr: errors.New("json error")}

c := cli.NewContext(app, set, nil)
_ = set.Parse([]string{"--host=orion", "--data={}", "--safeString=on"})
err := entityUpsert(c)

if assert.Error(t, err) {
ngsiErr := err.(*ngsiCmdError)
assert.Equal(t, 4, ngsiErr.ErrNo)
assert.Equal(t, "json error", ngsiErr.Message)
} else {
t.FailNow()
}
}

func TestEntityUpsertErrorHTTP(t *testing.T) {
ngsi, set, app, _ := setupTest()

setupAddBroker(t, ngsi, "orion", "https://orion", "v2")

reqRes := MockHTTPReqRes{}
reqRes.Res.StatusCode = http.StatusOK
reqRes.Path = "/ngsi-ld/v1/entities"
reqRes.ResHeader = http.Header{"Ngsild-Results-Count": []string{"8"}}
mock := NewMockHTTP()
mock.ReqRes = append(mock.ReqRes, reqRes)
ngsi.HTTP = mock
setupFlagString(set, "host,data")

c := cli.NewContext(app, set, nil)
_ = set.Parse([]string{"--host=orion", "--data={}"})
err := entityUpsert(c)

if assert.Error(t, err) {
ngsiErr := err.(*ngsiCmdError)
assert.Equal(t, 5, ngsiErr.ErrNo)
assert.Equal(t, "url error", ngsiErr.Message)
} else {
t.FailNow()
}
}

func TestEntityUpsertErrorHTTPStatus(t *testing.T) {
ngsi, set, app, _ := setupTest()

setupAddBroker(t, ngsi, "orion", "https://orion", "v2")

reqRes := MockHTTPReqRes{}
reqRes.Res.StatusCode = http.StatusBadRequest
reqRes.Path = "/v2/entities"
mock := NewMockHTTP()
mock.ReqRes = append(mock.ReqRes, reqRes)
ngsi.HTTP = mock
setupFlagString(set, "host,data")

c := cli.NewContext(app, set, nil)
_ = set.Parse([]string{"--host=orion", "--data={}"})
err := entityUpsert(c)

if assert.Error(t, err) {
ngsiErr := err.(*ngsiCmdError)
assert.Equal(t, 6, ngsiErr.ErrNo)
} else {
t.FailNow()
}
}

func TestEntityDeleteV2(t *testing.T) {
ngsi, set, app, _ := setupTest()

Expand Down
23 changes: 18 additions & 5 deletions internal/ngsicmd/ngsi.go
Expand Up @@ -171,7 +171,7 @@ var copyCmd = cli.Command{

var countCmd = cli.Command{
Name: "wc",
Usage: "print number of entities, subscriptions, registrations, or types",
Usage: "print number of entities, subscriptions, registrations or types",
Category: "CONVENIENCE",
Flags: []cli.Flag{
hostFlag,
Expand Down Expand Up @@ -684,7 +684,7 @@ var createCmd = cli.Command{

var deleteCmd = cli.Command{
Name: "delete",
Usage: "delete entity(ies), attribute, subscription, or registration",
Usage: "delete entity(ies), attribute, subscription or registration",
Category: "NGSI",
Flags: []cli.Flag{
hostFlag,
Expand Down Expand Up @@ -755,7 +755,7 @@ var deleteCmd = cli.Command{

var getCmd = cli.Command{
Name: "get",
Usage: "get entity(ies), attribute(s), subscription, registration, or type",
Usage: "get entity(ies), attribute(s), subscription, registration or type",
Category: "NGSI",
Flags: []cli.Flag{
hostFlag,
Expand Down Expand Up @@ -869,7 +869,7 @@ var getCmd = cli.Command{

var listCmd = cli.Command{
Name: "list",
Usage: "list types, entities, subscriptions, or registrations",
Usage: "list types, entities, subscriptions or registrations",
Category: "NGSI",
Flags: []cli.Flag{
hostFlag,
Expand Down Expand Up @@ -992,7 +992,7 @@ var replaceCmd = cli.Command{

var updateCmd = cli.Command{
Name: "update",
Usage: "update entities, attribute(s), or subscription",
Usage: "update entities, attribute(s) or subscription",
Category: "NGSI",
Flags: []cli.Flag{
hostFlag,
Expand Down Expand Up @@ -1095,6 +1095,19 @@ var upsertCmd = cli.Command{
scopeFlag,
},
Subcommands: []*cli.Command{
{
Name: "entity",
Usage: "upsert entity",
Flags: []cli.Flag{
dataFlag,
keyValuesFlag,
linkFlag,
safeStringFlag,
},
Action: func(c *cli.Context) error {
return entityUpsert(c)
},
},
{
Name: "entities",
Usage: "upsert entities",
Expand Down
1 change: 1 addition & 0 deletions internal/ngsicmd/ngsi_test.go
Expand Up @@ -102,6 +102,7 @@ func TestNGSICommand(t *testing.T) {
{args: []string{"update", "attrs", "--id", "abc"}, rc: 1},
{args: []string{"update", "attr", "--id", "abc", "--attrName", "abc"}, rc: 1},
{args: []string{"update", "subscription", "--id", "abc", "--url", "abc"}, rc: 1},
{args: []string{"upsert", "entity"}, rc: 1},
{args: []string{"upsert", "entities"}, rc: 1},
{args: []string{"token"}, rc: 1},
{args: []string{"debug"}, rc: 1},
Expand Down

0 comments on commit e4b799e

Please sign in to comment.