diff --git a/api/spec/openapi.gen.go b/api/spec/openapi.gen.go index 199640d74..514adf4dc 100644 --- a/api/spec/openapi.gen.go +++ b/api/spec/openapi.gen.go @@ -168,51 +168,51 @@ var swaggerSpec = []string{ "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", } // GetSwagger returns the content of the embedded swagger specification file diff --git a/docs/v1/openapi.yaml b/docs/v1/openapi.yaml index befa405d0..11a25db50 100644 --- a/docs/v1/openapi.yaml +++ b/docs/v1/openapi.yaml @@ -89,6 +89,18 @@ paths: in: path required: true description: Profile ID + - schema: + type: string + name: txID + in: query + required: false + description: Issuance transaction ID + - schema: + type: string + name: credentialID + in: query + required: false + description: Credential ID get: summary: Request Credential Issuance history. responses: diff --git a/pkg/restapi/v1/issuer/controller.go b/pkg/restapi/v1/issuer/controller.go index 2a7608a4b..3fdc30be5 100644 --- a/pkg/restapi/v1/issuer/controller.go +++ b/pkg/restapi/v1/issuer/controller.go @@ -81,6 +81,8 @@ type credentialIssuanceHistoryStore interface { GetIssuedCredentialsMetadata( ctx context.Context, profileID string, + txID *string, + credentialID *string, ) ([]*credentialstatus.CredentialMetadata, error) } @@ -1027,9 +1029,18 @@ func (c *Controller) PrepareBatchCredential(e echo.Context) error { // CredentialIssuanceHistory returns Credential Issuance history. // GET /issuer/profiles/{profileID}/issued-credentials. -func (c *Controller) CredentialIssuanceHistory(e echo.Context, profileID string) error { +func (c *Controller) CredentialIssuanceHistory( + e echo.Context, + profileID string, + extraParams CredentialIssuanceHistoryParams, +) error { credentialMetadata, err := c.credentialIssuanceHistoryStore. - GetIssuedCredentialsMetadata(e.Request().Context(), profileID) + GetIssuedCredentialsMetadata( + e.Request().Context(), + profileID, + extraParams.TxID, + extraParams.CredentialID, + ) if err != nil { return err } diff --git a/pkg/restapi/v1/issuer/controller_test.go b/pkg/restapi/v1/issuer/controller_test.go index 3ff321e1e..b36144268 100644 --- a/pkg/restapi/v1/issuer/controller_test.go +++ b/pkg/restapi/v1/issuer/controller_test.go @@ -27,6 +27,8 @@ import ( "github.com/stretchr/testify/require" timeutil "github.com/trustbloc/did-go/doc/util/time" "github.com/trustbloc/vc-go/verifiable" + nooptracer "go.opentelemetry.io/otel/trace/noop" + "github.com/trustbloc/vcs/pkg/doc/vc" vcsverifiable "github.com/trustbloc/vcs/pkg/doc/verifiable" "github.com/trustbloc/vcs/pkg/event/spi" @@ -37,7 +39,6 @@ import ( "github.com/trustbloc/vcs/pkg/restapi/v1/util" "github.com/trustbloc/vcs/pkg/service/credentialstatus" "github.com/trustbloc/vcs/pkg/service/oidc4ci" - nooptracer "go.opentelemetry.io/otel/trace/noop" ) const ( @@ -2825,21 +2826,22 @@ func TestCredentialIssuanceHistory(t *testing.T) { credentialIssuanceStore := NewMockCredentialIssuanceHistoryStore(gomock.NewController(t)) t.Run("Success", func(t *testing.T) { - txID := uuid.NewString() + txID := lo.ToPtr(uuid.NewString()) iss := timeutil.NewTime(time.Now()) + credID := lo.ToPtr(uuid.NewString()) credentialMetadata := &credentialstatus.CredentialMetadata{ CredentialID: "credentialID", Issuer: "testIssuer", ProfileVersion: profileVersion, CredentialType: []string{"verifiableCredential"}, - TransactionID: txID, + TransactionID: *txID, IssuanceDate: iss, ExpirationDate: nil, } credentialIssuanceStore.EXPECT(). - GetIssuedCredentialsMetadata(gomock.Any(), profileID). + GetIssuedCredentialsMetadata(gomock.Any(), profileID, txID, credID). Times(1). Return([]*credentialstatus.CredentialMetadata{credentialMetadata}, nil) @@ -2851,7 +2853,10 @@ func TestCredentialIssuanceHistory(t *testing.T) { echoCtx := echoContext(withRecorder(recorder)) - err := c.CredentialIssuanceHistory(echoCtx, profileID) + err := c.CredentialIssuanceHistory(echoCtx, profileID, CredentialIssuanceHistoryParams{ + TxID: txID, + CredentialID: credID, + }) assert.NoError(t, err) var gotResponse []CredentialIssuanceHistoryData @@ -2865,7 +2870,7 @@ func TestCredentialIssuanceHistory(t *testing.T) { ExpirationDate: nil, IssuanceDate: lo.ToPtr(iss.Time.Format(time.RFC3339)), Issuer: "testIssuer", - TransactionId: &txID, + TransactionId: txID, ProfileVersion: lo.ToPtr(profileVersion), }, } @@ -2875,7 +2880,7 @@ func TestCredentialIssuanceHistory(t *testing.T) { t.Run("credentialIssuanceHistoryStore error", func(t *testing.T) { credentialIssuanceStore.EXPECT(). - GetIssuedCredentialsMetadata(gomock.Any(), profileID). + GetIssuedCredentialsMetadata(gomock.Any(), profileID, nil, nil). Times(1). Return(nil, errors.New("some error")) @@ -2887,7 +2892,7 @@ func TestCredentialIssuanceHistory(t *testing.T) { echoCtx := echoContext(withRecorder(recorder)) - err := c.CredentialIssuanceHistory(echoCtx, profileID) + err := c.CredentialIssuanceHistory(echoCtx, profileID, CredentialIssuanceHistoryParams{}) assert.Error(t, err) }) } diff --git a/pkg/restapi/v1/issuer/openapi.gen.go b/pkg/restapi/v1/issuer/openapi.gen.go index 4f06a7c60..c689c803f 100644 --- a/pkg/restapi/v1/issuer/openapi.gen.go +++ b/pkg/restapi/v1/issuer/openapi.gen.go @@ -630,6 +630,15 @@ type StoreAuthorizationCodeRequestJSONBody = StoreAuthorizationCodeRequest // ValidatePreAuthorizedCodeRequestJSONBody defines parameters for ValidatePreAuthorizedCodeRequest. type ValidatePreAuthorizedCodeRequestJSONBody = ValidatePreAuthorizedCodeRequest +// CredentialIssuanceHistoryParams defines parameters for CredentialIssuanceHistory. +type CredentialIssuanceHistoryParams struct { + // Issuance transaction ID + TxID *string `form:"txID,omitempty" json:"txID,omitempty"` + + // Credential ID + CredentialID *string `form:"credentialID,omitempty" json:"credentialID,omitempty"` +} + // PostIssueCredentialsJSONBody defines parameters for PostIssueCredentials. type PostIssueCredentialsJSONBody = IssueCredentialData @@ -895,7 +904,7 @@ type ClientInterface interface { ValidatePreAuthorizedCodeRequest(ctx context.Context, body ValidatePreAuthorizedCodeRequestJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) // CredentialIssuanceHistory request - CredentialIssuanceHistory(ctx context.Context, profileID string, reqEditors ...RequestEditorFn) (*http.Response, error) + CredentialIssuanceHistory(ctx context.Context, profileID string, params *CredentialIssuanceHistoryParams, reqEditors ...RequestEditorFn) (*http.Response, error) // PostIssueCredentials request with any body PostIssueCredentialsWithBody(ctx context.Context, profileID string, profileVersion string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) @@ -1123,8 +1132,8 @@ func (c *Client) ValidatePreAuthorizedCodeRequest(ctx context.Context, body Vali return c.Client.Do(req) } -func (c *Client) CredentialIssuanceHistory(ctx context.Context, profileID string, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewCredentialIssuanceHistoryRequest(c.Server, profileID) +func (c *Client) CredentialIssuanceHistory(ctx context.Context, profileID string, params *CredentialIssuanceHistoryParams, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewCredentialIssuanceHistoryRequest(c.Server, profileID, params) if err != nil { return nil, err } @@ -1593,7 +1602,7 @@ func NewValidatePreAuthorizedCodeRequestRequestWithBody(server string, contentTy } // NewCredentialIssuanceHistoryRequest generates requests for CredentialIssuanceHistory -func NewCredentialIssuanceHistoryRequest(server string, profileID string) (*http.Request, error) { +func NewCredentialIssuanceHistoryRequest(server string, profileID string, params *CredentialIssuanceHistoryParams) (*http.Request, error) { var err error var pathParam0 string @@ -1618,6 +1627,42 @@ func NewCredentialIssuanceHistoryRequest(server string, profileID string) (*http return nil, err } + queryValues := queryURL.Query() + + if params.TxID != nil { + + if queryFrag, err := runtime.StyleParamWithLocation("form", true, "txID", runtime.ParamLocationQuery, *params.TxID); err != nil { + return nil, err + } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + return nil, err + } else { + for k, v := range parsed { + for _, v2 := range v { + queryValues.Add(k, v2) + } + } + } + + } + + if params.CredentialID != nil { + + if queryFrag, err := runtime.StyleParamWithLocation("form", true, "credentialID", runtime.ParamLocationQuery, *params.CredentialID); err != nil { + return nil, err + } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + return nil, err + } else { + for k, v := range parsed { + for _, v2 := range v { + queryValues.Add(k, v2) + } + } + } + + } + + queryURL.RawQuery = queryValues.Encode() + req, err := http.NewRequest("GET", queryURL.String(), nil) if err != nil { return nil, err @@ -1957,7 +2002,7 @@ type ClientWithResponsesInterface interface { ValidatePreAuthorizedCodeRequestWithResponse(ctx context.Context, body ValidatePreAuthorizedCodeRequestJSONRequestBody, reqEditors ...RequestEditorFn) (*ValidatePreAuthorizedCodeRequestResponse, error) // CredentialIssuanceHistory request - CredentialIssuanceHistoryWithResponse(ctx context.Context, profileID string, reqEditors ...RequestEditorFn) (*CredentialIssuanceHistoryResponse, error) + CredentialIssuanceHistoryWithResponse(ctx context.Context, profileID string, params *CredentialIssuanceHistoryParams, reqEditors ...RequestEditorFn) (*CredentialIssuanceHistoryResponse, error) // PostIssueCredentials request with any body PostIssueCredentialsWithBodyWithResponse(ctx context.Context, profileID string, profileVersion string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostIssueCredentialsResponse, error) @@ -2456,8 +2501,8 @@ func (c *ClientWithResponses) ValidatePreAuthorizedCodeRequestWithResponse(ctx c } // CredentialIssuanceHistoryWithResponse request returning *CredentialIssuanceHistoryResponse -func (c *ClientWithResponses) CredentialIssuanceHistoryWithResponse(ctx context.Context, profileID string, reqEditors ...RequestEditorFn) (*CredentialIssuanceHistoryResponse, error) { - rsp, err := c.CredentialIssuanceHistory(ctx, profileID, reqEditors...) +func (c *ClientWithResponses) CredentialIssuanceHistoryWithResponse(ctx context.Context, profileID string, params *CredentialIssuanceHistoryParams, reqEditors ...RequestEditorFn) (*CredentialIssuanceHistoryResponse, error) { + rsp, err := c.CredentialIssuanceHistory(ctx, profileID, params, reqEditors...) if err != nil { return nil, err } @@ -2944,7 +2989,7 @@ type ServerInterface interface { ValidatePreAuthorizedCodeRequest(ctx echo.Context) error // Request Credential Issuance history. // (GET /issuer/profiles/{profileID}/issued-credentials) - CredentialIssuanceHistory(ctx echo.Context, profileID string) error + CredentialIssuanceHistory(ctx echo.Context, profileID string, params CredentialIssuanceHistoryParams) error // Issue credential // (POST /issuer/profiles/{profileID}/{profileVersion}/credentials/issue) PostIssueCredentials(ctx echo.Context, profileID string, profileVersion string) error @@ -3074,8 +3119,24 @@ func (w *ServerInterfaceWrapper) CredentialIssuanceHistory(ctx echo.Context) err return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter profileID: %s", err)) } + // Parameter object where we will unmarshal all parameters from the context + var params CredentialIssuanceHistoryParams + // ------------- Optional query parameter "txID" ------------- + + err = runtime.BindQueryParameter("form", true, false, "txID", ctx.QueryParams(), ¶ms.TxID) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter txID: %s", err)) + } + + // ------------- Optional query parameter "credentialID" ------------- + + err = runtime.BindQueryParameter("form", true, false, "credentialID", ctx.QueryParams(), ¶ms.CredentialID) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter credentialID: %s", err)) + } + // Invoke the callback with all the unmarshalled arguments - err = w.Handler.CredentialIssuanceHistory(ctx, profileID) + err = w.Handler.CredentialIssuanceHistory(ctx, profileID, params) return err } diff --git a/pkg/storage/mongodb/vcissuancehistorystore/vc_issuance_histsory_store.go b/pkg/storage/mongodb/vcissuancehistorystore/vc_issuance_histsory_store.go index 10c3bca33..ad2734b68 100644 --- a/pkg/storage/mongodb/vcissuancehistorystore/vc_issuance_histsory_store.go +++ b/pkg/storage/mongodb/vcissuancehistorystore/vc_issuance_histsory_store.go @@ -11,6 +11,7 @@ import ( "fmt" "time" + "github.com/samber/lo" timeutil "github.com/trustbloc/did-go/doc/util/time" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/mongo/options" @@ -74,11 +75,23 @@ func (p *Store) Put( func (p *Store) GetIssuedCredentialsMetadata( ctx context.Context, profileID string, + txID *string, + credentialID *string, ) ([]*credentialstatus.CredentialMetadata, error) { opts := options.Find().SetSort(bson.D{{Key: "credentialMetadata.issuanceDate", Value: -1}}) - cursor, err := p.mongoClient.Database().Collection(vcStatusStoreName).Find(ctx, bson.D{ + + query := bson.D{ {Key: profileIDMongoDBFieldName, Value: profileID}, - }, opts) + } + + if lo.FromPtr(txID) != "" { + query = append(query, bson.E{Key: "credentialMetadata.transactionId", Value: *txID}) + } + if lo.FromPtr(credentialID) != "" { + query = append(query, bson.E{Key: "credentialMetadata.vcID", Value: *credentialID}) + } + + cursor, err := p.mongoClient.Database().Collection(vcStatusStoreName).Find(ctx, query, opts) if err != nil { return nil, fmt.Errorf("find credential metadata list MongoDB: %w", err) } diff --git a/pkg/storage/mongodb/vcissuancehistorystore/vc_issuance_histsory_store_test.go b/pkg/storage/mongodb/vcissuancehistorystore/vc_issuance_histsory_store_test.go index f4d30a528..ed0f814bb 100644 --- a/pkg/storage/mongodb/vcissuancehistorystore/vc_issuance_histsory_store_test.go +++ b/pkg/storage/mongodb/vcissuancehistorystore/vc_issuance_histsory_store_test.go @@ -73,7 +73,7 @@ func TestVCIssuanceHistoryStore(t *testing.T) { assert.NoError(t, err) // Get credential metadata by same profile version. - metadataFromDB, err := store.GetIssuedCredentialsMetadata(ctx, testProfile) + metadataFromDB, err := store.GetIssuedCredentialsMetadata(ctx, testProfile, nil, nil) assert.NoError(t, err) assert.Equal(t, []*credentialstatus.CredentialMetadata{credentialMeta}, metadataFromDB) @@ -87,7 +87,7 @@ func TestVCIssuanceHistoryStore(t *testing.T) { assert.NoError(t, err) // Get credential metadata by same profile version. - metadataFromDB, err = store.GetIssuedCredentialsMetadata(ctx, testProfile) + metadataFromDB, err = store.GetIssuedCredentialsMetadata(ctx, testProfile, nil, nil) assert.NoError(t, err) assert.Equal(t, []*credentialstatus.CredentialMetadata{credentialMetaNew, credentialMeta}, metadataFromDB) @@ -95,12 +95,75 @@ func TestVCIssuanceHistoryStore(t *testing.T) { t.Run("Find non-existing document", func(t *testing.T) { // Get credential metadata by different profile version. - metadataFromDB, err := store.GetIssuedCredentialsMetadata(ctx, testProfile+"unknown") + metadataFromDB, err := store.GetIssuedCredentialsMetadata(ctx, testProfile+"unknown", nil, nil) assert.NoError(t, err) assert.Empty(t, metadataFromDB) }) -} + t.Run("Test transaction id filter", func(t *testing.T) { + transactionID := uuid.NewString() + credentialMeta := &credentialstatus.CredentialMetadata{ + CredentialID: "credentialID", + ProfileVersion: testProfileVersion10, + Issuer: "credentialIssuerID", + CredentialType: []string{"verifiableCredential"}, + TransactionID: transactionID, + IssuanceDate: timeutil.NewTime(time.Now().Round(time.Second).UTC()), + ExpirationDate: nil, + } + assert.NoError(t, store.Put(ctx, testProfile, testProfileVersion10, credentialMeta)) + + transactionID2 := uuid.NewString() + credentialMeta2 := &credentialstatus.CredentialMetadata{ + CredentialID: "credentialID", + ProfileVersion: testProfileVersion10, + Issuer: "credentialIssuerID", + CredentialType: []string{"verifiableCredential"}, + TransactionID: transactionID2, + IssuanceDate: timeutil.NewTime(time.Now().Round(time.Second).UTC()), + ExpirationDate: nil, + } + assert.NoError(t, store.Put(ctx, testProfile, testProfileVersion10, credentialMeta2)) + + resp, err := store.GetIssuedCredentialsMetadata(ctx, testProfile, &transactionID2, nil) + assert.NoError(t, err) + assert.Len(t, resp, 1) + + assert.EqualValues(t, credentialMeta2.TransactionID, resp[0].TransactionID) + }) + + t.Run("Test credential id filter", func(t *testing.T) { + transactionID := uuid.NewString() + credentialMeta := &credentialstatus.CredentialMetadata{ + CredentialID: "credentialID123", + ProfileVersion: testProfileVersion10, + Issuer: "credentialIssuerID", + CredentialType: []string{"verifiableCredential"}, + TransactionID: transactionID, + IssuanceDate: timeutil.NewTime(time.Now().Round(time.Second).UTC()), + ExpirationDate: nil, + } + assert.NoError(t, store.Put(ctx, testProfile, testProfileVersion10, credentialMeta)) + + transactionID2 := uuid.NewString() + credentialMeta2 := &credentialstatus.CredentialMetadata{ + CredentialID: "777", + ProfileVersion: testProfileVersion10, + Issuer: "credentialIssuerID", + CredentialType: []string{"verifiableCredential"}, + TransactionID: transactionID2, + IssuanceDate: timeutil.NewTime(time.Now().Round(time.Second).UTC()), + ExpirationDate: nil, + } + assert.NoError(t, store.Put(ctx, testProfile, testProfileVersion10, credentialMeta2)) + + resp, err := store.GetIssuedCredentialsMetadata(ctx, testProfile, nil, &credentialMeta2.CredentialID) + assert.NoError(t, err) + assert.Len(t, resp, 1) + + assert.EqualValues(t, credentialMeta2.CredentialID, resp[0].CredentialID) + }) +} func TestTimeouts(t *testing.T) { pool, mongoDBResource := startMongoDBContainer(t) @@ -128,7 +191,7 @@ func TestTimeouts(t *testing.T) { }) t.Run("Find GetIssuedCredentialsMetadata", func(t *testing.T) { - resp, err := store.GetIssuedCredentialsMetadata(ctxWithTimeout, testProfile) + resp, err := store.GetIssuedCredentialsMetadata(ctxWithTimeout, testProfile, nil, nil) assert.Nil(t, resp) assert.ErrorContains(t, err, "context deadline exceeded")