Skip to content

Commit

Permalink
feat: add API to batch insert identities
Browse files Browse the repository at this point in the history
  • Loading branch information
hperl committed Mar 23, 2023
1 parent 756bed4 commit 4f852a6
Show file tree
Hide file tree
Showing 30 changed files with 2,640 additions and 177 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ jobs:
GOGC: 100
with:
args: --timeout 10m0s
version: v1.49.0
version: v1.50.1
skip-go-installation: true
skip-pkg-cache: true
- name: Build Kratos
Expand Down
111 changes: 95 additions & 16 deletions identity/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,11 @@ import (
"github.com/ory/kratos/driver/config"
)

const RouteCollection = "/identities"
const RouteItem = RouteCollection + "/:id"
const RouteCredentialItem = RouteItem + "/credentials/:type"
const (
RouteCollection = "/identities"
RouteItem = RouteCollection + "/:id"
RouteCredentialItem = RouteItem + "/credentials/:type"
)

type (
handlerDependencies interface {
Expand Down Expand Up @@ -98,6 +100,7 @@ func (h *Handler) RegisterAdminRoutes(admin *x.RouterAdmin) {
admin.PATCH(RouteItem, h.patch)

admin.POST(RouteCollection, h.create)
admin.PATCH(RouteCollection, h.batchPatchIdentities)
admin.PUT(RouteItem, h.update)

admin.DELETE(RouteCredentialItem, h.deleteIdentityCredentials)
Expand Down Expand Up @@ -404,12 +407,33 @@ func (h *Handler) create(w http.ResponseWriter, r *http.Request, _ httprouter.Pa
return
}

i, err := h.identityFromCreateIdentityBody(r.Context(), &cr)
if err != nil {
h.r.Writer().WriteError(w, r, err)
return
}

if err := h.r.IdentityManager().Create(r.Context(), i); err != nil {
h.r.Writer().WriteError(w, r, err)
return
}

h.r.Writer().WriteCreated(w, r,
urlx.AppendPaths(
h.r.Config().SelfAdminURL(r.Context()),
"identities",
i.ID.String(),
).String(),
WithCredentialsMetadataAndAdminMetadataInJSON(*i),
)
}

func (h *Handler) identityFromCreateIdentityBody(ctx context.Context, cr *CreateIdentityBody) (*Identity, error) {
stateChangedAt := sqlxx.NullTime(time.Now())
state := StateActive
if cr.State != "" {
if err := cr.State.IsValid(); err != nil {
h.r.Writer().WriteError(w, r, errors.WithStack(herodot.ErrBadRequest.WithReasonf("%s", err).WithWrap(err)))
return
return nil, errors.WithStack(herodot.ErrBadRequest.WithReasonf("%s", err).WithWrap(err))
}
state = cr.State
}
Expand All @@ -425,24 +449,79 @@ func (h *Handler) create(w http.ResponseWriter, r *http.Request, _ httprouter.Pa
MetadataPublic: []byte(cr.MetadataPublic),
}

if err := h.importCredentials(r.Context(), i, cr.Credentials); err != nil {
h.r.Writer().WriteError(w, r, err)
if err := h.importCredentials(ctx, i, cr.Credentials); err != nil {
return nil, err
}

return i, nil
}

// swagger:route PATCH /admin/identities identity batchPatchIdentities
//
// # Create and deletes multiple identities
//
// Creates or delete multiple
// [identities](https://www.ory.sh/docs/kratos/concepts/identity-user-model).
// This endpoint can also be used to [import
// credentials](https://www.ory.sh/docs/kratos/manage-identities/import-user-accounts-identities)
// for instance passwords, social sign in configurations or multifactor methods.
//
// Consumes:
// - application/json
//
// Produces:
// - application/json
//
// Schemes: http, https
//
// Security:
// oryAccessToken:
//
// Responses:
// 200: batchPatchIdentitiesResponse
// 400: errorGeneric
// 409: errorGeneric
// default: errorGeneric
func (h *Handler) batchPatchIdentities(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
var (
req BatchPatchIdentitiesBody
res batchPatchIdentitiesResponse
)
if err := jsonx.NewStrictDecoder(r.Body).Decode(&req); err != nil {
h.r.Writer().WriteErrorCode(w, r, http.StatusBadRequest, errors.WithStack(err))
return
}

if err := h.r.IdentityManager().Create(r.Context(), i); err != nil {
res.Identities = make([]*BatchIdentityPatchResponse, len(req.Identities))
// Array to look up the index of the identity in the identities array.
indexInIdentities := make([]int, len(req.Identities))
identities := make([]*Identity, 0, len(req.Identities))

for i, patch := range req.Identities {
if patch.Create != nil {
res.Identities[i] = &BatchIdentityPatchResponse{
Action: ActionCreate,
PatchID: patch.ID,
}
identity, err := h.identityFromCreateIdentityBody(r.Context(), patch.Create)
if err != nil {
h.r.Writer().WriteError(w, r, err)
return
}
identities = append(identities, identity)
indexInIdentities[i] = len(identities) - 1
}
}

if err := h.r.IdentityManager().CreateIdentities(r.Context(), identities); err != nil {
h.r.Writer().WriteError(w, r, err)
return
}
for resIdx, identitiesIdx := range indexInIdentities {
res.Identities[resIdx].IdentityID = &identities[identitiesIdx].ID
}

h.r.Writer().WriteCreated(w, r,
urlx.AppendPaths(
h.r.Config().SelfAdminURL(r.Context()),
"identities",
i.ID.String(),
).String(),
WithCredentialsMetadataAndAdminMetadataInJSON(*i),
)
h.r.Writer().Write(w, r, &res)
}

// Update Identity Parameters
Expand Down
Loading

0 comments on commit 4f852a6

Please sign in to comment.