Elixirのステッカー用Vk API拡張

画像







はじめに



VKにはステッカーパックがあり、その一部は無料です。 ただし、VKには、サードパーティのサイトでこの機能を使用するためのパブリックAPIはありません。 タスクは、 Elixir関数型言語を使用して、 APIとしてVKのステッカーストレージの場所に拡張機能を書き込むことです。







私の意見では、メソッドの名前、およびそれらが取るパラメーターは次のようになります。 ステッカーを操作するためのAPIメソッドのコレクションの一般的な名前空間はstickersキーワードであり、メソッド自体はおそらく次のようになります。







pack_ids



次のパラメーターを使用: pack_ids



pack_id



fields



;

sticker_ids



次のパラメーター: sticker_ids



sticker_id



fields









VKにあるステッカーを作成または編集する方法はないため、このAPIには読み取り専用のメソッドしかありません 。 正直なところ、推測するのは難しく、ソーシャルネットワークの開発者を真似したくないので、メソッド名の発明に限定します。 また、VKスタイルのAPIを実装しませんが、これにより拡張機能に一般的なIDが追加されます。







これらは、ステッカーを操作するために実装するメソッドです。







セットのメソッド:







 GET /packs GET /packs/{id} GET /packs/{id}/stickers
      
      





ステッカーの方法:







 GET /stickers GET /stickers/{id} GET /stickers/{id}/pack
      
      





実装



上で書いたように、 Elixirはプログラミングを書くための言語として選ばれています。 プロジェクトのデータベースはPostgreSQLであり、 Postgrex



Postgrex



を使用してデータベースとやり取りします。 Cowboy



がWebサーバーとして使用されます。 Poison



は、 json形式でデータをシリアル化する責任を負います。 全体のタスクは、かさばらず複雑ではないため、 Phoenix



は使用されません。







新しいアプリケーションを作成するには、 mix new api_vk_stickers



使用して、 API Bkの拡張機能が構築される基本構造を作成します。







最初にすべきことは、 mix.exs



ファイルを編集するmix.exs



です。このファイルには、アプリケーションに関する基本情報と、使用される外部依存関係のリストが含まれています。







 # mix.exs defmodule ApiVkStickers.Mixfile do use Mix.Project # ... defp deps do [{:postgrex, "~> 0.13"}, {:ecto, "~> 2.1.1"}, {:cowboy, "~> 1.0.4"}, {:plug, "~> 1.1.0"}, {:poison, "~> 3.0"}] end end
      
      





依存関係のリストを編集した後、それらをすべてインストールする必要があります。このため、 mix deps.get



コマンドが使用mix deps.get



ます。







次に、拡張機能自体のロジックを記述しましょう。 プロジェクトの構造は次のとおりです。







 models/ pack.ex sticker.ex decorators/ pack_decorator.ex sticker_decorator.ex encoders/ packs_encoder.ex stickers_encoder.ex finders/ packs_finder.ex stickers_finder.ex parsers/ ids_param_parser.ex controllers/ packs_controller.ex stickers_controller.ex router.ex
      
      





モデル



モデルはEcto.Schema



モジュールを使用して作成されます。 Pack



モデルには、 title



フィールドとともに、いくつかの追加のオプションフィールドがあります。







モデルの構造は、式schema/2



引数として使用して、ソースの名前、つまりテーブルの名前を取ります。 フィールドは、式filed/3



を使用してschema/2



本体で指定されschema/2



filed/3



は、フィールド名、フィールドタイプ(デフォルト:string



)、および追加のオプション関数(デフォルト[]



)を受け入れます。







1 has_many/3



関係を定義するには、式has_many/3



ます。







 # pack.ex defmodule ApiVkStickers.Pack do use Ecto.Schema schema "packs" do field :title field :author field :slug has_many :stickers, ApiVkStickers.Sticker end end
      
      





反対の1対1の関係では、 belongs_to/3



意図されています。







ステッカーコード
 # sticker.ex defmodule ApiVkStickers.Sticker do use Ecto.Schema schema "stickers" do field :src, :map, virtual: true belongs_to :pack, ApiVkStickers.Pack end end
      
      





デコレータ



Elixirでは、明らかな理由で、オブジェクトはありませんが、モデルを展開するためのロジックは、接尾辞_decorator



