Skip to content

Commit

Permalink
Add connect/dbs
Browse files Browse the repository at this point in the history
  • Loading branch information
koendelaat committed Jan 26, 2024
1 parent 81bc374 commit d877417
Show file tree
Hide file tree
Showing 9 changed files with 748 additions and 23 deletions.
30 changes: 19 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,7 @@ A HSDP API client library enabling Go programs to interact with various HSDP API

The current implement covers only a subset of HSDP APIs. Basically, we implement functionality as needed.

- [x] Blob Repository
- [x] Blob Metadata
- [x] Access Policy
- [x] Access URL
- [x] Multipart Upload
- [x] BlobStore Policy management
- [ ] Topic management
- [ ] Store Access
- [ ] Bucket management
- [ ] Contract management
- [ ] Subscription management

- [x] Cartel c.q. Container Host management ([examples](cartel/README.md))
- [x] Clinical Data Repository (CDR)
- [x] Tenant Onboarding
Expand Down Expand Up @@ -53,6 +43,24 @@ The current implement covers only a subset of HSDP APIs. Basically, we implement
- [x] Subscriber Types
- [x] Resources Limits
- [x] Authentication Methods
- [x] Data Broker
- [ ] Data Items
- [x] Subscribers
- [x] SQS
- [ ] Kinesis
- [x] Subscriptions
- [ ] Access Details
- [x] Blob Repository
- [x] Blob Metadata
- [x] Access Policy
- [x] Access URL
- [x] Multipart Upload
- [x] BlobStore Policy management
- [ ] Topic management
- [ ] Store Access
- [ ] Bucket management
- [ ] Contract management
- [ ] Subscription management
- [x] Secure Transport Layer (STL) / Edge
- [x] Device queries
- [x] Application Resources management
Expand Down
3 changes: 3 additions & 0 deletions config/hsdp.json
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,9 @@
"discovery": {
"url": "https://discovery-client-test.eu01.connect.hsdp.io/client-test/core/discovery"
},
"dbs": {
"url": "https://databroker-client-test.eu01.connect.hsdp.io/client-test/connect/databroker"
},
"has": {
"url": "https://has-client-test.eu-west.philips-healthsuite.com"
},
Expand Down
225 changes: 225 additions & 0 deletions connect/dbs/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,225 @@
// Package dbs provides support the HSDP Data Broker services
package dbs

import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
"path"
"strings"

"github.com/go-playground/validator/v10"
"github.com/google/go-querystring/query"
autoconf "github.com/philips-software/go-hsdp-api/config"
"github.com/philips-software/go-hsdp-api/iam"
"github.com/philips-software/go-hsdp-api/internal"
)

const (
userAgent = "go-hsdp-api/dbs/" + internal.LibraryVersion
APIVersion = "1"
)

// OptionFunc is the function signature function for options
type OptionFunc func(*http.Request) error

// Config contains the configuration of a Client
type Config struct {
Region string
Environment string
BaseURL string
DebugLog io.Writer
Retry int
}

// A Client manages communication with HSDP Data Broker APIs
type Client struct {
// HTTP Client used to communicate with IAM API
*iam.Client
config *Config
baseURL *url.URL

// User agent used when communicating with the HSDP Blob Repository API
UserAgent string

validate *validator.Validate

Subscribers *SubscribersService
Subscriptions *SubscriptionService
}

// NewClient returns a new DBS client
func NewClient(iamClient *iam.Client, config *Config) (*Client, error) {
validate := validator.New()
if err := validate.Struct(config); err != nil {
return nil, err
}
if iamClient == nil {
return nil, fmt.Errorf("iamClient cannot be nil")
}
doAutoconf(config)
c := &Client{Client: iamClient, config: config, UserAgent: userAgent, validate: validator.New()}

if err := c.SetBaseURL(config.BaseURL); err != nil {
return nil, err
}

c.Subscribers = &SubscribersService{Client: c, validate: validator.New()}
c.Subscriptions = &SubscriptionService{Client: c, validate: validator.New()}

return c, nil
}

func doAutoconf(config *Config) {
if config.Region != "" && config.Environment != "" {
c, err := autoconf.New(
autoconf.WithRegion(config.Region),
autoconf.WithEnv(config.Environment))
if err == nil {
theService := c.Service("dbs")
if theService.URL != "" && config.BaseURL == "" {
config.BaseURL = theService.URL
}
}
}
}

// Close releases allocated resources of clients
func (c *Client) Close() {
}

// GetBaseURL returns the base URL as configured
func (c *Client) GetBaseURL() string {
if c.baseURL == nil {
return ""
}
return c.baseURL.String()
}

// SetBaseURL sets the base URL for API requests
func (c *Client) SetBaseURL(urlStr string) error {
if urlStr == "" {
return ErrBaseURLCannotBeEmpty
}
// Make sure the given URL ends with a slash
if !strings.HasSuffix(urlStr, "/") {
urlStr += "/"
}
var err error
c.baseURL, err = url.Parse(urlStr)
return err
}

// GetEndpointURL returns the Discovery Endpoint URL as configured
func (c *Client) GetEndpointURL() string {
return c.GetBaseURL()
}

