diff --git a/API_DOC.md b/API_DOC.md index 92e64b5..cd6d9f9 100644 --- a/API_DOC.md +++ b/API_DOC.md @@ -111,18 +111,20 @@ Also Support camelCase. Get transactions list and paginate it. -> GET /api/transactions +> GET /api/v2/transactions #### params +Also Support camelCase. + ```ruby { "account": "the addr transactions related to (from or to)", # hash string "from": "the addr transactions from", # hash string "to": "the addr transactions to", # hash string - "valueFormat": "decimal", # set value to decimal number, default hex number + "value_format": "decimal", # set value to decimal number, default hex number "page": "1", # integer, default 1 - "perPage": "10", # integer, default 10 + "per_page": "10", # integer, default 10 # offset and limit has lower priority than page and perPage "offset": "1", # integer, default to 0 "limit": "10", # integer, default to 10 diff --git a/app/controllers/api/v2/transactions_controller.rb b/app/controllers/api/v2/transactions_controller.rb new file mode 100644 index 0000000..11ce4b6 --- /dev/null +++ b/app/controllers/api/v2/transactions_controller.rb @@ -0,0 +1,49 @@ +# frozen_string_literal: true + +class Api::V2::TransactionsController < ApplicationController + # params: + # { + # "account": "the addr transactions related to", // hash + # "from": "the addr transactions from", // hash + # "to": "the addr transactions to", // hash + # "page": "1", // default 1 + # "per_page": "10", // default 10 + # "value_format": "decimal", // set value to decimal number, default hex number + # + # # offset and limit has lower priority than page and perPage + # "offset": "1", // default to 0 + # "limit": "10", // default to 10 + # } + # + # GET /api/v2/transactions + def index + params.transform_keys!(&:underscore) + + # use ILIKE to ignore case + options = { + from_or_to_matches: params[:account], + from_matches: params[:from], + to_matches: params[:to] + } + + # FIXME: should order by block_number and index desc, change block_number to integer + transactions = Transaction.ransack(options).result.order(updated_at: :desc) + + if params[:page].nil? && (params[:offset].present? || params[:limit].present?) + offset = params[:offset] || 0 + limit = params[:limit] || 10 + transactions = transactions.offset(offset).limit(limit) + else + transactions = transactions.page(params[:page]).per(params[:per_page]) + end + + # TODO: provide V2 api to set default value decimal + decimal_value = params[:value_format] == "decimal" + + render json: { + result: { + transactions: ActiveModelSerializers::SerializableResource.new(transactions, each_serializer: ::Api::TransactionSerializer, decimal_value: decimal_value) + } + } + end +end diff --git a/config/routes.rb b/config/routes.rb index 7c691b9..785a3e9 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -21,6 +21,7 @@ namespace :v2 do resources :blocks, only: [:index] + resources :transactions, only: [:index] end end diff --git a/spec/controllers/api/v2/transactions_controller_spec.rb b/spec/controllers/api/v2/transactions_controller_spec.rb new file mode 100644 index 0000000..11f38b0 --- /dev/null +++ b/spec/controllers/api/v2/transactions_controller_spec.rb @@ -0,0 +1,117 @@ +require 'rails_helper' + +def random(num) + "0x" + SecureRandom.hex(num) +end + +RSpec.describe Api::V2::TransactionsController, type: :controller do + let(:from) { random(20) } + let(:to) { random(20) } + + let(:body) { Oj.load(response.body).with_indifferent_access } + let(:result) { body[:result] } + let(:count) { result[:transactions].size } + + before do + mock_get_meta_data + + block_one = create :block_one + 13.times do + create :transaction, block: block_one, tx_hash: random(32) + end + + create :transaction, block: block_one, tx_hash: random(32), from: from + create :transaction, block: block_one, tx_hash: random(32), to: to + end + + context "index" do + it "no params" do + post :index, params: {} + expect(count).to eq 10 + end + + context "with account" do + it "equal to from" do + post :index, params: { account: from } + expect(count).to eq 1 + end + + it "equal to to" do + post :index, params: { account: to } + expect(count).to eq 1 + end + end + + it "with from" do + post :index, params: { from: from } + expect(count).to eq 1 + end + + it "with to" do + post :index, params: { to: to } + expect(count).to eq 1 + end + + context "ignore case" do + let(:swapcase_from) { from.swapcase } + let(:swapcase_to) { to.swapcase } + + it "with swapcase from" do + post :index, params: { from: swapcase_from } + expect(count).to eq 1 + end + + it "with swapcase to" do + post :index, params: { to: swapcase_to } + expect(count).to eq 1 + end + + it "with swapcase account" do + post :index, params: { account: swapcase_from } + expect(count).to eq 1 + end + end + + it "with page and perPage" do + post :index, params: { page: 2, perPage: 10 } + expect(result[:transactions].count).to eq 5 + end + + it "with offset and limit" do + post :index, params: { offset: 10, limit: 10 } + expect(count).to eq 5 + end + + it "with offset" do + post :index, params: { offset: 0 } + expect(count).to eq 10 + end + + it "with limit" do + post :index, params: { limit: 5 } + expect(count).to eq 5 + end + + context "with valueFormat" do + let(:value_decimal) { attributes_for(:transaction)[:value] } + let(:value_hex) { "0x0000000000000000000000000000000000000000000000000000000000001000" } + it "return decimal if valueFormat equals decimal" do + get :index, params: { valueFormat: "decimal" } + + expect(result["transactions"].map {|n| n["value"]}.uniq).to match_array [value_decimal] + end + + it "return hex if valueFormat equals hex" do + get :index, params: { valueFormat: 'hex' } + + expect(result["transactions"].map {|n| n["value"]}.uniq).to match_array [value_hex] + end + + it "return hex if valueFormat not set" do + get :index + + expect(result["transactions"].map {|n| n["value"]}.uniq).to match_array [value_hex] + end + end + end +end