Skip to content

Commit

Permalink
feat: Use Echo routing for different DID URLs (#44)
Browse files Browse the repository at this point in the history
  • Loading branch information
Toktar authored Sep 7, 2022
2 parents 61df27e + 15984d1 commit ed34f5a
Show file tree
Hide file tree
Showing 21 changed files with 1,158 additions and 595 deletions.
38 changes: 38 additions & 0 deletions cmd/error_handler.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package cmd

import (
"net/http"

"github.com/cheqd/did-resolver/types"
"github.com/labstack/echo/v4"
"github.com/rs/zerolog/log"
)

func CustomHTTPErrorHandler(err error, c echo.Context) {
if err == nil {
return
}
identityError := generateIdentityError(err)
if identityError.Code == http.StatusInternalServerError {
log.Error().Err(identityError.Internal)
} else {
log.Warn().Err(identityError.Internal)
}
c.Response().Header().Set(echo.HeaderContentType, string(identityError.ContentType))
err = c.JSONPretty(identityError.Code, identityError.DisplayMessage(), " ")
if err != nil {
log.Error().Err(err)
}
}

func generateIdentityError(err error) *types.IdentityError {
identityError, ok := err.(*types.IdentityError)
if ok {
return identityError
}
he, ok := err.(*echo.HTTPError)
if !ok || he.Code != http.StatusNotFound {
return types.NewInternalError("", types.JSON, err, false)
}
return types.NewInvalidDIDUrlError("", types.JSON, err, false)
}
45 changes: 7 additions & 38 deletions cmd/serve.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@ import (
"net/http"
"strings"

cheqdUtils "github.com/cheqd/cheqd-node/x/cheqd/utils"
"github.com/cheqd/did-resolver/services"
"github.com/cheqd/did-resolver/types"
"github.com/cheqd/did-resolver/utils"
"github.com/labstack/echo/v4"
"github.com/labstack/echo/v4/middleware"
Expand Down Expand Up @@ -43,6 +41,7 @@ func serve() {

// Echo instance
e := echo.New()
e.HTTPErrorHandler = CustomHTTPErrorHandler

// Middleware
e.Use(middleware.Logger())
Expand All @@ -66,42 +65,12 @@ func serve() {
requestService := services.NewRequestService(config.Resolver.Method, ledgerService)

// Routes
e.GET(config.Api.ResolverPath, func(c echo.Context) error {
didUrl := c.Param("did")
log.Debug().Msgf("DID: %s", didUrl)

accept := c.Request().Header.Get(echo.HeaderAccept)
log.Trace().Msgf("Accept: %s", accept)

var requestedContentType types.ContentType

if strings.Contains(accept, "*/*") || strings.Contains(accept, string(types.DIDJSONLD)) {
requestedContentType = types.DIDJSONLD
} else if strings.Contains(accept, string(types.DIDJSON)) {
requestedContentType = types.DIDJSON
} else if strings.Contains(accept, string(types.JSONLD)) {
requestedContentType = types.JSONLD
} else {
requestedContentType = types.JSON
}
log.Debug().Msgf("Requested content type: %s", requestedContentType)

_, path, _, _, _ := cheqdUtils.TrySplitDIDUrl(didUrl)
log.Debug().Msg(path)
if utils.IsCollectionResourcesPathRedirect(path) {
return c.Redirect(http.StatusMovedPermanently, "all")
}
resolutionResponse := requestService.ProcessDIDRequest(didUrl, types.ResolutionOption{Accept: requestedContentType})

c.Response().Header().Set(echo.HeaderContentType, resolutionResponse.GetContentType())

// if contentType != dereferencingOptions.Accept {
// return didDereferencing.ContentStream, statusCode, contentType
// }
if utils.IsResourceDataPath(path) && resolutionResponse.GetStatus() == http.StatusOK {
return c.Blob(resolutionResponse.GetStatus(), resolutionResponse.GetContentType(), resolutionResponse.GetBytes())
}
return c.JSONPretty(resolutionResponse.GetStatus(), resolutionResponse, " ")
e.GET(config.Api.ResolverPath+":did", requestService.ResolveDIDDoc)
e.GET(config.Api.ResolverPath+":did/resources/:resource", requestService.DereferenceResourceData)
e.GET(config.Api.ResolverPath+":did/resources/:resource/metadata", requestService.DereferenceResourceMetadata)
e.GET(config.Api.ResolverPath+":did/resources/all", requestService.DereferenceCollectionResources)
e.GET(config.Api.ResolverPath+":did/resources/", func(c echo.Context) error {
return c.Redirect(http.StatusMovedPermanently, "all")
})

log.Info().Msg("Starting listener")
Expand Down
2 changes: 1 addition & 1 deletion config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@ resolver:

api:
listener: "0.0.0.0:8080"
resolverPath: "/1.0/identifiers/:did"
resolverPath: "/1.0/identifiers/"

logLevel: "warn"
123 changes: 122 additions & 1 deletion services/diddoc_service.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,26 @@
package services

import (
"net/url"
"strings"

cheqdTypes "github.com/cheqd/cheqd-node/x/cheqd/types"
cheqdUtils "github.com/cheqd/cheqd-node/x/cheqd/utils"
"github.com/cheqd/did-resolver/types"
"github.com/rs/zerolog/log"
)

type DIDDocService struct{}
type DIDDocService struct {
didMethod string
ledgerService LedgerServiceI
}

func NewDIDDocService(didMethod string, ledgerService LedgerServiceI) DIDDocService {
return DIDDocService{
didMethod: didMethod,
ledgerService: ledgerService,
}
}

func (DIDDocService) GetDIDFragment(fragmentId string, didDoc types.DidDoc) types.ContentStreamI {
for _, verMethod := range didDoc.VerificationMethod {
Expand All @@ -22,3 +36,110 @@ func (DIDDocService) GetDIDFragment(fragmentId string, didDoc types.DidDoc) type

return nil
}

func (dds DIDDocService) ProcessDIDRequest(did string, fragmentId string, queries url.Values, flag *string, contentType types.ContentType) (types.ResolutionResultI, *types.IdentityError) {
log.Trace().Msgf("ProcessDIDRequest %s, %s, %s", did, fragmentId, queries)
var result types.ResolutionResultI
var err *types.IdentityError
var isDereferencing bool

if len(queries) > 0 || flag != nil {
return nil, types.NewRepresentationNotSupportedError(did, contentType, nil, true)
} else if fragmentId != "" {
log.Trace().Msgf("Dereferencing %s, %s, %s", did, fragmentId, queries)
result, err = dds.dereferenceSecondary(did, fragmentId, contentType)
isDereferencing = true
} else {
log.Trace().Msgf("Resolving %s", did)
result, err = dds.Resolve(did, contentType)
isDereferencing = false
}

if err != nil {
err.IsDereferencing = isDereferencing
return nil, err
}
return result, nil
}

// https://w3c-ccg.github.io/did-resolution/#resolving
func (dds DIDDocService) Resolve(did string, contentType types.ContentType) (*types.DidResolution, *types.IdentityError) {
if !contentType.IsSupported() {
return nil, types.NewRepresentationNotSupportedError(did, types.JSON, nil, false)
}
didResolutionMetadata := types.NewResolutionMetadata(did, contentType, "")

if didMethod, _, _, _ := cheqdUtils.TrySplitDID(did); didMethod != dds.didMethod {
return nil, types.NewMethodNotSupportedError(did, contentType, nil, false)
}
if !cheqdUtils.IsValidDID(did, "", dds.ledgerService.GetNamespaces()) {
return nil, types.NewInvalidDIDError(did, contentType, nil, false)
}

protoDidDoc, metadata, err := dds.ledgerService.QueryDIDDoc(did)
if err != nil {
err.ContentType = contentType
return nil, err
}

resolvedMetadata, mErr := dds.resolveMetadata(did, *metadata, contentType)
if mErr != nil {
mErr.ContentType = contentType
return nil, mErr
}
didDoc := types.NewDidDoc(*protoDidDoc)
if didResolutionMetadata.ContentType == types.DIDJSONLD || didResolutionMetadata.ContentType == types.JSONLD {
didDoc.AddContext(types.DIDSchemaJSONLD)
} else {
didDoc.RemoveContext()
}
return &types.DidResolution{Did: &didDoc, Metadata: *resolvedMetadata, ResolutionMetadata: didResolutionMetadata}, nil
}

// https://w3c-ccg.github.io/did-resolution/#dereferencing
func (dds DIDDocService) dereferenceSecondary(did string, fragmentId string, contentType types.ContentType) (*types.DidDereferencing, *types.IdentityError) {
didResolution, err := dds.Resolve(did, contentType)
if err != nil {
err.IsDereferencing = true
return nil, err
}

metadata := didResolution.Metadata

var contentStream types.ContentStreamI
if fragmentId != "" {
contentStream = dds.GetDIDFragment(fragmentId, *didResolution.Did)
metadata = types.TransformToFragmentMetadata(metadata)
} else {
contentStream = didResolution.Did
}

if contentStream == nil {
return nil, types.NewNotFoundError(did, contentType, nil, true)
}

if contentType == types.DIDJSONLD || contentType == types.JSONLD {
contentStream.AddContext(types.DIDSchemaJSONLD)
} else {
contentStream.RemoveContext()
}

return &types.DidDereferencing{
ContentStream: contentStream,
Metadata: metadata,
DereferencingMetadata: types.DereferencingMetadata(didResolution.ResolutionMetadata),
}, nil
}

func (dds DIDDocService) resolveMetadata(did string, metadata cheqdTypes.Metadata, contentType types.ContentType) (*types.ResolutionDidDocMetadata, *types.IdentityError) {
if metadata.Resources == nil {
resolvedMetadata := types.NewResolutionDidDocMetadata(did, metadata, nil)
return &resolvedMetadata, nil
}
resources, err := dds.ledgerService.QueryCollectionResources(did)
if err != nil {
return nil, err
}
resolvedMetadata := types.NewResolutionDidDocMetadata(did, metadata, resources)
return &resolvedMetadata, nil
}
Loading

0 comments on commit ed34f5a

Please sign in to comment.