持つモジュールに配置されます。 APIは 、データベースから取得した属性とともに、いくつかの追加属性も返します。 セットの場合、これは2つのサイズのカバーのコレクションと、VKでこのセットを自分に追加できる場所のURLになります。







 # pack_decorator.ex defmodule ApiVkStickers.PackDecorator do @storage_url "https://vk.com/images/store/stickers" @shop_url "https://vk.com/stickers" def source_urls(pack) do id = pack.id %{small: "#{@storage_url}/#{id}/preview1_296.jpg", large: "#{@storage_url}/#{id}/preview1_592.jpg"} end def showcase_url(pack) do "#{@shop_url}/#{pack.slug}" end end
      
      





ステッカーの場合、追加の属性は4つのバリエーションの画像アドレスのコレクションになります。







ステッカーデコレーターコード
 # sticker_decorator.ex defmodule ApiVkStickers.StickerDecorator do @storage_url "https://vk.com/images/stickers" def source_urls(sticker) do id = sticker.id %{thumb: "#{@storage_url}/#{id}/64.png", small: "#{@storage_url}/#{id}/128.png", medium: "#{@storage_url}/#{id}/256.png", large: "#{@storage_url}/#{id}/512.png"} end end
      
      





エンコーダー



シリアライザーは、属性をjson形式に変換する責任があります。 まず、基本的な属性を持つ連想配列がモデルから作成され、デコレータから取得された追加の属性がモデルに追加されます。 最後のステップは、 Poison.Encoder.Map



モジュールを使用して配列をJSONに変換することです。 PacksEncoder



モジュールには、1つのパブリックcall/1



メソッドがあります。







 # packs_encoder.ex defmodule ApiVkStickers.PacksEncoder do alias ApiVkStickers.PackDecorator defimpl Poison.Encoder, for: ApiVkStickers.Pack do def encode(pack, options) do Map.take(pack, [:id, :title, :author]) |> Map.put(:source_urls, PackDecorator.source_urls(pack)) |> Map.put(:showcase_url, PackDecorator.showcase_url(pack)) |> Poison.Encoder.Map.encode(options) end end def call(stickers) do Poison.encode!(stickers) end end
      
      





ステッカーのシリアライザーは同じです。







ステッカーエンコーダーコード
 # stickers_encoder.ex defmodule ApiVkStickers.StickersEncoder do alias ApiVkStickers.StickerDecorator defimpl Poison.Encoder, for: ApiVkStickers.Sticker do def encode(sticker, options) do Map.take(sticker, [:id, :pack_id]) |> Map.put(:source_urls, StickerDecorator.source_urls(sticker)) |> Poison.Encoder.Map.encode(options) end end def call(stickers) do Poison.encode!(stickers) end end
      
      





ファインダー



データベースクエリのロジックをコントローラーに保存しないために、フェーダー(申し訳ありませんが、クローラー)を使用します。 モデルの数に応じて、2つもあります。 セットファインダには3つの基本機能がありますby_ids/1



セットのコレクションを受信、 by_ids/1



単一のセットを受信、 by_ids/1



送信されたid



応じてセットのコレクションを受信







 # packs_finder.ex defmodule ApiVkStickers.PacksFinder do import Ecto.Query alias ApiVkStickers.{Repo, Pack} def all(query \\ Pack) do Repo.all(from p in query, order_by: p.id) end def one(id) do Repo.get(Pack, id) end def by_ids(ids) do all(from p in Pack, where: p.id in ^ids) end end
      
      





3番目のby_pack_id/1



関数を除き、ステッカーフェンダーでも同様の機能を利用できます。この関数は、 id



ではなくpack_id



でステッカーのコレクションを返します。







ステッカーファインダーコード
 # stickers_finder.ex defmodule ApiVkStickers.StickersFinder do import Ecto.Query alias ApiVkStickers.{Repo, Sticker} def all(query \\ Sticker) do Repo.all(from s in query, order_by: s.id) end def one(id) do Repo.get(Sticker, id) end def by_pack_ids(pack_ids) do all(from s in Sticker, where: s.pack_id in ^pack_ids) end end
      
      





パーサー



このサービスが必要なのは、 Plug



自動的に配列を提示するような方法でGETリクエストのURLにパラメーターを渡す方法が認識されなかったためです。 そして一般get/3



Plug.Router



モジュールのget/3



get/3



受け入れられたパラメーターを指定せずに、転送されたid



セットに対して何らかの方法で変数を作成しました。







 # ids_param_parser.ex defmodule ApiVkStickers.IdsParamParser do def call(query_string, param_name \\ "ids") do ids = Plug.Conn.Query.decode(query_string)[param_name] if ids do String.split(ids, ",") end end end
      
      





コントローラー



コントローラはPlug.Router



モジュールに基づいており、 DSLは多くのSinatraフレームワーク思い出させます。 しかし、コントローラー自体に進む前に、ルートを担当するモジュールを組み立てる必要があります。







