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 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