Skip to content

Commit

Permalink
feat: allow import/validation of arrays
Browse files Browse the repository at this point in the history
  • Loading branch information
zepatrik committed Sep 28, 2020
1 parent a5f46d2 commit d11ac32
Show file tree
Hide file tree
Showing 9 changed files with 134 additions and 125 deletions.
44 changes: 0 additions & 44 deletions cmd/cliclient/identity.go

This file was deleted.

8 changes: 2 additions & 6 deletions cmd/identities/delete_test.go
Expand Up @@ -11,7 +11,6 @@ import (

"github.com/ory/kratos/driver/configuration"
"github.com/ory/kratos/identity"
"github.com/ory/kratos/internal/clihelpers"
"github.com/ory/kratos/x"
"github.com/ory/x/sqlcon"
)
Expand Down Expand Up @@ -48,11 +47,8 @@ func TestDeleteCmd(t *testing.T) {
})

t.Run("case=fails with unknown ID", func(t *testing.T) {
stdOut, stdErr, err := exec(deleteCmd, nil, x.NewUUID().String())
require.True(t, errors.Is(err, clihelpers.NoPrintButFailError))
stdErr := execErr(t, deleteCmd, x.NewUUID().String())

// expect ID and no error
assert.Len(t, stdOut, 0, stdErr)
assert.Contains(t, stdErr, "[DELETE /identities/{id}][404] deleteIdentityNotFound", stdOut)
assert.Contains(t, stdErr, "[DELETE /identities/{id}][404] deleteIdentityNotFound", stdErr)
})
}
8 changes: 2 additions & 6 deletions cmd/identities/get_test.go
Expand Up @@ -3,10 +3,8 @@ package identities
import (
"context"
"encoding/json"
"errors"
"testing"

"github.com/ory/kratos/internal/clihelpers"
"github.com/ory/kratos/x"

"github.com/stretchr/testify/assert"
Expand Down Expand Up @@ -43,10 +41,8 @@ func TestGetCmd(t *testing.T) {
})

