20201019のGoに関する記事は4件です。

GORMでマイグレーションする際の覚書

はじめに

structでモデル定義、AutoMigrateでマイグレーションを実行、といった辺りで少し調べた部分をメモしておきます。

IDにUUIDを使用するには

UUIDの自動生成をライブラリに任せつつ、テストやシードデータの投入時には固定のUUIDも指定できるようにしたかったため、以下のように対応しました。

まずはstructでのモデルの定義

package model

type User struct {
  ID string `gorm:"primaryKey;size:255;default:uuid_generate_v4()" json:"id"`
  ...
}

上記uuid_generate_v4()を利用するために、AutoMigrate(&User{}) する前にpostgresに拡張機能 uuid-ossp をインストール

db, err = gorm.Open("postgres", fmt.Sprintf(
  "host=%s port=%s user=%s password=%s dbname=%s", host, port, user, password, dbname,
))

if err != nil {
  panic(err)
}

db.Exec(`CREATE EXTENSION IF NOT EXISTS "uuid-ossp";`)

result := db.AutoMigrate(&model.User{})
...

Postgresのenum型を使用するには

structのタグで型を指定

type User struct {
  Role string `gorm:"type:role_enum;default:'user'" json:"role"`
  ...
}

AutoMigrateする前にpostgresに型の定義を追加

db.Exec(`
DO
$$
BEGIN
  IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'role_enum') THEN
    create type role_enum AS ENUM ('admin', 'editor', 'user');
  END IF;
END
$$;
`)

result := db.AutoMigrate(&model.User{})
...

まとめ

GORMのマイグレーションまわりに関してのメモでした。また他にあれば追記していきます。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Golang】testing で関数のエラー終了 "os.Exit(1)" をテストする

Go 言語(以下 golang)の testing で関数が os.Exit(1) するのをテストしたい。

「"golang" test "os.Exit(1)"」でググってもドンピシャの記事がなかったので、自分のググラビリティとして。

TL; DR

処理を別プロセスで実行して、ステータスを取得する。
テスト自体には、環境変数でフラグが立っていた場合のみ該当する関数を実行するように分岐させておく。

TS; DR

Golang のユニットテスト機能 testingos.Exit(1) する関数の動作テストをしたかったのです。

Golang には try-catch-finally といった例外処理がないため、エラーを throw するのではなく、error インターフェースを実装して戻り値として返すのが一般的なようです。

しかし、単純に os.Exit(1) する関数の動作テストをするために実装するのかぁ、と思っていたところ中国の掲示板から「2014 年の Google I/O で言及されている」との情報を得ました。

「別プロセスで実行させる」という目から鱗の方法でした。もっとシンプルにテストができる方法があれば教えてください。勉強して記事を更新いたします。

シンプルなサンプル

下記は sayonara() がステータス 1(os.Exit(1))で終了することをテストするサンプルです。

main.go
package main

import (
    "fmt"
    "os"
)

// この関数の動作テストがしたい
func sayonara() {
    fmt.Println("Sayonara!")
    os.Exit(1)
}

func main() {
    Sayonara()
}

main_test.go
package main

import (
    "os"
    "os/exec"
    "testing"
)

func Test_sayonara(t *testing.T) {
    // 環境変数 "FLAG_RUN_SAYONARA" のフラグが立っていた場合に sayonara() を実行させる
    if os.Getenv("FLAG_RUN_SAYONARA") == "1" {
        sayonara()
        return
    }

    // 外部プロセスの実行コマンド設定
    var cmd = exec.Command(os.Args[0], "-test.run=Test_sayonara")
    // 外部プロセス実行時の環境変数をセット(FLAG_RUN_SAYONARA -> 1)
    cmd.Env = append(os.Environ(), "FLAG_RUN_SAYONARA=1")
    // 外部プロセスの外部実行
    var err = cmd.Run()
    // 外部プロセスの実行結果取得。エラーの場合は正常終了
    if e, ok := err.(*exec.ExitError); ok && !e.Success() {
        return
    }
    // 実行ステータスがエラーでない(ステータスが 0)の場合は Fail させる
    t.Fatalf("process ran with err %v, want exit status 1", err)
}