func (c *Client) NewRequest(method, requestPath string, opt interface{}, options ...OptionFunc) (*http.Request, error) {
u := *c.baseURL
// Set the encoded opaque data
u.Opaque = path.Join(c.baseURL.Path, requestPath)

req := &http.Request{
Method: method,
URL: &u,
Proto: "HTTP/1.1",
ProtoMajor: 1,
ProtoMinor: 1,
Header: make(http.Header),
Host: u.Host,
}
if opt != nil {
q, err := query.Values(opt)
if err != nil {
return nil, err
}
u.RawQuery = strings.Replace(q.Encode(), "+", "%20", -1) // https://github.com/golang/go/issues/4013
}

if method == "POST" || method == "PUT" {
bodyBytes, err := json.Marshal(opt)
if err != nil {
return nil, err
}
bodyReader := bytes.NewReader(bodyBytes)

u.RawQuery = ""
req.Body = io.NopCloser(bodyReader)
req.ContentLength = int64(bodyReader.Len())
req.Header.Set("Content-Type", "application/json")
}
token, err := c.Token()
if err != nil {
return nil, err
}
req.Header.Set("Accept", "application/json")
req.Header.Set("Authorization", "Bearer "+token)
req.Header.Set("API-Version", APIVersion)
if c.UserAgent != "" {
req.Header.Set("User-Agent", c.UserAgent)
}
for _, fn := range options {
if fn == nil {
continue
}
if err := fn(req); err != nil {
return nil, err
}
}
return req, nil
}

// Response is a HSDP DBS API response. This wraps the standard http.Response
// returned from HSDP DBS and provides convenient access to things like errors
type Response struct {
*http.Response
}

func (r *Response) StatusCode() int {
if r.Response != nil {
return r.Response.StatusCode
}
return 0
}

// newResponse creates a new Response for the provided http.Response.
func newResponse(r *http.Response) *Response {
response := &Response{Response: r}
return response
}

// Do performs a http request. If v implements the io.Writer
// interface, the raw response body will be written to v, without attempting to
// first decode it.
func (c *Client) Do(req *http.Request, v interface{}) (*Response, error) {
resp, err := c.HttpClient().Do(req)
if err != nil {
return nil, err
}

response := newResponse(resp)

err = internal.CheckResponse(resp)
if err != nil {
// even though there was an error, we still return the response
// in case the caller wants to inspect it further
return response, err
}

if v != nil {
defer func() {
_ = resp.Body.Close()
}() // Only close if we plan to read it
if w, ok := v.(io.Writer); ok {
_, err = io.Copy(w, resp.Body)
} else {
err = json.NewDecoder(resp.Body).Decode(v)
}
}

return response, err
}
24 changes: 12 additions & 12 deletions blr/client_test.go → connect/dbs/client_test.go
Original file line number Diff line number Diff line change
@@ -1,32 +1,32 @@
package blr_test
package dbs_test

import (
"github.com/philips-software/go-hsdp-api/connect/dbs"
"io"
"net/http"
"net/http/httptest"
_ "os"
"testing"

"github.com/philips-software/go-hsdp-api/blr"
"github.com/philips-software/go-hsdp-api/iam"
"github.com/stretchr/testify/assert"
)

var (
muxIAM *http.ServeMux
serverIAM *httptest.Server
muxBLR *http.ServeMux
serverBLR *httptest.Server
muxDBS *http.ServeMux
serverDBS *httptest.Server

iamClient *iam.Client
blrClient *blr.Client
dbsClient *dbs.Client
)

func setup(t *testing.T) func() {
muxIAM = http.NewServeMux()
serverIAM = httptest.NewServer(muxIAM)
muxBLR = http.NewServeMux()
serverBLR = httptest.NewServer(muxBLR)
muxDBS = http.NewServeMux()
serverDBS = httptest.NewServer(muxDBS)

var err error

Expand All @@ -36,16 +36,16 @@ func setup(t *testing.T) func() {
SharedKey: "SharedKey",
SecretKey: "SecretKey",
IAMURL: serverIAM.URL,
IDMURL: serverBLR.URL,
IDMURL: serverDBS.URL,
})
if err != nil {
t.Fatalf("Failed to create iamClient: %v", err)
}
blrClient, err = blr.NewClient(iamClient, &blr.Config{
BaseURL: serverBLR.URL + "/connect/blobrepository",
dbsClient, err = dbs.NewClient(iamClient, &dbs.Config{
BaseURL: serverDBS.URL + "/client-test/connect/databroker",
})
if err != nil {
t.Fatalf("Failed to create mdmClient: %v", err)
t.Fatalf("Failed to create dbsClient: %v", err)
}

token := "44d20214-7879-4e35-923d-f9d4e01c9746"
Expand Down Expand Up @@ -131,7 +131,7 @@ func setup(t *testing.T) func() {

return func() {
serverIAM.Close()
serverBLR.Close()
serverDBS.Close()
}
}

Expand Down
22 changes: 22 additions & 0 deletions connect/dbs/errors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package dbs

import (
"errors"
)

var (
ErrNotFound = errors.New("entity not found")
ErrBaseURLCannotBeEmpty = errors.New("base URL cannot be empty")
ErrEmptyResult = errors.New("empty result")
ErrInvalidEndpointURL = errors.New("invalid endpoint URL")
ErrMissingName = errors.New("missing name value")
ErrMissingDescription = errors.New("missing description value")
ErrMalformedInputValue = errors.New("malformed input value")
ErrMissingOrganization = errors.New("missing organization")
ErrMissingProposition = errors.New("missing proposition")
ErrMissingGlobalReference = errors.New("missing global reference")
ErrNotImplementedByHSDP = errors.New("method not implemented by HSDP")
ErrEmptyResults = errors.New("empty results")
ErrOperationFailed = errors.New("operation failed")
ErrCouldNoReadResourceAfterCreate = errors.New("could not read resource after create")
)
Loading

0 comments on commit d877417

Please sign in to comment.