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 +4 -4
- data/Readme.md +25 -0
- data/lib/connect/api_adapter.rb +24 -0
- data/lib/connect/migration.rb +1 -0
- data/lib/connect/railtie.rb +1 -0
- data/lib/connect/record.rb +21 -1
- data/lib/generators/connect/models_generator.rb +1 -0
- data/lib/rails-salesforce-connect.rb +1 -0
- data/lib/tasks/diff_salesforce.rake +62 -0
- data/lib/tasks/diff_schema.rake +25 -23
- data/rails-salesforce-connect.gemspec +4 -1
- metadata +46 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6ad61d450dcdb23872b876fc2686f3fa0f81bcff71193b6114071336325ba82a
|
4
|
+
data.tar.gz: e2e316121c58fc32652f7c5a3d9dbea7356de4c3cb1caeac69f1814f0bcb0f66
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
data/lib/connect/migration.rb
CHANGED
data/lib/connect/railtie.rb
CHANGED
data/lib/connect/record.rb
CHANGED
@@ -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?
|
@@ -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
|
data/lib/tasks/diff_schema.rake
CHANGED
@@ -1,6 +1,7 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
namespace :db do
|
2
3
|
desc "Identify differences from your schema"
|
3
|
-
task :
|
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(
|
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
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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.
|
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.
|
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
|
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
|