Modules【Go】
Goのversion 1.12以降では、$GOPATH/src以下のパッケージ管理を改め、go.modによって依存関係を管理できるようになりました。
まずは、Goのversionを確認し、必要に応じて最新化します。
$ go version $ brew upgrade go
Modulesを試すには、下記コマンドにより環境変数を設定します。
$ export GO111MODULE=on
VS Codeでは、起動時に環境変数を読み込むので、開いている途中でgo modを導入した場合には、再起動しないと、エディタ上のエラーが表示され続けるので注意します。
なお、1.13ではデフォルトで有効になるそうです。
この環境変数の設定は、bashファイルなどに設定して自動実行させない限り、毎回シェルの起動時に手動で打ち込む必要があるので、下記コマンドを実行し、自動的に実行させるのがオススメです。
// ログイン時に実行されるファイル $ nano ~/.bash_profile // シェル起動時に実行されるファイル $ nano ~/.bashrc
go.modは下記コマンドにより作成します。
$ go mod init // init の後に任意のディレクトリを指定できる // go modコマンドは、go help modで確認できる
これによって、dep コマンドは実行不要になり、importに記載された依存関係がgo buildやgo test時に自動的に解決されます。
明示的に依存関係の解決を行いたい時は、下記コマンドを実行します。
ローカルにキャッシュされるため、エディタ上のエラーを消し去りたい時に使えます。
$ go mod download
また、不必要な依存関係を削除したい場合には、下記コマンドを実行します。
$ go mod tidy
会社などで運用しているリポジトリを持ち、商用環境がある場合には、商用環境用のDockerfileを利用してgo mod tidyを実行すべきです。なぜなら、上記コマンドの場合、ローカル環境での依存関係を考慮した結果となるからです。
プロジェクトのディレクトリで、下記コマンドを実行し、go mod tidyを1度するためだけのコンテナを起動します。
$ docker-compose run --rm --no-deps api go mod tidy
上記は一度だけコマンドを実行するrunを使って、起動が終わったら削除するrmオプションを指定、さらにdocker-compose.yamlで、linksまたはdepends_onに指定されたサービスは起動しないno depsオプションを利用しています。指定サービスであるapiにて、コマンドgo mod tidyを実行します。apiは、docker-compose.yamlに記載されているserviceの1つです。
Dockerfileの使い方【Docker】
使い方
Docker imageを自分で作成して使用する場合に利用するファイルです。
配置場所は、プロジェクト配下であればどこでも大丈夫です。
Dockerfileを構成するコマンドは公式で案内されているようにいくつかありますが、下記で一例を示します。
FROM golang:1.11-alpine3.8 COPY . /go/src/github.com/work WORKDIR /go/src/github.com/work/src RUN apk --no-cache --update upgrade \ && apk add --no-cache git \ && go get -u github.com/codegangsta/gin \ && go get -u github.com/golang/dep/cmd/dep \ && dep ensure CMD gin -i run main.go dependency.go routing.go
FROM
ベースとするイメージを指定します。COPY
左に記載したローカルファイルを右に記載したディレクトリにコピーします。
「/」から始めると絶対パスに、それ以外はWORKDIRからの相対パスになります。WORKDIR
Dockerfileに記述されたコマンドが実行される基準パスを指定します。RUN
イメージ構築時に実行するOSコマンドを指定します。CMD
コンテナ起動時(イメージ構築後)で実行するコマンドを指定します。
複数のCMDが記述されている場合、最後のコマンドのみ実行されます。
注意点
Dockerfileの各コマンドは別々のコンテナを起動して実行されています。そのため、移動した先でコマンドを実行したい場合、下記のような記述にすると意図した結果を得られません。
RUN cd /dir1 RUN touch memo.txt
memo.txtは「/」に作成されてしまいます。
連続した処理にしたい場合は&&を使って連結します。
RUN cd /dir1 \ && touch memo.txt
上記であれば、「/dir1」配下にmemo.txtが作成されます。
キャッシュ
Dockerビルド時に、Dockerfileにより実行されたコマンドの内容はキャッシュされます。何かをインストールするなど、結果の内容が変わりにくいコマンドはなるべく最初に寄せることで、ビルドにかかる時間を短縮することができます。
Swaggerの使い方 - 分割と結合も紹介【Swagger】
Swaggerとは
SwaggerはREST APIのドキュメントや、テストできるUIを提供してくれるツールです。
APIのドキュメントはこれまでスプレッドシートなどで書かれることが多かったかと思いますが、Swaggerであれば、リッチなWebサイトのようなUIを提供してくれるので、見やすくかつ理解しやすいドキュメントを作ることができます。さらにパラメータを付与するなどして、返り値を確認するテストを実行することができます。
(GETメソッドのパラメータにbodyが用いられていますが、ミスです。ご容赦ください。)
Swaggerによるドキュメント作成の流れ
SwaggerはYAMLまたはJSONで記述され、そのフォーマットはSwagger Specificationといいます。このSpecificationを読み込ませると、先述のUIが出来上がります。Specificationを作成する基本的な方法が2つあります。Swagger Editorという公式に提供されたWeb上のエディタで編集し出力する方法と、ローカルでファイルを作成、編集する方法です。
Swagger Editorはウィンドウの左半分にSpecification、右には左で書かれた内容をリアルタイムに反映するUIで構成されています。ブラウザ内での記述が即座にUIに反映されて確認できるため、慣れていない場合にはオススメです。編集結果に満足したらSpecificationファイルを出力します。
ローカルで作成する場合は、YAMLまたはJSONファイルを作成し、フォーマットに則って記述します。
作成したSpecificationをSwagger Editorに読み込むことで、Swagger UIを確認することができます(APIのドキュメンテーションとして扱える)が、Dockerを用いて自分が用意する環境にホストすることもできます。Dockerではswagger-uiなどのimageが提供されています。方法についてはこちらで紹介しています。
Specificationの記述方法
ここからはSpecificationの書き方を、各構成要素を紹介する形で簡単に解説したいと思います。詳しい書き方は公式やQiita記事を参照ください。
Specificationは大きく下記の構成で成り立ちます。
- swagger
バージョンを指定 - host
APIのホスト先を記載 - schems
APIのスキーム(HTTP/HTTPS)記載 - basePath
APIのベースURLを記載(http://localhost:3000/xxxxx/xxx/api_pathの、/x~の部分) - produces
APIが生成するデータの形式を記載 - info
問い合わせ先などを記載(任意) - paths
APIのパス、引数、レスポンスなど各APIの詳細を記載 - definitions
APIのレスポンスなどに使用するデータ形式(型)を記載 - responses
ステータス返却など汎用的なレスポンスを記載
definitions、responsesは必須ではありませんが、汎用的な部品をまとめて記述量を減らすためにも利用すべきかと思います。Specificationの記述量はあっという間に膨らみますので、削れるところは削りましょう。
definitionsにexampleを記入することで、responseなどに内容の例示をすることができます。
Specificationの分割
Specificationの記述量はすぐに膨らみ、1つのファイルに書き連ねていくと見辛く編集しにくいファイルになってしまいます。
そこで、refという参照先を示すことができるプロパティを使用してファイルを分割していきます。分割したファイルは何らかの形で結合させる必要がありますが、今回はNode.jsによる例を紹介します。
ディレクトリ構造は下記のようにします。
index.yml update_api_document swagger.yml resolve.js definitions index.yml info index.yml paths index.yml <他各パスを定義した.yml> responses index.yml package.json yarn.lock node_modules
ルートディレクトリのindex.ymlは下記の通りです。swagger.ymlの骨格となる情報が記述されています。詳細部分は$refによって外部ファイルに委譲しています。
swagger: "2.0" host: localhost:3001 schemes: - http basePath: /noauth/v1 produces: - application/json info: $ref: ./info/index.yml paths: $ref: ./paths/index.yml definitions: $ref: ./definitions/index.yml responses: $ref: ./responses/index.yml
委譲先の各ディレクトリには必ずindex.ymlが配置され、先に読み込まれるようにしています。pathsのようにファイルを分けられるものは、各ディレクトリの中でも$refを用いて詳細を委譲します。
paths/index.yml
/orders: $ref: ./orders.yml /reservations: $ref: ./reservations.yml
paths/orders.yml
get: tags: - "注文管理" consumes: - "application/json" description: "注文一覧を取得" parameters: - description: "検索条件" in: "body" name: "body" required: true schema: $ref: "#/definitions/OrderFilter" produces: - "application/json" responses: 200: description: "ok" schema: type: "array" items: $ref: "#/definitions/OrderGet" 404: $ref: "#/responses/NotFound" summary: "注文一覧取得"
注意してほしいのは、参照を続けた先の末端のファイル(上記例ではpaths/orders.yml)の$refは、"#/~"となっているように同じファイル内を指していることです。これは結合後の状態を前提としているためです。
下記infoはただの一例ですが、補足情報を記載します。
info/index.yml
contact: email: "support@swagger.io" name: "API Support" url: "http://www.swagger.io/support" description: "This is a sample server celler server." license: name: "Apache 2.0" url: "http://www.apache.org/licenses/LICENSE-2.0.html" termsOfService: "http://swagger.io/terms/" title: "Swagger Example API" version: "1.0" tags: - name: "注文管理" description: "注文に関するAPI"
responseには、404などの汎用的なステータス返却などの情報を記述します。
responses/index.yml
BadRequest: description: 不正なリクエスト schema: $ref: "#/definitions/Error" Unauthorized: description: ログインしてください Forbidden: description: 権限がありません NotFound: description: 対象が存在しません InternalServerError: description: サーバエラー
続いてNode.jsファイルの内容を紹介します。
watch.jsファイルで、ルートディレクトリ以下に更新があったらプログラムを作動させswagger.ymlを更新するようにします。
watch.js
var chokidar = require('chokidar'); // ファイル監視モジュール var execSync = require('child_process').execSync; // コマンド実行用モジュール var watcher = chokidar.watch('.', { persistent: true, // 監視を続けている間プロセスを終了しない ignored: 'swagger.yml', // 監視対象外 ignoreInitial: true, // ファイルやフォルダの監視開始時にaddイベントやaddDirイベントを発生させない cwd: '.' // 基準パス }); watcher.unwatch('yarn.lock') // 監視対象外 .unwatch('package.json') .unwatch('node_modules'); var update_swagger_yml = path=> { // コマンドを実行する関数を変数化 console.log(path + ' changed. Update swagger.yml and copy it to viewer.'); execSync('./update_api_document.sh'); // 引数に指定されたファイルのコマンドを実行 } console.log('start watching...'); watcher.on('add', update_swagger_yml) // ファイルが新規作成された場合、第2引数を実行 .on('addDir', update_swagger_yml) // フォルダが新規作成された場合、第2引数を実行 .on('unlink', update_swagger_yml) // ファイルが削除された場合、第2引数を実行 .on('unlinkDir', update_swagger_yml) // フォルダが削除された場合、第2引数を実行 .on('change', update_swagger_yml); // ファイル内容が変更された場合、第2引数を実行
監視中のコマンドによってswagger.ymlが更新されるので、swagger.ymlは監視対象外にします。
上記watch.jsで指定されている「コマンド内容を記載したファイル」は、下記のようにします。
update_api_document.sh
#!/bin/sh node ./resolve.js > swagger.yml # resolve.jsの実行結果をswagger.ymlに出力 cp ./swagger.yml ../api/src/docs/yaml/ # swagger.ymlを右側(任意の場所)にコピー
resolve.jsは、$refを解決するプログラムです。
resolve.js
var resolve = require('json-refs').resolveRefs; var YAML = require('js-yaml'); var fs = require('fs'); var root = YAML.load(fs.readFileSync('./index.yml').toString()); // ./index.ymlをロード var options = { filter: ['relative', 'remote'], // relativeとremoteのrefを対象とする loaderOptions: { processContent: (res, callback) => { // responseの内容を利用するcallbackを定義 callback(null, YAML.load(res.text)); } } }; resolve(root, options).then( results => { console.log(YAML.dump(results.resolved)); // 解決した結果全てをyamlファイルとして出力 });
filterのrelativeとremoteは、相対パスと同じサーバーにあるパスが指定された$refを解決の対象にすることを意味します。他にlocal('#/~')やinvalid(無効なパス)があります。
watch.jsにより変更が監視され、変更があれば、update_api_document.shによってresolve.jsが実行されて、swagger.ymlが生成されます。.shの最後の行で、生成されたファイルの内容を別ディレクトリにコピーしていますが、これはdocker-composeで指定したswagger-uiの読み込み先です。docker-composeによるSwagger UIの使い方はこちらを参照ください。
手間はかかりますが、分割してみると、その見通しの良さに快感を覚えるはずです。
エラーになる時
Swagger UIで確認した時、definitionsが解決できないなどのエラーになる場合、resolveに失敗している可能性があります。
refのresolveプログラムはファイルの上から解決していくため、途中で失敗すると、以降のrefも解決されません。参照に指定されたものがdefinitionsに定義されていないなどがないか確認してみてください。
また、exampleなど文字列の中にVS Codeなどのエディターに表示されない不正な文字が含まれている場合もあります。ルール通りに見えるのに、エラーが起きてしまう場合には、一度definitionsなどをコピーしてSwagger Editorに貼り付けてみてください。Swagger Editorなら不正な文字を検出してくれますし、デバッグに便利です。
日付の範囲指定、月指定など - GoのSquirrelでの方法も紹介【MySQL】
日付の範囲を指定したり、7月だけといったように指定する方法を紹介します。
日付の範囲
// min ~ max WHERE <カラム名> BETWEEN min AND max // min ~ maxではない範囲 WHERE <カラム名> NOT BETWEEN min AND max
年、月、日の指定
// 年月日の指定 WHERE DATE_FORMAT(<カラム名>, %Y%m%d) = '20190714' // 年月の指定 WHERE DATE_FORMAT(<カラム名>, %Y%m) = '201907' // 年の指定 WHERE DATE_FORMAT(<カラム名>, %Y) = '2019' // 月日の指定 WHERE DATE_FORMAT(<カラム名>, %m%d) = '0714' // 月の指定 WHERE DATE_FORMAT(<カラム名>, %m) = '07' // 日の指定 WHERE DATE_FORMAT(<カラム名>, %d) = '14'
なお、Go言語でsquirrelというSQLビルダーがありますが、年の変数と月の変数などを連結して対象を指定する場合には、事前に連結した変数を定義し、それを使用する必要があります。
ym := year + month q = sq.Select( "id", "date", "customer_name", ). From("bookings"). Where(sq.Eq{"DATE_FORMAT(date, '%Y%m')": ym})
docker-composeでSwagger【Swagger】【Docker】
docker-compose経由でSwagger UIを起動する方法を紹介します。
docker-composeは.yamlにswaggerに関する命令を記述すれば大丈夫です。
services: ... swagger: image: swaggerapi/swagger-ui volumes: - ./api/src/docs/swagger.yaml:/swagger.yaml ports: - "8080:8080" environment: SWAGGER_JSON: /swagger.yaml # API_URL:
services配下にswaggerに関して記述します。
imageは公開されているswaggerapi/swagger-uiを指定します。
environment配下には、yamlまたはjsonファイルを読み込む方法を指定します。SWAGGER_JSONは、コンテナに配置したファイルを読み込む場合に使用します。API_URLはurlを指定して読み込む場合に使用します。
SWAGGER_JSONを使用する場合、volumesにはアップロードするyamlもしくはjsonファイルを指定します。
portsには、コロンを挟んで左側にホスト側のポート、右側に割り当てる先のコンテナのポートを指定します。
yamlはswaggerのルールに従って記述し、docker-compose.yamlに指定したディレクトリに配置します。
その上で、上記例ですと、ポート8080(例:localhost:8080)にアクセスすることでSwagger UIが表示されます。
swagger-uiの他にも、swagger-editorなどがあります。
dep パッケージの依存関係管理ツール【Go】
depはパッケージのバージョン・依存関係管理ツールで、Node.jsでいうところのnpmにあたるものです。
go getコマンドでインストールする場合と異なり、同じプロジェクト内で安全に同じバージョンのパッケージを使うよう管理することができます。チームで開発する時には必須とも言えるツールです。
go getの場合、インストールしたパッケージは$GOPATH/src/github.comに格納されますが、depの場合はプロジェクトのapi/src/vendorディレクトリに格納されます。
depでは2つのファイル「Gopkg.toml」と「Gopkg.lock」によってパージョン管理が行われます。
Gopkg.lockはプロジェクトのソースコードをスキャンし、importに指定されているパッケージの最新バージョンを設定として書き出されたファイルです。
さらにGopkg.tomlを編集することで、パッケージの使用するバージョンを指定することができます。
ソースコード上のimportの内容と、この2つのファイルによってインストールされるパッケージが決まり、vendorディレクトリにインストールされます。
- インストール
$ go get -u github.com/golang/dep/cmd/dep // または $ brew install dep $ brew upgrade dep
以下、コマンドを紹介します。
- プロジェクトの初期化
プロジェクト内に、Gopkg.toml、Gopkg.lock、vendorディレクトリが作成されます。
$ dep init
- 依存関係の安全確保
Gopkg.toml、Gopkg.lockとimportの内容に従って、vendorディレクトリにパッケージがインストールされます。
ただし、Gopkg.tomlに記載されていないパッケージは最新のバージョンがインストールされます。
$ dep ensure
- パッケージの最新化
Gopkg.tomlに指定したバージョンを無視して最新バージョンのパッケージをインストールします。
$ dep ensure -update
- 依存関係の安全確保(厳密)
Gopkg.toml、Gopkg.lockとimportの通りに、vendorディレクトリにパッケージがインストールされます。
Gopkg.tomlに指定されていないパッケージも最新化されることはありません。
$ dep ensure -vendor-only
- パッケージの追加
vendorディレクトリにパッケージがインストールされ、Gopkg.tomlとGopkg.lockにそのパッケージが指定されます。
ただし、importにそのパッケージを指定せず、dep ensureを行なった場合、Gopkg.lockとvendorからそのパッケージが削除されます。
$ dep ensure -add <パッケージ名>
dep を使用した Go の依存関係管理 | Boatswainブログ
Golangのパッケージバージョン管理をdepで行う - ブロックチェーンエンジニアとして生きる