diff --git a/auth/oauth/openid.go b/auth/oauth/openid.go
index f657cc0f5a..852ecae90c 100644
--- a/auth/oauth/openid.go
+++ b/auth/oauth/openid.go
@@ -1,3 +1,21 @@
+/*
+ * Copyright (C) 2023 Nuts community
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ */
+
package oauth
// algValuesSupported contains a list of supported cipher suites for jwt_vc_json & jwt_vp_json presentation formats
diff --git a/cmd/root.go b/cmd/root.go
index c9d1d9eda2..e33ce818fc 100644
--- a/cmd/root.go
+++ b/cmd/root.go
@@ -44,6 +44,7 @@ import (
didmanAPI "github.com/nuts-foundation/nuts-node/didman/api/v1"
didmanCmd "github.com/nuts-foundation/nuts-node/didman/cmd"
"github.com/nuts-foundation/nuts-node/discovery"
+ discoveryAPI "github.com/nuts-foundation/nuts-node/discovery/api/v1"
discoveryCmd "github.com/nuts-foundation/nuts-node/discovery/cmd"
"github.com/nuts-foundation/nuts-node/events"
eventsCmd "github.com/nuts-foundation/nuts-node/events/cmd"
@@ -221,6 +222,7 @@ func CreateSystem(shutdownCallback context.CancelFunc) *core.System {
system.RegisterRoutes(authIAMAPI.New(authInstance, credentialInstance, vdrInstance, storageInstance))
system.RegisterRoutes(&authMeansAPI.Wrapper{Auth: authInstance})
system.RegisterRoutes(&didmanAPI.Wrapper{Didman: didmanInstance})
+ system.RegisterRoutes(&discoveryAPI.Wrapper{Server: discoveryInstance})
// Register engines
// without dependencies
diff --git a/codegen/configs/discovery_v1.yaml b/codegen/configs/discovery_v1.yaml
new file mode 100644
index 0000000000..ffa5d4adbb
--- /dev/null
+++ b/codegen/configs/discovery_v1.yaml
@@ -0,0 +1,10 @@
+package: v1
+generate:
+ echo-server: true
+ client: true
+ models: true
+ strict-server: true
+output-options:
+ skip-prune: true
+ exclude-schemas:
+ - VerifiablePresentation
diff --git a/discovery/api/v1/generated.go b/discovery/api/v1/generated.go
new file mode 100644
index 0000000000..34076c2ab2
--- /dev/null
+++ b/discovery/api/v1/generated.go
@@ -0,0 +1,1041 @@
+// Package v1 provides primitives to interact with the openapi HTTP API.
+//
+// Code generated by github.com/deepmap/oapi-codegen/v2 version v2.0.0 DO NOT EDIT.
+package v1
+
+import (
+ "bytes"
+ "context"
+ "encoding/json"
+ "fmt"
+ "io"
+ "net/http"
+ "net/url"
+ "strings"
+
+ "github.com/labstack/echo/v4"
+ "github.com/oapi-codegen/runtime"
+ strictecho "github.com/oapi-codegen/runtime/strictmiddleware/echo"
+)
+
+const (
+ JwtBearerAuthScopes = "jwtBearerAuth.Scopes"
+)
+
+// SearchResult defines model for SearchResult.
+type SearchResult struct {
+ // Fields Input descriptor IDs and their mapped values that from the Verifiable Credential.
+ Fields map[string]string `json:"fields"`
+
+ // Id The ID of the Verifiable Presentation.
+ Id string `json:"id"`
+
+ // Vp Verifiable Presentation
+ Vp VerifiablePresentation `json:"vp"`
+}
+
+// GetPresentationsParams defines parameters for GetPresentations.
+type GetPresentationsParams struct {
+ Tag *string `form:"tag,omitempty" json:"tag,omitempty"`
+}
+
+// SearchPresentationsParams defines parameters for SearchPresentations.
+type SearchPresentationsParams struct {
+ Query map[string]string `form:"query" json:"query"`
+}
+
+// RegisterPresentationJSONRequestBody defines body for RegisterPresentation for application/json ContentType.
+type RegisterPresentationJSONRequestBody = VerifiablePresentation
+
+// RequestEditorFn is the function signature for the RequestEditor callback function
+type RequestEditorFn func(ctx context.Context, req *http.Request) error
+
+// Doer performs HTTP requests.
+//
+// The standard http.Client implements this interface.
+type HttpRequestDoer interface {
+ Do(req *http.Request) (*http.Response, error)
+}
+
+// Client which conforms to the OpenAPI3 specification for this service.
+type Client struct {
+ // The endpoint of the server conforming to this interface, with scheme,
+ // https://api.deepmap.com for example. This can contain a path relative
+ // to the server, such as https://api.deepmap.com/dev-test, and all the
+ // paths in the swagger spec will be appended to the server.
+ Server string
+
+ // Doer for performing requests, typically a *http.Client with any
+ // customized settings, such as certificate chains.
+ Client HttpRequestDoer
+
+ // A list of callbacks for modifying requests which are generated before sending over
+ // the network.
+ RequestEditors []RequestEditorFn
+}
+
+// ClientOption allows setting custom parameters during construction
+type ClientOption func(*Client) error
+
+// Creates a new Client, with reasonable defaults
+func NewClient(server string, opts ...ClientOption) (*Client, error) {
+ // create a client with sane default values
+ client := Client{
+ Server: server,
+ }
+ // mutate client and add all optional params
+ for _, o := range opts {
+ if err := o(&client); err != nil {
+ return nil, err
+ }
+ }
+ // ensure the server URL always has a trailing slash
+ if !strings.HasSuffix(client.Server, "/") {
+ client.Server += "/"
+ }
+ // create httpClient, if not already present
+ if client.Client == nil {
+ client.Client = &http.Client{}
+ }
+ return &client, nil
+}
+
+// WithHTTPClient allows overriding the default Doer, which is
+// automatically created using http.Client. This is useful for tests.
+func WithHTTPClient(doer HttpRequestDoer) ClientOption {
+ return func(c *Client) error {
+ c.Client = doer
+ return nil
+ }
+}
+
+// WithRequestEditorFn allows setting up a callback function, which will be
+// called right before sending the request. This can be used to mutate the request.
+func WithRequestEditorFn(fn RequestEditorFn) ClientOption {
+ return func(c *Client) error {
+ c.RequestEditors = append(c.RequestEditors, fn)
+ return nil
+ }
+}
+
+// The interface specification for the client above.
+type ClientInterface interface {
+ // GetPresentations request
+ GetPresentations(ctx context.Context, serviceID string, params *GetPresentationsParams, reqEditors ...RequestEditorFn) (*http.Response, error)
+
+ // RegisterPresentationWithBody request with any body
+ RegisterPresentationWithBody(ctx context.Context, serviceID string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error)
+
+ RegisterPresentation(ctx context.Context, serviceID string, body RegisterPresentationJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error)
+
+ // SearchPresentations request
+ SearchPresentations(ctx context.Context, serviceID string, params *SearchPresentationsParams, reqEditors ...RequestEditorFn) (*http.Response, error)
+}
+
+func (c *Client) GetPresentations(ctx context.Context, serviceID string, params *GetPresentationsParams, reqEditors ...RequestEditorFn) (*http.Response, error) {
+ req, err := NewGetPresentationsRequest(c.Server, serviceID, params)
+ if err != nil {
+ return nil, err
+ }
+ req = req.WithContext(ctx)
+ if err := c.applyEditors(ctx, req, reqEditors); err != nil {
+ return nil, err
+ }
+ return c.Client.Do(req)
+}
+
+func (c *Client) RegisterPresentationWithBody(ctx context.Context, serviceID string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) {
+ req, err := NewRegisterPresentationRequestWithBody(c.Server, serviceID, contentType, body)
+ if err != nil {
+ return nil, err
+ }
+ req = req.WithContext(ctx)
+ if err := c.applyEditors(ctx, req, reqEditors); err != nil {
+ return nil, err
+ }
+ return c.Client.Do(req)
+}
+
+func (c *Client) RegisterPresentation(ctx context.Context, serviceID string, body RegisterPresentationJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) {
+ req, err := NewRegisterPresentationRequest(c.Server, serviceID, body)
+ if err != nil {
+ return nil, err
+ }
+ req = req.WithContext(ctx)
+ if err := c.applyEditors(ctx, req, reqEditors); err != nil {
+ return nil, err
+ }
+ return c.Client.Do(req)
+}
+
+func (c *Client) SearchPresentations(ctx context.Context, serviceID string, params *SearchPresentationsParams, reqEditors ...RequestEditorFn) (*http.Response, error) {
+ req, err := NewSearchPresentationsRequest(c.Server, serviceID, params)
+ if err != nil {
+ return nil, err
+ }
+ req = req.WithContext(ctx)
+ if err := c.applyEditors(ctx, req, reqEditors); err != nil {
+ return nil, err
+ }
+ return c.Client.Do(req)
+}
+
+// NewGetPresentationsRequest generates requests for GetPresentations
+func NewGetPresentationsRequest(server string, serviceID string, params *GetPresentationsParams) (*http.Request, error) {
+ var err error
+
+ var pathParam0 string
+
+ pathParam0, err = runtime.StyleParamWithLocation("simple", false, "serviceID", runtime.ParamLocationPath, serviceID)
+ if err != nil {
+ return nil, err
+ }
+
+ serverURL, err := url.Parse(server)
+ if err != nil {
+ return nil, err
+ }
+
+ operationPath := fmt.Sprintf("/discovery/%s", pathParam0)
+ if operationPath[0] == '/' {
+ operationPath = "." + operationPath
+ }
+
+ queryURL, err := serverURL.Parse(operationPath)
+ if err != nil {
+ return nil, err
+ }
+
+ if params != nil {
+ queryValues := queryURL.Query()
+
+ if params.Tag != nil {
+
+ if queryFrag, err := runtime.StyleParamWithLocation("form", true, "tag", runtime.ParamLocationQuery, *params.Tag); 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
+ }
+
+ return req, nil
+}
+
+// NewRegisterPresentationRequest calls the generic RegisterPresentation builder with application/json body
+func NewRegisterPresentationRequest(server string, serviceID string, body RegisterPresentationJSONRequestBody) (*http.Request, error) {
+ var bodyReader io.Reader
+ buf, err := json.Marshal(body)
+ if err != nil {
+ return nil, err
+ }
+ bodyReader = bytes.NewReader(buf)
+ return NewRegisterPresentationRequestWithBody(server, serviceID, "application/json", bodyReader)
+}
+
+// NewRegisterPresentationRequestWithBody generates requests for RegisterPresentation with any type of body
+func NewRegisterPresentationRequestWithBody(server string, serviceID string, contentType string, body io.Reader) (*http.Request, error) {
+ var err error
+
+ var pathParam0 string
+
+ pathParam0, err = runtime.StyleParamWithLocation("simple", false, "serviceID", runtime.ParamLocationPath, serviceID)
+ if err != nil {
+ return nil, err
+ }
+
+ serverURL, err := url.Parse(server)
+ if err != nil {
+ return nil, err
+ }
+
+ operationPath := fmt.Sprintf("/discovery/%s", pathParam0)
+ if operationPath[0] == '/' {
+ operationPath = "." + operationPath
+ }
+
+ queryURL, err := serverURL.Parse(operationPath)
+ if err != nil {
+ return nil, err
+ }
+
+ req, err := http.NewRequest("POST", queryURL.String(), body)
+ if err != nil {
+ return nil, err
+ }
+
+ req.Header.Add("Content-Type", contentType)
+
+ return req, nil
+}
+
+// NewSearchPresentationsRequest generates requests for SearchPresentations
+func NewSearchPresentationsRequest(server string, serviceID string, params *SearchPresentationsParams) (*http.Request, error) {
+ var err error
+
+ var pathParam0 string
+
+ pathParam0, err = runtime.StyleParamWithLocation("simple", false, "serviceID", runtime.ParamLocationPath, serviceID)
+ if err != nil {
+ return nil, err
+ }
+
+ serverURL, err := url.Parse(server)
+ if err != nil {
+ return nil, err
+ }
+
+ operationPath := fmt.Sprintf("/discovery/%s/search", pathParam0)
+ if operationPath[0] == '/' {
+ operationPath = "." + operationPath
+ }
+
+ queryURL, err := serverURL.Parse(operationPath)
+ if err != nil {
+ return nil, err
+ }
+
+ if params != nil {
+ queryValues := queryURL.Query()
+
+ if queryFrag, err := runtime.StyleParamWithLocation("form", true, "query", runtime.ParamLocationQuery, params.Query); 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
+ }
+
+ return req, nil
+}
+
+func (c *Client) applyEditors(ctx context.Context, req *http.Request, additionalEditors []RequestEditorFn) error {
+ for _, r := range c.RequestEditors {
+ if err := r(ctx, req); err != nil {
+ return err
+ }
+ }
+ for _, r := range additionalEditors {
+ if err := r(ctx, req); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+// ClientWithResponses builds on ClientInterface to offer response payloads
+type ClientWithResponses struct {
+ ClientInterface
+}
+
+// NewClientWithResponses creates a new ClientWithResponses, which wraps
+// Client with return type handling
+func NewClientWithResponses(server string, opts ...ClientOption) (*ClientWithResponses, error) {
+ client, err := NewClient(server, opts...)
+ if err != nil {
+ return nil, err
+ }
+ return &ClientWithResponses{client}, nil
+}
+
+// WithBaseURL overrides the baseURL.
+func WithBaseURL(baseURL string) ClientOption {
+ return func(c *Client) error {
+ newBaseURL, err := url.Parse(baseURL)
+ if err != nil {
+ return err
+ }
+ c.Server = newBaseURL.String()
+ return nil
+ }
+}
+
+// ClientWithResponsesInterface is the interface specification for the client with responses above.
+type ClientWithResponsesInterface interface {
+ // GetPresentationsWithResponse request
+ GetPresentationsWithResponse(ctx context.Context, serviceID string, params *GetPresentationsParams, reqEditors ...RequestEditorFn) (*GetPresentationsResponse, error)
+
+ // RegisterPresentationWithBodyWithResponse request with any body
+ RegisterPresentationWithBodyWithResponse(ctx context.Context, serviceID string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*RegisterPresentationResponse, error)
+
+ RegisterPresentationWithResponse(ctx context.Context, serviceID string, body RegisterPresentationJSONRequestBody, reqEditors ...RequestEditorFn) (*RegisterPresentationResponse, error)
+
+ // SearchPresentationsWithResponse request
+ SearchPresentationsWithResponse(ctx context.Context, serviceID string, params *SearchPresentationsParams, reqEditors ...RequestEditorFn) (*SearchPresentationsResponse, error)
+}
+
+type GetPresentationsResponse struct {
+ Body []byte
+ HTTPResponse *http.Response
+ JSON200 *struct {
+ Entries []VerifiablePresentation `json:"entries"`
+ Tag string `json:"tag"`
+ }
+ ApplicationproblemJSONDefault *struct {
+ // Detail A human-readable explanation specific to this occurrence of the problem.
+ Detail string `json:"detail"`
+
+ // Status HTTP statuscode
+ Status float32 `json:"status"`
+
+ // Title A short, human-readable summary of the problem type.
+ Title string `json:"title"`
+ }
+}
+
+// Status returns HTTPResponse.Status
+func (r GetPresentationsResponse) Status() string {
+ if r.HTTPResponse != nil {
+ return r.HTTPResponse.Status
+ }
+ return http.StatusText(0)
+}
+
+// StatusCode returns HTTPResponse.StatusCode
+func (r GetPresentationsResponse) StatusCode() int {
+ if r.HTTPResponse != nil {
+ return r.HTTPResponse.StatusCode
+ }
+ return 0
+}
+
+type RegisterPresentationResponse struct {
+ Body []byte
+ HTTPResponse *http.Response
+ ApplicationproblemJSON400 *struct {
+ // Detail A human-readable explanation specific to this occurrence of the problem.
+ Detail string `json:"detail"`
+
+ // Status HTTP statuscode
+ Status float32 `json:"status"`
+
+ // Title A short, human-readable summary of the problem type.
+ Title string `json:"title"`
+ }
+ ApplicationproblemJSONDefault *struct {
+ // Detail A human-readable explanation specific to this occurrence of the problem.
+ Detail string `json:"detail"`
+
+ // Status HTTP statuscode
+ Status float32 `json:"status"`
+
+ // Title A short, human-readable summary of the problem type.
+ Title string `json:"title"`
+ }
+}
+
+// Status returns HTTPResponse.Status
+func (r RegisterPresentationResponse) Status() string {
+ if r.HTTPResponse != nil {
+ return r.HTTPResponse.Status
+ }
+ return http.StatusText(0)
+}
+
+// StatusCode returns HTTPResponse.StatusCode
+func (r RegisterPresentationResponse) StatusCode() int {
+ if r.HTTPResponse != nil {
+ return r.HTTPResponse.StatusCode
+ }
+ return 0
+}
+
+type SearchPresentationsResponse struct {
+ Body []byte
+ HTTPResponse *http.Response
+ JSON200 *[]SearchResult
+ ApplicationproblemJSONDefault *struct {
+ // Detail A human-readable explanation specific to this occurrence of the problem.
+ Detail string `json:"detail"`
+
+ // Status HTTP statuscode
+ Status float32 `json:"status"`
+
+ // Title A short, human-readable summary of the problem type.
+ Title string `json:"title"`
+ }
+}
+
+// Status returns HTTPResponse.Status
+func (r SearchPresentationsResponse) Status() string {
+ if r.HTTPResponse != nil {
+ return r.HTTPResponse.Status
+ }
+ return http.StatusText(0)
+}
+
+// StatusCode returns HTTPResponse.StatusCode
+func (r SearchPresentationsResponse) StatusCode() int {
+ if r.HTTPResponse != nil {
+ return r.HTTPResponse.StatusCode
+ }
+ return 0
+}
+
+// GetPresentationsWithResponse request returning *GetPresentationsResponse
+func (c *ClientWithResponses) GetPresentationsWithResponse(ctx context.Context, serviceID string, params *GetPresentationsParams, reqEditors ...RequestEditorFn) (*GetPresentationsResponse, error) {
+ rsp, err := c.GetPresentations(ctx, serviceID, params, reqEditors...)
+ if err != nil {
+ return nil, err
+ }
+ return ParseGetPresentationsResponse(rsp)
+}
+
+// RegisterPresentationWithBodyWithResponse request with arbitrary body returning *RegisterPresentationResponse
+func (c *ClientWithResponses) RegisterPresentationWithBodyWithResponse(ctx context.Context, serviceID string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*RegisterPresentationResponse, error) {
+ rsp, err := c.RegisterPresentationWithBody(ctx, serviceID, contentType, body, reqEditors...)
+ if err != nil {
+ return nil, err
+ }
+ return ParseRegisterPresentationResponse(rsp)
+}
+
+func (c *ClientWithResponses) RegisterPresentationWithResponse(ctx context.Context, serviceID string, body RegisterPresentationJSONRequestBody, reqEditors ...RequestEditorFn) (*RegisterPresentationResponse, error) {
+ rsp, err := c.RegisterPresentation(ctx, serviceID, body, reqEditors...)
+ if err != nil {
+ return nil, err
+ }
+ return ParseRegisterPresentationResponse(rsp)
+}
+
+// SearchPresentationsWithResponse request returning *SearchPresentationsResponse
+func (c *ClientWithResponses) SearchPresentationsWithResponse(ctx context.Context, serviceID string, params *SearchPresentationsParams, reqEditors ...RequestEditorFn) (*SearchPresentationsResponse, error) {
+ rsp, err := c.SearchPresentations(ctx, serviceID, params, reqEditors...)
+ if err != nil {
+ return nil, err
+ }
+ return ParseSearchPresentationsResponse(rsp)
+}
+
+// ParseGetPresentationsResponse parses an HTTP response from a GetPresentationsWithResponse call
+func ParseGetPresentationsResponse(rsp *http.Response) (*GetPresentationsResponse, error) {
+ bodyBytes, err := io.ReadAll(rsp.Body)
+ defer func() { _ = rsp.Body.Close() }()
+ if err != nil {
+ return nil, err
+ }
+
+ response := &GetPresentationsResponse{
+ Body: bodyBytes,
+ HTTPResponse: rsp,
+ }
+
+ switch {
+ case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200:
+ var dest struct {
+ Entries []VerifiablePresentation `json:"entries"`
+ Tag string `json:"tag"`
+ }
+ if err := json.Unmarshal(bodyBytes, &dest); err != nil {
+ return nil, err
+ }
+ response.JSON200 = &dest
+
+ case strings.Contains(rsp.Header.Get("Content-Type"), "json") && true:
+ var dest struct {
+ // Detail A human-readable explanation specific to this occurrence of the problem.
+ Detail string `json:"detail"`
+
+ // Status HTTP statuscode
+ Status float32 `json:"status"`
+
+ // Title A short, human-readable summary of the problem type.
+ Title string `json:"title"`
+ }
+ if err := json.Unmarshal(bodyBytes, &dest); err != nil {
+ return nil, err
+ }
+ response.ApplicationproblemJSONDefault = &dest
+
+ }
+
+ return response, nil
+}
+
+// ParseRegisterPresentationResponse parses an HTTP response from a RegisterPresentationWithResponse call
+func ParseRegisterPresentationResponse(rsp *http.Response) (*RegisterPresentationResponse, error) {
+ bodyBytes, err := io.ReadAll(rsp.Body)
+ defer func() { _ = rsp.Body.Close() }()
+ if err != nil {
+ return nil, err
+ }
+
+ response := &RegisterPresentationResponse{
+ Body: bodyBytes,
+ HTTPResponse: rsp,
+ }
+
+ switch {
+ case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400:
+ var dest struct {
+ // Detail A human-readable explanation specific to this occurrence of the problem.
+ Detail string `json:"detail"`
+
+ // Status HTTP statuscode
+ Status float32 `json:"status"`
+
+ // Title A short, human-readable summary of the problem type.
+ Title string `json:"title"`
+ }
+ if err := json.Unmarshal(bodyBytes, &dest); err != nil {
+ return nil, err
+ }
+ response.ApplicationproblemJSON400 = &dest
+
+ case strings.Contains(rsp.Header.Get("Content-Type"), "json") && true:
+ var dest struct {
+ // Detail A human-readable explanation specific to this occurrence of the problem.
+ Detail string `json:"detail"`
+
+ // Status HTTP statuscode
+ Status float32 `json:"status"`
+
+ // Title A short, human-readable summary of the problem type.
+ Title string `json:"title"`
+ }
+ if err := json.Unmarshal(bodyBytes, &dest); err != nil {
+ return nil, err
+ }
+ response.ApplicationproblemJSONDefault = &dest
+
+ }
+
+ return response, nil
+}
+
+// ParseSearchPresentationsResponse parses an HTTP response from a SearchPresentationsWithResponse call
+func ParseSearchPresentationsResponse(rsp *http.Response) (*SearchPresentationsResponse, error) {
+ bodyBytes, err := io.ReadAll(rsp.Body)
+ defer func() { _ = rsp.Body.Close() }()
+ if err != nil {
+ return nil, err
+ }
+
+ response := &SearchPresentationsResponse{
+ Body: bodyBytes,
+ HTTPResponse: rsp,
+ }
+
+ switch {
+ case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200:
+ var dest []SearchResult
+ if err := json.Unmarshal(bodyBytes, &dest); err != nil {
+ return nil, err
+ }
+ response.JSON200 = &dest
+
+ case strings.Contains(rsp.Header.Get("Content-Type"), "json") && true:
+ var dest struct {
+ // Detail A human-readable explanation specific to this occurrence of the problem.
+ Detail string `json:"detail"`
+
+ // Status HTTP statuscode
+ Status float32 `json:"status"`
+
+ // Title A short, human-readable summary of the problem type.
+ Title string `json:"title"`
+ }
+ if err := json.Unmarshal(bodyBytes, &dest); err != nil {
+ return nil, err
+ }
+ response.ApplicationproblemJSONDefault = &dest
+
+ }
+
+ return response, nil
+}
+
+// ServerInterface represents all server handlers.
+type ServerInterface interface {
+ // Retrieves the presentations of a discovery service.
+ // (GET /discovery/{serviceID})
+ GetPresentations(ctx echo.Context, serviceID string, params GetPresentationsParams) error
+ // Register a presentation on the discovery service.
+ // (POST /discovery/{serviceID})
+ RegisterPresentation(ctx echo.Context, serviceID string) error
+ // Searches for presentations registered on the discovery service.
+ // (GET /discovery/{serviceID}/search)
+ SearchPresentations(ctx echo.Context, serviceID string, params SearchPresentationsParams) error
+}
+
+// ServerInterfaceWrapper converts echo contexts to parameters.
+type ServerInterfaceWrapper struct {
+ Handler ServerInterface
+}
+
+// GetPresentations converts echo context to params.
+func (w *ServerInterfaceWrapper) GetPresentations(ctx echo.Context) error {
+ var err error
+ // ------------- Path parameter "serviceID" -------------
+ var serviceID string
+
+ err = runtime.BindStyledParameterWithLocation("simple", false, "serviceID", runtime.ParamLocationPath, ctx.Param("serviceID"), &serviceID)
+ if err != nil {
+ return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter serviceID: %s", err))
+ }
+
+ ctx.Set(JwtBearerAuthScopes, []string{})
+
+ // Parameter object where we will unmarshal all parameters from the context
+ var params GetPresentationsParams
+ // ------------- Optional query parameter "tag" -------------
+
+ err = runtime.BindQueryParameter("form", true, false, "tag", ctx.QueryParams(), ¶ms.Tag)
+ if err != nil {
+ return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter tag: %s", err))
+ }
+
+ // Invoke the callback with all the unmarshaled arguments
+ err = w.Handler.GetPresentations(ctx, serviceID, params)
+ return err
+}
+
+// RegisterPresentation converts echo context to params.
+func (w *ServerInterfaceWrapper) RegisterPresentation(ctx echo.Context) error {
+ var err error
+ // ------------- Path parameter "serviceID" -------------
+ var serviceID string
+
+ err = runtime.BindStyledParameterWithLocation("simple", false, "serviceID", runtime.ParamLocationPath, ctx.Param("serviceID"), &serviceID)
+ if err != nil {
+ return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter serviceID: %s", err))
+ }
+
+ ctx.Set(JwtBearerAuthScopes, []string{})
+
+ // Invoke the callback with all the unmarshaled arguments
+ err = w.Handler.RegisterPresentation(ctx, serviceID)
+ return err
+}
+
+// SearchPresentations converts echo context to params.
+func (w *ServerInterfaceWrapper) SearchPresentations(ctx echo.Context) error {
+ var err error
+ // ------------- Path parameter "serviceID" -------------
+ var serviceID string
+
+ err = runtime.BindStyledParameterWithLocation("simple", false, "serviceID", runtime.ParamLocationPath, ctx.Param("serviceID"), &serviceID)
+ if err != nil {
+ return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter serviceID: %s", err))
+ }
+
+ ctx.Set(JwtBearerAuthScopes, []string{})
+
+ // Parameter object where we will unmarshal all parameters from the context
+ var params SearchPresentationsParams
+ // ------------- Required query parameter "query" -------------
+
+ err = runtime.BindQueryParameter("form", true, true, "query", ctx.QueryParams(), ¶ms.Query)
+ if err != nil {
+ return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter query: %s", err))
+ }
+
+ // Invoke the callback with all the unmarshaled arguments
+ err = w.Handler.SearchPresentations(ctx, serviceID, params)
+ return err
+}
+
+// This is a simple interface which specifies echo.Route addition functions which
+// are present on both echo.Echo and echo.Group, since we want to allow using
+// either of them for path registration
+type EchoRouter interface {
+ CONNECT(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route
+ DELETE(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route
+ GET(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route
+ HEAD(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route
+ OPTIONS(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route
+ PATCH(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route
+ POST(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route
+ PUT(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route
+ TRACE(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route
+}
+
+// RegisterHandlers adds each server route to the EchoRouter.
+func RegisterHandlers(router EchoRouter, si ServerInterface) {
+ RegisterHandlersWithBaseURL(router, si, "")
+}
+
+// Registers handlers, and prepends BaseURL to the paths, so that the paths
+// can be served under a prefix.
+func RegisterHandlersWithBaseURL(router EchoRouter, si ServerInterface, baseURL string) {
+
+ wrapper := ServerInterfaceWrapper{
+ Handler: si,
+ }
+
+ router.GET(baseURL+"/discovery/:serviceID", wrapper.GetPresentations)
+ router.POST(baseURL+"/discovery/:serviceID", wrapper.RegisterPresentation)
+ router.GET(baseURL+"/discovery/:serviceID/search", wrapper.SearchPresentations)
+
+}
+
+type GetPresentationsRequestObject struct {
+ ServiceID string `json:"serviceID"`
+ Params GetPresentationsParams
+}
+
+type GetPresentationsResponseObject interface {
+ VisitGetPresentationsResponse(w http.ResponseWriter) error
+}
+
+type GetPresentations200JSONResponse struct {
+ Entries []VerifiablePresentation `json:"entries"`
+ Tag string `json:"tag"`
+}
+
+func (response GetPresentations200JSONResponse) VisitGetPresentationsResponse(w http.ResponseWriter) error {
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(200)
+
+ return json.NewEncoder(w).Encode(response)
+}
+
+type GetPresentationsdefaultApplicationProblemPlusJSONResponse struct {
+ Body struct {
+ // Detail A human-readable explanation specific to this occurrence of the problem.
+ Detail string `json:"detail"`
+
+ // Status HTTP statuscode
+ Status float32 `json:"status"`
+
+ // Title A short, human-readable summary of the problem type.
+ Title string `json:"title"`
+ }
+ StatusCode int
+}
+
+func (response GetPresentationsdefaultApplicationProblemPlusJSONResponse) VisitGetPresentationsResponse(w http.ResponseWriter) error {
+ w.Header().Set("Content-Type", "application/problem+json")
+ w.WriteHeader(response.StatusCode)
+
+ return json.NewEncoder(w).Encode(response.Body)
+}
+
+type RegisterPresentationRequestObject struct {
+ ServiceID string `json:"serviceID"`
+ Body *RegisterPresentationJSONRequestBody
+}
+
+type RegisterPresentationResponseObject interface {
+ VisitRegisterPresentationResponse(w http.ResponseWriter) error
+}
+
+type RegisterPresentation201Response struct {
+}
+
+func (response RegisterPresentation201Response) VisitRegisterPresentationResponse(w http.ResponseWriter) error {
+ w.WriteHeader(201)
+ return nil
+}
+
+type RegisterPresentation400ApplicationProblemPlusJSONResponse struct {
+ // Detail A human-readable explanation specific to this occurrence of the problem.
+ Detail string `json:"detail"`
+
+ // Status HTTP statuscode
+ Status float32 `json:"status"`
+
+ // Title A short, human-readable summary of the problem type.
+ Title string `json:"title"`
+}
+
+func (response RegisterPresentation400ApplicationProblemPlusJSONResponse) VisitRegisterPresentationResponse(w http.ResponseWriter) error {
+ w.Header().Set("Content-Type", "application/problem+json")
+ w.WriteHeader(400)
+
+ return json.NewEncoder(w).Encode(response)
+}
+
+type RegisterPresentationdefaultApplicationProblemPlusJSONResponse struct {
+ Body struct {
+ // Detail A human-readable explanation specific to this occurrence of the problem.
+ Detail string `json:"detail"`
+
+ // Status HTTP statuscode
+ Status float32 `json:"status"`
+
+ // Title A short, human-readable summary of the problem type.
+ Title string `json:"title"`
+ }
+ StatusCode int
+}
+
+func (response RegisterPresentationdefaultApplicationProblemPlusJSONResponse) VisitRegisterPresentationResponse(w http.ResponseWriter) error {
+ w.Header().Set("Content-Type", "application/problem+json")
+ w.WriteHeader(response.StatusCode)
+
+ return json.NewEncoder(w).Encode(response.Body)
+}
+
+type SearchPresentationsRequestObject struct {
+ ServiceID string `json:"serviceID"`
+ Params SearchPresentationsParams
+}
+
+type SearchPresentationsResponseObject interface {
+ VisitSearchPresentationsResponse(w http.ResponseWriter) error
+}
+
+type SearchPresentations200JSONResponse []SearchResult
+
+func (response SearchPresentations200JSONResponse) VisitSearchPresentationsResponse(w http.ResponseWriter) error {
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(200)
+
+ return json.NewEncoder(w).Encode(response)
+}
+
+type SearchPresentationsdefaultApplicationProblemPlusJSONResponse struct {
+ Body struct {
+ // Detail A human-readable explanation specific to this occurrence of the problem.
+ Detail string `json:"detail"`
+
+ // Status HTTP statuscode
+ Status float32 `json:"status"`
+
+ // Title A short, human-readable summary of the problem type.
+ Title string `json:"title"`
+ }
+ StatusCode int
+}
+
+func (response SearchPresentationsdefaultApplicationProblemPlusJSONResponse) VisitSearchPresentationsResponse(w http.ResponseWriter) error {
+ w.Header().Set("Content-Type", "application/problem+json")
+ w.WriteHeader(response.StatusCode)
+
+ return json.NewEncoder(w).Encode(response.Body)
+}
+
+// StrictServerInterface represents all server handlers.
+type StrictServerInterface interface {
+ // Retrieves the presentations of a discovery service.
+ // (GET /discovery/{serviceID})
+ GetPresentations(ctx context.Context, request GetPresentationsRequestObject) (GetPresentationsResponseObject, error)
+ // Register a presentation on the discovery service.
+ // (POST /discovery/{serviceID})
+ RegisterPresentation(ctx context.Context, request RegisterPresentationRequestObject) (RegisterPresentationResponseObject, error)
+ // Searches for presentations registered on the discovery service.
+ // (GET /discovery/{serviceID}/search)
+ SearchPresentations(ctx context.Context, request SearchPresentationsRequestObject) (SearchPresentationsResponseObject, error)
+}
+
+type StrictHandlerFunc = strictecho.StrictEchoHandlerFunc
+type StrictMiddlewareFunc = strictecho.StrictEchoMiddlewareFunc
+
+func NewStrictHandler(ssi StrictServerInterface, middlewares []StrictMiddlewareFunc) ServerInterface {
+ return &strictHandler{ssi: ssi, middlewares: middlewares}
+}
+
+type strictHandler struct {
+ ssi StrictServerInterface
+ middlewares []StrictMiddlewareFunc
+}
+
+// GetPresentations operation middleware
+func (sh *strictHandler) GetPresentations(ctx echo.Context, serviceID string, params GetPresentationsParams) error {
+ var request GetPresentationsRequestObject
+
+ request.ServiceID = serviceID
+ request.Params = params
+
+ handler := func(ctx echo.Context, request interface{}) (interface{}, error) {
+ return sh.ssi.GetPresentations(ctx.Request().Context(), request.(GetPresentationsRequestObject))
+ }
+ for _, middleware := range sh.middlewares {
+ handler = middleware(handler, "GetPresentations")
+ }
+
+ response, err := handler(ctx, request)
+
+ if err != nil {
+ return err
+ } else if validResponse, ok := response.(GetPresentationsResponseObject); ok {
+ return validResponse.VisitGetPresentationsResponse(ctx.Response())
+ } else if response != nil {
+ return fmt.Errorf("unexpected response type: %T", response)
+ }
+ return nil
+}
+
+// RegisterPresentation operation middleware
+func (sh *strictHandler) RegisterPresentation(ctx echo.Context, serviceID string) error {
+ var request RegisterPresentationRequestObject
+
+ request.ServiceID = serviceID
+
+ var body RegisterPresentationJSONRequestBody
+ if err := ctx.Bind(&body); err != nil {
+ return err
+ }
+ request.Body = &body
+
+ handler := func(ctx echo.Context, request interface{}) (interface{}, error) {
+ return sh.ssi.RegisterPresentation(ctx.Request().Context(), request.(RegisterPresentationRequestObject))
+ }
+ for _, middleware := range sh.middlewares {
+ handler = middleware(handler, "RegisterPresentation")
+ }
+
+ response, err := handler(ctx, request)
+
+ if err != nil {
+ return err
+ } else if validResponse, ok := response.(RegisterPresentationResponseObject); ok {
+ return validResponse.VisitRegisterPresentationResponse(ctx.Response())
+ } else if response != nil {
+ return fmt.Errorf("unexpected response type: %T", response)
+ }
+ return nil
+}
+
+// SearchPresentations operation middleware
+func (sh *strictHandler) SearchPresentations(ctx echo.Context, serviceID string, params SearchPresentationsParams) error {
+ var request SearchPresentationsRequestObject
+
+ request.ServiceID = serviceID
+ request.Params = params
+
+ handler := func(ctx echo.Context, request interface{}) (interface{}, error) {
+ return sh.ssi.SearchPresentations(ctx.Request().Context(), request.(SearchPresentationsRequestObject))
+ }
+ for _, middleware := range sh.middlewares {
+ handler = middleware(handler, "SearchPresentations")
+ }
+
+ response, err := handler(ctx, request)
+
+ if err != nil {
+ return err
+ } else if validResponse, ok := response.(SearchPresentationsResponseObject); ok {
+ return validResponse.VisitSearchPresentationsResponse(ctx.Response())
+ } else if response != nil {
+ return fmt.Errorf("unexpected response type: %T", response)
+ }
+ return nil
+}
diff --git a/discovery/api/v1/types.go b/discovery/api/v1/types.go
new file mode 100644
index 0000000000..7a9ff02004
--- /dev/null
+++ b/discovery/api/v1/types.go
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2023 Nuts community
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ */
+
+package v1
+
+import "github.com/nuts-foundation/go-did/vc"
+
+// VerifiablePresentation is a type alias for the VerifiablePresentation from the go-did library.
+type VerifiablePresentation = vc.VerifiablePresentation
diff --git a/discovery/api/v1/wapper.go b/discovery/api/v1/wapper.go
new file mode 100644
index 0000000000..c7c9e94976
--- /dev/null
+++ b/discovery/api/v1/wapper.go
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2023 Nuts community
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ */
+
+package v1
+
+import (
+ "context"
+ "errors"
+ "github.com/labstack/echo/v4"
+ "github.com/nuts-foundation/nuts-node/core"
+ "github.com/nuts-foundation/nuts-node/discovery"
+ "net/http"
+)
+
+var _ StrictServerInterface = (*Wrapper)(nil)
+var _ core.ErrorStatusCodeResolver = (*Wrapper)(nil)
+
+type Wrapper struct {
+ Server discovery.Server
+ Client discovery.Client
+}
+
+func (w *Wrapper) ResolveStatusCode(err error) int {
+ // todo
+ switch {
+ case errors.Is(err, discovery.ErrServerModeDisabled):
+ return http.StatusBadRequest
+ case errors.Is(err, discovery.ErrInvalidPresentation):
+ return http.StatusBadRequest
+ default:
+ return http.StatusInternalServerError
+ }
+}
+
+func (w *Wrapper) Routes(router core.EchoRouter) {
+ RegisterHandlers(router, NewStrictHandler(w, []StrictMiddlewareFunc{
+ func(f StrictHandlerFunc, operationID string) StrictHandlerFunc {
+ return func(ctx echo.Context, request interface{}) (response interface{}, err error) {
+ ctx.Set(core.OperationIDContextKey, operationID)
+ ctx.Set(core.ModuleNameContextKey, discovery.ModuleName)
+ ctx.Set(core.StatusCodeResolverContextKey, w)
+ return f(ctx, request)
+ }
+ },
+ }))
+}
+
+func (w *Wrapper) GetPresentations(_ context.Context, request GetPresentationsRequestObject) (GetPresentationsResponseObject, error) {
+ var tag *discovery.Tag
+ if request.Params.Tag != nil {
+ tag = new(discovery.Tag)
+ *tag = discovery.Tag(*request.Params.Tag)
+ }
+ presentations, newTag, err := w.Server.Get(request.ServiceID, tag)
+ if err != nil {
+ return nil, err
+ }
+ return GetPresentations200JSONResponse{
+ Entries: presentations,
+ Tag: string(*newTag),
+ }, nil
+}
+
+func (w *Wrapper) RegisterPresentation(_ context.Context, request RegisterPresentationRequestObject) (RegisterPresentationResponseObject, error) {
+ err := w.Server.Add(request.ServiceID, *request.Body)
+ if err != nil {
+ return nil, err
+ }
+ return RegisterPresentation201Response{}, nil
+}
+
+func (w *Wrapper) SearchPresentations(_ context.Context, request SearchPresentationsRequestObject) (SearchPresentationsResponseObject, error) {
+ searchResults, err := w.Client.Search(request.ServiceID, request.Params.Query)
+ if err != nil {
+ return nil, err
+ }
+ var result []SearchResult
+ for _, searchResult := range searchResults {
+ result = append(result, SearchResult{
+ Vp: searchResult.VP,
+ Id: searchResult.VP.ID.String(),
+ Fields: searchResult.Fields,
+ })
+ }
+ return SearchPresentations200JSONResponse(result), nil
+}
diff --git a/discovery/api/v1/wapper_test.go b/discovery/api/v1/wapper_test.go
new file mode 100644
index 0000000000..6c70b0707e
--- /dev/null
+++ b/discovery/api/v1/wapper_test.go
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2023 Nuts community
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ */
+
+package v1
+
+import (
+ ssi "github.com/nuts-foundation/go-did"
+ "github.com/nuts-foundation/go-did/vc"
+ "github.com/nuts-foundation/nuts-node/discovery"
+ "github.com/nuts-foundation/nuts-node/vcr/credential"
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+ "go.uber.org/mock/gomock"
+ "testing"
+)
+
+const serviceID = "wonderland"
+
+func TestWrapper_GetPresentations(t *testing.T) {
+ t.Run("no tag", func(t *testing.T) {
+ latestTag := discovery.Tag("latest")
+ test := newMockContext(t)
+ presentations := []vc.VerifiablePresentation{}
+ test.server.EXPECT().Get(serviceID, nil).Return(presentations, &latestTag, nil)
+
+ response, err := test.wrapper.GetPresentations(nil, GetPresentationsRequestObject{ServiceID: serviceID})
+
+ require.NoError(t, err)
+ require.IsType(t, GetPresentations200JSONResponse{}, response)
+ assert.Equal(t, latestTag, discovery.Tag(response.(GetPresentations200JSONResponse).Tag))
+ assert.Equal(t, presentations, response.(GetPresentations200JSONResponse).Entries)
+ })
+ t.Run("with tag", func(t *testing.T) {
+ givenTag := discovery.Tag("given")
+ latestTag := discovery.Tag("latest")
+ test := newMockContext(t)
+ presentations := []vc.VerifiablePresentation{}
+ test.server.EXPECT().Get(serviceID, &givenTag).Return(presentations, &latestTag, nil)
+
+ response, err := test.wrapper.GetPresentations(nil, GetPresentationsRequestObject{
+ ServiceID: serviceID,
+ Params: GetPresentationsParams{
+ Tag: (*string)(&givenTag),
+ },
+ })
+
+ require.NoError(t, err)
+ require.IsType(t, GetPresentations200JSONResponse{}, response)
+ assert.Equal(t, latestTag, discovery.Tag(response.(GetPresentations200JSONResponse).Tag))
+ assert.Equal(t, presentations, response.(GetPresentations200JSONResponse).Entries)
+ })
+}
+
+func TestWrapper_RegisterPresentation(t *testing.T) {
+ t.Run("ok", func(t *testing.T) {
+ test := newMockContext(t)
+ presentation := vc.VerifiablePresentation{}
+ test.server.EXPECT().Add(serviceID, presentation).Return(nil)
+
+ response, err := test.wrapper.RegisterPresentation(nil, RegisterPresentationRequestObject{
+ ServiceID: serviceID,
+ Body: &presentation,
+ })
+
+ assert.NoError(t, err)
+ assert.IsType(t, RegisterPresentation201Response{}, response)
+ })
+ t.Run("error", func(t *testing.T) {
+ test := newMockContext(t)
+ presentation := vc.VerifiablePresentation{}
+ test.server.EXPECT().Add(serviceID, presentation).Return(discovery.ErrInvalidPresentation)
+
+ _, err := test.wrapper.RegisterPresentation(nil, RegisterPresentationRequestObject{
+ ServiceID: serviceID,
+ Body: &presentation,
+ })
+
+ assert.ErrorIs(t, err, discovery.ErrInvalidPresentation)
+ })
+}
+
+func TestWrapper_SearchPresentations(t *testing.T) {
+ query := map[string]string{
+ "foo": "bar",
+ }
+ id, _ := ssi.ParseURI("did:nuts:foo#1")
+ vp := vc.VerifiablePresentation{
+ ID: id,
+ VerifiableCredential: []vc.VerifiableCredential{credential.ValidNutsOrganizationCredential(t)},
+ }
+ t.Run("ok", func(t *testing.T) {
+ test := newMockContext(t)
+ results := []discovery.SearchResult{
+ {
+ VP: vp,
+ Fields: nil,
+ },
+ }
+ test.client.EXPECT().Search(serviceID, query).Return(results, nil)
+
+ response, err := test.wrapper.SearchPresentations(nil, SearchPresentationsRequestObject{
+ ServiceID: serviceID,
+ Params: SearchPresentationsParams{Query: query},
+ })
+
+ assert.NoError(t, err)
+ assert.IsType(t, SearchPresentations200JSONResponse{}, response)
+ actual := response.(SearchPresentations200JSONResponse)
+ require.Len(t, actual, 1)
+ assert.Equal(t, vp, actual[0].Vp)
+ assert.Equal(t, vp.ID.String(), actual[0].Id)
+ })
+ t.Run("error", func(t *testing.T) {
+ test := newMockContext(t)
+ test.client.EXPECT().Search(serviceID, query).Return(nil, discovery.ErrServiceNotFound)
+
+ _, err := test.wrapper.SearchPresentations(nil, SearchPresentationsRequestObject{
+ ServiceID: serviceID,
+ Params: SearchPresentationsParams{Query: query},
+ })
+
+ assert.ErrorIs(t, err, discovery.ErrServiceNotFound)
+ })
+}
+
+type mockContext struct {
+ ctrl *gomock.Controller
+ server *discovery.MockServer
+ client *discovery.MockClient
+ wrapper Wrapper
+}
+
+func newMockContext(t *testing.T) mockContext {
+ ctrl := gomock.NewController(t)
+ server := discovery.NewMockServer(ctrl)
+ client := discovery.NewMockClient(ctrl)
+ return mockContext{
+ ctrl: ctrl,
+ server: server,
+ client: client,
+ wrapper: Wrapper{Server: server, Client: client},
+ }
+}
diff --git a/discovery/interface.go b/discovery/interface.go
index d308eb91a3..4167a99cc7 100644
--- a/discovery/interface.go
+++ b/discovery/interface.go
@@ -26,8 +26,6 @@ import (
"strings"
)
-// Tag is value that references a point in the list.
-// It is used by clients to request new entries since their last query.
// It is opaque for clients: they should not try to interpret it.
// The server who issued the tag can interpret it as Lamport timestamp.
type Tag string
@@ -86,7 +84,7 @@ var ErrPresentationAlreadyExists = errors.New("presentation already exists")
// Server defines the API for Discovery Servers.
type Server interface {
// Add registers a presentation on the given Discovery Service.
- // If the presentation is not valid or it does not conform to the Service ServiceDefinition, it returns an error.
+ // If the presentation is not valid, or it does not conform to the Service ServiceDefinition, it returns an error.
Add(serviceID string, presentation vc.VerifiablePresentation) error
// Get retrieves the presentations for the given service, starting at the given timestamp.
Get(serviceID string, startAt *Tag) ([]vc.VerifiablePresentation, *Tag, error)
@@ -94,5 +92,10 @@ type Server interface {
// Client defines the API for Discovery Clients.
type Client interface {
- Search(serviceID string, query map[string]string) ([]vc.VerifiablePresentation, error)
+ Search(serviceID string, query map[string]string) ([]SearchResult, error)
+}
+
+type SearchResult struct {
+ VP vc.VerifiablePresentation
+ Fields map[string]string
}
diff --git a/discovery/mock.go b/discovery/mock.go
index ade7b84915..9b1320c756 100644
--- a/discovery/mock.go
+++ b/discovery/mock.go
@@ -92,10 +92,10 @@ func (m *MockClient) EXPECT() *MockClientMockRecorder {
}
// Search mocks base method.
-func (m *MockClient) Search(serviceID string, query map[string]string) ([]vc.VerifiablePresentation, error) {
+func (m *MockClient) Search(serviceID string, query map[string]string) ([]SearchResult, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Search", serviceID, query)
- ret0, _ := ret[0].([]vc.VerifiablePresentation)
+ ret0, _ := ret[0].([]SearchResult)
ret1, _ := ret[1].(error)
return ret0, ret1
}
diff --git a/discovery/module.go b/discovery/module.go
index 3685e7025c..d17747d0e1 100644
--- a/discovery/module.go
+++ b/discovery/module.go
@@ -35,12 +35,29 @@ import (
const ModuleName = "Discovery"
+// ErrServerModeDisabled is returned when a client invokes a Discovery Server (Add or Get) operation on the node,
+// for a Discovery Service which it doesn't serve.
var ErrServerModeDisabled = errors.New("node is not a discovery server for this service")
+// ErrInvalidPresentation is returned when a client tries to register a Verifiable Presentation that is invalid.
+var ErrInvalidPresentation = errors.New("presentation is invalid for registration")
+
+var (
+ errUnsupportedPresentationFormat = errors.New("only JWT presentations are supported")
+ errPresentationWithoutID = errors.New("presentation does not have an ID")
+ errPresentationWithoutExpiration = errors.New("presentation does not have an expiration")
+ errPresentationValidityExceedsCredentials = errors.New("presentation is valid longer than the credential(s) it contains")
+ errPresentationDoesNotFulfillDefinition = errors.New("presentation does not fulfill Presentation ServiceDefinition")
+ errRetractionReferencesUnknownPresentation = errors.New("retraction presentation refers to a non-existing presentation")
+ errRetractionContainsCredentials = errors.New("retraction presentation must not contain credentials")
+ errInvalidRetractionJTIClaim = errors.New("invalid/missing 'retract_jti' claim for retraction presentation")
+)
+
var _ core.Injectable = &Module{}
var _ core.Runnable = &Module{}
var _ core.Configurable = &Module{}
var _ Server = &Module{}
+var _ Client = &Module{}
var retractionPresentationType = ssi.MustParseURI("RetractedVerifiablePresentation")
@@ -107,6 +124,8 @@ func (m *Module) Config() interface{} {
return &m.config
}
+// Add registers a presentation on the given Discovery Service.
+// See interface.go for more information.
func (m *Module) Add(serviceID string, presentation vc.VerifiablePresentation) error {
// First, simple sanity checks
definition, isServer := m.serverDefinitions[serviceID]
@@ -114,10 +133,10 @@ func (m *Module) Add(serviceID string, presentation vc.VerifiablePresentation) e
return ErrServerModeDisabled
}
if presentation.Format() != vc.JWTPresentationProofFormat {
- return errors.New("only JWT presentations are supported")
+ return errors.Join(ErrInvalidPresentation, errUnsupportedPresentationFormat)
}
if presentation.ID == nil {
- return errors.New("presentation does not have an ID")
+ return errors.Join(ErrInvalidPresentation, errPresentationWithoutID)
}
// Make sure the presentation is intended for this service
if err := validateAudience(definition, presentation.JWT().Audience()); err != nil {
@@ -125,11 +144,11 @@ func (m *Module) Add(serviceID string, presentation vc.VerifiablePresentation) e
}
expiration := presentation.JWT().Expiration()
if expiration.IsZero() {
- return errors.New("presentation does not have an expiration")
+ return errors.Join(ErrInvalidPresentation, errPresentationWithoutExpiration)
}
// VPs should not be valid for too long, as that would prevent the server from pruning them.
if int(expiration.Sub(time.Now()).Seconds()) > definition.PresentationMaxValidity {
- return fmt.Errorf("presentation is valid for too long (max %s)", time.Duration(definition.PresentationMaxValidity)*time.Second)
+ return errors.Join(ErrInvalidPresentation, fmt.Errorf("presentation is valid for too long (max %s)", time.Duration(definition.PresentationMaxValidity)*time.Second))
}
// Check if the presentation already exists
credentialSubjectID, err := credential.PresentationSigner(presentation)
@@ -141,7 +160,7 @@ func (m *Module) Add(serviceID string, presentation vc.VerifiablePresentation) e
return err
}
if exists {
- return ErrPresentationAlreadyExists
+ return errors.Join(ErrInvalidPresentation, ErrPresentationAlreadyExists)
}
// Depending on the presentation type, we need to validate different properties before storing it.
if presentation.IsType(retractionPresentationType) {
@@ -150,12 +169,12 @@ func (m *Module) Add(serviceID string, presentation vc.VerifiablePresentation) e
err = m.validateRegistration(definition, presentation)
}
if err != nil {
- return err
+ return errors.Join(ErrInvalidPresentation, err)
}
// Check signature of presentation and contained credential(s)
_, err = m.vcrInstance.Verifier().VerifyVP(presentation, true, true, nil)
if err != nil {
- return fmt.Errorf("presentation verification failed: %w", err)
+ return errors.Join(ErrInvalidPresentation, fmt.Errorf("presentation verification failed: %w", err))
}
return m.store.add(definition.ID, presentation, nil)
}
@@ -165,7 +184,7 @@ func (m *Module) validateRegistration(definition ServiceDefinition, presentation
expiration := presentation.JWT().Expiration()
for _, cred := range presentation.VerifiableCredential {
if cred.ExpirationDate != nil && expiration.After(*cred.ExpirationDate) {
- return fmt.Errorf("presentation is valid longer than the credential(s) it contains")
+ return errPresentationValidityExceedsCredentials
}
}
// VP must fulfill the PEX Presentation ServiceDefinition
@@ -175,7 +194,7 @@ func (m *Module) validateRegistration(definition ServiceDefinition, presentation
return err
}
if len(creds) != len(presentation.VerifiableCredential) {
- return errors.New("presentation does not fulfill Presentation ServiceDefinition")
+ return errPresentationDoesNotFulfillDefinition
}
return nil
}
@@ -184,29 +203,28 @@ func (m *Module) validateRetraction(serviceID string, presentation vc.Verifiable
// Presentation might be a retraction (deletion of an earlier credentialRecord) must contain no credentials, and refer to the VP being retracted by ID.
// If those conditions aren't met, we don't need to register the retraction.
if len(presentation.VerifiableCredential) > 0 {
- return errors.New("retraction presentation must not contain credentials")
+ return errRetractionContainsCredentials
}
// Check that the retraction refers to an existing presentation.
// If not, it might've already been removed due to expiry or superseded by a newer presentation.
- var retractJTIString string
- if retractJTIRaw, ok := presentation.JWT().Get("retract_jti"); !ok {
- return errors.New("retraction presentation does not contain 'retract_jti' claim")
- } else {
- if retractJTIString, ok = retractJTIRaw.(string); !ok {
- return errors.New("retraction presentation 'retract_jti' claim is not a string")
- }
+ retractJTIRaw, _ := presentation.JWT().Get("retract_jti")
+ retractJTI, ok := retractJTIRaw.(string)
+ if !ok {
+ return errInvalidRetractionJTIClaim
}
signerDID, _ := credential.PresentationSigner(presentation) // checked before
- exists, err := m.store.exists(serviceID, signerDID.String(), retractJTIString)
+ exists, err := m.store.exists(serviceID, signerDID.String(), retractJTI)
if err != nil {
return err
}
if !exists {
- return errors.New("retraction presentation refers to a non-existing presentation")
+ return errRetractionReferencesUnknownPresentation
}
return nil
}
+// Get retrieves the presentations for the given service, starting at the given tag.
+// See interface.go for more information.
func (m *Module) Get(serviceID string, tag *Tag) ([]vc.VerifiablePresentation, *Tag, error) {
if _, exists := m.serverDefinitions[serviceID]; !exists {
return nil, nil, ErrServerModeDisabled
@@ -241,6 +259,25 @@ func loadDefinitions(directory string) (map[string]ServiceDefinition, error) {
return result, nil
}
+func (m *Module) Search(serviceID string, query map[string]string) ([]SearchResult, error) {
+ if _, exists := m.serverDefinitions[serviceID]; !exists {
+ return nil, ErrServerModeDisabled
+ }
+ matchingVPs, err := m.store.search(serviceID, query)
+ if err != nil {
+ return nil, err
+ }
+ var result []SearchResult
+ for _, matchingVP := range matchingVPs {
+ result = append(result, SearchResult{
+ VP: matchingVP,
+ // TODO: TokenIntrospection is also missing map[InputDescriptorId]CredentialValue ?
+ //Fields: matchingVP.Fields,
+ })
+ }
+ return result, nil
+}
+
// validateAudience checks if the given audience of the presentation matches the service ID.
func validateAudience(service ServiceDefinition, audience []string) error {
for _, audienceID := range audience {
diff --git a/discovery/module_test.go b/discovery/module_test.go
index 6c32c28e6b..0367bfc464 100644
--- a/discovery/module_test.go
+++ b/discovery/module_test.go
@@ -56,7 +56,7 @@ func Test_Module_Add(t *testing.T) {
presentationVerifier.EXPECT().VerifyVP(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, errors.New("failed"))
err := m.Add(testServiceID, vpAlice)
- require.EqualError(t, err, "presentation verification failed: failed")
+ require.EqualError(t, err, "presentation is invalid for registration\npresentation verification failed: failed")
_, tag, err := m.Get(testServiceID, nil)
require.NoError(t, err)
@@ -70,7 +70,7 @@ func Test_Module_Add(t *testing.T) {
err := m.Add(testServiceID, vpAlice)
assert.NoError(t, err)
err = m.Add(testServiceID, vpAlice)
- assert.EqualError(t, err, "presentation already exists")
+ assert.ErrorIs(t, err, ErrPresentationAlreadyExists)
})
t.Run("valid for too long", func(t *testing.T) {
m, _ := setupModule(t, storageEngine)
@@ -80,7 +80,7 @@ func Test_Module_Add(t *testing.T) {
m.serverDefinitions[testServiceID] = def
err := m.Add(testServiceID, vpAlice)
- assert.EqualError(t, err, "presentation is valid for too long (max 1s)")
+ assert.EqualError(t, err, "presentation is invalid for registration\npresentation is valid for too long (max 1s)")
})
t.Run("no expiration", func(t *testing.T) {
m, _ := setupModule(t, storageEngine)
@@ -88,7 +88,7 @@ func Test_Module_Add(t *testing.T) {
claims[jwt.AudienceKey] = []string{testServiceID}
delete(claims, "exp")
}))
- assert.EqualError(t, err, "presentation does not have an expiration")
+ assert.ErrorIs(t, err, errPresentationWithoutExpiration)
})
t.Run("presentation does not contain an ID", func(t *testing.T) {
m, _ := setupModule(t, storageEngine)
@@ -98,12 +98,12 @@ func Test_Module_Add(t *testing.T) {
delete(claims, "jti")
}, vcAlice)
err := m.Add(testServiceID, vpWithoutID)
- assert.EqualError(t, err, "presentation does not have an ID")
+ assert.ErrorIs(t, err, errPresentationWithoutID)
})
t.Run("not a JWT", func(t *testing.T) {
m, _ := setupModule(t, storageEngine)
err := m.Add(testServiceID, vc.VerifiablePresentation{})
- assert.EqualError(t, err, "only JWT presentations are supported")
+ assert.ErrorIs(t, err, errUnsupportedPresentationFormat)
})
t.Run("registration", func(t *testing.T) {
@@ -129,7 +129,7 @@ func Test_Module_Add(t *testing.T) {
claims[jwt.AudienceKey] = []string{testServiceID}
}, vcAlice)
err := m.Add(testServiceID, vpAlice)
- assert.EqualError(t, err, "presentation is valid longer than the credential(s) it contains")
+ assert.ErrorIs(t, err, errPresentationValidityExceedsCredentials)
})
t.Run("not conform to Presentation Definition", func(t *testing.T) {
m, _ := setupModule(t, storageEngine)
@@ -163,7 +163,7 @@ func Test_Module_Add(t *testing.T) {
t.Run("non-existent presentation", func(t *testing.T) {
m, _ := setupModule(t, storageEngine)
err := m.Add(testServiceID, vpAliceRetract)
- assert.EqualError(t, err, "retraction presentation refers to a non-existing presentation")
+ assert.ErrorIs(t, err, errRetractionReferencesUnknownPresentation)
})
t.Run("must not contain credentials", func(t *testing.T) {
m, _ := setupModule(t, storageEngine)
@@ -172,7 +172,7 @@ func Test_Module_Add(t *testing.T) {
claims[jwt.AudienceKey] = []string{testServiceID}
}, vcAlice)
err := m.Add(testServiceID, vp)
- assert.EqualError(t, err, "retraction presentation must not contain credentials")
+ assert.ErrorIs(t, err, errRetractionContainsCredentials)
})
t.Run("missing 'retract_jti' claim", func(t *testing.T) {
m, _ := setupModule(t, storageEngine)
@@ -181,7 +181,7 @@ func Test_Module_Add(t *testing.T) {
claims[jwt.AudienceKey] = []string{testServiceID}
})
err := m.Add(testServiceID, vp)
- assert.EqualError(t, err, "retraction presentation does not contain 'retract_jti' claim")
+ assert.ErrorIs(t, err, errInvalidRetractionJTIClaim)
})
t.Run("'retract_jti' claim in not a string", func(t *testing.T) {
m, _ := setupModule(t, storageEngine)
@@ -191,7 +191,7 @@ func Test_Module_Add(t *testing.T) {
claims[jwt.AudienceKey] = []string{testServiceID}
})
err := m.Add(testServiceID, vp)
- assert.EqualError(t, err, "retraction presentation 'retract_jti' claim is not a string")
+ assert.ErrorIs(t, err, errInvalidRetractionJTIClaim)
})
})
}
@@ -276,3 +276,27 @@ func TestModule_Configure(t *testing.T) {
assert.ErrorContains(t, err, "unable to read definitions directory 'test/non_existent'")
})
}
+
+func TestModule_Search(t *testing.T) {
+ storageEngine := storage.NewTestStorageEngine(t)
+ require.NoError(t, storageEngine.Start())
+ t.Run("ok", func(t *testing.T) {
+ m, _ := setupModule(t, storageEngine)
+ require.NoError(t, m.store.add(testServiceID, vpAlice, nil))
+ results, err := m.Search(testServiceID, map[string]string{
+ "credentialSubject.id": aliceDID.String(),
+ })
+ assert.NoError(t, err)
+ assert.Equal(t, []SearchResult{
+ {
+ VP: vpAlice,
+ Fields: nil,
+ },
+ }, results)
+ })
+ t.Run("not a client for this service ID", func(t *testing.T) {
+ m, _ := setupModule(t, storageEngine)
+ _, err := m.Search("other", nil)
+ assert.ErrorIs(t, err, ErrServerModeDisabled)
+ })
+}
diff --git a/docs/_static/discovery/v1.yaml b/docs/_static/discovery/v1.yaml
index 84be706c9e..76eeecf636 100644
--- a/docs/_static/discovery/v1.yaml
+++ b/docs/_static/discovery/v1.yaml
@@ -92,6 +92,7 @@ paths:
# See https://stackoverflow.com/questions/49582559/how-to-document-dynamic-query-parameter-names-in-openapi-swagger
- in: query
name: query
+ required: true
schema:
type: object
additionalProperties:
@@ -131,23 +132,31 @@ paths:
schema:
type: array
items:
- type: object
- required:
- - id
- - credential
- properties:
- id:
- type: string
- description: The ID of the Verifiable Presentation.
- credential:
- type: object
- description: The Verifiable Credential that matched the query.
+ $ref: "#/components/schemas/SearchResult"
default:
$ref: "../common/error_response.yaml"
components:
schemas:
VerifiablePresentation:
$ref: "../common/ssi_types.yaml#/components/schemas/VerifiablePresentation"
+ SearchResult:
+ type: object
+ required:
+ - id
+ - vp
+ - fields
+ properties:
+ id:
+ type: string
+ description: The ID of the Verifiable Presentation.
+ vp:
+ $ref: "#/components/schemas/VerifiablePresentation"
+ fields:
+ type: object
+ description: Input descriptor IDs and their mapped values that from the Verifiable Credential.
+ additionalProperties:
+ type: string
+ description: The value of the field that matched the query.
securitySchemes:
jwtBearerAuth:
type: http
diff --git a/docs/pages/integrating/api.rst b/docs/pages/integrating/api.rst
index c59379ba43..a8d7f84d18 100644
--- a/docs/pages/integrating/api.rst
+++ b/docs/pages/integrating/api.rst
@@ -7,6 +7,7 @@ Below you can discover the Nuts Node APIs and download their OpenAPI specificati
- Common: `SSI types <../../_static/common/ssi_types.yaml>`_, `Default Error <../../_static/common/error_response.yaml>`_
- `DID Manager <../../_static/didman/v1.yaml>`_
+- `Discovery Service <../../_static/discovery/v1.yaml>`_
- `Crypto <../../_static/crypto/v1.yaml>`_
- `Verifiable Credential Registry (v2) <../../_static/vcr/vcr_v2.yaml>`_
- `Verifiable Data Registry <../../_static/vdr/v1.yaml>`_
@@ -30,6 +31,7 @@ Below you can discover the Nuts Node APIs and download their OpenAPI specificati
"dom_id": "#swagger-ui",
urls: [
{url: "../../_static/didman/v1.yaml", name: "DID Manager"},
+ {url: "../../_static/discovery/v1.yaml", name: "Discovery Service"},
{url: "../../_static/crypto/v1.yaml", name: "Crypto"},
{url: "../../_static/vcr/vcr_v2.yaml", name: "Verifiable Credential Registry (v2)"},
{url: "../../_static/vdr/v1.yaml", name: "Verifiable Data Registry"},
diff --git a/makefile b/makefile
index 4932e9302a..1365b36384 100644
--- a/makefile
+++ b/makefile
@@ -70,6 +70,7 @@ gen-api:
oapi-codegen --config codegen/configs/auth_employeeid.yaml auth/services/selfsigned/web/spec.yaml | gofmt > auth/services/selfsigned/web/generated.go
oapi-codegen --config codegen/configs/auth_iam.yaml docs/_static/auth/iam.yaml | gofmt > auth/api/iam/generated.go
oapi-codegen --config codegen/configs/didman_v1.yaml docs/_static/didman/v1.yaml | gofmt > didman/api/v1/generated.go
+ oapi-codegen --config codegen/configs/discovery_v1.yaml docs/_static/discovery/v1.yaml | gofmt > discovery/api/v1/generated.go
oapi-codegen --config codegen/configs/crypto_store_client.yaml https://raw.githubusercontent.com/nuts-foundation/secret-store-api/main/nuts-storage-api-v1.yaml | gofmt > crypto/storage/external/generated.go
oapi-codegen --config codegen/configs/policy_client_v1.yaml docs/_static/policy/v1.yaml | gofmt > policy/api/v1/client/generated.go
diff --git a/vdr/management/management_mock.go b/vdr/management/management_mock.go
index 6ae0dbd41f..f3d5f10cc2 100644
--- a/vdr/management/management_mock.go
+++ b/vdr/management/management_mock.go
@@ -14,138 +14,9 @@ import (
did "github.com/nuts-foundation/go-did/did"
crypto "github.com/nuts-foundation/nuts-node/crypto"
- resolver "github.com/nuts-foundation/nuts-node/vdr/resolver"
gomock "go.uber.org/mock/gomock"
)
-// MockManager is a mock of Manager interface.
-type MockManager struct {
- ctrl *gomock.Controller
- recorder *MockManagerMockRecorder
-}
-
-// MockManagerMockRecorder is the mock recorder for MockManager.
-type MockManagerMockRecorder struct {
- mock *MockManager
-}
-
-// NewMockManager creates a new mock instance.
-func NewMockManager(ctrl *gomock.Controller) *MockManager {
- mock := &MockManager{ctrl: ctrl}
- mock.recorder = &MockManagerMockRecorder{mock}
- return mock
-}
-
-// EXPECT returns an object that allows the caller to indicate expected use.
-func (m *MockManager) EXPECT() *MockManagerMockRecorder {
- return m.recorder
-}
-
-// AddVerificationMethod mocks base method.
-func (m *MockManager) AddVerificationMethod(ctx context.Context, id did.DID, keyUsage DIDKeyFlags) (*did.VerificationMethod, error) {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "AddVerificationMethod", ctx, id, keyUsage)
- ret0, _ := ret[0].(*did.VerificationMethod)
- ret1, _ := ret[1].(error)
- return ret0, ret1
-}
-
-// AddVerificationMethod indicates an expected call of AddVerificationMethod.
-func (mr *MockManagerMockRecorder) AddVerificationMethod(ctx, id, keyUsage any) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddVerificationMethod", reflect.TypeOf((*MockManager)(nil).AddVerificationMethod), ctx, id, keyUsage)
-}
-
-// Create mocks base method.
-func (m *MockManager) Create(ctx context.Context, method string, options DIDCreationOptions) (*did.Document, crypto.Key, error) {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "Create", ctx, method, options)
- ret0, _ := ret[0].(*did.Document)
- ret1, _ := ret[1].(crypto.Key)
- ret2, _ := ret[2].(error)
- return ret0, ret1, ret2
-}
-
-// Create indicates an expected call of Create.
-func (mr *MockManagerMockRecorder) Create(ctx, method, options any) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Create", reflect.TypeOf((*MockManager)(nil).Create), ctx, method, options)
-}
-
-// Deactivate mocks base method.
-func (m *MockManager) Deactivate(ctx context.Context, id did.DID) error {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "Deactivate", ctx, id)
- ret0, _ := ret[0].(error)
- return ret0
-}
-
-// Deactivate indicates an expected call of Deactivate.
-func (mr *MockManagerMockRecorder) Deactivate(ctx, id any) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Deactivate", reflect.TypeOf((*MockManager)(nil).Deactivate), ctx, id)
-}
-
-// IsOwner mocks base method.
-func (m *MockManager) IsOwner(arg0 context.Context, arg1 did.DID) (bool, error) {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "IsOwner", arg0, arg1)
- ret0, _ := ret[0].(bool)
- ret1, _ := ret[1].(error)
- return ret0, ret1
-}
-
-// IsOwner indicates an expected call of IsOwner.
-func (mr *MockManagerMockRecorder) IsOwner(arg0, arg1 any) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsOwner", reflect.TypeOf((*MockManager)(nil).IsOwner), arg0, arg1)
-}
-
-// ListOwned mocks base method.
-func (m *MockManager) ListOwned(ctx context.Context) ([]did.DID, error) {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "ListOwned", ctx)
- ret0, _ := ret[0].([]did.DID)
- ret1, _ := ret[1].(error)
- return ret0, ret1
-}
-
-// ListOwned indicates an expected call of ListOwned.
-func (mr *MockManagerMockRecorder) ListOwned(ctx any) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListOwned", reflect.TypeOf((*MockManager)(nil).ListOwned), ctx)
-}
-
-// RemoveVerificationMethod mocks base method.
-func (m *MockManager) RemoveVerificationMethod(ctx context.Context, id did.DID, keyID did.DIDURL) error {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "RemoveVerificationMethod", ctx, id, keyID)
- ret0, _ := ret[0].(error)
- return ret0
-}
-
-// RemoveVerificationMethod indicates an expected call of RemoveVerificationMethod.
-func (mr *MockManagerMockRecorder) RemoveVerificationMethod(ctx, id, keyID any) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemoveVerificationMethod", reflect.TypeOf((*MockManager)(nil).RemoveVerificationMethod), ctx, id, keyID)
-}
-
-// Resolve mocks base method.
-func (m *MockManager) Resolve(id did.DID, metadata *resolver.ResolveMetadata) (*did.Document, *resolver.DocumentMetadata, error) {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "Resolve", id, metadata)
- ret0, _ := ret[0].(*did.Document)
- ret1, _ := ret[1].(*resolver.DocumentMetadata)
- ret2, _ := ret[2].(error)
- return ret0, ret1, ret2
-}
-
-// Resolve indicates an expected call of Resolve.
-func (mr *MockManagerMockRecorder) Resolve(id, metadata any) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Resolve", reflect.TypeOf((*MockManager)(nil).Resolve), id, metadata)
-}
-
// MockDocCreator is a mock of DocCreator interface.
type MockDocCreator struct {
ctrl *gomock.Controller
@@ -170,9 +41,9 @@ func (m *MockDocCreator) EXPECT() *MockDocCreatorMockRecorder {
}
// Create mocks base method.
-func (m *MockDocCreator) Create(ctx context.Context, method string, options DIDCreationOptions) (*did.Document, crypto.Key, error) {
+func (m *MockDocCreator) Create(ctx context.Context, options DIDCreationOptions) (*did.Document, crypto.Key, error) {
m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "Create", ctx, method, options)
+ ret := m.ctrl.Call(m, "Create", ctx, options)
ret0, _ := ret[0].(*did.Document)
ret1, _ := ret[1].(crypto.Key)
ret2, _ := ret[2].(error)
@@ -180,9 +51,9 @@ func (m *MockDocCreator) Create(ctx context.Context, method string, options DIDC
}
// Create indicates an expected call of Create.
-func (mr *MockDocCreatorMockRecorder) Create(ctx, method, options any) *gomock.Call {
+func (mr *MockDocCreatorMockRecorder) Create(ctx, options any) *gomock.Call {
mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Create", reflect.TypeOf((*MockDocCreator)(nil).Create), ctx, method, options)
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Create", reflect.TypeOf((*MockDocCreator)(nil).Create), ctx, options)
}
// MockDocUpdater is a mock of DocUpdater interface.