github.com/cidverse/cid-sdk-go@v0.0.0-20240318001225-c193d83f053e/client.go (about)

     1  package cidsdk
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"errors"
     7  	"fmt"
     8  	"io"
     9  	"net"
    10  	"net/http"
    11  	"net/url"
    12  	"os"
    13  	"strconv"
    14  	"strings"
    15  	"time"
    16  
    17  	"github.com/go-resty/resty/v2"
    18  )
    19  
    20  func NewSDK() (*SDK, error) {
    21  	client := resty.New()
    22  	client.SetDisableWarn(true)
    23  	client.SetRetryCount(0)
    24  	client.SetTimeout(60 * time.Minute)
    25  
    26  	// socket endpoint
    27  	unixSocket := os.Getenv("CID_API_SOCKET")
    28  	if len(unixSocket) > 0 {
    29  		transport := http.Transport{
    30  			DialContext: func(_ context.Context, _ string, _ string) (net.Conn, error) {
    31  				return net.Dial("unix", unixSocket)
    32  			},
    33  		}
    34  
    35  		// base url
    36  		client.SetTransport(&transport).SetBaseURL("http://localhost")
    37  	}
    38  
    39  	// http endpoint
    40  	addr := os.Getenv("CID_API_ADDR")
    41  	if len(addr) > 0 {
    42  		client.SetBaseURL(addr)
    43  	}
    44  
    45  	// auth
    46  	secret := os.Getenv("CID_API_SECRET")
    47  	if len(secret) > 0 {
    48  		client.SetAuthScheme("Bearer")
    49  		client.SetAuthToken(secret)
    50  	}
    51  
    52  	if client == nil {
    53  		return nil, errors.New("failed to initialize api client")
    54  	}
    55  	return &SDK{client: client}, nil
    56  }
    57  
    58  type SDKClient interface {
    59  	Health() (*HealthcheckResponse, error)
    60  	Log(req LogMessageRequest) error
    61  	ModuleAction(cfg any) (ModuleActionData, error)
    62  	ProjectAction(cfg any) (ProjectActionData, error)
    63  	Env() (map[string]string, error)
    64  	Modules() (*[]ProjectModule, error)
    65  	CurrentModule() (*ProjectModule, error)
    66  	CurrentConfig() (*CurrentConfig, error)
    67  	VCSCommits(request VCSCommitsRequest) (*[]VCSCommit, error)
    68  	VCSCommitByHash(request VCSCommitByHashRequest) (*VCSCommit, error)
    69  	VCSTags() (*[]VCSTag, error)
    70  	VCSReleases(request VCSReleasesRequest) (*[]VCSRelease, error)
    71  	VCSDiff(request VCSDiffRequest) (*[]VCSDiff, error)
    72  	ExecuteCommand(req ExecuteCommandRequest) (*ExecuteCommandResponse, error)
    73  	FileRead(file string) (string, error)
    74  	FileList(req FileRequest) (files []File, err error)
    75  	FileRename(old string, new string) error
    76  	FileRemove(file string) error
    77  	FileWrite(file string, content []byte) error
    78  	FileExists(file string) bool
    79  	ZIPCreate(inputDirectory string, archiveFile string) error
    80  	ZIPExtract(archiveFile string, outputDirectory string) error
    81  	TARCreate(inputDirectory string, archiveFile string) error
    82  	TARExtract(archiveFile string, outputDirectory string) error
    83  	ArtifactList(request ArtifactListRequest) (*[]ActionArtifact, error)
    84  	ArtifactUpload(request ArtifactUploadRequest) error
    85  	ArtifactDownload(request ArtifactDownloadRequest) error
    86  	ArtifactDownloadByteArray(request ArtifactDownloadByteArrayRequest) ([]byte, error)
    87  	UUID() string
    88  }
    89  
    90  type SDK struct {
    91  	client *resty.Client
    92  }
    93  
    94  // Health request
    95  func (sdk SDK) Health() (*HealthcheckResponse, error) {
    96  	resp, err := sdk.client.R().
    97  		SetHeader("Accept", "application/json").
    98  		SetResult(&HealthcheckResponse{}).
    99  		SetError(&APIError{}).
   100  		Get("/health")
   101  
   102  	if err != nil {
   103  		return nil, err
   104  	} else if resp.IsSuccess() {
   105  		fmt.Printf("RESULT: %+v\n", resp.Result())
   106  		return resp.Result().(*HealthcheckResponse), nil
   107  	} else {
   108  		return nil, resp.Error().(*APIError)
   109  	}
   110  }
   111  
   112  // Env request
   113  func (sdk SDK) Env() (map[string]string, error) {
   114  	resp, err := sdk.client.R().
   115  		SetHeader("Accept", "application/json").
   116  		SetResult(&map[string]string{}).
   117  		SetError(&APIError{}).
   118  		Get("/env")
   119  
   120  	if err != nil {
   121  		return nil, err
   122  	} else if resp.IsSuccess() {
   123  		return *resp.Result().(*map[string]string), nil
   124  	} else {
   125  		return nil, resp.Error().(*APIError)
   126  	}
   127  }
   128  
   129  // CurrentConfig request
   130  func (sdk SDK) CurrentConfig() (*CurrentConfig, error) {
   131  	resp, err := sdk.client.R().
   132  		SetHeader("Accept", "application/json").
   133  		SetResult(&CurrentConfig{}).
   134  		SetError(&APIError{}).
   135  		Get("/config/current")
   136  
   137  	if err != nil {
   138  		return nil, err
   139  	} else if resp.IsSuccess() {
   140  		return resp.Result().(*CurrentConfig), nil
   141  	} else {
   142  		return nil, resp.Error().(*APIError)
   143  	}
   144  }
   145  
   146  // Modules request
   147  func (sdk SDK) Modules() (*[]ProjectModule, error) {
   148  	resp, err := sdk.client.R().
   149  		SetHeader("Accept", "application/json").
   150  		SetResult(&[]ProjectModule{}).
   151  		SetError(&APIError{}).
   152  		Get("/module")
   153  
   154  	if err != nil {
   155  		return nil, err
   156  	} else if resp.IsSuccess() {
   157  		return resp.Result().(*[]ProjectModule), nil
   158  	} else {
   159  		return nil, resp.Error().(*APIError)
   160  	}
   161  }
   162  
   163  // CurrentModule request
   164  func (sdk SDK) CurrentModule() (*ProjectModule, error) {
   165  	resp, err := sdk.client.R().
   166  		SetHeader("Accept", "application/json").
   167  		SetResult(&ProjectModule{}).
   168  		SetError(&APIError{}).
   169  		Get("/module/current")
   170  
   171  	if err != nil {
   172  		return nil, err
   173  	} else if resp.IsSuccess() {
   174  		return resp.Result().(*ProjectModule), nil
   175  	} else {
   176  		return nil, resp.Error().(*APIError)
   177  	}
   178  }
   179  
   180  type VCSCommitsRequest struct {
   181  	FromHash       string `json:"from"`
   182  	ToHash         string `json:"to"`
   183  	IncludeChanges bool   `json:"changes"`
   184  	Limit          int    `json:"limit"`
   185  }
   186  
   187  // VCSCommits request
   188  func (sdk SDK) VCSCommits(request VCSCommitsRequest) (*[]VCSCommit, error) {
   189  	resp, err := sdk.client.R().
   190  		SetHeader("Accept", "application/json").
   191  		SetResult(&[]VCSCommit{}).
   192  		SetError(&APIError{}).
   193  		Get(fmt.Sprintf("/vcs/commit?from=%s&to=%s&changes=%s&limit=%s", request.FromHash, request.ToHash, strconv.FormatBool(request.IncludeChanges), strconv.Itoa(request.Limit)))
   194  
   195  	if err != nil {
   196  		return nil, err
   197  	} else if resp.IsSuccess() {
   198  		return resp.Result().(*[]VCSCommit), nil
   199  	} else {
   200  		return nil, resp.Error().(*APIError)
   201  	}
   202  }
   203  
   204  type VCSCommitByHashRequest struct {
   205  	Hash           string `json:"hash"`
   206  	IncludeChanges bool   `json:"changes"`
   207  }
   208  
   209  // VCSCommitByHash request
   210  func (sdk SDK) VCSCommitByHash(request VCSCommitByHashRequest) (*VCSCommit, error) {
   211  	resp, err := sdk.client.R().
   212  		SetHeader("Accept", "application/json").
   213  		SetResult(&VCSCommit{}).
   214  		SetError(&APIError{}).
   215  		Get(fmt.Sprintf("/vcs/commit/%s?changes=%s", request.Hash, strconv.FormatBool(request.IncludeChanges)))
   216  
   217  	if err != nil {
   218  		return nil, err
   219  	} else if resp.IsSuccess() {
   220  		return resp.Result().(*VCSCommit), nil
   221  	} else {
   222  		return nil, resp.Error().(*APIError)
   223  	}
   224  }
   225  
   226  // VCSTags request
   227  func (sdk SDK) VCSTags() (*[]VCSTag, error) {
   228  	resp, err := sdk.client.R().
   229  		SetHeader("Accept", "application/json").
   230  		SetResult(&[]VCSTag{}).
   231  		SetError(&APIError{}).
   232  		Get("/vcs/tag")
   233  
   234  	if err != nil {
   235  		return nil, err
   236  	} else if resp.IsSuccess() {
   237  		return resp.Result().(*[]VCSTag), nil
   238  	} else {
   239  		return nil, resp.Error().(*APIError)
   240  	}
   241  }
   242  
   243  type VCSReleasesRequest struct {
   244  	Type string `json:"type"` // Type of the release: stable, unstable
   245  }
   246  
   247  // VCSReleases request
   248  func (sdk SDK) VCSReleases(request VCSReleasesRequest) (*[]VCSRelease, error) {
   249  	resp, err := sdk.client.R().
   250  		SetHeader("Accept", "application/json").
   251  		SetResult(&[]VCSRelease{}).
   252  		SetError(&APIError{}).
   253  		Get(fmt.Sprintf("/vcs/release?type=%s", request.Type))
   254  
   255  	if err != nil {
   256  		return nil, err
   257  	} else if resp.IsSuccess() {
   258  		return resp.Result().(*[]VCSRelease), nil
   259  	} else {
   260  		return nil, resp.Error().(*APIError)
   261  	}
   262  }
   263  
   264  type VCSDiffRequest struct {
   265  	FromHash string `json:"from"`
   266  	ToHash   string `json:"to"`
   267  }
   268  
   269  // VCSDiff request
   270  func (sdk SDK) VCSDiff(request VCSDiffRequest) (*[]VCSDiff, error) {
   271  	resp, err := sdk.client.R().
   272  		SetHeader("Accept", "application/json").
   273  		SetResult(&[]VCSDiff{}).
   274  		SetError(&APIError{}).
   275  		Get(fmt.Sprintf("/vcs/diff?from=%s&to=%s", request.FromHash, request.ToHash))
   276  
   277  	if err != nil {
   278  		return nil, err
   279  	} else if resp.IsSuccess() {
   280  		return resp.Result().(*[]VCSDiff), nil
   281  	} else {
   282  		return nil, resp.Error().(*APIError)
   283  	}
   284  }
   285  
   286  type ArtifactListRequest struct {
   287  	Query string `json:"query"`
   288  }
   289  
   290  // ArtifactList request
   291  func (sdk SDK) ArtifactList(request ArtifactListRequest) (*[]ActionArtifact, error) {
   292  	resp, err := sdk.client.R().
   293  		SetHeader("Accept", "application/json").
   294  		SetResult(&[]ActionArtifact{}).
   295  		SetError(&APIError{}).
   296  		Get(fmt.Sprintf("/artifact?query=%s", url.QueryEscape(request.Query)))
   297  
   298  	if err != nil {
   299  		return nil, err
   300  	} else if resp.IsSuccess() {
   301  		return resp.Result().(*[]ActionArtifact), nil
   302  	} else {
   303  		return nil, resp.Error().(*APIError)
   304  	}
   305  }
   306  
   307  type ArtifactUploadRequest struct {
   308  	File          string `json:"file"`
   309  	Content       string `json:"content"`
   310  	ContentBytes  []byte `json:"content_bytes"`
   311  	Module        string `json:"module"`
   312  	Type          string `json:"type"`
   313  	Format        string `json:"format"`
   314  	FormatVersion string `json:"format_version"`
   315  	ExtractFile   bool   `json:"extract_file"`
   316  }
   317  
   318  // ArtifactUpload request
   319  func (sdk SDK) ArtifactUpload(request ArtifactUploadRequest) error {
   320  	// upload
   321  	payload := map[string]string{
   322  		"type":           request.Type,
   323  		"module":         request.Module,
   324  		"format":         request.Format,
   325  		"format_version": request.FormatVersion,
   326  	}
   327  	if request.ExtractFile {
   328  		payload["extract_file"] = "true"
   329  	}
   330  
   331  	if request.Content != "" || request.ContentBytes != nil {
   332  		var reader io.Reader
   333  		if request.Content != "" {
   334  			reader = strings.NewReader(request.Content)
   335  		} else {
   336  			reader = bytes.NewReader(request.ContentBytes)
   337  		}
   338  
   339  		resp, err := sdk.client.R().
   340  			SetFormData(payload).
   341  			SetFileReader("file", request.File, reader).
   342  			SetContentLength(true).
   343  			SetError(&APIError{}).
   344  			Post("/artifact")
   345  		if err != nil {
   346  			return err
   347  		} else if resp.IsError() {
   348  			return resp.Error().(*APIError)
   349  		}
   350  	} else if request.File != "" {
   351  		resp, err := sdk.client.R().
   352  			SetFormData(payload).
   353  			SetFile("file", request.File).
   354  			SetContentLength(true).
   355  			SetError(&APIError{}).
   356  			Post("/artifact")
   357  		if err != nil {
   358  			return err
   359  		} else if resp.IsError() {
   360  			return resp.Error().(*APIError)
   361  		}
   362  	} else {
   363  		return fmt.Errorf("file, content or contentBytes must be provided")
   364  	}
   365  
   366  	return nil
   367  }
   368  
   369  type ArtifactDownloadRequest struct {
   370  	ID         string `json:"id"`
   371  	TargetFile string `json:"target_file"`
   372  }
   373  
   374  // ArtifactDownload request
   375  func (sdk SDK) ArtifactDownload(request ArtifactDownloadRequest) error {
   376  	resp, err := sdk.client.R().
   377  		SetQueryParam("id", request.ID).
   378  		SetOutput(request.TargetFile).
   379  		SetError(&APIError{}).
   380  		Get("/artifact/download")
   381  	if err != nil {
   382  		return err
   383  	} else if resp.IsError() {
   384  		return resp.Error().(*APIError)
   385  	}
   386  
   387  	return nil
   388  }
   389  
   390  type ArtifactDownloadByteArrayRequest struct {
   391  	ID string `json:"id"`
   392  }
   393  
   394  // ArtifactDownloadByteArray request
   395  func (sdk SDK) ArtifactDownloadByteArray(request ArtifactDownloadByteArrayRequest) ([]byte, error) {
   396  	resp, err := sdk.client.R().
   397  		SetQueryParam("id", request.ID).
   398  		SetError(&APIError{}).
   399  		Get("/artifact/download")
   400  	if err != nil {
   401  		return nil, err
   402  	} else if resp.IsError() {
   403  		return nil, resp.Error().(*APIError)
   404  	}
   405  
   406  	return resp.Body(), nil
   407  }