👌

Goでsumaregi api クライアントライブラリ開発

2024/09/14に公開

Sumaregi-go: GoでのSumaregi APIクライアントライブラリ開発入門 (Part 1)

1. はじめに

はじめまして!この記事では、Sumaregi APIとやり取りするためのGoライブラリ「Sumaregi-go」について詳しく説明します。本記事を書くきっかけは、より簡単かつ効率的にSumaregi APIと連携できる方法を提供したいと考えたからです。この記事を通じて、Goを用いてSumaregi APIを活用したい皆様の参考になれば幸いです。
リポジトリはこちら:
https://github.com/Tsubasa-2005/sumaregi-go

2. 対象読者

この記事は、Sumaregi APIを利用してアプリケーションを開発したいGoプログラマーを対象にしています。Goの基本的な知識がある方、もしくはAPIと連携する経験がある方には特に役立つ内容となっています。初心者の方でも基本概念を理解することで、この記事を通じてプロジェクトをスムーズに進めることができるでしょう。

3. 記事を読むメリット

この記事を読むことで以下のメリットがあります:

  • Sumaregi-goプロジェクトの構造を理解できる
  • Sumaregi APIとの連携方法を学べる
  • 実際のコード例を通じて具体的な実装方法を確認できる
  • 環境設定とトークン管理についての知識を得られる

これらの知識を習得することで、Sumaregi APIを用いたアプリケーション開発がより効率的に行えるようになります。

4. 結論

Sumaregi-goライブラリを使うことで、Sumaregi APIとの連携が非常に簡単になります。このライブラリを活用することで、効率的にAPI操作を行えるようになり、開発時間の短縮とコードの効率化が実現できます。ここでは、その具体的な方法とライブラリの詳細について解説します。

5. 本文

プロジェクト構成

以下は、プロジェクトの構造と各ファイル・ディレクトリの目的の概要です

ルートディレクトリ

  • token.go: スマレジからtokenを取得します。
  • .env: APIキーやその他の機密情報を保存する環境設定ファイル。
  • env.go: 環境変数を読み込みます。
  • scopes.go: APIアクセスの異なるスコープを定義します。
  • products.go: Sumaregiシステム内の商品の操作を行います。
  • client.go: Sumaregiシステム内の商品の操作を行います。

サンプル

  • examples/main.go: Sumaregi-goライブラリの使用方法を示すサンプルのmainファイル。

ドメイン

  • domain/format_to_ISO8601.go: 日付と時間をISO 8601標準にフォーマットするための関数を含みます。

設定

  • config.go: クライアントの全体的な設定を管理します。

クライアント

  • client.go: クライアント関連の操作とAPIリクエストを担当します。

開始方法

  1. インストール: このライブラリを使用するには、Goがシステムにインストールされている必要があります。リポジトリをクローンし、プロジェクトディレクトリに移動します。

    git clone https://github.com/Tsubasa-2005/sumaregi-go.git
    cd sumaregi-go
    
  2. 設定: .env.exampleファイルの名前を .envへ変更し、Sumaregiの認証情報を追加します。

    mv .env.example .env
    
  3. サンプルの実行: examplesディレクトリに移動し、サンプルのmainファイルを実行してライブラリの動作を確認します。

    cd examples
    go run main.go
    

主なコンポーネント

トークン管理

token.goファイルは、APIとのやり取りに必要なトークンの作成と更新を行います。

token.go の説明

token.goファイルは、Sumaregi APIとのやり取りに必要なアクセストークンを取得するための関数を提供します。アクセストークンは、APIリクエストの認証に使用される一時的なトークンです。このファイルでは getAccessToken 関数を通じて新しいアクセストークンを取得するプロセスが定義されています。

コードの詳細

1. データ構造

// token.go
type AccessTokenResponse struct {
	Scope       string `json:"scope"`
	TokenType   string `json:"token_type"`
	ExpiresIn   int    `json:"expires_in"`
	AccessToken string `json:"access_token"`
}
  • AccessTokenResponse: Sumaregi APIからのアクセストークン応答を表す構造体です。APIの応答から必要な情報を格納するために使用します。
    • Scope: 取得したアクセストークンが使用できるスコープ(権限)。
    • TokenType: トークンのタイプ(通常は "Bearer")。
    • ExpiresIn: トークンの有効期限(秒単位)。
    • AccessToken: 実際のアクセストークン。

2. アクセストークンを取得する関数

