sf_cli 0.0.5 → 0.0.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +21 -15
- data/README.rdoc +81 -12
- data/lib/sf_cli/sf/core/base.rb +2 -0
- data/lib/sf_cli/sf/model/base_methods.rb +62 -0
- data/lib/sf_cli/sf/model/class_definition.rb +34 -43
- data/lib/sf_cli/sf/model/dml_methods.rb +36 -0
- data/lib/sf_cli/sf/model/generator.rb +8 -7
- data/lib/sf_cli/sf/model/query_condition.rb +136 -0
- data/lib/sf_cli/sf/model/query_methods.rb +58 -0
- data/lib/sf_cli/sf/model/sf_command_connection.rb +60 -0
- data/lib/sf_cli/sf/model.rb +10 -2
- data/lib/sf_cli/sf/sobject/core.rb +10 -2
- data/lib/sf_cli/sf/sobject/schema.rb +47 -0
- metadata +8 -3
- data/lib/sf_cli/sf/model/schema.rb +0 -33
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1996a0f93b6d2f48ea8762961526eab00df5601c902f2ec81f21331382973e56
|
4
|
+
data.tar.gz: da4da9fe6855d6f44f7995d932e6f658565b6e41091bd4c908e651a8286ece95
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 36743c7b02fbdf21626a4dcf7ce299bdce68d99e0281bac62b08180ac48792b112b130402e3b75d589f13e57acdf33b33d139db00237818b784b6ab1cd37aef4
|
7
|
+
data.tar.gz: d4008513bbbf74b24de5c94f0cfc868367b37c9d2d876806e8eb2f546669768db94c5c1f981dbad732214c16ca26ee969eeb7027c59abda23d49a6e7b8e038fa
|
data/CHANGELOG.md
CHANGED
@@ -1,19 +1,25 @@
|
|
1
|
+
## 0.0.6 - 2024-09-16
|
2
|
+
- NEW: Object Model Support renewal;
|
3
|
+
- `SfCli::Sf::Model.connection` represents the connection to Salesforce. It can be set by `set_connection` class method in the module. As of now there is only `SfCommandConnection`, which is based on sf command, as connection adapter. After the connection is set by `set_connection`, it is also automatically set to classes when `SfCli::Sf::Model.generate` method is called.
|
4
|
+
- Each object model class has `create` class method for creating a new record.
|
5
|
+
- Each object model has `#save` and `#delete` instance methods to create, update and delete the record it represents.
|
6
|
+
- Each object class has query interface such as `where`, `select`, `limit` and `order` methods that can be chainable.
|
7
|
+
- Each object class has query interface such as `find`, `all`, `take` and `pluck`.
|
8
|
+
- CHANGE: `sf.sobject.describe` changed to return `schema` object.
|
1
9
|
## 0.0.5 - 2024-09-08
|
2
|
-
- target CLI version is changed: from 2.54.6 to 2.56.7
|
3
|
-
-
|
4
|
-
-
|
5
|
-
-
|
6
|
-
-
|
7
|
-
-
|
8
|
-
-
|
9
|
-
-
|
10
|
-
-
|
11
|
-
-
|
12
|
-
- sf
|
13
|
-
-
|
14
|
-
-
|
15
|
-
- add `--browser` option to login web
|
16
|
-
|
10
|
+
- CHANGE: target CLI version is changed: from 2.54.6 to 2.56.7
|
11
|
+
- NEW: new command features are added;
|
12
|
+
- `--bulk` option to `sf.data.query`
|
13
|
+
- `sf.data.query_resume`
|
14
|
+
- `sf.data.upsert_bulk`
|
15
|
+
- `sf.data.upsert_resume`
|
16
|
+
- `sf.data.delete_bulk`
|
17
|
+
- `sf.data.delete_resume`
|
18
|
+
- `sf.data.resume`
|
19
|
+
- `sf.data.search`
|
20
|
+
- `sf.org.list`
|
21
|
+
- `sf.login_access_token`
|
22
|
+
- `--browser` option to `sf.login_web`
|
17
23
|
## 0.0.4 - 2024-09-02
|
18
24
|
- breaking change:
|
19
25
|
- Sf class doesn't exist anymore. You can not write like `sf = SfCli::Sf.new`. Instead of that, global `sf` method is introduced. You can directly type like `sf.org.display`, which is as almost same usability as the original command.
|
data/README.rdoc
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
= Salesforce CLI library
|
1
|
+
= Salesforce CLI library for Ruby
|
2
2
|
https://badge.fury.io/rb/sf_cli.png
|
3
3
|
|
4
4
|
This is a class library for introducing {Salesforce CLI}[https://developer.salesforce.com/docs/atlas.en-us.sfdx_cli_reference.meta/sfdx_cli_reference/cli_reference_top.htm] to Ruby scripting.<br>
|
@@ -10,10 +10,10 @@ Salesforce CLI must be installed.<br>
|
|
10
10
|
As of as of September in 2024, ver.2.56.7 is the development target.
|
11
11
|
|
12
12
|
== install
|
13
|
-
|
13
|
+
==== Rubygem
|
14
14
|
$ gem install sf_cli
|
15
15
|
|
16
|
-
|
16
|
+
==== Bundler
|
17
17
|
in Gemfile:
|
18
18
|
gem 'sf_cli'
|
19
19
|
|
@@ -48,19 +48,88 @@ then,
|
|
48
48
|
sf.project.generate 'MyProject'
|
49
49
|
|
50
50
|
== \Object Model support (experimental, since 0.0.4)
|
51
|
-
|
51
|
+
=== generate Models
|
52
52
|
require 'sf_cli/sf/model'
|
53
|
+
require 'sf_cli/sf/model/sf_command_connection'
|
53
54
|
|
54
|
-
#
|
55
|
-
SfCli::Sf::Model.
|
55
|
+
# first, you must prepare the connection
|
56
|
+
conn = SfCli::Sf::Model::SfCommandConnection.new target_org: your_org, instance_url: your_org_url
|
56
57
|
|
57
|
-
|
58
|
-
|
58
|
+
# then set it to Model module
|
59
|
+
SfCli::Sf::Model.set_connection conn
|
60
|
+
|
61
|
+
# generates some model classes (for example, Account and Contact)
|
62
|
+
SfCli::Sf::Model.generate %w[Account Contact]
|
63
|
+
=== apply to sf command
|
64
|
+
rows = sf.data.query "SELECT Id, Name FROM Contact WHERE Name = 'Akin Kristen'", model_class: Contact
|
59
65
|
|
60
|
-
|
61
|
-
rows.size
|
62
|
-
rows.first
|
63
|
-
rows.first.
|
66
|
+
# the array object contains Contact object instances
|
67
|
+
rows.size # 1
|
68
|
+
rows.first # <Contact: @Id="0035j00001RW3xbAAD", @Name="Akin Kristen">
|
69
|
+
rows.first.Name # Akin Kristen
|
70
|
+
|
71
|
+
=== using models Independently
|
72
|
+
==== initialize and save a record
|
73
|
+
c = Contact.new(:Name => "John Smith")
|
74
|
+
c.Name # "John Smith"
|
75
|
+
c.save
|
76
|
+
|
77
|
+
==== find and update a record
|
78
|
+
# find by record ID and update
|
79
|
+
c2 = Contact.find(c.id)
|
80
|
+
c2.Name = "Johnny Smith"
|
81
|
+
c2.save # update
|
82
|
+
|
83
|
+
==== delete a record
|
84
|
+
c2 = Contact.find(c.id)
|
85
|
+
c2.delete
|
86
|
+
|
87
|
+
==== query and get a record
|
88
|
+
contact = Contact.select(:Id, :Name).where(Name: 'Akin Kristen').take
|
89
|
+
contact # => #<Contact: @Id="0035j00001RW3xbAAD", @Name="Akin Kristen">
|
90
|
+
contact.Name # Akin Kristen
|
91
|
+
|
92
|
+
==== query and get records
|
93
|
+
contacts = Contact.select(:Id, :Name).where(LastModifiedDate: :Yesterday).all # get all records that is modified yesterday
|
94
|
+
|
95
|
+
# these 2 example are the same meaning
|
96
|
+
Contact.select(Name).where(Name: 'John Smith', LastModifiedDate: :Yesterday).take
|
97
|
+
Contact.select(Name).where(Name: 'John Smith').where(LastModifiedDate: :Yesterday).take
|
98
|
+
|
99
|
+
==== child-parent relationship
|
100
|
+
contact = Contact.select(:Id, :Name, "Account.Name").where(Name: 'Akin Kristen').take
|
101
|
+
contact # <Contact: @Id="0035j00001RW3xbAAD", @Name="Akin Kristen", @Account= #<Account @Name="Aethna Home Products">>
|
102
|
+
contact.Account.Name # Aethna Home Products
|
103
|
+
|
104
|
+
==== parent-children relationship
|
105
|
+
account = Account.select(:Id, :Name, "(SELECT Name FROM Contacts)").take
|
106
|
+
account # <Account @Contacts=[#<Contact @Name="Akin Kristen">], @Id="0015j00001dsDuhAAE", @Name="Aethna Home Products">
|
107
|
+
account.Name # Aethna Home Products
|
108
|
+
rows.Contacts # [#<Contact @Name="Akin Kristen">]
|
109
|
+
rows.Contacts.first.Name # Akin Kristen
|
110
|
+
|
111
|
+
==== get all fields in a record
|
112
|
+
# just by not using 'select' method
|
113
|
+
contact = Contact.find(record_id) # <Contact @Id="...", @Name="...", other fields...>
|
114
|
+
|
115
|
+
==== time keywords such as 'yesterday' or 'LAST_N_DAYS:N' with symbol style
|
116
|
+
Contact.select(:Name).where(LastModifiedDate: :Yesterday).take # "SELECT Id, Name FROM Contact WHERE LastModifiedDate = Yesterday"
|
117
|
+
Contact.select(:Name).where(LastModifiedDate: :"LAST_N_DAYS:5").take # "SELECT Id, Name FROM Contact WHERE LastModifiedDate = LAST_N_DAYS:5"
|
118
|
+
|
119
|
+
==== array for 'IN' keyword
|
120
|
+
Contact.select(:Name).where(Name: ['Jonny Good', 'John Smith']).all # same as "SELECT Name FROM Contact WHERE Name IN ('Jonny Good', 'John Smith')"
|
121
|
+
|
122
|
+
==== using partial soql directly
|
123
|
+
Contact.select("Id, Name").where("LastModifiedDate = LAST_N_DAYS:5").all
|
124
|
+
|
125
|
+
==== ternary style
|
126
|
+
Contact.select(:Id, :Name).where(:LastModifiedDate, :>=, :"LAST_N_DAYS:5").all # SELECT Id, Name FROM Contact WHERE LastModifiedDate >= LAST_N_DAYS:5
|
127
|
+
Account.select(:Id, :Name).where(:Name, :LIKE, "%OIL%").all # SELECT Id, Name FROM Account WHERE Name LIKE '%OIL%'
|
128
|
+
|
129
|
+
==== get schema
|
130
|
+
schema = Account.describe
|
131
|
+
schema.name # Account
|
132
|
+
schema.field_names # [Id, Name, ....]
|
64
133
|
|
65
134
|
== Documents
|
66
135
|
The following steps generate *doc* directory, which all documents are generated in.
|
data/lib/sf_cli/sf/core/base.rb
CHANGED
@@ -0,0 +1,62 @@
|
|
1
|
+
module SfCli
|
2
|
+
module Sf
|
3
|
+
module Model
|
4
|
+
module BaseMethods
|
5
|
+
def self.included(c)
|
6
|
+
c.extend ClassMethods
|
7
|
+
end
|
8
|
+
|
9
|
+
module ClassMethods
|
10
|
+
def connection
|
11
|
+
@connection
|
12
|
+
end
|
13
|
+
|
14
|
+
def connection=(conn)
|
15
|
+
@connection = conn
|
16
|
+
end
|
17
|
+
|
18
|
+
def describe
|
19
|
+
connection.describe(name.to_sym)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def initialize(attributes = {})
|
24
|
+
@original_attributes = {}
|
25
|
+
@current_attributes = {}
|
26
|
+
@updated_attributes = {}
|
27
|
+
|
28
|
+
attributes.each do |k, v|
|
29
|
+
field_name = k.to_sym
|
30
|
+
if self.class.field_names.include?(field_name)
|
31
|
+
@original_attributes[field_name] = v
|
32
|
+
__send__ (field_name.to_s + '='), v
|
33
|
+
elsif self.class.parent_relations.find{|r| r[:name] == field_name}
|
34
|
+
__send__ (field_name.to_s + '='), v
|
35
|
+
elsif self.class.children_relations.find{|r| r[:name] == field_name}
|
36
|
+
__send__ (field_name.to_s + '='), (v.nil? ? [] : v)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def to_h(keys: nil)
|
42
|
+
self.class.field_names.each_with_object({}) do |name, hash|
|
43
|
+
if keys&.instance_of?(Array)
|
44
|
+
hash[name] = __send__(name) if keys.include?(name)
|
45
|
+
else
|
46
|
+
hash[name] = __send__(name)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def new_record?
|
52
|
+
self.Id.nil?
|
53
|
+
end
|
54
|
+
|
55
|
+
def persisted?
|
56
|
+
new_record? == false
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
@@ -1,4 +1,6 @@
|
|
1
|
-
require_relative './
|
1
|
+
require_relative './base_methods'
|
2
|
+
require_relative './dml_methods'
|
3
|
+
require_relative './query_methods'
|
2
4
|
|
3
5
|
module SfCli
|
4
6
|
module Sf
|
@@ -7,60 +9,27 @@ module SfCli
|
|
7
9
|
attr_reader :schema
|
8
10
|
|
9
11
|
def initialize(schema)
|
10
|
-
@schema =
|
12
|
+
@schema = schema
|
11
13
|
end
|
12
14
|
|
13
15
|
def to_s
|
14
16
|
<<~Klass
|
15
17
|
Class.new do
|
16
|
-
|
18
|
+
include ::SfCli::Sf::Model::BaseMethods
|
19
|
+
include ::SfCli::Sf::Model::DmlMethods
|
20
|
+
include ::SfCli::Sf::Model::QueryMethods
|
17
21
|
|
18
|
-
|
19
|
-
|
20
|
-
|
22
|
+
attr_reader :original_attributes, :current_attributes, :updated_attributes
|
23
|
+
|
24
|
+
#{ class_methods }
|
21
25
|
|
26
|
+
#{ field_attribute_methods }
|
22
27
|
#{ parent_relation_methods }
|
23
28
|
#{ children_relation_methods }
|
24
|
-
|
25
|
-
#{ define_initialize }
|
26
|
-
|
27
|
-
#{ define_to_h }
|
28
29
|
end
|
29
30
|
Klass
|
30
31
|
end
|
31
32
|
|
32
|
-
def define_initialize
|
33
|
-
<<~EOS
|
34
|
-
def initialize(attributes = {})
|
35
|
-
attributes.each do |k, v|
|
36
|
-
field_name = k.to_sym
|
37
|
-
if self.class.field_names.include?(field_name)
|
38
|
-
#instance_variable_set ('@' + field_name.to_s).to_sym, v
|
39
|
-
__send__ (field_name.to_s + '='), v
|
40
|
-
elsif self.class.parent_relations.find{|r| r[:name] == field_name}
|
41
|
-
__send__ (field_name.to_s + '='), v
|
42
|
-
elsif self.class.children_relations.find{|r| r[:name] == field_name}
|
43
|
-
__send__ (field_name.to_s + '='), (v.nil? ? [] : v)
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|
47
|
-
EOS
|
48
|
-
end
|
49
|
-
|
50
|
-
def define_to_h
|
51
|
-
<<~EOS
|
52
|
-
def to_h(keys: nil)
|
53
|
-
self.class.field_names.each_with_object({}) do |name, hash|
|
54
|
-
if keys&.instance_of?(Array)
|
55
|
-
hash[name] = __send__(name) if keys.include?(name)
|
56
|
-
else
|
57
|
-
hash[name] = __send__(name)
|
58
|
-
end
|
59
|
-
end
|
60
|
-
end
|
61
|
-
EOS
|
62
|
-
end
|
63
|
-
|
64
33
|
def class_methods
|
65
34
|
<<~EOS
|
66
35
|
class << self
|
@@ -79,6 +48,28 @@ module SfCli
|
|
79
48
|
EOS
|
80
49
|
end
|
81
50
|
|
51
|
+
def field_attribute_methods
|
52
|
+
schema.field_names.each_with_object('') do |name, s|
|
53
|
+
s << <<~EOS
|
54
|
+
def #{name}
|
55
|
+
@#{name}
|
56
|
+
end
|
57
|
+
|
58
|
+
def #{name}=(value)
|
59
|
+
@#{name} = value
|
60
|
+
return if %i[Id LastModifiedDate IsDeleted SystemModstamp CreatedById CreatedDate LastModifiedById].include?(:#{name})
|
61
|
+
|
62
|
+
current_attributes[:#{name}] = value
|
63
|
+
if current_attributes[:#{name}] == original_attributes[:#{name}]
|
64
|
+
updated_attributes[:#{name}] = nil
|
65
|
+
else
|
66
|
+
updated_attributes[:#{name}] = value
|
67
|
+
end
|
68
|
+
end
|
69
|
+
EOS
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
82
73
|
def parent_relation_methods
|
83
74
|
schema.parent_relations.each_with_object('') do |r, s|
|
84
75
|
s << <<~EOS
|
@@ -87,7 +78,7 @@ module SfCli
|
|
87
78
|
end
|
88
79
|
|
89
80
|
def #{r[:name]}=(attributes)
|
90
|
-
@#{r[:name]} = #{r[:class_name]}.new(attributes)
|
81
|
+
@#{r[:name]} = attributes.nil? ? nil : #{r[:class_name]}.new(attributes)
|
91
82
|
end
|
92
83
|
EOS
|
93
84
|
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module SfCli
|
2
|
+
module Sf
|
3
|
+
module Model
|
4
|
+
module DmlMethods
|
5
|
+
def self.included(c)
|
6
|
+
c.extend ClassMethods
|
7
|
+
end
|
8
|
+
|
9
|
+
def save
|
10
|
+
if new_record?
|
11
|
+
self.Id = self.class.connection.create(self.class.name.to_sym, current_attributes.reject{|_,v| v.nil?})
|
12
|
+
else
|
13
|
+
self.class.connection.update(self.class.name.to_sym, self.Id, updated_attributes.reject{|_,v| v.nil?})
|
14
|
+
end
|
15
|
+
|
16
|
+
@original_attributes = current_attributes.dup
|
17
|
+
@updated_attributes = {}
|
18
|
+
|
19
|
+
self.Id
|
20
|
+
end
|
21
|
+
|
22
|
+
def delete
|
23
|
+
return if self.Id.nil?
|
24
|
+
|
25
|
+
self.class.connection.delete(self.class.name.to_sym, self.Id)
|
26
|
+
end
|
27
|
+
|
28
|
+
module ClassMethods
|
29
|
+
def create(values = {})
|
30
|
+
connection.create(name.to_sym, values, Object.const_get(name.to_sym))
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -1,25 +1,26 @@
|
|
1
|
-
require_relative '../sobject/core'
|
2
1
|
require_relative './class_definition'
|
3
2
|
|
4
3
|
module SfCli
|
5
4
|
module Sf
|
6
5
|
module Model
|
7
6
|
class Generator
|
8
|
-
attr_reader :sf_sobject, :
|
7
|
+
attr_reader :sf_sobject, :connection
|
9
8
|
|
10
|
-
def initialize(
|
11
|
-
@
|
12
|
-
@target_org = target_org
|
9
|
+
def initialize(connection)
|
10
|
+
@connection = connection
|
13
11
|
end
|
14
12
|
|
15
13
|
def generate(object_name)
|
16
|
-
|
14
|
+
schema = describe(object_name)
|
15
|
+
class_definition = ClassDefinition.new(schema)
|
17
16
|
|
18
17
|
instance_eval "::#{object_name} = #{class_definition}"
|
18
|
+
klass = Object.const_get object_name.to_sym
|
19
|
+
klass.connection = connection
|
19
20
|
end
|
20
21
|
|
21
22
|
def describe(object_name)
|
22
|
-
|
23
|
+
connection.describe object_name
|
23
24
|
end
|
24
25
|
end
|
25
26
|
end
|
@@ -0,0 +1,136 @@
|
|
1
|
+
require 'date'
|
2
|
+
|
3
|
+
module SfCli
|
4
|
+
module Sf
|
5
|
+
module Model
|
6
|
+
module QueryMethods
|
7
|
+
class QueryCondition
|
8
|
+
attr_reader :connection, :object_name, :all_field_names, :fields, :conditions, :limit_num, :row_order
|
9
|
+
|
10
|
+
def initialize(connection, object_name, field_names)
|
11
|
+
@object_name = object_name
|
12
|
+
@all_field_names = field_names
|
13
|
+
@connection = connection
|
14
|
+
@fields = []
|
15
|
+
@conditions = []
|
16
|
+
@limit_num = nil
|
17
|
+
@row_order = nil
|
18
|
+
end
|
19
|
+
|
20
|
+
def where(*expr)
|
21
|
+
return self if expr&.empty?
|
22
|
+
return self if expr.map{|o| (o == '' || o == {} || o == []) ? nil : o}.compact.size.zero?
|
23
|
+
return self unless [Hash, Symbol, String].any?{|klass| expr.first.instance_of? klass}
|
24
|
+
|
25
|
+
if expr.size > 1
|
26
|
+
return self if expr.size < 3
|
27
|
+
|
28
|
+
value = case expr[2].class.name.to_sym
|
29
|
+
when :String
|
30
|
+
%|'#{expr[2]}'|
|
31
|
+
when :Time
|
32
|
+
expr[2].to_datetime
|
33
|
+
when :Array
|
34
|
+
candidates = expr[2].map do |o|
|
35
|
+
case o.class.name.to_sym
|
36
|
+
when :String
|
37
|
+
%|'#{o}'|
|
38
|
+
when :Time
|
39
|
+
o.to_datetime
|
40
|
+
else
|
41
|
+
o
|
42
|
+
end
|
43
|
+
end
|
44
|
+
%|IN (#{candidates.join(', ')})|
|
45
|
+
else
|
46
|
+
expr[2]
|
47
|
+
end
|
48
|
+
conditions << %|#{expr[0]} #{expr[1]} #{value}|
|
49
|
+
|
50
|
+
return self
|
51
|
+
end
|
52
|
+
|
53
|
+
if expr[0].instance_of? String
|
54
|
+
conditions << expr[0]
|
55
|
+
return self
|
56
|
+
end
|
57
|
+
|
58
|
+
new_conditions =
|
59
|
+
expr[0].map do |k,v|
|
60
|
+
case v.class.name.to_sym
|
61
|
+
when :String
|
62
|
+
%|#{k} = '#{v}'|
|
63
|
+
when :Time
|
64
|
+
%|#{k} = #{v.to_datetime}|
|
65
|
+
when :Array
|
66
|
+
candidates = v.map do |o|
|
67
|
+
case o.class.name.to_sym
|
68
|
+
when :String
|
69
|
+
%|'#{o}'|
|
70
|
+
when :Time
|
71
|
+
%|#{o.to_datetime}|
|
72
|
+
else
|
73
|
+
o
|
74
|
+
end
|
75
|
+
end
|
76
|
+
%|#{k} IN (#{candidates.join(', ')})|
|
77
|
+
else
|
78
|
+
"#{k} = #{v}"
|
79
|
+
end
|
80
|
+
end
|
81
|
+
conditions.append new_conditions
|
82
|
+
return self
|
83
|
+
end
|
84
|
+
|
85
|
+
def select(*expr)
|
86
|
+
return self if expr&.empty?
|
87
|
+
|
88
|
+
if expr.size > 1
|
89
|
+
@fields = self.fields + expr
|
90
|
+
else
|
91
|
+
self.fields << expr.first
|
92
|
+
end
|
93
|
+
return self
|
94
|
+
end
|
95
|
+
|
96
|
+
def limit(num)
|
97
|
+
@limit_num = num
|
98
|
+
return self
|
99
|
+
end
|
100
|
+
|
101
|
+
def order(*fields)
|
102
|
+
return self if fields&.empty?
|
103
|
+
|
104
|
+
@row_order = fields
|
105
|
+
return self
|
106
|
+
end
|
107
|
+
|
108
|
+
def to_soql
|
109
|
+
base = 'SELECT %{select} FROM %{object}' % {select: select_fields, object: object_name}
|
110
|
+
where = conditions.size.zero? ? nil : 'WHERE %{where}' % {where: conditions.flatten.join(' AND ')}
|
111
|
+
_order = row_order.nil? ? nil : 'ORDER BY %{order}' % {order: row_order.join(', ')}
|
112
|
+
limit = limit_num.nil? ? nil : 'LIMIT %{limit}' % {limit: limit_num}
|
113
|
+
|
114
|
+
[base, where, _order, limit].compact.join(' ')
|
115
|
+
end
|
116
|
+
|
117
|
+
def all
|
118
|
+
connection.query(to_soql, Object.const_get(object_name.to_sym))
|
119
|
+
end
|
120
|
+
|
121
|
+
def pluck(field_name)
|
122
|
+
all.map{|record| record.__send__(field_name.to_sym)}
|
123
|
+
end
|
124
|
+
|
125
|
+
def take
|
126
|
+
all.first
|
127
|
+
end
|
128
|
+
|
129
|
+
def select_fields
|
130
|
+
(fields.empty? ? all_field_names : fields).join(', ')
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require_relative './query_condition'
|
2
|
+
|
3
|
+
module SfCli
|
4
|
+
module Sf
|
5
|
+
module Model
|
6
|
+
module QueryMethods
|
7
|
+
def self.included(c)
|
8
|
+
c.extend ClassMethods
|
9
|
+
end
|
10
|
+
|
11
|
+
module ClassMethods
|
12
|
+
def where(*expr)
|
13
|
+
qc = QueryCondition.new(connection, self.name, self.field_names)
|
14
|
+
qc.where(*expr)
|
15
|
+
return qc
|
16
|
+
end
|
17
|
+
|
18
|
+
def select(*fields)
|
19
|
+
qc = QueryCondition.new(connection, self.name, self.field_names)
|
20
|
+
qc.select(*fields)
|
21
|
+
return qc
|
22
|
+
end
|
23
|
+
|
24
|
+
def find(id)
|
25
|
+
connection.find(name.to_sym, id, Object.const_get(name.to_sym))
|
26
|
+
end
|
27
|
+
|
28
|
+
def limit(num)
|
29
|
+
qc = QueryCondition.new(connection, self.name, self.field_names)
|
30
|
+
qc.limit(num)
|
31
|
+
qc
|
32
|
+
end
|
33
|
+
|
34
|
+
def order(*field_names)
|
35
|
+
qc = QueryCondition.new(connection, self.name, self.field_names)
|
36
|
+
qc.order(*field_names)
|
37
|
+
qc
|
38
|
+
end
|
39
|
+
|
40
|
+
def all
|
41
|
+
qc = QueryCondition.new(connection, self.name, self.field_names)
|
42
|
+
qc.all
|
43
|
+
end
|
44
|
+
|
45
|
+
def pluck(field_name)
|
46
|
+
qc = QueryCondition.new(connection, self.name, self.field_names)
|
47
|
+
qc.pluck(field_name)
|
48
|
+
end
|
49
|
+
|
50
|
+
def take
|
51
|
+
qc = QueryCondition.new(connection, self.name, self.field_names)
|
52
|
+
qc.take
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require_relative '../sobject/core'
|
2
|
+
require_relative '../data/core'
|
3
|
+
require_relative '../org/core'
|
4
|
+
|
5
|
+
module SfCli
|
6
|
+
module Sf
|
7
|
+
module Model
|
8
|
+
class SfCommandConnection
|
9
|
+
attr_reader :sf_data, :sf_org, :sf_sobject, :target_org, :instance_url
|
10
|
+
|
11
|
+
def initialize(target_org: nil, instance_url: nil)
|
12
|
+
@sf_org = ::SfCli::Sf::Org::Core.new
|
13
|
+
@sf_data = ::SfCli::Sf::Data::Core.new
|
14
|
+
@sf_sobject = ::SfCli::Sf::Sobject::Core.new
|
15
|
+
@target_org = target_org
|
16
|
+
@instance_url = instance_url
|
17
|
+
end
|
18
|
+
|
19
|
+
def open
|
20
|
+
if ENV['SF_ACCESS_TOKEN']
|
21
|
+
sf_org.login_access_token target_org: target_org, instance_url: instance_url
|
22
|
+
else
|
23
|
+
sf_org.login_web target_org: target_org, instance_url: instance_url
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def find(object_type, id, klass)
|
28
|
+
sf_data.get_record object_type, record_id: id, target_org: target_org, model_class: klass
|
29
|
+
end
|
30
|
+
|
31
|
+
def create(object_type, values, klass = nil)
|
32
|
+
id = sf_data.create_record object_type, values: values, target_org: target_org
|
33
|
+
return id if klass.nil?
|
34
|
+
|
35
|
+
find(object_type, id, klass)
|
36
|
+
end
|
37
|
+
|
38
|
+
def update(object_type, id, values)
|
39
|
+
sf_data.update_record object_type, record_id: id, where: nil, values: values, target_org: target_org
|
40
|
+
end
|
41
|
+
|
42
|
+
def delete(object_type, id)
|
43
|
+
sf_data.delete_record object_type, record_id: id, where: nil, target_org: target_org
|
44
|
+
end
|
45
|
+
|
46
|
+
def query(soql, klass)
|
47
|
+
sf_data.query soql, target_org: target_org, model_class: klass
|
48
|
+
end
|
49
|
+
|
50
|
+
def exec_query(soql, format: nil, bulk: false, timeout: nil, model_class: nil)
|
51
|
+
sf_data.query(soql, target_org: target_org, format: format, bulk: bulk, timeout: timeout, model_class: model_class)
|
52
|
+
end
|
53
|
+
|
54
|
+
def describe(object_type)
|
55
|
+
sf_sobject.describe(object_type, target_org: target_org)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
data/lib/sf_cli/sf/model.rb
CHANGED
@@ -9,8 +9,16 @@ module SfCli
|
|
9
9
|
# see the section {"Object Model support"}[link://files/README_rdoc.html#label-Object+Model+support+-28experimental-2C+since+0.0.4-29] in README.
|
10
10
|
#
|
11
11
|
module Model
|
12
|
-
def self.
|
13
|
-
|
12
|
+
def self.connection
|
13
|
+
@@connection
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.set_connection(conn)
|
17
|
+
@@connection = conn
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.generate(object_names)
|
21
|
+
generator = Generator.new(connection)
|
14
22
|
|
15
23
|
object_names.each do |object_name|
|
16
24
|
generator.generate(object_name)
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require_relative '../core/base'
|
2
|
+
require_relative './schema'
|
2
3
|
|
3
4
|
module SfCli
|
4
5
|
module Sf
|
@@ -11,12 +12,19 @@ module SfCli
|
|
11
12
|
class Core
|
12
13
|
include ::SfCli::Sf::Core::Base
|
13
14
|
|
14
|
-
# returns a
|
15
|
+
# returns a schema object containing the Salesforce object schema
|
15
16
|
#
|
16
17
|
# *objectType* --- object type (ex: Account)<br>
|
17
18
|
#
|
18
19
|
# *target_org* --- an alias of paticular org, or username can be used<br>
|
19
20
|
#
|
21
|
+
# ======
|
22
|
+
# schema = sf.sobject.describe :Account
|
23
|
+
# schema.name # Account
|
24
|
+
# schema.label # Account
|
25
|
+
# schema.field_names # [:Id, :Name, ....]
|
26
|
+
# schema.fields[:Name] # => {"aggregatable"=>true, "aiPredictionField"=>false, "autoNumber"=>false,...}
|
27
|
+
#
|
20
28
|
# For more command details, see {the command reference}[https://developer.salesforce.com/docs/atlas.en-us.sfdx_cli_reference.meta/sfdx_cli_reference/cli_reference_sobject_commands_unified.htm#cli_reference_sobject_describe_unified]
|
21
29
|
#
|
22
30
|
def describe(object_type, target_org: nil)
|
@@ -25,7 +33,7 @@ module SfCli
|
|
25
33
|
:"target-org" => target_org,
|
26
34
|
}
|
27
35
|
json = exec(__method__, flags: flags, redirection: :null_stderr)
|
28
|
-
json['result']
|
36
|
+
Schema.new(json['result'])
|
29
37
|
end
|
30
38
|
|
31
39
|
# returns a list of Salesforce object name
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module SfCli
|
2
|
+
module Sf
|
3
|
+
module Sobject
|
4
|
+
class Schema
|
5
|
+
attr_reader :schema
|
6
|
+
|
7
|
+
def initialize(schema)
|
8
|
+
@schema = schema
|
9
|
+
end
|
10
|
+
|
11
|
+
def all
|
12
|
+
schema
|
13
|
+
end
|
14
|
+
|
15
|
+
def name
|
16
|
+
@name ||= schema['name']
|
17
|
+
end
|
18
|
+
|
19
|
+
def label
|
20
|
+
@label ||= schema['label']
|
21
|
+
end
|
22
|
+
|
23
|
+
def field_names
|
24
|
+
@field_names ||= schema['fields'].map{|f| f['name'].to_sym}
|
25
|
+
end
|
26
|
+
|
27
|
+
def fields
|
28
|
+
@field_map ||= schema['fields'].each_with_object({}) do |f, h|
|
29
|
+
h[f['name'].to_sym] = f
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def children_relations
|
34
|
+
schema['childRelationships']
|
35
|
+
.select{|r| r['relationshipName'].nil? == false}
|
36
|
+
.map{|r| {name: r['relationshipName'].to_sym, field: r['field'].to_sym, class_name: r['childSObject'].to_sym}}
|
37
|
+
end
|
38
|
+
|
39
|
+
def parent_relations
|
40
|
+
schema['fields']
|
41
|
+
.select{|f| f['relationshipName'].nil? == false && f['referenceTo']&.size > 0}
|
42
|
+
.map{|f| {name: f['relationshipName'].to_sym, field: f['name'].to_sym, class_name: f['referenceTo'].first.to_sym}}
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sf_cli
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Takanobu Maekawa
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-09-
|
11
|
+
date: 2024-09-16 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: This is a class library for introducing Salesforce CLI to Ruby scripting.
|
14
14
|
Currenty only sf command is the target of development.
|
@@ -40,15 +40,20 @@ files:
|
|
40
40
|
- lib/sf_cli/sf/data/upsert_resume.rb
|
41
41
|
- lib/sf_cli/sf/main.rb
|
42
42
|
- lib/sf_cli/sf/model.rb
|
43
|
+
- lib/sf_cli/sf/model/base_methods.rb
|
43
44
|
- lib/sf_cli/sf/model/class_definition.rb
|
45
|
+
- lib/sf_cli/sf/model/dml_methods.rb
|
44
46
|
- lib/sf_cli/sf/model/generator.rb
|
45
|
-
- lib/sf_cli/sf/model/
|
47
|
+
- lib/sf_cli/sf/model/query_condition.rb
|
48
|
+
- lib/sf_cli/sf/model/query_methods.rb
|
49
|
+
- lib/sf_cli/sf/model/sf_command_connection.rb
|
46
50
|
- lib/sf_cli/sf/org/core.rb
|
47
51
|
- lib/sf_cli/sf/org/display.rb
|
48
52
|
- lib/sf_cli/sf/org/list.rb
|
49
53
|
- lib/sf_cli/sf/org/login.rb
|
50
54
|
- lib/sf_cli/sf/project/core.rb
|
51
55
|
- lib/sf_cli/sf/sobject/core.rb
|
56
|
+
- lib/sf_cli/sf/sobject/schema.rb
|
52
57
|
homepage: https://github.com/tmkw/sf_cli
|
53
58
|
licenses:
|
54
59
|
- MIT
|
@@ -1,33 +0,0 @@
|
|
1
|
-
module SfCli
|
2
|
-
module Sf
|
3
|
-
module Model
|
4
|
-
class Schema
|
5
|
-
attr_reader :schema
|
6
|
-
|
7
|
-
def initialize(schema)
|
8
|
-
@schema = schema
|
9
|
-
end
|
10
|
-
|
11
|
-
def name
|
12
|
-
schema['name']
|
13
|
-
end
|
14
|
-
|
15
|
-
def field_names
|
16
|
-
schema['fields'].map{|f| f['name'].to_sym}
|
17
|
-
end
|
18
|
-
|
19
|
-
def children_relations
|
20
|
-
schema['childRelationships']
|
21
|
-
.select{|r| r['relationshipName'].nil? == false}
|
22
|
-
.map{|r| {name: r['relationshipName'].to_sym, class_name: r['childSObject'].to_sym}}
|
23
|
-
end
|
24
|
-
|
25
|
-
def parent_relations
|
26
|
-
schema['fields']
|
27
|
-
.select{|f| f['relationshipName'].nil? == false && f['referenceTo']&.size > 0}
|
28
|
-
.map{|f| {name: f['relationshipName'].to_sym, class_name: f['referenceTo'].first.to_sym}}
|
29
|
-
end
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|