コードrouter.ex
 defmodule ApiVkStickers.Router do use Plug.Router plug Plug.Logger plug :match plug :dispatch forward "/packs", to: ApiVkStickers.PacksController forward "/stickers", to: ApiVkStickers.StickersController match _ do conn |> put_resp_content_type("application/json") |> send_resp(404, ~s{"error":"not found"})) end end
      
      





実際、コントローラーも同じルートモジュールになりますが、基本的には、これらのモジュールをcontrollers



フォルダーに配置することが正しい判断であると考えられてcontrollers



ます。







 # packs_controller defmodule ApiVkStickers.PacksController do # ... get "/" do ids = IdsParamParser.call(conn.query_string) packs = if ids do PacksFinder.by_ids(ids) else PacksFinder.all end |> PacksEncoder.call send_json_resp(conn, packs) end get "/:id" do pack = PacksFinder.one(id) |> PacksEncoder.call send_json_resp(conn, pack) end get "/:id/stickers" do stickers = StickersFinder.by_pack_ids([id]) |> StickersEncoder.call send_json_resp(conn, stickers) end # ... end
      
      





ステッカーコントローラーコード
 # stickers_controller defmodule ApiVkStickers.StickersController do # ... get "/" do pack_ids = IdsParamParser.call(conn.query_string, "pack_ids") stickers = if pack_ids do StickersFinder.by_pack_ids(pack_ids) else StickersFinder.all end |> StickersEncoder.call send_json_resp(conn, stickers) end get "/:id" do sticker = StickersFinder.one(id) |> StickersEncoder.call send_json_resp(conn, sticker) end get "/:id/pack" do sticker = StickersFinder.one(id) pack = PacksFinder.one(sticker.pack_id) |> PacksEncoder.call send_json_resp(conn, pack) end # ... end
      
      





結果



$ curl -X GET --header 'Accept:application / json' 'http:// localhost:4000 / packs'
 [{"title":"", "source_urls":{"small":"https://vk.com/images/store/stickers/1/preview1_296.jpg", "large":"https://vk.com/images/store/stickers/1/preview1_592.jpg"}, "showcase_url":"https://vk.com/stickers/spotty", "id":1,"author":" "}, {"title":"", "source_urls":{"small":"https://vk.com/images/store/stickers/2/preview1_296.jpg", "large":"https://vk.com/images/store/stickers/2/preview1_592.jpg"}, "showcase_url":"https://vk.com/stickers/persik", "id":2,"author":" "}, {"title":"", "source_urls":{"small":"https://vk.com/images/store/stickers/3/preview1_296.jpg", "large":"https://vk.com/images/store/stickers/3/preview1_592.jpg"}, "showcase_url":"https://vk.com/stickers/smilies", "id":3,"author":" "}, {"title":"", "source_urls":{"small":"https://vk.com/images/store/stickers/4/preview1_296.jpg", "large":"https://vk.com/images/store/stickers/4/preview1_592.jpg"}, "showcase_url":"https://vk.com/stickers/fruitables", "id":4,"author":" "}]
      
      





$ curl -X GET --header 'Accept:application / json' 'http:// localhost:4000 / packs /?ids = 2,3'
 [{"title":"", "source_urls":{"small":"https://vk.com/images/store/stickers/2/preview1_296.jpg", "large":"https://vk.com/images/store/stickers/2/preview1_592.jpg"},"showcase_url":"https://vk.com/stickers/persik", "id":2,"author":" "}, {"title":"", "source_urls":{"small":"https://vk.com/images/store/stickers/3/preview1_296.jpg", "large":"https://vk.com/images/store/stickers/3/preview1_592.jpg"},"showcase_url":"https://vk.com/stickers/smilies", "id":3,"author":" "}]
      
      





$ curl -X GET --header 'Accept:application / json' 'http:// localhost:4000 / packs / 1'
 {"title":"", "source_urls":{"small":"https://vk.com/images/store/stickers/1/preview1_296.jpg", "large":"https://vk.com/images/store/stickers/1/preview1_592.jpg"}, "showcase_url":"https://vk.com/stickers/spotty", "id":1,"author":" "}
      
      





$ curl -X GET --header 'Accept:application / json' 'http:// localhost:4000 / packs / 1 / stickers'
 [{"source_urls":{"thumb":"https://vk.com/images/stickers/1/64.png", "small":"https://vk.com/images/stickers/1/128.png", "medium":"https://vk.com/images/stickers/1/256.png", "large":"https://vk.com/images/stickers/1/512.png"}, "pack_id":1,"id":1},...,{"source_urls":{"thumb":"https://vk.com/images/stickers/48/64.png", "small":"https://vk.com/images/stickers/48/128.png", "medium":"https://vk.com/images/stickers/48/256.png", "large":"https://vk.com/images/stickers/48/512.png"}, "pack_id":1,"id":48}]
      
      