t.Run("case=fails with unknown ID", func(t *testing.T) {
stdOut, stdErr, err := exec(getCmd, nil, x.NewUUID().String())
require.True(t, errors.Is(err, clihelpers.NoPrintButFailError))
stdErr := execErr(t, getCmd, x.NewUUID().String())

assert.Len(t, stdOut, 0, stdErr)
assert.Contains(t, stdErr, "status 404", stdOut)
assert.Contains(t, stdErr, "status 404", stdErr)
})
}
43 changes: 42 additions & 1 deletion cmd/identities/definitions_test.go → cmd/identities/helpers.go
Expand Up @@ -3,8 +3,11 @@ package identities
import (
"bytes"
"context"
"errors"
"fmt"
"github.com/pkg/errors"
"github.com/tidwall/gjson"
"io"
"io/ioutil"
"testing"

"github.com/ory/kratos/identity"
Expand All @@ -21,6 +24,44 @@ import (
"github.com/ory/viper"
)

func parseIdentities(raw []byte) (rawIdentities []string) {
res := gjson.ParseBytes(raw)
if !res.IsArray() {
return []string{res.Raw}
}
res.ForEach(func(_, v gjson.Result) bool {
rawIdentities = append(rawIdentities, v.Raw)
return true
})
return
}

func readIdentities(cmd *cobra.Command, args []string) (map[string]string, error) {
rawIdentities := make(map[string]string)
if len(args) == 0 {
fc, err := ioutil.ReadAll(cmd.InOrStdin())
if err != nil {
_, _ = fmt.Fprintf(cmd.ErrOrStderr(), "STD_IN: Could not read: %s\n", err)
return nil, clihelpers.FailSilently(cmd)
}
for i, id := range parseIdentities(fc) {
rawIdentities[fmt.Sprintf("STD_IN[%d]", i)] = id
}
return rawIdentities, nil
}
for _, fn := range args {
fc, err := ioutil.ReadFile(fn)
if err != nil {
_, _ = fmt.Fprintf(cmd.ErrOrStderr(), "%s: Could not open identity file: %s\n", fn, err)
return nil, clihelpers.FailSilently(cmd)
}
for i, id := range parseIdentities(fc) {
rawIdentities[fmt.Sprintf("%s[%d]", fn, i)] = id
}
}
return rawIdentities, nil
}

func setup(t *testing.T, cmd *cobra.Command) driver.Registry {
_, reg := internal.NewRegistryDefaultWithDSN(t, configuration.DefaultSQLiteMemoryDSN)
_, admin := testhelpers.NewKratosServerWithCSRF(t, reg)
Expand Down
50 changes: 9 additions & 41 deletions cmd/identities/import.go
@@ -1,13 +1,9 @@
package identities

import (
"bytes"
"context"
"encoding/json"
"fmt"
"io/ioutil"
"time"

"github.com/spf13/cobra"

"github.com/ory/kratos/internal/clihelpers"
Expand All @@ -27,7 +23,7 @@ var importCmd = &cobra.Command{
cat file.json | kratos identities import
Files are expected to each contain a single identity. The validity of files can be tested beforehand using "... identities validate".
Files can contain only a single or an array of identities. The validity of files can be tested beforehand using "... identities validate".
WARNING: Importing credentials is not yet supported.`,
RunE: func(cmd *cobra.Command, args []string) error {
Expand All @@ -36,20 +32,19 @@ WARNING: Importing credentials is not yet supported.`,
imported := make([]*models.Identity, 0, len(args))
failed := make(map[string]error)

if len(args) == 0 {
fc, err := ioutil.ReadAll(cmd.InOrStdin())
if err != nil {
_, _ = fmt.Fprintf(cmd.ErrOrStderr(), "STD_IN: Could not read: %s\n", err)
return clihelpers.FailSilently(cmd)
}
is, err := readIdentities(cmd, args)
if err != nil {
return err
}

err = validateIdentity(cmd, "STD_IN", fc, c.Common.GetSchema)
for src, i := range is {
err = validateIdentity(cmd, src, i, c.Common.GetSchema)
if err != nil {
return err
}

var params models.CreateIdentity
err = json.NewDecoder(bytes.NewBuffer(fc)).Decode(&params)
err = json.Unmarshal([]byte(i), &params)
if err != nil {
_, _ = fmt.Fprintln(cmd.ErrOrStderr(), "STD_IN: Could not parse identity")
return clihelpers.FailSilently(cmd)
Expand All @@ -59,36 +54,9 @@ WARNING: Importing credentials is not yet supported.`,
Body: &params,
Context: context.Background(),
})

if err != nil {
failed["STD_IN"] = err
failed[src] = err
} else {
imported = append(imported, resp.Payload)
}
} else {
for _, fn := range args {
fc, err := validateIdentityFile(cmd, fn, c)
if err != nil {
return err
}

var params models.CreateIdentity
err = json.Unmarshal(fc, &params)
if err != nil {
_, _ = fmt.Fprintf(cmd.ErrOrStderr(), "%s: Could not parse identity file\n", fn)
return clihelpers.FailSilently(cmd)
}

resp, err := c.Admin.CreateIdentity(
admin.NewCreateIdentityParams().
WithBody(&params).
WithTimeout(time.Second))

if err != nil {
failed[fn] = err
continue
}

imported = append(imported, resp.Payload)
}
}
Expand Down
64 changes: 62 additions & 2 deletions cmd/identities/import_test.go
Expand Up @@ -43,7 +43,67 @@ func TestImportCmd(t *testing.T) {
assert.NoError(t, err)
})

t.Run("case=imports a new identity from stdIn", func(t *testing.T) {
t.Run("case=imports multiple identities from single file", func(t *testing.T) {
i := []models.CreateIdentity{
{
SchemaID: pointerx.String(configuration.DefaultIdentityTraitsSchemaID),
Traits: map[string]interface{}{},
},
{
SchemaID: pointerx.String(configuration.DefaultIdentityTraitsSchemaID),
Traits: map[string]interface{}{},
},
}
ij, err := json.Marshal(i)
require.NoError(t, err)
f, err := ioutil.TempFile("", "")
require.NoError(t, err)
_, err = f.Write(ij)
require.NoError(t, err)
require.NoError(t, f.Close())

stdOut := execNoErr(t, importCmd, f.Name())

id, err := uuid.FromString(gjson.Get(stdOut, "0.id").String())
require.NoError(t, err)
_, err = reg.Persister().GetIdentity(context.Background(), id)
assert.NoError(t, err)

id, err = uuid.FromString(gjson.Get(stdOut, "1.id").String())
require.NoError(t, err)
_, err = reg.Persister().GetIdentity(context.Background(), id)
assert.NoError(t, err)
})

t.Run("case=imports a new identity from STD_IN", func(t *testing.T) {
i := []models.CreateIdentity{
{
SchemaID: pointerx.String(configuration.DefaultIdentityTraitsSchemaID),
Traits: map[string]interface{}{},
},
{
SchemaID: pointerx.String(configuration.DefaultIdentityTraitsSchemaID),
Traits: map[string]interface{}{},
},
}
ij, err := json.Marshal(i)
require.NoError(t, err)

stdOut, stdErr, err := exec(importCmd, bytes.NewBuffer(ij))
require.NoError(t, err, stdOut, stdErr)

id, err := uuid.FromString(gjson.Get(stdOut, "0.id").String())
require.NoError(t, err)
_, err = reg.Persister().GetIdentity(context.Background(), id)
assert.NoError(t, err)

id, err = uuid.FromString(gjson.Get(stdOut, "1.id").String())
require.NoError(t, err)
_, err = reg.Persister().GetIdentity(context.Background(), id)
assert.NoError(t, err)
})

t.Run("case=imports multiple identities from STD_IN", func(t *testing.T) {
i := models.CreateIdentity{
SchemaID: pointerx.String(configuration.DefaultIdentityTraitsSchemaID),
Traits: map[string]interface{}{},
Expand All @@ -64,7 +124,7 @@ func TestImportCmd(t *testing.T) {
// validation is further tested with the validate command
stdOut, stdErr, err := exec(importCmd, bytes.NewBufferString("{}"))
assert.True(t, errors.Is(err, clihelpers.NoPrintButFailError))
assert.Contains(t, stdErr, "STD_IN: not valid")
assert.Contains(t, stdErr, "STD_IN[0]: not valid")
assert.Len(t, stdOut, 0)
})
}
2 changes: 0 additions & 2 deletions cmd/identities/list_test.go
Expand Up @@ -40,8 +40,6 @@ func TestListCmd(t *testing.T) {
stdoutP1 := execNoErr(t, listCmd, "1", "3")
stdoutP2 := execNoErr(t, listCmd, "2", "3")

t.Logf(stdoutP1, stdoutP2, strings.Join(ids, "\n"))

for _, id := range ids {
// exactly one of page 1 and 2 should contain the id
assert.True(t, strings.Contains(stdoutP1, id) != strings.Contains(stdoutP2, id))
Expand Down

0 comments on commit d11ac32

Please sign in to comment.