From 8acd3360dfa28fe130ebe7fb28f8950c39792b3d Mon Sep 17 00:00:00 2001 From: Rein Krul Date: Mon, 2 Mar 2020 14:47:39 +0100 Subject: [PATCH 1/3] Added property bag to endpoints for custom metadata --- api/api.go | 4 +-- api/api_test.go | 5 ++- api/client.go | 11 +++++-- api/client_test.go | 4 +-- api/conversion.go | 43 ++++++++++++++++++-------- api/generated.go | 9 ++++++ docs/_static/nuts-registry.yaml | 9 ++++++ engine/engine.go | 54 ++++++++++++++++++++++----------- engine/engine_test.go | 6 ++-- mock/mock_client.go | 8 ++--- pkg/admin.go | 3 +- pkg/admin_test.go | 7 ++--- pkg/db/db.go | 11 ++++--- pkg/db/memory.go | 1 + pkg/events/domain.go | 13 ++++---- pkg/registry.go | 2 +- 16 files changed, 129 insertions(+), 61 deletions(-) diff --git a/api/api.go b/api/api.go index 1effc0e..1b90e9c 100644 --- a/api/api.go +++ b/api/api.go @@ -61,7 +61,7 @@ func (apiResource ApiWrapper) RegisterEndpoint(ctx echo.Context, id string) erro if err = ep.validate(); err != nil { return ctx.String(http.StatusBadRequest, err.Error()) } - event, err := apiResource.R.RegisterEndpoint(unescapedID, ep.Identifier.String(), ep.URL, ep.EndpointType, ep.Status, ep.Version) + event, err := apiResource.R.RegisterEndpoint(unescapedID, ep.Identifier.String(), ep.URL, ep.EndpointType, ep.Status, ep.Version, fromEndpointProperties(ep.Properties)) if err != nil { return ctx.String(http.StatusInternalServerError, err.Error()) } @@ -145,7 +145,7 @@ func (apiResource ApiWrapper) EndpointsByOrganisationId(ctx echo.Context, params if err != nil { logrus.Warning(err.Error()) } else { - dupEndpoints = append(endpointsArrayFromDb(dbEndpoints), dupEndpoints...) + dupEndpoints = append(endpointsFromDb(dbEndpoints), dupEndpoints...) } if strict != nil && *strict && len(dbEndpoints) == 0 { diff --git a/api/api_test.go b/api/api_test.go index 15a1a47..6628403 100644 --- a/api/api_test.go +++ b/api/api_test.go @@ -720,12 +720,15 @@ func TestApiResource_RegisterEndpoint(t *testing.T) { t.Run("204", func(t *testing.T) { var registryClient = mock.NewMockRegistryClient(mockCtrl) e, wrapper := initMockEcho(registryClient) - registryClient.EXPECT().RegisterEndpoint("1", "abc", "foo:bar", "fhir", "", "") + registryClient.EXPECT().RegisterEndpoint("1", "abc", "foo:bar", "fhir", "", "", map[string]string{"key": "value"}) + props := EndpointProperties{} + props["key"] = "value" b, _ := json.Marshal(Endpoint{ Identifier: "abc", URL: "foo:bar", EndpointType: "fhir", + Properties: &props, }) req := httptest.NewRequest(echo.POST, "/", bytes.NewReader(b)) diff --git a/api/client.go b/api/client.go index 090579c..674ac2a 100644 --- a/api/client.go +++ b/api/client.go @@ -86,7 +86,7 @@ func (hb HttpClient) EndpointsByOrganizationAndType(legalEntity string, endpoint return nil, err } - return endpointsArrayToDb(endpoints), nil + return endpointsToDb(endpoints), nil } // SearchOrganizations is the client Api implementation for finding organizations by (partial) query @@ -156,19 +156,24 @@ func (hb HttpClient) searchOrganization(params SearchOrganizationsParams) ([]db. } } - return organizationsToFromDb(organizations), nil + return organizationsToDb(organizations), nil } // RegisterEndpoint is the client Api implementation for registering an endpoint for an organisation. -func (hb HttpClient) RegisterEndpoint(organizationID string, id string, url string, endpointType string, status string, version string) (events.Event, error) { +func (hb HttpClient) RegisterEndpoint(organizationID string, id string, url string, endpointType string, status string, version string, properties map[string]string) (events.Event, error) { ctx, cancel := context.WithTimeout(context.Background(), hb.Timeout) defer cancel() + props := EndpointProperties{} + for key, value := range properties { + props[key] = value + } res, err := hb.client().RegisterEndpoint(ctx, organizationID, RegisterEndpointJSONRequestBody{ URL: url, EndpointType: endpointType, Identifier: Identifier(id), Status: status, Version: version, + Properties: &props, }) if err != nil { return nil, err diff --git a/api/client_test.go b/api/client_test.go index 5873fbd..b143bed 100644 --- a/api/client_test.go +++ b/api/client_test.go @@ -174,7 +174,7 @@ func TestHttpClient_RegisterEndpoint(t *testing.T) { s := httptest.NewServer(handler{statusCode: http.StatusOK, responseData: event.Marshal()}) c := HttpClient{ServerAddress: s.URL, Timeout: time.Second} - event, err := c.RegisterEndpoint("orgId", "id", "url", "type", "status", "version") + event, err := c.RegisterEndpoint("orgId", "id", "url", "type", "status", "version", map[string]string{"foo": "bar"}) if !assert.NoError(t, err) { return } @@ -184,7 +184,7 @@ func TestHttpClient_RegisterEndpoint(t *testing.T) { s := httptest.NewServer(handler{statusCode: http.StatusInternalServerError, responseData: []byte{}}) c := HttpClient{ServerAddress: s.URL, Timeout: time.Second} - event, err := c.RegisterEndpoint("orgId", "id", "url", "type", "status", "version") + event, err := c.RegisterEndpoint("orgId", "id", "url", "type", "status", "version", nil) assert.EqualError(t, err, "registry returned HTTP 500 (expected: 200), response: ", "error") assert.Nil(t, event) }) diff --git a/api/conversion.go b/api/conversion.go index 50437c9..4b48ff0 100644 --- a/api/conversion.go +++ b/api/conversion.go @@ -19,7 +19,10 @@ package api -import "github.com/nuts-foundation/nuts-registry/pkg/db" +import ( + "fmt" + "github.com/nuts-foundation/nuts-registry/pkg/db" +) func (e Endpoint) fromDb(db db.Endpoint) Endpoint { e.URL = db.URL @@ -27,11 +30,16 @@ func (e Endpoint) fromDb(db db.Endpoint) Endpoint { e.Identifier = Identifier(db.Identifier) e.Status = db.Status e.Version = db.Version + props := EndpointProperties{} + for key, value := range db.Properties { + props[key] = value + } + e.Properties = &props return e } func (o Organization) fromDb(db db.Organization) Organization { - e := endpointsArrayFromDb(db.Endpoints) + e := endpointsFromDb(db.Endpoints) o.Identifier = Identifier(db.Identifier) o.Name = db.Name o.PublicKey = db.PublicKey @@ -64,20 +72,31 @@ func (o Organization) toDb() db.Organization { } if o.Endpoints != nil { - org.Endpoints = endpointsArrayToDb(*o.Endpoints) + org.Endpoints = endpointsToDb(*o.Endpoints) } return org } -func (a Endpoint) toDb() db.Endpoint { +func (e Endpoint) toDb() db.Endpoint { return db.Endpoint{ - URL: a.URL, - EndpointType: a.EndpointType, - Identifier: db.Identifier(a.Identifier), - Status: a.Status, - Version: a.Version, + URL: e.URL, + EndpointType: e.EndpointType, + Identifier: db.Identifier(e.Identifier), + Status: e.Status, + Version: e.Version, + Properties: fromEndpointProperties(e.Properties), + } +} + +func fromEndpointProperties(endpointProperties *EndpointProperties) map[string]string { + props := make(map[string]string, 0) + if endpointProperties != nil { + for key, value := range *endpointProperties { + props[key] = fmt.Sprintf("%s", value) + } } + return props } func jwkToMap(jwk []JWK) []interface{} { @@ -88,7 +107,7 @@ func jwkToMap(jwk []JWK) []interface{} { return em } -func endpointsArrayFromDb(endpointsIn []db.Endpoint) []Endpoint { +func endpointsFromDb(endpointsIn []db.Endpoint) []Endpoint { es := make([]Endpoint, len(endpointsIn)) for i, a := range endpointsIn { es[i] = Endpoint{}.fromDb(a) @@ -96,7 +115,7 @@ func endpointsArrayFromDb(endpointsIn []db.Endpoint) []Endpoint { return es } -func organizationsToFromDb(organizationsIn []Organization) []db.Organization { +func organizationsToDb(organizationsIn []Organization) []db.Organization { os := make([]db.Organization, len(organizationsIn)) for i, a := range organizationsIn { os[i] = a.toDb() @@ -104,7 +123,7 @@ func organizationsToFromDb(organizationsIn []Organization) []db.Organization { return os } -func endpointsArrayToDb(endpointsIn []Endpoint) []db.Endpoint { +func endpointsToDb(endpointsIn []Endpoint) []db.Endpoint { es := make([]db.Endpoint, len(endpointsIn)) for i, a := range endpointsIn { es[i] = a.toDb() diff --git a/api/generated.go b/api/generated.go index 29b8247..3feccfc 100644 --- a/api/generated.go +++ b/api/generated.go @@ -33,6 +33,9 @@ type Endpoint struct { // Generic identifier used for representing BSN, agbcode, etc. It's always constructed as an URN followed by a double colon (:) and then the identifying value of the given URN Identifier Identifier `json:"identifier"` + // A property bag, containing extra properties for endpoints + Properties *EndpointProperties `json:"properties,omitempty"` + // status of the endpoint Status string `json:"status"` @@ -40,6 +43,9 @@ type Endpoint struct { Version string `json:"version"` } +// EndpointProperties defines model for EndpointProperties. +type EndpointProperties map[string]interface{} + // Event defines model for Event. type Event struct { @@ -89,6 +95,9 @@ type RegisterEndpointEvent struct { // Generic identifier used for representing BSN, agbcode, etc. It's always constructed as an URN followed by a double colon (:) and then the identifying value of the given URN Organization Identifier `json:"organization"` + // A property bag, containing extra properties for endpoints + Properties *EndpointProperties `json:"properties,omitempty"` + // status of the endpoint Status string `json:"status"` diff --git a/docs/_static/nuts-registry.yaml b/docs/_static/nuts-registry.yaml index b57dab8..91b4fce 100644 --- a/docs/_static/nuts-registry.yaml +++ b/docs/_static/nuts-registry.yaml @@ -298,6 +298,8 @@ components: type: string description: location of the actual en endpoint on the internet example: tcp://127.0.0.1:1234, https://nuts.nl/endpoint + properties: + $ref: "#/components/schemas/EndpointProperties" RegisterVendorEvent: required: - identifier @@ -362,6 +364,8 @@ components: type: string description: location of the actual en endpoint on the internet example: tcp://127.0.0.1:1234, https://nuts.nl/endpoint + properties: + $ref: "#/components/schemas/EndpointProperties" Identifier: type: string description: > @@ -373,6 +377,11 @@ components: enum: [healthcare, personal, insurance] description: Domain the entity operates in. example: health + EndpointProperties: + type: object + additionalProperties: + type: string + description: A property bag, containing extra properties for endpoints JWK: description: as described by https://tools.ietf.org/html/rfc7517. Modelled as object so libraries can parse the tokens themselves. additionalProperties: {} diff --git a/engine/engine.go b/engine/engine.go index 75500f4..e9a2b64 100644 --- a/engine/engine.go +++ b/engine/engine.go @@ -34,6 +34,7 @@ import ( "github.com/spf13/pflag" "os" "os/signal" + "strings" ) // registryClientCreator is a variable to aid testability @@ -162,27 +163,46 @@ func cmd() *cobra.Command { }, }) - cmd.AddCommand(&cobra.Command{ - Use: "register-endpoint [org-identifier] [identifier] [type] [url] [version]", - Short: "Registers an endpoint", - Long: "Registers an endpoint for an organization.", - Args: cobra.ExactArgs(5), - RunE: func(cmd *cobra.Command, args []string) error { - cl := registryClientCreator() - event, err := cl.RegisterEndpoint(args[0], args[1], args[3], args[2], db.StatusActive, args[4]) - if err != nil { - logrus.Errorf("Unable to register endpoint: %v", err) - return err - } - logrus.Info("Endpoint registered.") - logEventToConsole(event) - return nil - }, - }) + { + var properties *[]string + command := &cobra.Command{ + Use: "register-endpoint [org-identifier] [identifier] [type] [url] [version]", + Short: "Registers an endpoint", + Long: "Registers an endpoint for an organization.", + Args: cobra.ExactArgs(5), + RunE: func(cmd *cobra.Command, args []string) error { + cl := registryClientCreator() + event, err := cl.RegisterEndpoint(args[0], args[1], args[3], args[2], db.StatusActive, args[4], parseCLIProperties(*properties)) + if err != nil { + logrus.Errorf("Unable to register endpoint: %v", err) + return err + } + logrus.Info("Endpoint registered.") + logEventToConsole(event) + return nil + }, + } + flagSet := pflag.NewFlagSet("register-endpoint", pflag.ContinueOnError) + properties = flagSet.StringArrayP("properties", "p", nil, "extra properties for the endpoint, in the format: key=value") + command.Flags().AddFlagSet(flagSet) + cmd.AddCommand(command) + } return cmd } +// parseCLIProperties parses a slice of key-value entries (key=value) to a map. +func parseCLIProperties(keysAndValues []string) map[string]string { + result := make(map[string]string, 0) + for _, keyAndValue := range keysAndValues { + parts := strings.SplitN(keyAndValue, "=", 2) + if len(parts) == 2 { + result[parts[0]] = parts[1] + } + } + return result +} + func logEventToConsole(event events.Event) { println("Event:", events.SuggestEventFileName(event)) println(string(event.Marshal())) diff --git a/engine/engine_test.go b/engine/engine_test.go index ff49714..ced7ec4 100644 --- a/engine/engine_test.go +++ b/engine/engine_test.go @@ -52,13 +52,13 @@ func TestRegisterEndpoint(t *testing.T) { command := cmd() t.Run("ok", withMock(func(t *testing.T, client *mock.MockRegistryClient) { event := events.CreateEvent(events.RegisterEndpoint, events.RegisterEndpointEvent{}) - client.EXPECT().RegisterEndpoint("orgId", "id", "url", "type", db.StatusActive, "version").Return(event, nil) - command.SetArgs([]string{"register-endpoint", "orgId", "id", "type", "url", "version"}) + client.EXPECT().RegisterEndpoint("orgId", "id", "url", "type", db.StatusActive, "version", map[string]string{"k1": "v1", "k2": "v2"}).Return(event, nil) + command.SetArgs([]string{"register-endpoint", "orgId", "id", "type", "url", "version", "-p", "k1=v1", "-p", "k2=v2"}) err := command.Execute() assert.NoError(t, err) })) t.Run("error", withMock(func(t *testing.T, client *mock.MockRegistryClient) { - client.EXPECT().RegisterEndpoint(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, errors.New("failed")) + client.EXPECT().RegisterEndpoint(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, errors.New("failed")) command.SetArgs([]string{"register-endpoint", "orgId", "id", "type", "url", "version"}) command.Execute() })) diff --git a/mock/mock_client.go b/mock/mock_client.go index fee506c..f40589e 100644 --- a/mock/mock_client.go +++ b/mock/mock_client.go @@ -95,18 +95,18 @@ func (mr *MockRegistryClientMockRecorder) ReverseLookup(name interface{}) *gomoc } // RegisterEndpoint mocks base method -func (m *MockRegistryClient) RegisterEndpoint(organizationID, id, url, endpointType, status, version string) (events.Event, error) { +func (m *MockRegistryClient) RegisterEndpoint(organizationID, id, url, endpointType, status, version string, properties map[string]string) (events.Event, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "RegisterEndpoint", organizationID, id, url, endpointType, status, version) + ret := m.ctrl.Call(m, "RegisterEndpoint", organizationID, id, url, endpointType, status, version, properties) ret0, _ := ret[0].(events.Event) ret1, _ := ret[1].(error) return ret0, ret1 } // RegisterEndpoint indicates an expected call of RegisterEndpoint -func (mr *MockRegistryClientMockRecorder) RegisterEndpoint(organizationID, id, url, endpointType, status, version interface{}) *gomock.Call { +func (mr *MockRegistryClientMockRecorder) RegisterEndpoint(organizationID, id, url, endpointType, status, version, properties interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RegisterEndpoint", reflect.TypeOf((*MockRegistryClient)(nil).RegisterEndpoint), organizationID, id, url, endpointType, status, version) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RegisterEndpoint", reflect.TypeOf((*MockRegistryClient)(nil).RegisterEndpoint), organizationID, id, url, endpointType, status, version, properties) } // VendorClaim mocks base method diff --git a/pkg/admin.go b/pkg/admin.go index 5f354ff..912a012 100644 --- a/pkg/admin.go +++ b/pkg/admin.go @@ -99,7 +99,7 @@ func (r *Registry) VendorClaim(vendorID string, orgID string, orgName string, or } // RegisterEndpoint registers an endpoint for an organization -func (r *Registry) RegisterEndpoint(organizationID string, id string, url string, endpointType string, status string, version string) (events.Event, error) { +func (r *Registry) RegisterEndpoint(organizationID string, id string, url string, endpointType string, status string, version string, properties map[string]string) (events.Event, error) { logrus.Infof("Registering endpoint, organization=%s, id=%s, type=%s, url=%s, status=%s, version=%s", organizationID, id, endpointType, url, status, version) return r.publishEvent(events.RegisterEndpoint, events.RegisterEndpointEvent{ @@ -109,6 +109,7 @@ func (r *Registry) RegisterEndpoint(organizationID string, id string, url string Identifier: events.Identifier(id), Status: status, Version: version, + Properties: properties, }) } diff --git a/pkg/admin_test.go b/pkg/admin_test.go index beb58cd..3afeb8e 100644 --- a/pkg/admin_test.go +++ b/pkg/admin_test.go @@ -32,7 +32,7 @@ func TestRegistryAdministration_RegisterEndpoint(t *testing.T) { }) t.Run("ok", func(t *testing.T) { - _, err := registry.RegisterEndpoint("orgId", "endpointId", "url", "type", "status", "version") + _, err := registry.RegisterEndpoint("orgId", "endpointId", "url", "type", "status", "version", map[string]string{"foo": "bar"}) if !assert.NoError(t, err) { return } @@ -42,6 +42,7 @@ func TestRegistryAdministration_RegisterEndpoint(t *testing.T) { assert.Equal(t, "type", event.EndpointType) assert.Equal(t, "version", event.Version) assert.Equal(t, "status", event.Status) + assert.Len(t, event.Properties, 1) }) } @@ -130,7 +131,6 @@ func TestRegistryAdministration_VendorClaim(t *testing.T) { }) } - func TestRegistryAdministration_RegisterVendor(t *testing.T) { repo, err := test.NewTestRepo(t.Name()) if !assert.NoError(t, err) { @@ -185,7 +185,6 @@ func TestRegistryAdministration_RegisterVendor(t *testing.T) { }) } - func createRegistry(repo test.TestRepo) Registry { registry := Registry{ Config: RegistryConfig{ @@ -214,4 +213,4 @@ func Test_marshalJwk(t *testing.T) { _, err := marshalJwk([]byte{1, 2, 3}, cert.VendorCACertificate) assert.Contains(t, err.Error(), "asn1: structure error") }) -} \ No newline at end of file +} diff --git a/pkg/db/db.go b/pkg/db/db.go index 9948660..c4e67c6 100644 --- a/pkg/db/db.go +++ b/pkg/db/db.go @@ -33,11 +33,12 @@ const StatusActive = "active" // Endpoint defines component schema for Endpoint. type Endpoint struct { - URL string `json:"URL"` - EndpointType string `json:"endpointType"` - Identifier Identifier `json:"identifier"` - Status string `json:"status"` - Version string `json:"version"` + URL string `json:"URL"` + EndpointType string `json:"endpointType"` + Identifier Identifier `json:"identifier"` + Status string `json:"status"` + Version string `json:"version"` + Properties map[string]string `json:"properties,omitempty"` } // Identifier defines component schema for Identifier. diff --git a/pkg/db/memory.go b/pkg/db/memory.go index 9867366..842b9e5 100644 --- a/pkg/db/memory.go +++ b/pkg/db/memory.go @@ -90,6 +90,7 @@ func (e endpoint) toDb() Endpoint { Identifier: toDbIdentifier(e.Identifier), Status: e.Status, Version: e.Version, + Properties: e.Properties, } } diff --git a/pkg/events/domain.go b/pkg/events/domain.go index fd73b36..ce3af54 100644 --- a/pkg/events/domain.go +++ b/pkg/events/domain.go @@ -35,12 +35,13 @@ type Identifier string // RegisterEndpointEvent event type RegisterEndpointEvent struct { - Organization Identifier `json:"organization"` - URL string `json:"URL"` - EndpointType string `json:"endpointType"` - Identifier Identifier `json:"identifier"` - Status string `json:"status"` - Version string `json:"version"` + Organization Identifier `json:"organization"` + URL string `json:"URL"` + EndpointType string `json:"endpointType"` + Identifier Identifier `json:"identifier"` + Status string `json:"status"` + Version string `json:"version"` + Properties map[string]string `json:"properties,omitempty"` } // RegisterVendorEvent event diff --git a/pkg/registry.go b/pkg/registry.go index 34e7fdc..b37d9ca 100644 --- a/pkg/registry.go +++ b/pkg/registry.go @@ -79,7 +79,7 @@ type RegistryClient interface { ReverseLookup(name string) (*db.Organization, error) // RegisterEndpoint registers an endpoint for an organization - RegisterEndpoint(organizationID string, id string, url string, endpointType string, status string, version string) (events.Event, error) + RegisterEndpoint(organizationID string, id string, url string, endpointType string, status string, version string, properties map[string]string) (events.Event, error) // VendorClaim registers an organization under a vendor. orgKeys are the organization's keys in JWK format VendorClaim(vendorID string, orgID string, orgName string, orgKeys []interface{}) (events.Event, error) From 65157fab8d6cbeeeb6c8bcdf73431b028aabe9ea Mon Sep 17 00:00:00 2001 From: Rein Krul Date: Mon, 2 Mar 2020 15:01:20 +0100 Subject: [PATCH 2/3] Deduplicated code --- api/client.go | 6 +----- api/conversion.go | 14 +++++++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/api/client.go b/api/client.go index 674ac2a..fa624c7 100644 --- a/api/client.go +++ b/api/client.go @@ -163,17 +163,13 @@ func (hb HttpClient) searchOrganization(params SearchOrganizationsParams) ([]db. func (hb HttpClient) RegisterEndpoint(organizationID string, id string, url string, endpointType string, status string, version string, properties map[string]string) (events.Event, error) { ctx, cancel := context.WithTimeout(context.Background(), hb.Timeout) defer cancel() - props := EndpointProperties{} - for key, value := range properties { - props[key] = value - } res, err := hb.client().RegisterEndpoint(ctx, organizationID, RegisterEndpointJSONRequestBody{ URL: url, EndpointType: endpointType, Identifier: Identifier(id), Status: status, Version: version, - Properties: &props, + Properties: toEndpointProperties(properties), }) if err != nil { return nil, err diff --git a/api/conversion.go b/api/conversion.go index 4b48ff0..ac7cc68 100644 --- a/api/conversion.go +++ b/api/conversion.go @@ -30,11 +30,7 @@ func (e Endpoint) fromDb(db db.Endpoint) Endpoint { e.Identifier = Identifier(db.Identifier) e.Status = db.Status e.Version = db.Version - props := EndpointProperties{} - for key, value := range db.Properties { - props[key] = value - } - e.Properties = &props + e.Properties = toEndpointProperties(db.Properties) return e } @@ -99,6 +95,14 @@ func fromEndpointProperties(endpointProperties *EndpointProperties) map[string]s return props } +func toEndpointProperties(properties map[string]string) *EndpointProperties { + props := EndpointProperties{} + for key, value := range properties { + props[key] = value + } + return &props +} + func jwkToMap(jwk []JWK) []interface{} { em := make([]interface{}, len(jwk)) for i, k := range jwk { From fcb86648f0e9eee2485acbccfe0c07db35345468 Mon Sep 17 00:00:00 2001 From: Rein Krul Date: Mon, 2 Mar 2020 15:11:08 +0100 Subject: [PATCH 3/3] Added docs --- docs/pages/administration/registry.rst | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/docs/pages/administration/registry.rst b/docs/pages/administration/registry.rst index cc06086..2ad9e5f 100644 --- a/docs/pages/administration/registry.rst +++ b/docs/pages/administration/registry.rst @@ -129,4 +129,10 @@ In the following example we register a Corda consent endpoint for the previously "urn:ietf:rfc:1779:O=BecauseWeCare B.V.,C=NL,L=Almere,CN=Kunstgebit Thuiszorg" \ urn:nuts:endpoint:consent \ "tcp://1.2.3.4:4321" \ - 1.0.0 \ No newline at end of file + 1.0.0 + +Endpoints can have extra metadata in the form of string properties. Property are specified as **key=value** using the **-p** flag: + +.. code-block:: shell + + NUTS_MODE=cli ./nuts ...(etc) -p key1=Hello -p key2=World