rails-salesforce-connect 0.0.3 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d97089920b2fbb007f340056f6eefee687bb36b49f014ce716ba5f7f6b6e0809
4
- data.tar.gz: c63e6dfd969e9a40b45d6c8fc845342ad694341e38d6fa315add3fea13a47d19
3
+ metadata.gz: 6ad61d450dcdb23872b876fc2686f3fa0f81bcff71193b6114071336325ba82a
4
+ data.tar.gz: e2e316121c58fc32652f7c5a3d9dbea7356de4c3cb1caeac69f1814f0bcb0f66
5
5
  SHA512:
6
- metadata.gz: 6b04c1335277396cafa230e7049ce1781dbf44ac2d3acc7d2a39390e4e02ed6fca1aafd774cf4de0f0387e6217446ddeab17df0eccb660aa46f930718fd54c82
7
- data.tar.gz: '069cbf7dc202df66c95e8d3f4518e34f035256537992ff163912b6c1ddf0bd78df230438b6ec4419407d7ea317f11431e8499b099ec390ee460f791870989561'
6
+ metadata.gz: ccaab598ba67b116fb6db22fc21381a5cf38055a18061e68c3cc5135c3aa1fb4ac4edac43928b21eb20dd4a7e9c3b803e5bae338b16f4e5eaef63f427794cf75
7
+ data.tar.gz: edb3ef5bd8eae1c70199cb9332ae0a2a34df291e8ee873f61d6d4c20044368f6e1a336a00e0aebb4b7b962608c10a796f486cab7e177e7a30fa8d67600ee6bd7
data/Readme.md CHANGED
@@ -5,16 +5,41 @@ This gem eases heroku-connect based integrations with salesforce for rails proje
5
5
  It provides:
6
6
 
7
7
  * A rails generator for commonly used salesforce models (`rails g connect:models`)
8
+ * An API client constructor for RestForce (`Connect::ApiAdapter`)
8
9
  * A concern, `Connect::Record`, to include in salesforce-connected activerecord models (`require 'connect/record'`)
9
10
  * A concern, `Connect::Migration`, for migrations which should only run in development (eg because heroku connect is managing the schema in production).
10
11
  * A rake task `db:diff_schema` for describing the difference between your mapped fields in heroku and your local database.
12
+ * A rake task `salesforce:schema:dump[heroku-app-name]` to serialize your salesforce configuration to JSON
13
+ * A rake task `salesforce:schema:diff[old_file,new_file]` to compare two serialized salesforce configurations
11
14
 
12
15
  ## Usage
13
16
 
14
17
  Add to your gemfile:
15
18
 
19
+ ### Classes
20
+
21
+ To use `Connect::ApiAdapter`, you must provide the following environment variables:
22
+
23
+ * SALESFORCE_REST_API_HOST
24
+ * SALESFORCE_REST_API_CLIENT_ID
25
+ * SALESFORCE_REST_API_CLIENT_SECRET
26
+ * SALESFORCE_REST_API_USERNAME
27
+ * SALESFORCE_REST_API_PASSWORD
28
+ * SALESFORCE_REST_API_SECURITY_TOKEN
29
+
30
+ ### Rake tasks
31
+
16
32
  `gem "rails-salesforce-connect"`
17
33
 
34
+ To use `rake db:diff_schema` you must specify a connection string or HC_URL env var.
35
+ For instance:
36
+
37
+ `export HC_URL="$(heroku config:get DATABASE_URL)"`
38
+
39
+ To use `rake salesforce:schema:dump`, you must provide one of:
40
+
41
+ * Environment variables for `Connect::ApiAdapter`, or
42
+ * A heroku app name, which the current machine is authorized to read environment variabes from, with those variables set.
18
43
 
19
44
  ## Status
20
45
 
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Connect
4
+ class ApiAdapter
5
+
6
+ class << self
7
+ def describe(env=ENV)
8
+ client(env).describe
9
+ end
10
+
11
+ def client(env=ENV)
12
+ @client ||= Restforce.new(
13
+ api_version: env.fetch('API_VERSION', "41.0"),
14
+ host: env["SALESFORCE_REST_API_HOST"],
15
+ client_id: env["SALESFORCE_REST_API_CLIENT_ID"],
16
+ client_secret: env["SALESFORCE_REST_API_CLIENT_SECRET"],
17
+ username: env["SALESFORCE_REST_API_USERNAME"],
18
+ password: env["SALESFORCE_REST_API_PASSWORD"],
19
+ security_token: env["SALESFORCE_REST_API_SECURITY_TOKEN"],
20
+ )
21
+ end
22
+ end
23
+ end
24
+ end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'active_support/concern'
2
3
 
3
4
  module Connect
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  class Connect::Railtie < Rails::Railtie
2
3
  rake_tasks do
3
4
  load 'tasks/diff_schema.rake'
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'active_support/concern'
2
3
 
3
4
  module Connect
@@ -6,10 +7,12 @@ module Connect
6
7
 