参考文献

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Golang】testing で、関数のエラー終了 "os.Exit(1)" をテストする

Go 言語(以下 golang)の testing で関数が os.Exit(1) するのをテストしたい。

「"golang" test "os.Exit(1)"」でググってもドンピシャの記事がなかったので、自分のググラビリティとして。

TL; DR

処理を別プロセスで実行して、ステータスを取得する。
テスト自体には、環境変数でフラグが立っていた場合のみ該当する関数を実行するように分岐させておく。

ただし、カバレッジには反映されないので注意。カバレッジを 100% にするには、いささか工夫が必要です。(記事反映準備中)

TS; DR

Golang のユニットテスト機能 testingos.Exit(1) する関数の動作テストをしたかったのです。

Golang には try-catch-finally といった例外処理がないため、エラーを throw するのではなく、error インターフェースを実装して戻り値として返すのが一般的なようです。

しかし、単純に os.Exit(1) する関数の動作テストをするために実装するのかぁ、と思っていたところ中国の掲示板から「2014 年の Google I/O で言及されている」との情報を得ました。

「別プロセスで実行させる」という目から鱗の方法でした。もっとシンプルにテストができる方法があれば教えてください。勉強して記事を更新いたします。

シンプルなサンプル

下記は sayonara() がステータス 1(os.Exit(1))で終了することをテストするサンプルです。

main.go
package main

import (
    "fmt"
    "os"
)

// この関数の動作テストがしたい
func sayonara() {
    fmt.Println("Sayonara!")
    os.Exit(1)
}

func main() {
    sayonara()
}

main_test.go
package main

import (
    "os"
    "os/exec"
    "testing"
)

func Test_sayonara(t *testing.T) {
    // 環境変数 "FLAG_RUN_SAYONARA" のフラグが立っていた場合に sayonara() を実行させる
    if os.Getenv("FLAG_RUN_SAYONARA") == "1" {
        sayonara()
        return
    }

    // 外部プロセスの実行コマンド設定
    var cmd = exec.Command(os.Args[0], "-test.run=Test_sayonara")
    // 外部プロセス実行時の環境変数をセット(FLAG_RUN_SAYONARA -> 1)
    cmd.Env = append(os.Environ(), "FLAG_RUN_SAYONARA=1")
    // 外部プロセスの外部実行
    var err = cmd.Run()
    // 外部プロセスの実行結果取得。エラーの場合は正常終了
    if e, ok := err.(*exec.ExitError); ok && !e.Success() {
        return
    }
    // 実行ステータスがエラーでない(ステータスが 0)の場合は Fail させる
    t.Fatalf("process ran with err %v, want exit status 1", err)
}

問題は、この方法だとプロセスが別なためコードカバレッジに反映されないので網羅したことにならないんですよね。

コメントを元に、カバレッジを 100% 網羅したサンプルは出来たのですが、やる気が帰って来ていないので準備中なので記事への反映はお待ちください。適宜、更新いたします。

参考文献

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

タグ1つでブログを収益化できる投げ銭サービスを支える技術

はじめに

こちらに今回の開発を通して一番重要だと感じていることを記事にしました。
【個人開発】開発プロセスが何より大事だった ~ タグ1つでブログを収益化できる投げ銭サービス ~

先日 plog というサービスをリリースしました。

plog.gif

YouTube に使い方など載せています。

最近noteやzennなど、有料記事によるブログ収益化サービスが流行っています。
とても良いサービスですが、プラットフォームに依存した収益ですし、何より手数料が高いな・・・と思っていました。

そこで、プラットフォームに依存しない有料ブログの収益化サービスを思いつき、先日α版をローンチしました。

plogのシステム設計

全体のざっくりとしたイメージではこのような形になっています。

