Skip to content
This repository has been archived by the owner on Dec 12, 2024. It is now read-only.

SIP 2 - Create Credential Manifests CRUD #91

Merged
merged 5 commits into from
Sep 9, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ type ServicesConfig struct {
SchemaConfig SchemaServiceConfig `toml:"schema,omitempty"`
CredentialConfig CredentialServiceConfig `toml:"credential,omitempty"`
KeyStoreConfig KeyStoreServiceConfig `toml:"keystore,omitempty"`
ManifestConfig ManifestServiceConfig `toml:"manifest,omitempty"`
}

// BaseServiceConfig represents configurable properties for a specific component of the SSI Service
Expand Down Expand Up @@ -88,6 +89,10 @@ type CredentialServiceConfig struct {
// TODO(gabe) supported key and signature types
}

type ManifestServiceConfig struct {
*BaseServiceConfig
}

type KeyStoreServiceConfig struct {
*BaseServiceConfig
// Service key password. Used by a KDF whose key is used by a symmetric cypher for key encryption.
Expand Down
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ require (

require go.etcd.io/bbolt v1.3.6

require github.com/oliveagle/jsonpath v0.0.0-20180606110733-2e52cf6e6852 // indirect
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see where I added this or used this? When I try to remove it brings it back when I mage build


require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect
Expand Down
1 change: 1 addition & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ github.com/multiformats/go-multicodec v0.5.0 h1:EgU6cBe/D7WRwQb1KmnBvU7lrcFGMggZ
github.com/multiformats/go-multicodec v0.5.0/go.mod h1:DiY2HFaEp5EhEXb/iYzVAunmyX/aSFMxq2KMKfWEues=
github.com/multiformats/go-varint v0.0.6 h1:gk85QWKxh3TazbLxED/NlDVv8+q+ReFJk7Y2W/KhfNY=
github.com/multiformats/go-varint v0.0.6/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE=
github.com/oliveagle/jsonpath v0.0.0-20180606110733-2e52cf6e6852 h1:Yl0tPBa8QPjGmesFh1D0rDy+q1Twx6FyU7VWHi8wZbI=
github.com/oliveagle/jsonpath v0.0.0-20180606110733-2e52cf6e6852/go.mod h1:eqOVx5Vwu4gd2mmMZvVZsgIqNSaW3xxRThUJ0k/TPk4=
github.com/piprate/json-gold v0.4.1 h1:JYbYN36n6YcAYipKy3ttv3X2HDQPeqWqmwta35NPj04=
github.com/piprate/json-gold v0.4.1/go.mod h1:OK1z7UgtBZk06n2cDE2OSq1kffmjFFp5/2yhLLCz9UM=
Expand Down
184 changes: 184 additions & 0 deletions pkg/server/router/manifest.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
package router

import (
"context"
"fmt"
"github.com/TBD54566975/ssi-sdk/credential/exchange"
manifestsdk "github.com/TBD54566975/ssi-sdk/credential/manifest"
"github.com/tbd54566975/ssi-service/pkg/service/manifest"
"net/http"

"github.com/pkg/errors"
"github.com/sirupsen/logrus"

"github.com/tbd54566975/ssi-service/pkg/server/framework"
svcframework "github.com/tbd54566975/ssi-service/pkg/service/framework"
)

type ManifestRouter struct {
service *manifest.Service
}

func NewManifestRouter(s svcframework.Service) (*ManifestRouter, error) {
if s == nil {
return nil, errors.New("service cannot be nil")
}
credService, ok := s.(*manifest.Service)
if !ok {
return nil, fmt.Errorf("could not create manifest router with service type: %s", s.Type())
}
return &ManifestRouter{
service: credService,
}, nil
}

type CreateManifestRequest struct {
Issuer string `json:"issuer" validate:"required"`
// A context is optional. If not present, we'll apply default, required context values.
Context string `json:"@context"`
OutputDescriptors []manifestsdk.OutputDescriptor `json:"outputDescriptors" validate:"required"`
PresentationDefinition exchange.PresentationDefinition `json:"presentationDefinition" validate:"required"`
}

func (c CreateManifestRequest) ToServiceRequest() manifest.CreateManifestRequest {
return manifest.CreateManifestRequest{
Issuer: c.Issuer,
Context: c.Context,
OutputDescriptors: c.OutputDescriptors,
PresentationDefinition: c.PresentationDefinition,
}
}

type CreateManifestResponse struct {
Manifest manifestsdk.CredentialManifest `json:"manifest"`
}

// CreateManifest godoc
// @Summary Create manifest
// @Description Create manifest
// @Tags ManifestAPI
// @Accept json
// @Produce json
// @Param request body CreateManifestRequest true "request body"
// @Success 201 {object} CreateManifestResponse
// @Failure 400 {string} string "Bad request"
// @Failure 500 {string} string "Internal server error"
// @Router /v1/manifests [put]
func (mr ManifestRouter) CreateManifest(ctx context.Context, w http.ResponseWriter, r *http.Request) error {
var request CreateManifestRequest
if err := framework.Decode(r, &request); err != nil {
errMsg := "invalid create manifest request"
logrus.WithError(err).Error(errMsg)
return framework.NewRequestError(errors.Wrap(err, errMsg), http.StatusBadRequest)
}

req := request.ToServiceRequest()
createManifestResponse, err := mr.service.CreateManifest(req)
if err != nil {
errMsg := "could not create manifest"
logrus.WithError(err).Error(errMsg)
return framework.NewRequestError(errors.Wrap(err, errMsg), http.StatusInternalServerError)
nitro-neal marked this conversation as resolved.
Show resolved Hide resolved
}

resp := CreateManifestResponse{Manifest: createManifestResponse.Manifest}

return framework.Respond(ctx, w, resp, http.StatusCreated)
}

type GetManifestResponse struct {
ID string `json:"id"`
Manifest manifestsdk.CredentialManifest `json:"manifest"`
}

// GetManifest godoc
// @Summary Get manifest
// @Description Get manifest by id
// @Tags ManifestAPI
// @Accept json
// @Produce json
// @Param id path string true "ID"
// @Success 200 {object} GetManifestResponse
// @Failure 400 {string} string "Bad request"
// @Router /v1/manifests/{id} [get]
func (mr ManifestRouter) GetManifest(ctx context.Context, w http.ResponseWriter, r *http.Request) error {
id := framework.GetParam(ctx, IDParam)
if id == nil {
errMsg := "cannot get manifest without ID parameter"
logrus.Error(errMsg)
return framework.NewRequestErrorMsg(errMsg, http.StatusBadRequest)
}

gotManifest, err := mr.service.GetManifest(manifest.GetManifestRequest{ID: *id})
if err != nil {
errMsg := fmt.Sprintf("could not get manifest with id: %s", *id)
logrus.WithError(err).Error(errMsg)
return framework.NewRequestError(errors.Wrap(err, errMsg), http.StatusBadRequest)
}

resp := GetManifestResponse{
ID: gotManifest.Manifest.ID,
Manifest: gotManifest.Manifest,
}
return framework.Respond(ctx, w, resp, http.StatusOK)
}

type GetManifestsResponse struct {
Manifests []manifestsdk.CredentialManifest `json:"manifests"`
}

// GetManifests godoc
// @Summary Get manifests
// @Description Checks for the presence of a query parameter and calls the associated filtered get method
// @Tags ManifestAPI
// @Accept json
// @Produce json
// @Param issuer query string false "string issuer"
// @Param schema query string false "string schema"
// @Param subject query string false "string subject"
// @Success 200 {object} GetManifestsResponse
// @Failure 400 {string} string "Bad request"
// @Failure 500 {string} string "Internal server error"
// @Router /v1/manifests [get]
func (mr ManifestRouter) GetManifests(ctx context.Context, w http.ResponseWriter, r *http.Request) error {
gotManifests, err := mr.service.GetManifests()

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

where do you handle the query parameters?

if err != nil {
errMsg := fmt.Sprintf("could not get manifests")
logrus.WithError(err).Error(errMsg)
return framework.NewRequestError(errors.Wrap(err, errMsg), http.StatusBadRequest)
}

resp := GetManifestsResponse{
Manifests: gotManifests.Manifests,
}

return framework.Respond(ctx, w, resp, http.StatusOK)
}

// DeleteManifest godoc
// @Summary Delete manifests
// @Description Delete manifest by ID
// @Tags ManifestAPI
// @Accept json
// @Produce json
// @Param id path string true "ID"
// @Success 200 {string} string "OK"
// @Failure 400 {string} string "Bad request"
// @Failure 500 {string} string "Internal server error"
// @Router /v1/manifests/{id} [delete]
func (mr ManifestRouter) DeleteManifest(ctx context.Context, w http.ResponseWriter, r *http.Request) error {
id := framework.GetParam(ctx, IDParam)
if id == nil {
errMsg := "cannot delete manifest without ID parameter"
logrus.Error(errMsg)
return framework.NewRequestErrorMsg(errMsg, http.StatusBadRequest)
}

if err := mr.service.DeleteManifest(manifest.DeleteManifestRequest{ID: *id}); err != nil {
errMsg := fmt.Sprintf("could not delete manifest with id: %s", *id)
logrus.WithError(err).Error(errMsg)
return framework.NewRequestError(errors.Wrap(err, errMsg), http.StatusInternalServerError)
}

return framework.Respond(ctx, w, nil, http.StatusOK)
}
95 changes: 95 additions & 0 deletions pkg/server/router/manifest_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package router

import (
"github.com/TBD54566975/ssi-sdk/credential/exchange"
manifestsdk "github.com/TBD54566975/ssi-sdk/credential/manifest"
"github.com/stretchr/testify/assert"
"github.com/tbd54566975/ssi-service/config"
"github.com/tbd54566975/ssi-service/pkg/service/framework"
"github.com/tbd54566975/ssi-service/pkg/service/manifest"
"github.com/tbd54566975/ssi-service/pkg/storage"
"os"
"testing"
)

func TestManifestRouter(t *testing.T) {
// remove the db file after the test
t.Cleanup(func() {
_ = os.Remove(storage.DBFile)
})

t.Run("Nil Service", func(tt *testing.T) {
manifestRouter, err := NewManifestRouter(nil)
assert.Error(tt, err)
assert.Empty(tt, manifestRouter)
assert.Contains(tt, err.Error(), "service cannot be nil")
})

t.Run("Bad Service", func(tt *testing.T) {
manifestRouter, err := NewManifestRouter(&testService{})
assert.Error(tt, err)
assert.Empty(tt, manifestRouter)
assert.Contains(tt, err.Error(), "could not create manifest router with service type: test")
})

t.Run("Manifest Service Test", func(tt *testing.T) {
bolt, err := storage.NewBoltDB()
assert.NoError(tt, err)
assert.NotEmpty(tt, bolt)

serviceConfig := config.ManifestServiceConfig{BaseServiceConfig: &config.BaseServiceConfig{Name: "manifest"}}
manifestService, err := manifest.NewManifestService(serviceConfig, bolt)
assert.NoError(tt, err)
assert.NotEmpty(tt, manifestService)

// check type and status
assert.Equal(tt, framework.Manifest, manifestService.Type())
assert.Equal(tt, framework.StatusReady, manifestService.Status().Status)

// good request
createManifestRequest := getValidManifestRequest()

createdManifest, err := manifestService.CreateManifest(createManifestRequest)
assert.NoError(tt, err)
assert.NotEmpty(tt, createdManifest)
assert.NotEmpty(tt, createdManifest.Manifest)
})
}

func getValidManifestRequest() manifest.CreateManifestRequest {
createManifestRequest := manifest.CreateManifestRequest{
Issuer: "did:abc:123",
Context: "context123",
PresentationDefinition: exchange.PresentationDefinition{
ID: "pres-def-id",
InputDescriptors: []exchange.InputDescriptor{
{
ID: "test-id",
Constraints: &exchange.Constraints{
Fields: []exchange.Field{
{
Path: []string{".vc.id"},
},
},
},
},
},
},
OutputDescriptors: []manifestsdk.OutputDescriptor{
{
ID: "id1",
Schema: "https://test.com/schema",
Name: "good ID",
Description: "it's all good",
},
{
ID: "id2",
Schema: "https://test.com/schema",
Name: "good ID",
Description: "it's all good",
},
},
}

return createManifestRequest
}
1 change: 1 addition & 0 deletions pkg/server/router/router_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,6 @@ func (s *testService) Config() config.ServicesConfig {
SchemaConfig: config.SchemaServiceConfig{},
CredentialConfig: config.CredentialServiceConfig{},
KeyStoreConfig: config.KeyStoreServiceConfig{},
ManifestConfig: config.ManifestServiceConfig{},
}
}
6 changes: 3 additions & 3 deletions pkg/server/router/schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ type GetSchemaResponse struct {
Schema schemalib.VCJSONSchema `json:"schema,omitempty"`
}

// GetSchemaByID godoc
// GetSchema godoc
// @Summary Get Schema
// @Description Get schema by ID
// @Tags SchemaAPI
Expand All @@ -110,7 +110,7 @@ type GetSchemaResponse struct {
// @Success 200 {object} GetSchemaResponse
// @Failure 400 {string} string "Bad request"
// @Router /v1/schemas/{id} [get]
func (sr SchemaRouter) GetSchemaByID(ctx context.Context, w http.ResponseWriter, r *http.Request) error {
func (sr SchemaRouter) GetSchema(ctx context.Context, w http.ResponseWriter, r *http.Request) error {
id := framework.GetParam(ctx, IDParam)
if id == nil {
errMsg := "cannot get schema without ID parameter"
Expand All @@ -119,7 +119,7 @@ func (sr SchemaRouter) GetSchemaByID(ctx context.Context, w http.ResponseWriter,
}

// TODO(gabe) differentiate between internal errors and not found schemas
gotSchema, err := sr.service.GetSchemaByID(schema.GetSchemaByIDRequest{ID: *id})
gotSchema, err := sr.service.GetSchema(schema.GetSchemaRequest{ID: *id})
if err != nil {
errMsg := fmt.Sprintf("could not get schema with id: %s", *id)
logrus.WithError(err).Error(errMsg)
Expand Down
4 changes: 2 additions & 2 deletions pkg/server/router/schema_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ func TestSchemaRouter(t *testing.T) {
assert.Equal(tt, 0, len(gotSchemas.Schemas))

// get schema that doesn't exist
_, err = schemaService.GetSchemaByID(schema.GetSchemaByIDRequest{ID: "bad"})
_, err = schemaService.GetSchema(schema.GetSchemaRequest{ID: "bad"})
assert.Error(tt, err)
assert.Contains(tt, err.Error(), "error getting schema")

Expand All @@ -75,7 +75,7 @@ func TestSchemaRouter(t *testing.T) {
assert.Equal(tt, "simple schema", createdSchema.Schema.Name)

// get schema by ID
gotSchema, err := schemaService.GetSchemaByID(schema.GetSchemaByIDRequest{ID: createdSchema.ID})
gotSchema, err := schemaService.GetSchema(schema.GetSchemaRequest{ID: createdSchema.ID})
assert.NoError(tt, err)
assert.NotEmpty(tt, gotSchema)
assert.EqualValues(tt, createdSchema.Schema, gotSchema.Schema)
Expand Down
Loading