Skip to content

Commit

Permalink
feat: Add resource metadata in DIDDoc metadata [DEV-1455]
Browse files Browse the repository at this point in the history
- Add basic metadata for resources under DIDDoc metadata. Please note that this is still a draft and further corrects to metadata are needed
  • Loading branch information
ankurdotb authored Jul 26, 2022
2 parents 9052d67 + 1fc1674 commit a60c64e
Show file tree
Hide file tree
Showing 10 changed files with 230 additions and 62 deletions.
12 changes: 6 additions & 6 deletions services/diddoc_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,24 +64,24 @@ func (ds DIDDocService) MarshallDID(didDoc cheqd.Did) (string, error) {
func (ds DIDDocService) MarshallContentStream(contentStream protoiface.MessageV1, contentType types.ContentType) (string, error) {
var mapContent orderedmap.OrderedMap
var err error
var context types.ContentType
var context []string
if contentType == types.DIDJSONLD || contentType == types.JSONLD {
context = types.DIDSchemaJSONLD
context = []string{types.DIDSchemaJSONLD}
}

switch contentStream := contentStream.(type) {
case *cheqd.VerificationMethod:
mapContent, err = ds.prepareJWKPubkey(contentStream)
case *cheqd.Did:
contentStream.Context = []string{string(context)}
contentStream.Context = context
jsonDid, err := ds.MarshallDID(*contentStream)
if err != nil {
return "", err
}
return string(jsonDid), nil
case *resource.Resource:
dResource := types.DereferencedResource{
Context: []string{string(context)},
Context: context,
CollectionId: contentStream.Header.CollectionId,
Id: contentStream.Header.Id,
Name: contentStream.Header.Name,
Expand All @@ -107,8 +107,8 @@ func (ds DIDDocService) MarshallContentStream(contentStream protoiface.MessageV1
}

// Context changes
if context != "" {
mapContent.Set("@"+didContext, context)
if len(context) != 0 {
mapContent.Set("@"+didContext, context[0])
mapContent.Sort(func(a *orderedmap.Pair, b *orderedmap.Pair) bool {
return a.Key() == "@"+didContext
})
Expand Down
25 changes: 25 additions & 0 deletions services/ledger_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
type LedgerServiceI interface {
QueryDIDDoc(did string) (cheqd.Did, cheqd.Metadata, bool, error)
QueryResource(collectionDid string, resourceId string) (resource.Resource, bool, error)
QueryCollectionResources(did string) ([]*resource.ResourceHeader, error)
GetNamespaces() []string
}

Expand Down Expand Up @@ -90,6 +91,30 @@ func (ls LedgerService) QueryResource(did string, resourceId string) (resource.R
return *resourceResponse.Resource, true, err
}

func (ls LedgerService) QueryCollectionResources(did string) ([]*resource.ResourceHeader, error) {
_, namespace, collectionId, _ := cheqdUtils.TrySplitDID(did)
serverAddr, namespaceFound := ls.ledgers[namespace]
if !namespaceFound {
return []*resource.ResourceHeader{}, fmt.Errorf("namespace not supported: %s", namespace)
}

conn, err := ls.openGRPCConnection(serverAddr)
if err != nil {
log.Error().Err(err).Msg("QueryResource: failed connection")
return []*resource.ResourceHeader{}, err
}

log.Info().Msgf("Querying did resource: %s", did)

client := resource.NewQueryClient(conn)
resourceResponse, err := client.CollectionResources(context.Background(), &resource.QueryGetCollectionResourcesRequest{CollectionId: collectionId})
if err != nil {
return []*resource.ResourceHeader{}, err
}

return resourceResponse.Resources, nil
}

func (ls *LedgerService) RegisterLedger(namespace string, url string) error {
if namespace == "" {
err := errors.New("namespace cannot be empty")
Expand Down
35 changes: 27 additions & 8 deletions services/request_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ import (

"github.com/rs/zerolog/log"

cheqdTypes "github.com/cheqd/cheqd-node/x/cheqd/types"
cheqdUtils "github.com/cheqd/cheqd-node/x/cheqd/utils"
resourceTypes "github.com/cheqd/cheqd-node/x/resource/types"
"github.com/cheqd/did-resolver/types"
"github.com/cheqd/did-resolver/utils"
"google.golang.org/protobuf/runtime/protoiface"
Expand Down Expand Up @@ -68,16 +70,16 @@ func (rs RequestService) prepareResolutionResult(did string, resolutionOptions t
return "", err
}

metadata, err := rs.didDocService.MarshallProto(&didResolution.Metadata)
metadata, err := json.Marshal(&didResolution.Metadata)
if err != nil {
return "", err
}

if didResolution.ResolutionMetadata.ResolutionError != "" {
didDoc, metadata = "", ""
didDoc, metadata = "", []byte{}
}

return createJsonResolution(didDoc, metadata, string(resolutionMetadata))
return createJsonResolution(didDoc, string(metadata), string(resolutionMetadata))
}

func (rs RequestService) prepareDereferencingResult(did string, dereferencingOptions types.DereferencingOption) (string, error) {
Expand All @@ -97,12 +99,12 @@ func (rs RequestService) prepareDereferencingResult(did string, dereferencingOpt
return createJsonDereferencing(nil, "", string(dereferencingMetadata))
}

metadata, err := rs.didDocService.MarshallProto(&didDereferencing.Metadata)
metadata, err := json.Marshal(didDereferencing.Metadata)
if err != nil {
return "", err
}

return createJsonDereferencing(didDereferencing.ContentStream, metadata, string(dereferencingMetadata))
return createJsonDereferencing(didDereferencing.ContentStream, string(metadata), string(dereferencingMetadata))
}

// https://w3c-ccg.github.io/did-resolution/#resolving
Expand All @@ -125,6 +127,11 @@ func (rs RequestService) Resolve(did string, resolutionOptions types.ResolutionO
return types.DidResolution{}, err
}

resolvedMetadata, err := rs.ResolveMetadata(did, metadata)
if err != nil {
return types.DidResolution{}, err
}

if !isFound {
didResolutionMetadata.ResolutionError = types.ResolutionNotFound
return types.DidResolution{ResolutionMetadata: didResolutionMetadata}, nil
Expand All @@ -137,8 +144,7 @@ func (rs RequestService) Resolve(did string, resolutionOptions types.ResolutionO
} else {
return types.DidResolution{}, fmt.Errorf("content type %s is not supported", didResolutionMetadata.ContentType)
}

return types.DidResolution{Did: didDoc, Metadata: metadata, ResolutionMetadata: didResolutionMetadata}, nil
return types.DidResolution{Did: didDoc, Metadata: resolvedMetadata, ResolutionMetadata: didResolutionMetadata}, nil
}

// https://w3c-ccg.github.io/did-resolution/#dereferencing
Expand Down Expand Up @@ -202,6 +208,7 @@ func (rs RequestService) dereferenceSecondary(did string, fragmentId string, did
if err != nil {
return types.DidDereferencing{}, err
}
metadata := didResolution.Metadata
dereferencingMetadata := types.DereferencingMetadata(didResolution.ResolutionMetadata)
if dereferencingMetadata.ResolutionError != "" {
return types.DidDereferencing{DereferencingMetadata: dereferencingMetadata}, nil
Expand All @@ -210,6 +217,7 @@ func (rs RequestService) dereferenceSecondary(did string, fragmentId string, did
var protoContent protoiface.MessageV1
if fragmentId != "" {
protoContent = rs.didDocService.GetDIDFragment(fragmentId, didResolution.Did)
metadata = types.TransformToFragmentMetadata(metadata)
} else {
protoContent = &didResolution.Did
}
Expand All @@ -225,7 +233,18 @@ func (rs RequestService) dereferenceSecondary(did string, fragmentId string, did
}
contentStream := json.RawMessage(jsonFragment)

return types.DidDereferencing{ContentStream: contentStream, Metadata: didResolution.Metadata, DereferencingMetadata: dereferencingMetadata}, nil
return types.DidDereferencing{ContentStream: contentStream, Metadata: metadata, DereferencingMetadata: dereferencingMetadata}, nil
}

func (rs RequestService) ResolveMetadata(did string, metadata cheqdTypes.Metadata) (types.ResolutionDidDocMetadata, error) {
if metadata.Resources == nil {
return types.NewResolutionDidDocMetadata(did, metadata, []*resourceTypes.ResourceHeader{}), nil
}
resources, err := rs.ledgerService.QueryCollectionResources(did)
if err != nil {
return types.ResolutionDidDocMetadata{}, err
}
return types.NewResolutionDidDocMetadata(did, metadata, resources), nil
}

func createJsonResolution(didDoc string, metadata string, resolutionMetadata string) (string, error) {
Expand Down
57 changes: 34 additions & 23 deletions services/request_service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,12 +103,21 @@ func (ls MockLedgerService) QueryResource(did string, resourceId string) (resour
return ls.Resource, isFound, nil
}

func (ls MockLedgerService) QueryCollectionResources(did string) ([]*resource.ResourceHeader, error) {
if ls.Metadata.Resources == nil {
return []*resource.ResourceHeader{}, nil
}
return []*resource.ResourceHeader{ls.Resource.Header}, nil
}

func (ls MockLedgerService) GetNamespaces() []string {
return []string{"testnet", "mainnet"}
}

func TestResolve(t *testing.T) {
validDIDDoc := validDIDDoc()
validMetadata := validMetadata()
validResource := validResource()
subtests := []struct {
name string
ledgerService MockLedgerService
Expand All @@ -117,18 +126,18 @@ func TestResolve(t *testing.T) {
method string
namespace string
expectedDID cheqd.Did
expectedMetadata cheqd.Metadata
expectedMetadata types.ResolutionDidDocMetadata
expectedError types.ErrorType
}{
{
name: "successful resolution",
ledgerService: NewMockLedgerService(validDIDDoc, validMetadata(), resource.Resource{}),
ledgerService: NewMockLedgerService(validDIDDoc, validMetadata, validResource),
resolutionType: types.DIDJSONLD,
identifier: validIdentifier,
method: validMethod,
namespace: validNamespace,
expectedDID: validDIDDoc,
expectedMetadata: validMetadata(),
expectedMetadata: types.NewResolutionDidDocMetadata(validDid, validMetadata, []*resource.ResourceHeader{validResource.Header}),
expectedError: "",
},
{
Expand All @@ -139,7 +148,7 @@ func TestResolve(t *testing.T) {
method: validMethod,
namespace: validNamespace,
expectedDID: cheqd.Did{},
expectedMetadata: cheqd.Metadata{},
expectedMetadata: types.ResolutionDidDocMetadata{},
expectedError: types.ResolutionNotFound,
},
{
Expand All @@ -150,7 +159,7 @@ func TestResolve(t *testing.T) {
method: validMethod,
namespace: validNamespace,
expectedDID: cheqd.Did{},
expectedMetadata: cheqd.Metadata{},
expectedMetadata: types.ResolutionDidDocMetadata{},
expectedError: types.ResolutionInvalidDID,
},
{
Expand All @@ -161,7 +170,7 @@ func TestResolve(t *testing.T) {
method: "not_supported_method",
namespace: validNamespace,
expectedDID: cheqd.Did{},
expectedMetadata: cheqd.Metadata{},
expectedMetadata: types.ResolutionDidDocMetadata{},
expectedError: types.ResolutionMethodNotSupported,
},
{
Expand All @@ -172,7 +181,7 @@ func TestResolve(t *testing.T) {
method: validMethod,
namespace: "invalid_namespace",
expectedDID: cheqd.Did{},
expectedMetadata: cheqd.Metadata{},
expectedMetadata: types.ResolutionDidDocMetadata{},
expectedError: types.ResolutionInvalidDID,
},
}
Expand Down Expand Up @@ -214,93 +223,95 @@ func TestDereferencing(t *testing.T) {
validResource := validResource()
validChecksum, _ := json.Marshal(validResource.Header.Checksum)
validData, _ := json.Marshal(validResource.Data)
validMetadata := validMetadata()
validFragmentMetadata := types.NewResolutionDidDocMetadata(validDid, validMetadata, []*resource.ResourceHeader{})
subtests := []struct {
name string
ledgerService MockLedgerService
dereferencingType types.ContentType
didUrl string
expectedContentStream string
expectedMetadata cheqd.Metadata
expectedMetadata types.ResolutionDidDocMetadata
expectedError types.ErrorType
}{
{
name: "successful resolution",
ledgerService: NewMockLedgerService(validDIDDoc, validMetadata(), validResource),
ledgerService: NewMockLedgerService(validDIDDoc, validMetadata, validResource),
dereferencingType: types.DIDJSONLD,
didUrl: validDid,
expectedContentStream: fmt.Sprintf("{\"@context\":[\"%s\"],\"id\":\"%s\",\"verificationMethod\":[{\"id\":\"%s\",\"type\":\"%s\",\"controller\":\"%s\",\"publicKeyJwk\":%s}],\"service\":[{\"id\":\"%s\",\"type\":\"%s\",\"serviceEndpoint\":\"%s\"}]}",
types.DIDSchemaJSONLD, validDid, validVerificationMethod.Id, validVerificationMethod.Type, validVerificationMethod.Controller, validPubKeyJWK, validService.Id, validService.Type, validService.ServiceEndpoint),
expectedMetadata: validMetadata(),
expectedMetadata: types.NewResolutionDidDocMetadata(validDid, validMetadata, []*resource.ResourceHeader{validResource.Header}),
expectedError: "",
},
{
name: "successful Secondary dereferencing (key)",
ledgerService: NewMockLedgerService(validDIDDoc, validMetadata(), validResource),
ledgerService: NewMockLedgerService(validDIDDoc, validMetadata, validResource),
dereferencingType: types.DIDJSONLD,
didUrl: validVerificationMethod.Id,
expectedContentStream: fmt.Sprintf("{\"@context\":\"%s\",\"id\":\"%s\",\"type\":\"%s\",\"controller\":\"%s\",\"publicKeyJwk\":%s}",
types.DIDSchemaJSONLD, validVerificationMethod.Id, validVerificationMethod.Type, validVerificationMethod.Controller, validPubKeyJWK),
expectedMetadata: validMetadata(),
expectedMetadata: validFragmentMetadata,
expectedError: "",
},
{
name: "successful Secondary dereferencing (service)",
ledgerService: NewMockLedgerService(validDIDDoc, validMetadata(), validResource),
ledgerService: NewMockLedgerService(validDIDDoc, validMetadata, validResource),
dereferencingType: types.DIDJSONLD,
didUrl: validService.Id,
expectedContentStream: fmt.Sprintf("{\"@context\":\"%s\",\"id\":\"%s\",\"type\":\"%s\",\"serviceEndpoint\":\"%s\"}",
types.DIDSchemaJSONLD, validService.Id, validService.Type, validService.ServiceEndpoint),
expectedMetadata: validMetadata(),
expectedMetadata: validFragmentMetadata,
expectedError: "",
},
{
name: "successful Primary dereferencing (resource)",
ledgerService: NewMockLedgerService(validDIDDoc, validMetadata(), validResource),
ledgerService: NewMockLedgerService(validDIDDoc, validMetadata, validResource),
dereferencingType: types.DIDJSONLD,
didUrl: validDid + "/resources/" + validResourceId,
didUrl: validDid + types.RESOURCE_PATH + validResourceId,
expectedContentStream: fmt.Sprintf("{\"@context\":[\"%s\"],\"collectionId\":\"%s\",\"id\":\"%s\",\"name\":\"%s\",\"resourceType\":\"%s\",\"mediaType\":\"%s\",\"checksum\":%s,\"data\":%s}",
types.DIDSchemaJSONLD, validResource.Header.CollectionId, validResource.Header.Id, validResource.Header.Name, validResource.Header.ResourceType, validResource.Header.MediaType, validChecksum, validData),
expectedMetadata: cheqd.Metadata{},
expectedMetadata: types.ResolutionDidDocMetadata{},
expectedError: "",
},
{
name: "invalid URL",
ledgerService: NewMockLedgerService(cheqd.Did{}, cheqd.Metadata{}, resource.Resource{}),
didUrl: "unvalid_url",
dereferencingType: types.DIDJSONLD,
expectedMetadata: cheqd.Metadata{},
expectedMetadata: types.ResolutionDidDocMetadata{},
expectedError: types.DereferencingInvalidDIDUrl,
},
{
name: "not supported path",
ledgerService: NewMockLedgerService(cheqd.Did{}, cheqd.Metadata{}, resource.Resource{}),
dereferencingType: types.DIDJSONLD,
didUrl: validDid + "/unknown_path",
expectedMetadata: cheqd.Metadata{},
expectedMetadata: types.ResolutionDidDocMetadata{},
expectedError: types.DereferencingNotSupported,
},
{
name: "not supported query",
ledgerService: NewMockLedgerService(cheqd.Did{}, cheqd.Metadata{}, resource.Resource{}),
dereferencingType: types.DIDJSONLD,
didUrl: validDid + "?unknown_query",
expectedMetadata: cheqd.Metadata{},
expectedMetadata: types.ResolutionDidDocMetadata{},
expectedError: types.DereferencingNotSupported,
},
{
name: "key not found",
ledgerService: NewMockLedgerService(cheqd.Did{}, cheqd.Metadata{}, resource.Resource{}),
dereferencingType: types.DIDJSONLD,
didUrl: validDid + "#notFoundKey",
expectedMetadata: cheqd.Metadata{},
expectedMetadata: types.ResolutionDidDocMetadata{},
expectedError: types.DereferencingNotFound,
},
{
name: "resource not found",
ledgerService: NewMockLedgerService(cheqd.Did{}, cheqd.Metadata{}, resource.Resource{}),
dereferencingType: types.DIDJSONLD,
didUrl: validDid + "/resources/00000000-0000-0000-0000-000000000000",
expectedMetadata: cheqd.Metadata{},
didUrl: validDid + types.RESOURCE_PATH + "00000000-0000-0000-0000-000000000000",
expectedMetadata: types.ResolutionDidDocMetadata{},
expectedError: types.DereferencingNotFound,
},
}
Expand Down
5 changes: 3 additions & 2 deletions tests/pytest/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@
RESOLVER_URL = "http://localhost:1313"
PATH = "/1.0/identifiers/"

TESTNET_DID = "did:cheqd:testnet:zFWM1mKVGGU2gHYuLAQcTJfZBebqBpGf"
TESTNET_DID = "did:cheqd:testnet:DAzMQo4MDMxCjgwM"
TESTNET_FRAGMENT = TESTNET_DID + "#key1"
FAKE_TESTNET_DID = "did:cheqd:testnet:zF7rhDBfUt9d1gJPjx7s1JXfUY7oVWkY"
TESTNET_RESOURCE = "did:cheqd:testnet:DAzMQo4MDMxCjgwM" + "/resources/44547089-170b-4f5a-bcbc-06e46e0089e4"
TESTNET_RESOURCE = TESTNET_DID + "/resources/44547089-170b-4f5a-bcbc-06e46e0089e4"
TESTNET_RESOURCE_NAME = "Demo Resource"
FAKE_TESTNET_FRAGMENT = TESTNET_DID + "#fake_key"
FAKE_TESTNET_RESOURCE = TESTNET_DID + "/resources/76471e8c-0d1c-4b97-9b11-17b65e024334"

Expand Down
Loading

0 comments on commit a60c64e

Please sign in to comment.