$ curl -X GET --header 'Accept:application / json' 'http:// localhost:4000 / stickers'
 [{"source_urls":{"thumb":"https://vk.com/images/stickers/1/64.png", "small":"https://vk.com/images/stickers/1/128.png", "medium":"https://vk.com/images/stickers/1/256.png", "large":"https://vk.com/images/stickers/1/512.png"}, "pack_id":1,"id":1}, {"source_urls":{"thumb":"https://vk.com/images/stickers/2/64.png", "small":"https://vk.com/images/stickers/2/128.png", "medium":"https://vk.com/images/stickers/2/256.png", "large":"https://vk.com/images/stickers/2/512.png"}, "pack_id":1,"id":2}, {"source_urls":{"thumb":"https://vk.com/images/stickers/3/64.png", "small":"https://vk.com/images/stickers/3/128.png", "medium":"https://vk.com/images/stickers/3/256.png", "large":"https://vk.com/images/stickers/3/512.png"}, "pack_id":1,"id":3},...,{"source_urls":{"thumb":"https://vk.com/images/stickers/167/64.png", "small":"https://vk.com/images/stickers/167/128.png", "medium":"https://vk.com/images/stickers/167/256.png", "large":"https://vk.com/images/stickers/167/512.png"}, "pack_id":4,"id":167}, {"source_urls":{"thumb":"https://vk.com/images/stickers/168/64.png", "small":"https://vk.com/images/stickers/168/128.png", "medium":"https://vk.com/images/stickers/168/256.png", "large":"https://vk.com/images/stickers/168/512.png"}, "pack_id":4,"id":168}]
      
      





$ curl -X GET --header 'Accept:application / json' 'http:// localhost:4000 / stickers /?pack_ids = 2,3'
 [{"source_urls":{"thumb":"https://vk.com/images/stickers/49/64.png", "small":"https://vk.com/images/stickers/49/128.png", "medium":"https://vk.com/images/stickers/49/256.png", "large":"https://vk.com/images/stickers/49/512.png"},"pack_id":2,"id":49}, ..., {"source_urls":{"thumb":"https://vk.com/images/stickers/128/64.png", "small":"https://vk.com/images/stickers/128/128.png", "medium":"https://vk.com/images/stickers/128/256.png", "large":"https://vk.com/images/stickers/128/512.png"},"pack_id":3,"id":128}]
      
      





$ curl -X GET --header 'Accept:application / json' 'http:// localhost:4000 / stickers / 1'
 {"source_urls":{"thumb":"https://vk.com/images/stickers/1/64.png", "small":"https://vk.com/images/stickers/1/128.png", "medium":"https://vk.com/images/stickers/1/256.png", "large":"https://vk.com/images/stickers/1/512.png"}, "pack_id":1,"id":1}
      
      





$ curl -X GET --header 'Accept:application / json' 'http:// localhost:4000 / stickers / 1 / pack'
 {"title":"", "source_urls":{"small":"https://vk.com/images/store/stickers/1/preview1_296.jpg", "large":"https://vk.com/images/store/stickers/1/preview1_592.jpg"}, "showcase_url":"https://vk.com/stickers/spotty", "id":1,"author":" "}
      
      





あとがき



PostgreSQLはプロジェクトから削除できます。 この場合、ステッカーのセットに関するすべてのデータは、それらに属するステッカーの間隔に関するデータを含むコードに保存されます。 プロジェクトは大幅に簡素化されませんが、データベースの速度についてはわかりません。







  1. 関数型プログラミング言語のElixirに興味がある場合、または単に共感している場合は、 Wunsh && ElixirおよびProElixir Telegramチャットに参加することをお勧めします。







  2. 国内のElixirコミュニティは、プロジェクトWunsh.ruに直面して単一のプラットフォームとして登場し始めています。 今、彼らはサイトの新しいバージョンを書いています。 しかし、彼らはすでにニュースレターを購読しています。 違法なものは何もありません。週に一度、ロシア語でエリキシルに関する記事のセレクションを含む手紙が届きます。


Elixirで独自のアプリケーションを作成するトピックに興味がある場合は、例を使用してElixirアプリケーションを作成するという記事をお勧めします。 初期化から公開https://habrahabr.ru/post/317444/まで








All Articles