sf_cli 0.0.5 → 0.0.6
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 +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
|