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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c53d056727e589cb48d79aced7900a9419015ad0840e59b5f4d25361a31468ad
4
- data.tar.gz: 491d45e9ce3907f931d07526eb37f9698e8c5b4cd95ffb3f38317244d99ea656
3
+ metadata.gz: 1996a0f93b6d2f48ea8762961526eab00df5601c902f2ec81f21331382973e56
4
+ data.tar.gz: da4da9fe6855d6f44f7995d932e6f658565b6e41091bd4c908e651a8286ece95
5
5
  SHA512:
6
- metadata.gz: a49220a708a8cab60b8ae22f09b4ac805c6b405c3ae0821809012d0cb4d3b34bfd66f69a3429ae4ed70799a0a6cd793c9edcd52e71b2811aaf7a887bdb8db075
7
- data.tar.gz: dc8d5efe8ea8001212d448ca71fe874b61625960a6daea6163d459d9603c0d5ccbbf1b8c4795694495e4b1b963100113dd02e9dba56160322e3ab27ebcada2b3
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
- - sf data:
4
- - add `--bulk` option to query
5
- - add query resume
6
- - add upsert bulk
7
- - add upsert resume
8
- - add delete bulk
9
- - add delete resume
10
- - add resume
11
- - add search
12
- - sf org:
13
- - add list
14
- - add login access-token
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
- === Rubygem
13
+ ==== Rubygem
14
14
  $ gem install sf_cli
15
15
 
16
- === Bundler
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
- require 'sf_cli'
51
+ === generate Models
52
52
  require 'sf_cli/sf/model'
53
+ require 'sf_cli/sf/model/sf_command_connection'
53
54
 
54
- # generates Model Class for Contact and Account
55
- SfCli::Sf::Model.generate %w[Contact Account]
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
- c = Contact.new(:Name => "John Smith")
58
- c.Name # => "John Smith"
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
- rows = sf.data.query 'SELECT Id, Name, Account.Name From Contact Limit 3', model_class: Contact
61
- rows.size # => 3
62
- rows.first.Name # => Name of the Contact
63
- rows.first.Account.Name # => Name of the Account
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.
@@ -1,3 +1,5 @@
1
+ require 'json'
2
+
1
3
  module SfCli
2
4
  module Sf
3
5
  module Core
@@ -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 './schema'
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 = Schema.new(schema)
12
+ @schema = schema
11
13
  end
12
14
 
13
15
  def to_s
14
16
  <<~Klass
15
17
  Class.new do
16
- #{ class_methods }
18
+ include ::SfCli::Sf::Model::BaseMethods
19
+ include ::SfCli::Sf::Model::DmlMethods
20
+ include ::SfCli::Sf::Model::QueryMethods
17
21
 
18
- field_names.each do |name|
19
- attr_accessor name
20
- end
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, :target_org
7
+ attr_reader :sf_sobject, :connection
9
8
 
10
- def initialize(target_org: nil)
11
- @sf_sobject = ::SfCli::Sf::Sobject::Core.new
12
- @target_org = target_org
9
+ def initialize(connection)
10
+ @connection = connection
13
11
  end
14
12
 
15
13
  def generate(object_name)
16
- class_definition = ClassDefinition.new(describe object_name)
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
- sf_sobject.describe object_name, target_org: target_org
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
@@ -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.generate(object_names, target_org: nil)
13
- generator = Generator.new(target_org: target_org)
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 hash object containing the Salesforce object schema
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.5
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-08 00:00:00.000000000 Z
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/schema.rb
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