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

Adding credential application CRUD and validation #94

Merged
merged 10 commits into from
Sep 19, 2022
154 changes: 152 additions & 2 deletions pkg/server/router/manifest.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import (
"context"
"fmt"
"github.com/TBD54566975/ssi-sdk/credential/exchange"
exchangensdk "github.com/TBD54566975/ssi-sdk/credential/exchange"
applicationsdk "github.com/TBD54566975/ssi-sdk/credential/manifest"
manifestsdk "github.com/TBD54566975/ssi-sdk/credential/manifest"
"github.com/tbd54566975/ssi-service/pkg/service/manifest"
"net/http"
Expand All @@ -23,15 +25,16 @@ func NewManifestRouter(s svcframework.Service) (*ManifestRouter, error) {
if s == nil {
return nil, errors.New("service cannot be nil")
}
credService, ok := s.(*manifest.Service)
manifestService, 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,
service: manifestService,
}, nil
}

// Manifest
type CreateManifestRequest struct {
Issuer string `json:"issuer" validate:"required"`
// A context is optional. If not present, we'll apply default, required context values.
Expand Down Expand Up @@ -182,3 +185,150 @@ func (mr ManifestRouter) DeleteManifest(ctx context.Context, w http.ResponseWrit

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

// Application
type CreateApplicationRequest struct {
ManifestID string `json:"manifestId" validate:"required"`
nitro-neal marked this conversation as resolved.
Show resolved Hide resolved
PresentationSubmission exchangensdk.PresentationSubmission `json:"presentationSubmission" validate:"required"`
}

func (c CreateApplicationRequest) ToServiceRequest() manifest.CreateApplicationRequest {
return manifest.CreateApplicationRequest{
PresentationSubmission: c.PresentationSubmission,
ManifestID: c.ManifestID,
}
}

type CreateApplicationResponse struct {
Application applicationsdk.CredentialApplication `json:"application"`
}

// CreateApplication godoc
// @Summary Create application
// @Description Create application
// @Tags ApplicationAPI
// @Accept json
// @Produce json
// @Param request body CreateApplicationRequest true "request body"
// @Success 201 {object} CreateApplicationResponse
// @Failure 400 {string} string "Bad request"
// @Failure 500 {string} string "Internal server error"
// @Router /v1/manifests/applications [put]
func (ar ManifestRouter) CreateApplication(ctx context.Context, w http.ResponseWriter, r *http.Request) error {
nitro-neal marked this conversation as resolved.
Show resolved Hide resolved
nitro-neal marked this conversation as resolved.
Show resolved Hide resolved
var request CreateApplicationRequest
if err := framework.Decode(r, &request); err != nil {
errMsg := "invalid create application request"
logrus.WithError(err).Error(errMsg)
return framework.NewRequestError(errors.Wrap(err, errMsg), http.StatusBadRequest)
}

req := request.ToServiceRequest()
createApplicationResponse, err := ar.service.CreateApplication(req)
if err != nil {
errMsg := "could not create application"
logrus.WithError(err).Error(errMsg)
return framework.NewRequestError(errors.Wrap(err, errMsg), http.StatusInternalServerError)
}

resp := CreateApplicationResponse{Application: createApplicationResponse.Application}

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

type GetApplicationResponse struct {
ID string `json:"id"`
Application applicationsdk.CredentialApplication `json:"application"`
}

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

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

resp := GetApplicationResponse{
ID: gotApplication.Application.Application.ID,
Application: gotApplication.Application,
}
return framework.Respond(ctx, w, resp, http.StatusOK)
}

type GetApplicationsResponse struct {
Applications []applicationsdk.CredentialApplication `json:"applications"`
}

// GetApplications godoc
// @Summary Get applications
// @Description Checks for the presence of a query parameter and calls the associated filtered get method
// @Tags ApplicationAPI
// @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} GetApplicationsResponse
// @Failure 400 {string} string "Bad request"
// @Failure 500 {string} string "Internal server error"
// @Router /v1/manifests/applications [get]
func (ar ManifestRouter) GetApplications(ctx context.Context, w http.ResponseWriter, r *http.Request) error {
nitro-neal marked this conversation as resolved.
Show resolved Hide resolved
gotApplications, err := ar.service.GetApplications()

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

resp := GetApplicationsResponse{
Applications: gotApplications.Applications,
}

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

// DeleteApplication godoc
// @Summary Delete applications
// @Description Delete application by ID
// @Tags ApplicationAPI
// @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/applications/{id} [delete]
func (ar ManifestRouter) DeleteApplication(ctx context.Context, w http.ResponseWriter, r *http.Request) error {
id := framework.GetParam(ctx, IDParam)
if id == nil {
errMsg := "cannot delete application without ID parameter"
logrus.Error(errMsg)
return framework.NewRequestErrorMsg(errMsg, http.StatusBadRequest)
}

if err := ar.service.DeleteApplication(manifest.DeleteApplicationRequest{ID: *id}); err != nil {
errMsg := fmt.Sprintf("could not delete application 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)
}
32 changes: 31 additions & 1 deletion pkg/server/router/manifest_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,14 +46,23 @@ func TestManifestRouter(t *testing.T) {
assert.Equal(tt, framework.Manifest, manifestService.Type())
assert.Equal(tt, framework.StatusReady, manifestService.Status().Status)

// good request
// good manifest request
createManifestRequest := getValidManifestRequest()

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

// good application request
createApplicationRequest := getValidApplicationRequest(createdManifest.Manifest.ID, createManifestRequest.PresentationDefinition.InputDescriptors[0].ID)

createdApplication, err := manifestService.CreateApplication(createApplicationRequest)
assert.NoError(tt, err)
assert.NotEmpty(tt, createdManifest)
assert.NotEmpty(tt, createdApplication.Application.Application.ID)
})

}

func getValidManifestRequest() manifest.CreateManifestRequest {
Expand Down Expand Up @@ -93,3 +102,24 @@ func getValidManifestRequest() manifest.CreateManifestRequest {

return createManifestRequest
}

func getValidApplicationRequest(manifestId string, submissionDescriptorId string) manifest.CreateApplicationRequest {

createApplicationRequest := manifest.CreateApplicationRequest{

ManifestID: manifestId,
nitro-neal marked this conversation as resolved.
Show resolved Hide resolved
PresentationSubmission: exchange.PresentationSubmission{
ID: "psid",
DefinitionID: "definitionId",
DescriptorMap: []exchange.SubmissionDescriptor{
{
ID: submissionDescriptorId,
Format: "jwt",
Path: "path",
},
},
},
}

return createApplicationRequest
}
33 changes: 20 additions & 13 deletions pkg/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,15 @@ import (
)

const (
HealthPrefix = "/health"
ReadinessPrefix = "/readiness"
V1Prefix = "/v1"
DIDsPrefix = "/dids"
SchemasPrefix = "/schemas"
CredentialsPrefix = "/credentials"
ManifestsPrefix = "/manifests"
KeyStorePrefix = "/keys"
HealthPrefix = "/health"
ReadinessPrefix = "/readiness"
V1Prefix = "/v1"
DIDsPrefix = "/dids"
SchemasPrefix = "/schemas"
CredentialsPrefix = "/credentials"
ManifestsPrefix = "/manifests"
ApplicationsPrefix = "/applications"
KeyStorePrefix = "/keys"
)

// SSIServer exposes all dependencies needed to run a http server and all its services
Expand Down Expand Up @@ -163,11 +164,17 @@ func (s *SSIServer) ManifestAPI(service svcframework.Service) (err error) {
return util.LoggingErrorMsg(err, "could not create manifest router")
}

handlerPath := V1Prefix + ManifestsPrefix
manifestHandlerPath := V1Prefix + ManifestsPrefix
applicationsHandlerPath := V1Prefix + ManifestsPrefix + ApplicationsPrefix

s.Handle(http.MethodPut, handlerPath, manifestRouter.CreateManifest)
s.Handle(http.MethodGet, handlerPath, manifestRouter.GetManifests)
s.Handle(http.MethodGet, path.Join(handlerPath, "/:id"), manifestRouter.GetManifest)
s.Handle(http.MethodDelete, path.Join(handlerPath, "/:id"), manifestRouter.DeleteManifest)
s.Handle(http.MethodPut, manifestHandlerPath, manifestRouter.CreateManifest)
s.Handle(http.MethodGet, manifestHandlerPath, manifestRouter.GetManifests)
s.Handle(http.MethodGet, path.Join(manifestHandlerPath, "/:id"), manifestRouter.GetManifest)
s.Handle(http.MethodDelete, path.Join(manifestHandlerPath, "/:id"), manifestRouter.DeleteManifest)

s.Handle(http.MethodPut, applicationsHandlerPath, manifestRouter.CreateApplication)
s.Handle(http.MethodGet, applicationsHandlerPath, manifestRouter.GetApplications)
s.Handle(http.MethodGet, path.Join(applicationsHandlerPath, "/:id"), manifestRouter.GetApplication)
s.Handle(http.MethodDelete, path.Join(applicationsHandlerPath, "/:id"), manifestRouter.DeleteApplication)
return
}
Loading