// token.go
func getAccessToken(scopes []string) (string, error) {
	requestURL := fmt.Sprintf("%s/app/%s/token", os.Getenv("SMAREGI_IDP_HOST"), os.Getenv("SMAREGI_SANDBOX_CONTRACT_ID"))
	auth := base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%s:%s", os.Getenv("SMAREGI_CLIENT_ID"), os.Getenv("SMAREGI_CLIENT_SECRET"))))

	formData := url.Values{}
	formData.Set("grant_type", "client_credentials")
	formData.Set("scope", joinScopes(scopes))

	req, err := http.NewRequest("POST", requestURL, strings.NewReader(formData.Encode()))
	if err != nil {
		return "", fmt.Errorf("error creating request: %w", err)
	}

	req.Header.Set("Authorization", "Basic "+auth)
	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")

	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		return "", fmt.Errorf("error making request: %w", err)
	}

	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return "", fmt.Errorf("error reading response: %w", err)
	}

	var accessTokenResult AccessTokenResponse
	err = json.Unmarshal(body, &accessTokenResult)
	if err != nil {
		return "", fmt.Errorf("error parsing response: %w", err)
	}

	return accessTokenResult.AccessToken, nil
}
  • 関数の引数:

    • scopes []string: アクセストークンを取得する際に必要なスコープ(権限)のリスト。
  • 戻り値:

    • string: 取得したアクセストークン。
    • error: エラーが発生した場合に返されるエラー情報。
  • アクセストークン取得のフロー:

    1. URLの生成:
      • SMAREGI_IDP_HOSTSMAREGI_SANDBOX_CONTRACT_ID という環境変数から、アクセストークンを取得するためのURLを生成します。
      • 例: "https://id.sumaregi.dev/app/{CONTRACT_ID}/token"
    2. 認証情報の作成:
      • SMAREGI_CLIENT_IDSMAREGI_CLIENT_SECRET の組み合わせをBase64エンコードしてBasic認証用のヘッダーを作成します。
      • 例: Basic <base64encoded_credentials>
    3. HTTPリクエストの作成:
      • http.NewRequest を使用してPOSTリクエストを作成し、リクエストのヘッダーとボディに必要なデータを設定します。
    4. HTTPリクエストの送信:
      • http.Client を使用してリクエストを送信し、レスポンスを受け取ります。
    5. レスポンスの処理:
      • レスポンスのボディを読み込み、JSONをパースして AccessTokenResponse 構造体に格納します。
    6. アクセストークンの返却:
      • 正常にパースされた場合、取得したアクセストークンを返します。

補足説明

  • joinScopes: このコードでは joinScopes(scopes) が記述されていますが、この関数はスコープのリストをスペース区切りの文字列に変換するものである。
// utils.go
func joinScopes(scopes []string) string {
	return strings.Join(scopes, " ")
}

エラーハンドリング

  • HTTPリクエストエラー:
    • リクエストが失敗した場合、エラーメッセージを返します。
  • JSONパースエラー:
    • APIの応答が予期した形式でない場合、パースエラーを返します。

Goのコード(token.go)

package sumaregi

import (
	"encoding/base64"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"os"
	"strings"
)

type AccessTokenResponse struct {
	Scope       string `json:"scope"`
	TokenType   string `json:"token_type"`
	ExpiresIn   int    `json:"expires_in"`
	AccessToken string `json:"access_token"`
}

func getAccessToken(scopes []string) (string, error) {
	requestURL := fmt.Sprintf("%s/app/%s/token", os.Getenv("SMAREGI_IDP_HOST"), os.Getenv("SMAREGI_SANDBOX_CONTRACT_ID"))
	auth := base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%s:%s", os.Getenv("SMAREGI_CLIENT_ID"), os.Getenv("SMAREGI_CLIENT_SECRET"))))

	formData := url.Values{}
	formData.Set("grant_type", "client_credentials")
	formData.Set("scope", joinScopes(scopes))

	req, err := http.NewRequest("POST", requestURL, strings.NewReader(formData.Encode()))
	if err != nil {
		return "", fmt.Errorf("error creating request: %w", err)
	}

	req.Header.Set("Authorization", "Basic "+auth)
	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")

	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		return "", fmt.Errorf("error making request: %w", err)
	}

	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return "", fmt.Errorf("error reading response: %w", err)
	}

	var accessTokenResult AccessTokenResponse
	err = json.Unmarshal(body, &accessTokenResult)
	if err != nil {
		return "", fmt.Errorf("error parsing response: %w", err)
	}

	return accessTokenResult.AccessToken, nil
}

6. 次回予告

次回の記事では、今回解説した関数を活用して、クライアントを初期化する部分の解説を行います。Sumaregi APIと対話するためのクライアントをどのように設計し、APIのエンドポイントに対してリクエストを送信するかを詳しく説明します。次回では、以下の内容に焦点を当てる予定です:

  • クライアントの初期化: NewClient 関数を使って、Sumaregi APIと対話するためのクライアントを作成する方法。
  • API呼び出しの実装: call 関数を使ってAPIのエンドポイントにリクエストを送信し、レスポンスを処理する方法。
  • エラーハンドリングとリトライ: API呼び出しのエラーハンドリングと、適切なエラーメッセージの提供方法。

このクライアントの実装により、Sumaregi APIの様々なエンドポイントに対するリクエストを簡単に行えるようになります。次回もどうぞお楽しみに!

https://zenn.dev/ttsbs/articles/937a083785a5cc
https://zenn.dev/ttsbs/articles/75a177a875ba38

Discussion