client.go raw
1 // Copyright 2022-2025 The sacloud/api-client-go Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 package client
16
17 import (
18 "fmt"
19 "net/http"
20 "runtime"
21 "strings"
22 "sync"
23
24 sacloudhttp "github.com/sacloud/go-http"
25 )
26
27 // DefaultUserAgent デフォルトのユーザーエージェント
28 var DefaultUserAgent = fmt.Sprintf(
29 "api-client-go/v%s (%s/%s; +https://github.com/sacloud/api-client-go) %s",
30 Version,
31 runtime.GOOS,
32 runtime.GOARCH,
33 sacloudhttp.DefaultUserAgent,
34 )
35
36 // Client APIクライアント
37 type Client struct {
38 // APIRootURL APIのリクエスト先URLプレフィックス
39 APIRootURL string
40
41 initOnce sync.Once
42 factory *Factory
43 }
44
45 func (c *Client) ServerURL() string {
46 v := c.APIRootURL
47 if !strings.HasSuffix(v, "/") {
48 v += "/"
49 }
50 return v
51 }
52
53 func (c *Client) NewHttpRequestDoer() HttpRequestDoer {
54 return c.factory.NewHttpRequestDoer()
55 }
56
57 func (c *Client) Options() *Options {
58 return c.factory.Options()
59 }
60
61 func (c *Client) init(params *ClientParams) error {
62 var initError error
63 c.initOnce.Do(func() {
64 var opts []*Options
65 // 1: Profile
66 if !params.DisableProfile {
67 o, err := OptionsFromProfile(params.Profile)
68 if err != nil {
69 initError = err
70 return
71 }
72 opts = append(opts, o)
73 }
74
75 // 2: Env
76 if !params.DisableEnv {
77 opts = append(opts, OptionsFromEnv())
78 }
79
80 // 3: UserAgent
81 opts = append(opts, &Options{
82 UserAgent: params.UserAgent,
83 })
84
85 // 4: Options
86 if params.Options != nil {
87 opts = append(opts, params.Options)
88 }
89
90 // 5: フィールドのAPIキー
91 opts = append(opts, &Options{
92 AccessToken: params.Token,
93 AccessTokenSecret: params.Secret,
94 })
95
96 // 6: Custom HTTP client (for httptest)
97 if params.HTTPClient != nil {
98 opts = append(opts, &Options{
99 HttpClient: params.HTTPClient,
100 })
101 }
102
103 c.factory = NewFactory(opts...)
104 })
105 return initError
106 }
107
108 // SDKライブラリから設定するパラメータ。WithXXXを使って特定のパラメータだけ設定可能
109 type ClientParams struct {
110 // APIのリクエスト先URLプレフィックス
111 APIRootURL string
112 // APIキー群
113 Token string
114 Secret string
115 // クライアントから送られるユーザエージェント
116 UserAgent string
117 // Options HTTPクライアント関連オプション
118 Options *Options
119 // Profile usacloud互換プロファイル名
120 Profile string
121 // usacloud互換プロファイルからの設定読み取りを無効化
122 DisableProfile bool
123 // 環境変数からの設定読み取りを無効化
124 DisableEnv bool
125 // カスタムHTTPクライアント (例: httptest.NewServer().Client())
126 HTTPClient *http.Client
127 // SDKライブラリから追加したいパラメータがあったら随時追加
128 }
129
130 type ClientParam func(*ClientParams)
131
132 func WithUserAgent(ua string) ClientParam {
133 return func(params *ClientParams) {
134 params.UserAgent = ua
135 }
136 }
137
138 func WithApiKeys(accessToken string, secretToken string) ClientParam {
139 return func(params *ClientParams) {
140 params.Token = accessToken
141 params.Secret = secretToken
142 }
143 }
144
145 func WithProfile(profile string) ClientParam {
146 return func(params *ClientParams) {
147 params.Profile = profile
148 }
149 }
150
151 func WithDisableProfile(disable bool) ClientParam {
152 return func(params *ClientParams) {
153 params.DisableProfile = disable
154 }
155 }
156
157 func WithDisableEnv(disable bool) ClientParam {
158 return func(params *ClientParams) {
159 params.DisableEnv = disable
160 }
161 }
162
163 func WithOptions(options *Options) ClientParam {
164 return func(params *ClientParams) {
165 params.Options = options
166 }
167 }
168
169 // WithHTTPClient allows setting a custom http.Client (e.g., from httptest)
170 func WithHTTPClient(client *http.Client) ClientParam {
171 return func(params *ClientParams) {
172 params.HTTPClient = client
173 }
174 }
175
176 // Clientを初期化してから返す。WithXXXを使って特定の設定を初期化可能
177 func NewClient(apiUrl string, params ...ClientParam) (*Client, error) {
178 clientParams := &ClientParams{
179 APIRootURL: apiUrl,
180 UserAgent: DefaultUserAgent,
181 }
182
183 for _, param := range params {
184 param(clientParams)
185 }
186
187 return NewClientWithParams(clientParams)
188 }
189
190 // 設定したいパラメータが多い場合は直接ClientParamsを初期化して渡す
191 func NewClientWithParams(params *ClientParams) (*Client, error) {
192 client := &Client{
193 APIRootURL: params.APIRootURL,
194 }
195 if err := client.init(params); err != nil {
196 return nil, err
197 }
198
199 return client, nil
200 }
201