1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

kintone 同期処理(レコード差分追加)

Last updated at Posted at 2019-10-10

kintoneアプリ間のレコード同期処理を cli-kintone を使い行います。
同期する先のアプリにレコードが無い場合は追加を行います。

同期シナリオ

  • レコードのバックアップ処理
  • 定期的なバックアップ

環境

  • macOS 10.13.6
  • Ruby 2.5.0
  • cli-kintone 0.9.4

前提条件

  • 更新用のキーフィールドを用意する。(今回はフィールドコード:顧客コードを用意)
  • 更新用のキーは数値、必須、重複無し、1から順に割り振られている。(1,2,3..,n)
  • 処理は更新元のマスタアプリからバックアップ先のアプリへの一方向。

処理概要

更新元のマスタアプリ(MA)に新しく追加されたレコードをバックアップ先アプリ(BA)へ追加します。

  1. バックアップ先の最後に追加された顧客コードを取得(例:BA顧客コード→20)
  2. マスタからバックアップ先の顧客コード20より後のレコードを取得(例:MA顧客コード→21,22)
  3. 取得したマスタのレコードをバックアップ先に追加

全体コード

add_difference_records.rb
require 'minitest/autorun'
require 'rubygems'
require 'bundler/setup'
require 'dotenv/load'
require 'open3'
require 'csv'
require 'stringio'
require 'pp'
require 'net/http'
require 'uri'
require 'json'
require 'base64'

SUBDOMAIN = ENV['SUBDOMAIN']
APP_FROM = ENV['APP_FROM']
API_FROM = ENV['API_FROM']
APP_TO = ENV['APP_TO']
API_TO = ENV['API_TO']
SELECT_COLUMN = ENV['SELECT_COLUMN']

# 1. バックアップ先の最後に追加された顧客コードを取得
def get_customer_code_by_last(domain, app_to, api_to, select_column)
  query = '"order by 顧客コード desc limit 1"'
  cap_str = %Q(cli-kintone -d #{domain} -a #{app_to} -c '\"#{select_column}\"' -t #{api_to}  -q #{query})
  out, err, status = Open3.capture3(cap_str)
  if (err != "")
    pp err; pp status; exit
  end
  if (out.size > 0)
    begin
      lines = []
      out.each_line { |line| lines << (line.chomp).parse_csv }
      lines[1][0]
    rescue => e
      pp e
      exit
    end
  else
    return 0
  end
end

# 2. バックアップ先とマスタの差分のレコードを取得
def fetch_records_by_master(domain, app_from, api_from, select_column, last_customer_code)
  query = "\"顧客コード > #{last_customer_code} order by 顧客コード asc\""
  cap_str = %Q(cli-kintone -d #{domain} -a #{app_from} -c '\"#{select_column}\"' -t #{api_from}  -q #{query})
  out, err, status = Open3.capture3(cap_str)
  if (err != "")
    pp err; pp status; exit
  end
  out
end

# 3. 取得した差分のレコードをバックアップ先に追加
def add_difference_records(domain, app_to, api_to, records_string)
  cap_str = %Q(cli-kintone --import -d #{domain} -a #{app_to} -t #{api_to})
  csv_out =  StringIO.new
  csv_out << records_string
  out, err, status = Open3.capture3(cap_str, :stdin_data => csv_out.string)
  if (err != "")
    pp err; pp status; exit
  end
  out
end

# UnitTest
class AddDifferenceRecordsTest < Minitest::Test
  def setup
    puts __method__
    unless (get_customer_code_by_last(SUBDOMAIN, APP_TO, API_TO, "顧客コード") == "20")
      test_setup
    end
  end
  def test_1
    puts __method__
    assert_equal "20", get_customer_code_by_last(SUBDOMAIN, APP_TO, API_TO, "顧客コード")
  end
  def test_2
    puts __method__
    records = fetch_records_by_master(SUBDOMAIN, APP_FROM, API_FROM, SELECT_COLUMN, "20")
    io_string = StringIO.new(records)
    ary = io_string.readlines
    assert_equal 3, ary.size
    assert_equal "\"21\"", ary[1].split(',')[4]
    assert_equal "22", CSV.parse(ary[2])[0][4]
  end
  def test_3
    puts __method__
    records = <<EOS
"住所","担当者名","部署名","FAX","顧客コード","顧客名","備考","郵便番号","TEL","メールアドレス"
"北海道","吉里","","","21","有限会社 吉里吉里","","123456","123-456-7890","hoge@test.com"
"北海道","ホワイト","","","22","ホワイトルーム","","123456","123-456-7890","hoge@test.com"
EOS
    ret = add_difference_records(SUBDOMAIN, APP_TO, API_TO, records)
    assert_equal false, ret !~ /SUCCESS/
  end
end

def test_setup
  puts __method__
  url = "https://#{ENV['SUBDOMAIN']}.cybozu.com/k/v1/records.json"
  uri = URI.parse(url)
  api_token = ENV['API_TO']
  app_id = ENV['APP_TO']
  req_get = Net::HTTP::Get.new(uri.path)
  req_get['X-Cybozu-API-Token'] = api_token
  req_get['Content-Type'] = 'application/json'
  fields = ["\$id", "顧客コード"]
  query = %q(顧客コード > "20")
  req_get.body = JSON.generate({"app" => app_id, "fields" => fields, "query" => query, "totalCount" => true})
  ary_ids = []
  Net::HTTP.start(uri.host, uri.port, :use_ssl => true) {|http|
    res_get = http.request(req_get)
    case res_get.code.to_i
    when 200
      pp JSON.parse(res_get.body)
      ary_ids = JSON.parse(res_get.body)['records'].map { |rec| rec['$id']['value'] } 
    else
      pp %Q(#{res_get.code} #{res_get.message})
      pp JSON.parse(res_get.body)
    end
  }
  req_delete = Net::HTTP::Delete.new(uri.path)
  req_delete['X-Cybozu-API-Token'] = api_token
  req_delete['Content-Type'] = 'application/json'
  req_delete.body = JSON.generate(
    {"app" => app_id,
     "ids" => ary_ids}
  )
  Net::HTTP.start(uri.host, uri.port, :use_ssl => true) {|http|
    res = http.request(req_delete)
    case res.code.to_i
    when 200
      pp JSON.parse(res.body)
    else
      pp %Q(#{res.code} #{res.message})
      pp JSON.parse(res.body)
    end
  }
end

処理説明

主な処理を説明します。

1. バックアップ先の最後に追加された顧客コードを取得

バックアップ先のアプリに対して、クエリで顧客コードの降順でソートした先頭1レコードを取得します。

顧客コードは重複無しの1,2,..nの順で付けている前提です。

  query = '"order by 顧客コード desc limit 1"'

2. マスタからバックアップ先の顧客コード20より後のレコードを取得

バックアップ元のマスタアプリとバックアップ先の差分のレコードを取得します。

last_customer_codeには、1.で取得した数値が入ってきます。(例えば "20" など)

  query = "\"顧客コード > #{last_customer_code} order by 顧客コード asc\""

3. 取得したマスタのレコードをバックアップ先に追加

--import オプションを付けて、標準出力からの入力を元にレコードを追加します。

  cap_str = %Q(cli-kintone --import -d #{domain} -a #{app_to} -t #{api_to})

参考リンク

cli-kintone関連

Ruby関連

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?