...

Source file src/github.com/sliard/interview-accountapi-form3/api/client.go

Documentation: github.com/sliard/interview-accountapi-form3/api

     1  package api
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"errors"
     7  	"io/ioutil"
     8  	"net/http"
     9  	"os"
    10  	"strconv"
    11  	"strings"
    12  	"sync"
    13  	"time"
    14  )
    15  
    16  const apiBaseUri = "/v1/organisation/accounts"
    17  
    18  const (
    19  	EnvApiAddress = "FORM3_API_ADDR"
    20  )
    21  
    22  // LeveledLogger interface implements the basic methods that a logger library needs
    23  type LeveledLogger interface {
    24  	Error(string, ...interface{})
    25  	Info(string, ...interface{})
    26  	Debug(string, ...interface{})
    27  	Warn(string, ...interface{})
    28  }
    29  
    30  // Config is used to configure the creation of the client.
    31  type Config struct {
    32  	modifyLock sync.RWMutex
    33  
    34  	// Address is the address of the form3 api server. This should be a complete
    35  	// URL such as "https://api-docs.form3.tech". If you need a custom SSL
    36  	// cert or use a proxy, you need to specify a custom HttpClient.
    37  	Address string
    38  
    39  	// HttpClient is the HTTP client to use. We set sane defaults for the
    40  	// http.Client and its associated http.Transport created in DefaultConfig.
    41  	HttpClient *http.Client
    42  
    43  	// Logger is the leveled logger to provide to the retryable HTTP client.
    44  	Logger LeveledLogger
    45  
    46  	// Timeout is for setting custom timeout parameter in the HttpClient
    47  	Timeout time.Duration
    48  }
    49  
    50  func DefaultConfig() *Config {
    51  	config := &Config{
    52  		Address: "http://127.0.0.1:8080",
    53  		HttpClient: &http.Client{
    54  			Timeout: time.Second * 60,
    55  		},
    56  		Timeout: time.Second * 60,
    57  	}
    58  
    59  	return config
    60  }
    61  
    62  // ReadEnvironment reads configuration information from the environment.
    63  func (c *Config) ReadEnvironment() {
    64  	// Parse the environment variables
    65  	if v := os.Getenv(EnvApiAddress); v != "" {
    66  		c.logDebug("EnvApiAddress=" + v)
    67  		c.Address = v
    68  	}
    69  }
    70  
    71  // Client is the client to the form3 API. Create a client with NewClient.
    72  type Client struct {
    73  	modifyLock sync.RWMutex
    74  
    75  	config *Config
    76  }
    77  
    78  func NewClient(c *Config) (*Client, error) {
    79  	def := DefaultConfig()
    80  
    81  	if c == nil {
    82  		c = def
    83  	}
    84  
    85  	client := &Client{
    86  		config: c,
    87  	}
    88  
    89  	return client, nil
    90  }
    91  
    92  func (c *Client) SetLogger(logger LeveledLogger) {
    93  	c.modifyLock.RLock()
    94  	defer c.modifyLock.RUnlock()
    95  	c.config.modifyLock.Lock()
    96  	defer c.config.modifyLock.Unlock()
    97  
    98  	c.config.Logger = logger
    99  }
   100  
   101  type accountDataDto struct {
   102  	Data  *AccountData `json:"data"`
   103  	Links interface{}  `json:"links,omitempty"`
   104  }
   105  
   106  type errorDto struct {
   107  	Message string `json:"error_message"`
   108  }
   109  
   110  // CreateAccount Create an account with AccountData
   111  func (c *Client) CreateAccount(account *AccountData) (*AccountData, error) {
   112  	c.config.logDebug("Start CreateAccount")
   113  
   114  	if account == nil {
   115  		c.config.logError("No account data")
   116  		return nil, errors.New("no account data")
   117  	}
   118  
   119  	jsonStr, err := json.Marshal(accountDataDto{Data: account})
   120  	if err != nil {
   121  		c.config.logError("Json marshal account error : ", err.Error())
   122  		return nil, err
   123  	}
   124  
   125  	req, _ := http.NewRequest("POST", c.config.Address+apiBaseUri, bytes.NewBuffer(jsonStr))
   126  	req.Header.Add("Content-Type", "application/vnd.api+json")
   127  	resp, err := c.config.HttpClient.Do(req)
   128  	if err != nil {
   129  		c.config.logError("Http call API error : ", err.Error())
   130  		return nil, err
   131  	}
   132  
   133  	if resp.StatusCode != http.StatusCreated {
   134  		c.config.logError("create account error ", resp.StatusCode)
   135  		return nil, getDetailError(resp)
   136  	}
   137  
   138  	body, err := ioutil.ReadAll(resp.Body)
   139  	if err != nil {
   140  		return nil, err
   141  	}
   142  
   143  	var accountDto *accountDataDto
   144  	err = json.Unmarshal(body, &accountDto)
   145  	if err != nil {
   146  		c.config.logError("Failure: Unmarshal HTTP response error : " + err.Error())
   147  		return nil, errors.New("failure: Unmarshal HTTP response error : " + err.Error())
   148  	}
   149  
   150  	return accountDto.Data, nil
   151  }
   152  
   153  func (c *Client) FetchAccount(accountId string) (*AccountData, error) {
   154  	c.config.logDebug("Start FetchAccount")
   155  
   156  	req, _ := http.NewRequest("GET", c.config.Address+apiBaseUri+"/"+accountId, strings.NewReader(""))
   157  	resp, err := c.config.HttpClient.Do(req)
   158  	if err != nil {
   159  		c.config.logError("Http call API error : ", err.Error())
   160  		return nil, err
   161  	}
   162  
   163  	if resp.StatusCode != http.StatusOK {
   164  		c.config.logError("get account error ", resp.StatusCode)
   165  		return nil, getDetailError(resp)
   166  	}
   167  
   168  	body, err := ioutil.ReadAll(resp.Body)
   169  	if err != nil {
   170  		return nil, err
   171  	}
   172  
   173  	var accountDto *accountDataDto
   174  	err = json.Unmarshal(body, &accountDto)
   175  	if err != nil {
   176  		c.config.logError("Failure: Unmarshal HTTP response error : " + err.Error())
   177  		return nil, errors.New("failure: Unmarshal HTTP response error : " + err.Error())
   178  	}
   179  
   180  	return accountDto.Data, nil
   181  }
   182  
   183  func (c *Client) DeleteAccount(accountId string, version int64) error {
   184  	c.config.logDebug("Start DeleteAccount")
   185  
   186  	req, _ := http.NewRequest("DELETE", c.config.Address+apiBaseUri+"/"+accountId+"?version="+strconv.FormatInt(version, 10), strings.NewReader(""))
   187  	resp, err := c.config.HttpClient.Do(req)
   188  	if err != nil {
   189  		c.config.logError("Http call API error : ", err.Error())
   190  		return err
   191  	}
   192  
   193  	if resp.StatusCode != http.StatusNoContent {
   194  		c.config.logError("delete account error ")
   195  		return getDetailError(resp)
   196  	}
   197  
   198  	return nil
   199  }
   200  
   201  func getDetailError(response *http.Response) error {
   202  	body, err := ioutil.ReadAll(response.Body)
   203  	if err != nil {
   204  		return err
   205  	}
   206  
   207  	var errorMessage *errorDto
   208  	err = json.Unmarshal(body, &errorMessage)
   209  	if err != nil {
   210  		return err
   211  	}
   212  
   213  	return errors.New(errorMessage.Message)
   214  }
   215  
   216  func (c *Config) logDebug(msg string, data ...interface{}) {
   217  	if c.Logger != nil {
   218  		c.Logger.Debug(msg, data)
   219  	}
   220  }
   221  
   222  func (c *Config) logError(msg string, data ...interface{}) {
   223  	if c.Logger != nil {
   224  		c.Logger.Error(msg, data)
   225  	}
   226  }
   227  

View as plain text