7
8
  included do
8
9
  self.inheritance_column = 'rails_type'
9
- self.primary_key = :sfid
10
10
 
11
11
  alias_attribute :created_at, :createddate
12
12
  alias_attribute :updated_at, :systemmodstamp
13
+
14
+ class_attribute :syncs_to_salesforce
15
+ validates :sfid, presence: true, unless: :syncs_to_salesforce?
13
16
  end
14
17
 
15
18
  class_methods do
@@ -21,6 +24,23 @@ module Connect
21
24
  def timestamp_attributes_for_update
22
25
  super << "systemmodstamp"
23
26
  end
27
+
28
+ def syncs_to_salesforce!
29
+ self.syncs_to_salesforce = true
30
+ end
31
+
32
+ # Lets you insert to salesforce tables from tests on an opt-in basis
33
+ def seeding_development_data
34
+ old_value = self.syncs_to_salesforce
35
+ self.syncs_to_salesforce = true
36
+ yield
37
+ self.syncs_to_salesforce = old_value
38
+ end
39
+ end
40
+
41
+ def readonly?
42
+ return false unless self.class.syncs_to_salesforce?
43
+ super
24
44
  end
25
45
 
26
46
  def synced?
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Connect
2
3
  class ModelsGenerator < Rails::Generators::Base
3
4
 
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
 
2
3
  module Connect
3
4
  end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+ namespace :salesforce do
3
+
4
+ namespace :schema do
5
+ desc "Save the salesforce schema for the given app"
6
+ task :dump, [:app_name] => [:environment] do |_t, args|
7
+ env = ENV
8
+ outfile = 'salesforce-schema.json'
9
+
10
+ if args[:app_name].present?
11
+ env = Dotenv::Parser.call(`heroku config --app #{args[:app_name]} --shell`)
12
+ raise "Error fetching heroku config for #{args[:app_name]}" unless $?.success?
13
+ outfile = "salesforce-schema-#{args[:app_name]}.json"
14
+ end
15
+
16
+ description = Salesforce::ApiAdapter.describe(env).group_by {|e| e["name"] }
17
+ description.each do |k, v|
18
+ raise "Expected name to be unique" unless v.one?
19
+ description[k] = v.first
20
+ description[k].delete("name")
21
+ end
22
+ File.write(
23
+ outfile,
24
+ JSON.pretty_generate(description)
25
+ )
26
+ end
27
+
28
+ desc "Diff two schema files against one another"
29
+ task :diff, [:old, :new] => [:environment] do |_t, args|
30
+ old_h = JSON.parse(File.read(args[:old]))
31
+ new_h = JSON.parse(File.read(args[:new]))
32
+
33
+ removed = old_h.keys - new_h.keys
34
+ added = new_h.keys - old_h.keys
35
+ diff = false
36
+ removed.each do |r|
37
+ puts "Removed object: #{r}"
38
+ diff = true
39
+ end
40
+ added.each do |a|
41
+ puts "Added object: #{a}"
42
+ diff = true
43
+ end
44
+
45
+ HashDiff.diff(old_h.except(*removed), new_h.except(*added)).each do |sym, key, old, new_val|
46
+ diff = true
47
+ case sym
48
+ when "~"
49
+ puts "Changed #{key} from #{old} to #{new_val}"
50
+ when "-"
51
+ puts "Removed #{key}"
52
+ when "+"
53
+ puts "Added #{key} = #{old}"
54
+ else
55
+ raise "Unknown HashDiff symbol '#{sym}', expected one of '~', '+' or '-'."
56
+ end
57
+ end
58
+
59
+ exit(1) if diff
60
+ end
61
+ end
62
+ end
@@ -1,6 +1,7 @@
1
+ # frozen_string_literal: true
1
2
  namespace :db do
2
3
  desc "Identify differences from your schema"
3
- task :diff_schema, [:remote_connection_string] => [:environment] do |task, args|
4
+ task :dif_schema, [:remote_connection_string] => [:environment] do |task, args|
4
5
  require 'pg'
5
6
 
6
7
  sql = <<-SQL
@@ -25,48 +26,49 @@ namespace :db do
25
26
  ;
26
27
  SQL
27
28
 
28
- remote_conn = args[:remote_connection_string] || ENV["HC_URL"] || fail(<<-MSG)
29
+ remote_conn = args[:remote_connection_string] || ENV["HC_URL"] || ENV["HEROKUCONNECT_URL"] || fail(<<-MSG)
29
30
  Must specify remote_connection_string or provide ENV[HC_URL].
30
31
  Try
31
32
  export HC_URL="$(heroku config:get DATABASE_URL)"
32
33
 
33
34
  MSG
34
- remote = PG::Connection.new(args[:remote_connection_string] || ENV["HC_URL"]).async_exec(sql).each.to_a
35
+ remote = PG::Connection.new(remote_conn).async_exec(sql).each.to_a
35
36
  local = ApplicationRecord.connection.raw_connection.async_exec(sql).each.to_a
