diff --git a/ocm/README.md b/ocm/README.md new file mode 100644 index 0000000000000000000000000000000000000000..06775a0b0c0c4c37e1416fa2f7b20381592b6625 --- /dev/null +++ b/ocm/README.md @@ -0,0 +1,26 @@ +# Go client for the OCM service + +This go package contains client for communication with the OCM service. + +### Installation + +```shell +go get code.vereign.com/gaiax/tsa/golib/ocm@latest +``` + +###Usage + +In order to use this package you must import it in your application and +instantiate the client given the OCM service address like this: + +``` +import "code.vereign.com/gaiax/tsa/golib/ocm" + +func main() { + client := ocm.New(ocmAddress) +} +``` + +###License + +See [LICENSE](../LICENSE) for the full license. \ No newline at end of file diff --git a/ocm/client.go b/ocm/client.go new file mode 100644 index 0000000000000000000000000000000000000000..abe0bacb2113bf4523507f009b08b7095de039c2 --- /dev/null +++ b/ocm/client.go @@ -0,0 +1,66 @@ +package ocm + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "io" + "net/http" +) + +// Client is the OCM service client +type Client struct { + addr string + httpClient *http.Client +} + +// New initializes an OCM service client given the OCM service address +func New(addr string, opts ...Option) *Client { + c := &Client{ + addr: addr, + httpClient: http.DefaultClient, + } + + for _, opt := range opts { + opt(c) + } + + return c +} + +// GetLoginProofInvitation calls the "invitation" endpoint on +// the "out-of-band" protocol in the OCM service. +func (c *Client) GetLoginProofInvitation(ctx context.Context, r *LoginProofInvitationRequest) (*LoginProofInvitationResponse, error) { + b, err := json.Marshal(r) + if err != nil { + return nil, err + } + + req, err := http.NewRequestWithContext(ctx, "POST", c.addr+"/out-of-band/1.0/invitation", bytes.NewReader(b)) + if err != nil { + return nil, err + } + + resp, err := c.httpClient.Do(req) + if err != nil { + return nil, err + } + defer resp.Body.Close() // nolint:errcheck + + if resp.StatusCode != http.StatusCreated && resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("unexpected response code: %d", resp.StatusCode) + } + + bytes, err := io.ReadAll(resp.Body) + if err != nil { + return nil, err + } + + var response *LoginProofInvitationResponse + if err := json.Unmarshal(bytes, &response); err != nil { + return nil, err + } + + return response, nil +} diff --git a/ocm/client_test.go b/ocm/client_test.go new file mode 100644 index 0000000000000000000000000000000000000000..4a58c1ffd46b0a6a3f3698ddd1094c7caf2db6b2 --- /dev/null +++ b/ocm/client_test.go @@ -0,0 +1,75 @@ +package ocm_test + +import ( + "context" + "encoding/json" + "io" + "net/http" + "net/http/httptest" + "testing" + + "github.com/stretchr/testify/assert" + + "code.vereign.com/gaiax/tsa/golib/ocm" +) + +func Test_GetLoginProofInvitationSuccess(t *testing.T) { + expected := &ocm.LoginProofInvitationResponse{ + StatusCode: 200, + Message: "success", + } + + ocmServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + expectedRequestBody := `{"comment":"test","attributes":null,"schemaId":"schema:1.0","participantId":"12345"}` + bodyBytes, err := io.ReadAll(r.Body) + assert.NoError(t, err) + + bodyString := string(bodyBytes) + assert.Equal(t, expectedRequestBody, bodyString) + + w.WriteHeader(http.StatusOK) + _ = json.NewEncoder(w).Encode(expected) + })) + + req := &ocm.LoginProofInvitationRequest{ + Comment: "test", + Attributes: nil, + SchemaID: "schema:1.0", + ParticipantID: "12345", + } + + client := ocm.New(ocmServer.URL) + res, err := client.GetLoginProofInvitation(context.Background(), req) + + assert.NoError(t, err) + assert.Equal(t, expected.StatusCode, res.StatusCode) + assert.Equal(t, expected.Message, res.Message) + assert.Equal(t, expected.Data, res.Data) +} + +func Test_GetLoginProofInvitationErr(t *testing.T) { + ocmServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + expectedRequestBody := `{"comment":"test","attributes":null,"schemaId":"schema:1.0","participantId":"12345"}` + bodyBytes, err := io.ReadAll(r.Body) + assert.NoError(t, err) + + bodyString := string(bodyBytes) + assert.Equal(t, expectedRequestBody, bodyString) + + w.WriteHeader(http.StatusInternalServerError) + })) + + req := &ocm.LoginProofInvitationRequest{ + Comment: "test", + Attributes: nil, + SchemaID: "schema:1.0", + ParticipantID: "12345", + } + + client := ocm.New(ocmServer.URL) + res, err := client.GetLoginProofInvitation(context.Background(), req) + + assert.Nil(t, res) + assert.Error(t, err) + assert.Contains(t, err.Error(), "unexpected response code") +} diff --git a/ocm/option.go b/ocm/option.go new file mode 100644 index 0000000000000000000000000000000000000000..3b7ba4cfbd986bdb96272ae92911e3f08b69781b --- /dev/null +++ b/ocm/option.go @@ -0,0 +1,11 @@ +package ocm + +import "net/http" + +type Option func(*Client) + +func WithHTTPClient(client *http.Client) Option { + return func(c *Client) { + c.httpClient = client + } +} diff --git a/ocm/types.go b/ocm/types.go new file mode 100644 index 0000000000000000000000000000000000000000..fa12f15a01869872b1328283928592a65aa75a2b --- /dev/null +++ b/ocm/types.go @@ -0,0 +1,25 @@ +package ocm + +type LoginProofInvitationRequest struct { + Comment string `json:"comment"` + Attributes []Attribute `json:"attributes"` + SchemaID string `json:"schemaId"` + ParticipantID string `json:"participantId"` +} + +type Attribute struct { + AttributeName string `json:"attributeName"` + Value string `json:"value"` + Condition string `json:"condition"` +} + +type LoginProofInvitationResponse struct { + StatusCode int `json:"statusCode"` + Message string `json:"message"` + Data LoginProofInvitationResponseData `json:"data"` +} + +type LoginProofInvitationResponseData struct { + PresentationID string `json:"presentationId"` + PresentationMessage string `json:"presentationMessage"` +}