名称未設定.jpg

  • ①100円の決済フォームくれ
    いくらのどんな記事のためのフォームが欲しいですよ!とクライアントからサーバへリクエスト

  • ②情報を元に100円の決済フォームくれ
    POST /v1/payment_intents stripeのapiを使用し、決済フォームをリクエスト

  • ③決済フォームの元を返却
    PaymentIntentsの情報が返却される

  • ④ClientSecretを返却
    返却されたpaymentintentから、ClientSecret取得しClientに返却

  • ⑤有料記事を購入
    ユーザにクレジットカード情報などを入力してもらい、決済ボタンをおす

  • ⑥決済実施
    ユーザが有料記事を購入ボタンをおしたら、stripe.confirmCardPayment(clientSecret,data?,options?) stripe.jsの関数を使用し、Stripeに決済実施リクエスト。

  • ⑦Webhookで決済完了したら情報格納
    Stripeのwebhookを使い、決済が正常に完了した場合に、正常に決済した情報や諸々をDBに格納し完了。

といった流れです。

MVPを意識して作ったため、とても簡単な作りになっています。

ここからが主題で、plogではStripeを使用しサービスを運営しています。
Stripeを使うにあたって何点か戸惑った箇所などがあったので、頭の整理も兼ねてまとめていきます。

対象読者

Stripeを使おうとしているけど、Charges Checkout PaymentIntentsどれ使えやいいねん!って思ってる人

Stripeの決済パターン

結果公式のStripe APIを見ればいい話ですが、
docsっていきなり読むにはちょっと重いので全体感がわかるようなイメージとしてまとめます。

まず決済のパターンは大きく分けてこの3つで、用途に適した手段を使えば良いです。

  • Charges API
  • Stripe Checkout
  • Payment Intents API

Charges API

https://stripe.com/docs/payments/charges-api

Charges APIは、カード認証の銀行リクエストを処理しない古い支払いAPIです。代わりに、新しいペイメントAPIと統合をお試しください。

PaymentIntentsの前進みたいな立ち位置だったのかな?
まず公式でもこのように言ってるので、使わない方が良さそうです。

簡単に使ってみたところ、ほぼほぼやりたいことはPaymentIntentsで出来るので、chargeを使おうとしているなら、
PaymentIntentsを使っちゃうで良いと思います。
※ 一部まだPaymentIntentsでは対応していなユースケースもあるそうなので、あくまでイメージとして捉えてください。

Stripe Checkout

overview.gif

Stripeが提供しているほぼ形が決まったテンプレートを使用して決済したい場合に使えそうです。
ただ、決済画面はStripe側にリダイレクトされてしまうため、決済時に何か特別に処理をしたいなどは難しそうな印象。

あと、Stripeに必要なデータ以外も入力して欲しい時などは、自分で決済フォームを作った方が楽なので、
PaymentIntentsを使った方が便利そうです。

PaymentIntents API

スクリーンショット 2020-10-08 21.23.13.png

こんな感じの自分で決済フォームをカスタマイズして作りたい場合は、PaymentIntentsを使うのが良さそうです。

↓私はこちらのdocsをみながら実装するのが一番近道になりました。
https://stripe.com/docs/payments/accept-a-payment?integration=elements

Webhook

後の方に知って、とても使えたのが、Stripeが提供するWebhookです。

スクリーンショット 2020-10-08 21.28.42.png

決済が完了した時や、決済が失敗した時など、
いろんなトリガーを指定して、実装を組み込むことが出来ます。

plogの場合は、決済が完了した時に、購入者情報などをDBに永続化するようにしています。

最後に

plogもローコードツールの類ですが、
Stripeのように、決済という難易度の高く面倒な箇所をローコードで簡単に導入することが出来るのは、
開発者にとってとてもありがたかったです。

また、こちらに今回の開発を通して一番重要だと感じていることを記事にしました。
【個人開発】開発プロセスが何より大事だった ~ タグ1つでブログを収益化できる投げ銭サービス ~

タグ1つでブログを収益化できる投げ銭サービス plogでした。
https://plog.cash/

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む