36
37
 
37
- local.each do |column|
38
- # Remove if it exists
39
- if remote.delete(column)
40
- # Same column exists in both.
41
- local.delete(column)
42
- next
43
- end
38
+ remote.each {|c| c["character_maximum_length"] = c["character_maximum_length"].to_i }
39
+ local.each {|c| c["character_maximum_length"] = c["character_maximum_length"].to_i }
40
+
41
+ def format_column(c)
42
+ nul = (c["is_nullable"] == "YES")
43
+ length = c["character_maximum_length"] ? ", limit: #{c["character_maximum_length"]}" : ""
44
+ precision = c["numeric_precision"] ? ", precision: #{c["numeric_precision"]}" : ""
45
+ type = c["data_type"].inspect
46
+ type = ':string' if c["data_type"] == "character varying"
47
+ "_column :#{c["table_name"]}, :#{c["column_name"]}, #{type}, null: #{nul.to_s} #{length} #{precision}"
44
48
  end
45
49
 
46
- if local.any?
50
+ local = local.map &method(:format_column)
51
+ remote = remote.map &method(:format_column)
52
+
53
+ if (local - remote).any?
47
54
  puts "***************************************************"
48
55
  puts "** Extra columns locally not in remote **"
49
56
  puts "***************************************************"
50
- local.each do |c|
51
- nul = (c["is_nullable"] == "YES")
52
- length = c["character_maximum_length"] ? ", length: #{c["character_maximum_length"]}" : ""
53
- precision = c["numeric_precision"] ? ", precision: #{c["numeric_precision"]}" : ""
54
- puts "remove_column :#{c["table_name"]}, :#{c["column_name"]}, #{c["data_type"].inspect}, null: #{nul.to_s}#{length}#{precision}"
57
+ (local - remote).each do |c|
58
+ puts "remove" + c
55
59
  end
56
60
 
57
61
  end
58
62
 
59
- if remote.any?
63
+ if (remote - local).any?
60
64
  puts "***************************************************"
61
65
  puts "** Extra columns on remote not available locally **"
62
66
  puts "***************************************************"
63
- remote.each do |c|
64
- nul = (c["is_nullable"] == "YES")
65
- length = c["character_maximum_length"] ? ", length: #{c["character_maximum_length"]}" : ""
66
- precision = c["numeric_precision"] ? ", precision: #{c["numeric_precision"]}" : ""
67
- puts "add_column :#{c["table_name"]}, :#{c["column_name"]}, #{c["data_type"].inspect}, null: #{nul.to_s} #{length} #{precision}"
67
+ (remote - local).each do |c|
68
+ puts "add" + c
68
69
  end
69
70
  end
70
- abort "Differences found" if local.any? or remote.any?
71
+
72
+ abort "Differences found" if (local - remote).any? or (remote - local).any?
71
73
  end
72
74
  end
@@ -1,7 +1,7 @@
1
1
  # gem 'activerecord'
2
2
  Gem::Specification.new do |s|
3
3
  s.name = 'rails-salesforce-connect'
4
- s.version = '0.0.3'
4
+ s.version = '0.0.4'
5
5
  s.licenses = ['MIT']
6
6
  s.summary = "Tools for using heroku connect with rails"
7
7
  s.description = "Base class for salesforce migrations, activerecord types; deduplication rules aware, and rake tasks to sync schema"
@@ -13,4 +13,7 @@ Gem::Specification.new do |s|
13
13
  s.add_runtime_dependency "activerecord"
14
14
  s.add_runtime_dependency "rake"
15
15
  s.add_runtime_dependency "pg"
16
+ s.add_runtime_dependency "dotenv"
17
+ s.add_runtime_dependency "hashdiff"
18
+ s.add_runtime_dependency "restforce"
16
19
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rails-salesforce-connect
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.0.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Daniel Heath
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-05-31 00:00:00.000000000 Z
11
+ date: 2018-10-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -52,6 +52,48 @@ dependencies:
52
52
  - - ">="
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: dotenv
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: hashdiff
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: restforce
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
55
97
  description: Base class for salesforce migrations, activerecord types; deduplication
56
98
  rules aware, and rake tasks to sync schema
57
99
  email: daniel@heath.cc
@@ -64,11 +106,13 @@ files:
64
106
  - Gemfile
65
107
  - Rakefile
66
108
  - Readme.md
109
+ - lib/connect/api_adapter.rb
67
110
  - lib/connect/migration.rb
68
111
  - lib/connect/railtie.rb
69
112
  - lib/connect/record.rb
70
113
  - lib/generators/connect/models_generator.rb
71
114
  - lib/rails-salesforce-connect.rb
115
+ - lib/tasks/diff_salesforce.rake
72
116
  - lib/tasks/diff_schema.rake
73
117
  - rails-salesforce-connect.gemspec
74
118
  homepage: https://github.com/reinteractive/rails-salesforce-connect