Repository: TBD54566975/ssi-service
Pull request #261 (2023-01-19 01:29:59)

Updated the Submission to contain the verifiablePresentation.
# Overview
Previously: a Submission object (which is returned with `GetSubmission`) contained metadata only.
Now: it contains the `VerifiablePresentation` object that was sent.

# Description
This is so API clients can retrieve what the information sent was. An follow up may be to include the JWT that was sent, instead of the actual decoded payload.

# How Has This Been Tested?
Unit




codecov-commenter: # [Codecov](https://codecov.io/gh/TBD54566975/ssi-service/pull/261?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=TBD54566975) Report
> Merging [#261](https://codecov.io/gh/TBD54566975/ssi-service/pull/261?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=TBD54566975) (f17ae68) into [main](https://codecov.io/gh/TBD54566975/ssi-service/commit/b2d967be6df9eafbc9c46ac4a17fd0c987bf6162?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=TBD54566975) (b2d967b) will **not change** coverage.
> The diff coverage is `n/a`.

```diff
@@           Coverage Diff           @@
##             main     #261   +/-   ##
=======================================
  Coverage   24.87%   24.87%           
=======================================
  Files          31       31           
  Lines        3063     3063           
=======================================
  Hits          762      762           
  Misses       2184     2184           
  Partials      117      117           
```



Help us with your feedback. Take ten seconds to tell us [how you rate us](https://about.codecov.io/nps?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=TBD54566975). Have a feature suggestion? [Share it here.](https://app.codecov.io/gh/feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=TBD54566975)


Patch:
diff --git a/pkg/service/presentation/model/model.go b/pkg/service/presentation/model/model.go
index df70e94..2cf85b3 100644
--- a/pkg/service/presentation/model/model.go
+++ b/pkg/service/presentation/model/model.go
@@ -4,6 +4,7 @@ import (
 	credsdk "github.com/TBD54566975/ssi-sdk/credential"
 	"github.com/TBD54566975/ssi-sdk/credential/exchange"
 	"github.com/TBD54566975/ssi-sdk/util"
+	"github.com/goccy/go-json"
 	"github.com/tbd54566975/ssi-service/internal/credential"
 	"github.com/tbd54566975/ssi-service/internal/keyaccess"
 	"github.com/tbd54566975/ssi-service/pkg/service/presentation/storage"
@@ -71,7 +72,24 @@ type Submission struct {
 	Status string `json:"status" validate:"required"`
 	// The reason why the submission was approved or denied.
 	Reason string `json:"reason"`
-	*exchange.PresentationSubmission
+	// The verifiable presentation containing the presentation_submission along with the credentials presented.
+	VerifiablePresentation *credsdk.VerifiablePresentation `json:"verifiablePresentation,omitempty"`
+}
+
+func (r Submission) GetSubmission() *exchange.PresentationSubmission {
+	switch m := r.VerifiablePresentation.PresentationSubmission.(type) {
+	case exchange.PresentationSubmission:
+		return &m
+	case *exchange.PresentationSubmission:
+		return m
+	case map[string]any:
+		var ts *exchange.PresentationSubmission
+		data, _ := json.Marshal(m)
+		_ = json.Unmarshal(data, &ts)
+		return ts
+	default:
+		return nil
+	}
 }
 
 type ListSubmissionResponse struct {
@@ -98,6 +116,6 @@ func ServiceModel(storedSubmission *storage.StoredSubmission) Submission {
 	return Submission{
 		Status:                 storedSubmission.Status.String(),
 		Reason:                 storedSubmission.Reason,
-		PresentationSubmission: &storedSubmission.Submission,
+		VerifiablePresentation: &storedSubmission.VerifiablePresentation,
 	}
 }
diff --git a/pkg/service/presentation/service.go b/pkg/service/presentation/service.go
index 4bdf8e1..76991c3 100644
--- a/pkg/service/presentation/service.go
+++ b/pkg/service/presentation/service.go
@@ -179,8 +179,8 @@ func (s Service) CreateSubmission(ctx context.Context, request model.CreateSubmi
 	}
 
 	storedSubmission := presentationstorage.StoredSubmission{
-		Status:     submission.StatusPending,
-		Submission: request.Submission,
+		Status:                 submission.StatusPending,
+		VerifiablePresentation: request.Presentation,
 	}
 
 	// TODO(andres): IO requests should be done in parallel, once we have context wired up.
@@ -188,7 +188,11 @@ func (s Service) CreateSubmission(ctx context.Context, request model.CreateSubmi
 		return nil, errors.Wrap(err, "could not store presentation")
 	}
 
-	opID := submission.IDFromSubmissionID(storedSubmission.Submission.ID)
+	sub, ok := storedSubmission.VerifiablePresentation.PresentationSubmission.(exchange.PresentationSubmission)
+	if !ok {
+		return nil, errors.New("interface is not exchange.PresentationSubmission")
+	}
+	opID := submission.IDFromSubmissionID(sub.ID)
 	storedOp := opstorage.StoredOperation{
 		ID:   opID,
 		Done: false,
diff --git a/pkg/service/presentation/storage.go b/pkg/service/presentation/storage.go
index ef8fb2f..782b547 100644
--- a/pkg/service/presentation/storage.go
+++ b/pkg/service/presentation/storage.go
@@ -140,7 +140,11 @@ func (ps *Storage) DeletePresentation(ctx context.Context, id string) error {
 }
 
 func (ps *Storage) StoreSubmission(ctx context.Context, s prestorage.StoredSubmission) error {
-	id := s.Submission.ID
+	sub, ok := s.VerifiablePresentation.PresentationSubmission.(exchange.PresentationSubmission)
+	if !ok {
+		return util.LoggingNewError("asserting that field is of type exchange.PresentationSubmission")
+	}
+	id := sub.ID
 	if id == "" {
 		err := errors.New("could not store submission definition without an ID")
 		logrus.WithError(err).Error()
diff --git a/pkg/service/presentation/storage/storage.go b/pkg/service/presentation/storage/storage.go
index 89b7b57..7a04479 100644
--- a/pkg/service/presentation/storage/storage.go
+++ b/pkg/service/presentation/storage/storage.go
@@ -1,6 +1,7 @@
 package storage
 
 import (
+	"github.com/TBD54566975/ssi-sdk/credential"
 	"github.com/TBD54566975/ssi-sdk/credential/exchange"
 	"github.com/pkg/errors"
 	opstorage "github.com/tbd54566975/ssi-service/pkg/service/operation/storage"
@@ -26,9 +27,9 @@ type DefinitionStorage interface {
 }
 
 type StoredSubmission struct {
-	Status     submission.Status               `json:"status"`
-	Submission exchange.PresentationSubmission `json:"submission"`
-	Reason     string                          `json:"reason"`
+	Status                 submission.Status                 `json:"status"`
+	Reason                 string                            `json:"reason"`
+	VerifiablePresentation credential.VerifiablePresentation `json:"vp"`
 }
 
 func (s StoredSubmission) FilterVariablesMap() map[string]any {


Test patch:
diff --git a/pkg/server/server_operation_test.go b/pkg/server/server_operation_test.go
index 647d171..5ec37f1 100644
--- a/pkg/server/server_operation_test.go
+++ b/pkg/server/server_operation_test.go
@@ -244,8 +244,7 @@ func TestOperationsAPI(t *testing.T) {
 			var resp router.Operation
 			assert.NoError(t, json.NewDecoder(w.Body).Decode(&resp))
 			assert.True(t, resp.Done)
-			assert.Contains(t, resp.Result.Response, "definition_id")
-			assert.Contains(t, resp.Result.Response, "descriptor_map")
+			assert.Contains(t, resp.Result.Response, "verifiablePresentation")
 			assert.Equal(t, "cancelled", resp.Result.Response.(map[string]any)["status"])
 		})
 
diff --git a/pkg/server/server_presentation_test.go b/pkg/server/server_presentation_test.go
index 0d2ecbf..506c2a1 100644
--- a/pkg/server/server_presentation_test.go
+++ b/pkg/server/server_presentation_test.go
@@ -199,8 +199,8 @@ func TestPresentationAPI(t *testing.T) {
 
 			var resp router.GetSubmissionResponse
 			assert.NoError(ttt, json.NewDecoder(w.Body).Decode(&resp))
-			assert.Equal(ttt, opstorage.StatusObjectID(op.ID), resp.Submission.ID)
-			assert.Equal(ttt, definition.PresentationDefinition.ID, resp.Submission.DefinitionID)
+			assert.Equal(ttt, opstorage.StatusObjectID(op.ID), resp.GetSubmission().ID)
+			assert.Equal(ttt, definition.PresentationDefinition.ID, resp.GetSubmission().DefinitionID)
 			assert.Equal(ttt, "pending", resp.Submission.Status)
 		})
 
@@ -260,9 +260,9 @@ func TestPresentationAPI(t *testing.T) {
 			var resp router.ReviewSubmissionResponse
 			assert.NoError(ttt, json.NewDecoder(w.Body).Decode(&resp))
 			assert.Equal(ttt, "because I want to", resp.Reason)
-			assert.NotEmpty(ttt, resp.ID)
+			assert.NotEmpty(ttt, resp.GetSubmission().ID)
 			assert.Equal(ttt, "approved", resp.Status)
-			assert.Equal(ttt, definition.PresentationDefinition.ID, resp.DefinitionID)
+			assert.Equal(ttt, definition.PresentationDefinition.ID, resp.GetSubmission().DefinitionID)
 		})
 
 		tt.Run("Review submission twice fails", func(ttt *testing.T) {
@@ -351,28 +351,50 @@ func TestPresentationAPI(t *testing.T) {
 			expectedSubmissions := []model.Submission{
 				{
 					Status: "pending",
-					PresentationSubmission: &exchange.PresentationSubmission{
-						ID:           opstorage.StatusObjectID(op.ID),
-						DefinitionID: definition.PresentationDefinition.ID,
+					VerifiablePresentation: &credential.VerifiablePresentation{
+						Context: []any{"https://www.w3.org/2018/credentials/v1"},
+						Holder:  holderDID.String(),
+						Type:    []any{"VerifiablePresentation"},
 					},
 				},
 				{
 					Status: "pending",
-					PresentationSubmission: &exchange.PresentationSubmission{
-						ID:           opstorage.StatusObjectID(op2.ID),
-						DefinitionID: definition.PresentationDefinition.ID,
+					VerifiablePresentation: &credential.VerifiablePresentation{
+						Context: []any{"https://www.w3.org/2018/credentials/v1"},
+						Holder:  mrTeeDID.String(),
+						Type:    []any{"VerifiablePresentation"},
 					},
 				},
 			}
 			diff := cmp.Diff(expectedSubmissions, resp.Submissions,
-				cmpopts.IgnoreFields(exchange.PresentationSubmission{}, "DescriptorMap"),
+				cmpopts.IgnoreFields(credential.VerifiablePresentation{}, "ID", "VerifiableCredential", "PresentationSubmission"),
 				cmpopts.SortSlices(func(l, r model.Submission) bool {
-					return l.PresentationSubmission.ID < r.PresentationSubmission.ID
+					return l.VerifiablePresentation.Holder < r.VerifiablePresentation.Holder
 				}),
 			)
 			if diff != "" {
 				ttt.Errorf("Mismatch on submissions (-want +got):\n%s", diff)
 			}
+			assert.Len(ttt, resp.Submissions[0].VerifiablePresentation.VerifiableCredential, 1)
+			assert.Len(ttt, resp.Submissions[1].VerifiablePresentation.VerifiableCredential, 1)
+
+			assert.ElementsMatch(ttt,
+				[]string{
+					opstorage.StatusObjectID(op.ID),
+					opstorage.StatusObjectID(op2.ID)},
+				[]string{
+					resp.Submissions[0].GetSubmission().ID,
+					resp.Submissions[1].GetSubmission().ID,
+				})
+			assert.Equal(ttt,
+				[]string{
+					definition.PresentationDefinition.ID,
+					definition.PresentationDefinition.ID,
+				},
+				[]string{
+					resp.Submissions[0].GetSubmission().DefinitionID,
+					resp.Submissions[1].GetSubmission().DefinitionID,
+				})
 		})
 
 		tt.Run("bad filter returns error", func(ttt *testing.T) {
@@ -423,21 +445,27 @@ func TestPresentationAPI(t *testing.T) {
 			expectedSubmissions := []model.Submission{
 				{
 					Status: "pending",
-					PresentationSubmission: &exchange.PresentationSubmission{
-						ID:           opstorage.StatusObjectID(op.ID),
-						DefinitionID: definition.PresentationDefinition.ID,
+					VerifiablePresentation: &credential.VerifiablePresentation{
+						Context: []any{"https://www.w3.org/2018/credentials/v1"},
+						Holder:  holderDID.String(),
+						Type:    []any{"VerifiablePresentation"},
 					},
 				},
 			}
 			diff := cmp.Diff(expectedSubmissions, resp.Submissions,
-				cmpopts.IgnoreFields(exchange.PresentationSubmission{}, "DescriptorMap"),
+				cmpopts.IgnoreFields(credential.VerifiablePresentation{}, "ID", "PresentationSubmission", "VerifiableCredential"),
 				cmpopts.SortSlices(func(l, r model.Submission) bool {
-					return l.PresentationSubmission.ID < r.PresentationSubmission.ID
+					return l.VerifiablePresentation.Holder < r.VerifiablePresentation.Holder
 				}),
 			)
 			if diff != "" {
 				ttt.Errorf("Mismatch on submissions (-want +got):\n%s", diff)
 			}
+
+			assert.Len(ttt, resp.Submissions, 1)
+			assert.Len(ttt, resp.Submissions[0].VerifiablePresentation.VerifiableCredential, 1)
+			assert.Equal(ttt, opstorage.StatusObjectID(op.ID), resp.Submissions[0].GetSubmission().ID)
+			assert.Equal(ttt, definition.PresentationDefinition.ID, resp.Submissions[0].GetSubmission().DefinitionID)
 		})
 
 		tt.Run("List submissions filter returns empty when status does not match", func(ttt *testing.T) {