rails-salesforce-connect 0.0.3 → 0.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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