tla-sbuilder-salesforce 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 6d4d256a494f4333b3530162ee02b5a4c4452c86
4
+ data.tar.gz: b6461ca51c77841d44724decec1a824b47b1e02a
5
+ SHA512:
6
+ metadata.gz: 1115fedbc63c901b4ca17639ef527ef59af7e63b13a6fb28149fac949d9b7327336a7d3757d4d86b066a515bee90c56ad7a2594d040611ca30c4ce0429876fd5
7
+ data.tar.gz: bbba7d806ce6a025b1b65edc8aa492ce05d39c97494bed8a3782d459b8002fe7cb61c13875b0a38b2e4e88ac9fa9074d77aaa9a43399380a517d7e3f2adb4757
data/README.md ADDED
@@ -0,0 +1,122 @@
1
+ <link href="../site.css" rel="stylesheet"></link>
2
+ [Up](../index.php) [Readme](README.html) [Releases](RELEASES.html) [Todo](TODO.html)
3
+
4
+
5
+ # tla-sbuilder-salesforce - Salesforce API interface loader (for tla-sbuilder) - $Release:0.0.1$
6
+
7
+ API loader plugin for
8
+ [Sbuilder](https://github.com/jarjuk/tla-sbuilder) to load Salasforce
9
+ API interfaces in a Sbuilder specification model.
10
+
11
+ ## <a id="USAGE">Usage</a>
12
+
13
+ ### Create Gemfile
14
+
15
+ Create `Gemfile` with the following content
16
+
17
+ ```
18
+ source "https://rubygems.org"
19
+ gem 'tla-sbuilder-salesforce'
20
+ ```
21
+
22
+ and run
23
+
24
+ bundle install
25
+
26
+ ### Include Plugin to Sbuilder
27
+
28
+ To include the plugin into Sbuilder add an entry to `extend.loaders`
29
+ array in `sbuilder.yaml` -configuration file as shown below:
30
+
31
+ extend:
32
+ loaders:
33
+ - gem: tla-sbuilder-salesforce
34
+ className: Sbuilder::Interfaces::Salesforce::SalesforceLoader
35
+ configuration:
36
+ client_id: xxxxxxxxxxxxxxx
37
+ client_secret: xxxxxxxxxx
38
+ username: xxxxxxxe
39
+ password: 'xxxxxxxx'
40
+ security_token: 'xxxxxxxxxx'
41
+
42
+ In the entry, modify following properties to match your environment:
43
+
44
+ * `cliend_id`: salesforce client_id to use in connection
45
+ * `client_secret` : salesforce client_secret to use in connection
46
+ * `username`: username to login to salesforce
47
+ * `password`: password to login to salesforce
48
+ * `security_token`: additional security_token needed in salesforce login
49
+
50
+ Additional confiuration properties supported:
51
+
52
+ * `api_version`: api version to use (default 34.0)
53
+
54
+
55
+ ### Configure API Loader Instanace
56
+
57
+ Add an array entry `interfaces` section in `sbuilder.yaml`
58
+ -configuration file referring to an interface configuration file for
59
+ the plugin:
60
+
61
+ interfaces:
62
+
63
+ - className: Sbuilder::Interfaces::Salesforce::SalesforceLoader
64
+ infrastructureServices: true
65
+ file: salesforce-api.yaml
66
+ cache: sf
67
+
68
+
69
+ In the configuration above:
70
+
71
+ * `className`: `Sbuilder::Interfaces::Salesforce::SalesforceLoader`
72
+ matches `className` property in `extend.loaders` section
73
+
74
+ * `infrastructureServices`: value `true` when embedding Salesforce
75
+ services as part of composite service, value `false`, when
76
+ Salesforce services be called directly in the generated
77
+ specification model
78
+
79
+ * `file`: name of configuration file in `cnf` -directory defining
80
+ Salesforce API (see below)
81
+
82
+ * `cache`: optional cache file name prefix for the API loader to cache
83
+ Salesforce API descriptions
84
+
85
+ ## Define Salaseforce API
86
+
87
+ Saleforce API Loader plugin uses a configuration file to define API
88
+ operation on Salesforce sObjects. The configuration file is an array
89
+ of entries with the following properties:
90
+
91
+ * `sObject`: Salesforce sObject for the API operation
92
+ * `path`: REST entrypoint for the interface
93
+ * `action`: REST action (put, get, post, ..)
94
+ * `request`: name of request attribute holding sObject
95
+ * `reply`: name of reponse attribute holding replyt sObject
96
+
97
+
98
+ For example, to access Salasesforce sObjects `Lead` and `Account` use
99
+ configuration:
100
+
101
+ - sObject: Account
102
+ path: /sf/account
103
+ action: get
104
+ request: account
105
+ reply: account
106
+
107
+ - sObject: Lead
108
+ path: /sf/lead
109
+ action: post
110
+ request: account
111
+ reply: account
112
+
113
+ - sObject: Lead
114
+ path: /sf/lead
115
+ action: put
116
+ request: account
117
+ reply: account
118
+
119
+ ## License
120
+
121
+ MIT
122
+
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.1
@@ -0,0 +1,70 @@
1
+ require 'logger'
2
+
3
+ module Sbuilder
4
+
5
+ module Interfaces
6
+
7
+ module Salesforce
8
+
9
+ class Config
10
+ class << self
11
+
12
+
13
+ # @return [Confugration] current configuration
14
+ def configuration
15
+ @configuration ||= Configuration.new
16
+ end
17
+
18
+ # yields configuration
19
+ def configure
20
+ yield configuration
21
+ end
22
+ end # static Config
23
+
24
+ # Configs for salesforce
25
+ class Configuration
26
+
27
+ extend ExtendAccessors
28
+
29
+ # [String] salesforce client_secret to use in connection
30
+ attr_accessor :client_secret
31
+
32
+ # [String] salesforce client_id to use in connection
33
+ attr_accessor :client_id
34
+
35
+
36
+ # [String] username to login to salesforce
37
+ attr_accessor :username
38
+
39
+ # [String] password to login to salesforce
40
+ attr_accessor :password
41
+
42
+ # [String] additional security_token needed in salesforce login
43
+ attr_accessor :security_token
44
+
45
+ # default api version to use
46
+ attr_accessor_with_default :api_version, "34.0"
47
+
48
+ # [Proc] returining logger
49
+ attr_accessor_with_default :logger, lambda { |progname|
50
+ l = ::Logger.new( STDOUT )
51
+ l.level = ::Logger::ERROR
52
+ l
53
+ }
54
+
55
+ # attr_accessor_with_default :log_level, ::Logger::ERROR
56
+
57
+ # # attr_accessor_with_default :log_level, ::Logger::DEBUG
58
+ # attr_accessor_with_default :logfile, "sbuilder-sf.log"
59
+
60
+ # attr_accessor_with_default :home, lambda { ENV['HOME'] }
61
+
62
+ attr_accessor_with_default :app, "sf-loader"
63
+
64
+ end
65
+
66
+ end # class Config
67
+
68
+ end # module Salesforce
69
+ end # module Interfaces
70
+ end # module Sbuilder
@@ -0,0 +1,29 @@
1
+ # http://4thmouse.com/index.php/2011/01/31/attr_accessor-with-default-in-ruby/
2
+
3
+ # use lamdba (and not block) to set default values
4
+
5
+ # attr_accessor_with_default :log_level, :debug
6
+ # attr_accessor_with_default :home, lambda { ENV['HOME'] }
7
+
8
+
9
+ module ExtendAccessors
10
+ def attr_accessor_with_default name, *default, &block
11
+ if( default.size != 1)
12
+ raise "Must either provide a default value or a default code block"
13
+ end
14
+ if ! default[0].respond_to?( :call )
15
+ define_method name.to_sym do
16
+ instance_variable_set("@#{name}", default[0]) unless instance_variable_defined?("@#{name}")
17
+ instance_variable_get("@#{name}")
18
+ end
19
+ else
20
+ define_method name.to_sym do
21
+ instance_variable_set("@#{name}", default[0] ) unless instance_variable_defined?("@#{name}")
22
+ instance_variable_get("@#{name}")
23
+ end
24
+ end
25
+ define_method "#{name}=".to_sym do |value|
26
+ instance_variable_set("@#{name}",value)
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,39 @@
1
+ require 'logger'
2
+
3
+ module Sbuilder
4
+
5
+ module Interfaces
6
+
7
+ module Salesforce
8
+
9
+ module Logger
10
+
11
+ # class NullLoger < Logger
12
+ # def initialize(*args)
13
+ # end
14
+
15
+ # def add(*args, &block)
16
+ # end
17
+
18
+ # def debug?
19
+ # false
20
+ # end
21
+ # end
22
+
23
+ @@logfile = nil # absolute path to log file
24
+
25
+ def logger( options={} )
26
+ return @logger if @logger
27
+ # logger name format 'app:class-name'
28
+ progname = Config.configuration.app + ':' + self.class.name.split('::').last
29
+ # create logger - using default lamba or lambda set in SalesforceLoader#setFacade
30
+ @logger = Config.configuration.logger.call( progname )
31
+ @logger
32
+ end
33
+
34
+
35
+ end # module logger
36
+
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,83 @@
1
+ # loader.rb
2
+
3
+ require 'restforce'
4
+ require 'yaml'
5
+
6
+ module Sbuilder
7
+
8
+ module Interfaces
9
+
10
+ module Salesforce
11
+
12
+ class Reader
13
+
14
+ # [Hash:SObjectName->Describe]
15
+ attr_reader :describes
16
+
17
+ # mix logge
18
+ include Sbuilder::Interfaces::Salesforce::Logger
19
+
20
+
21
+ # ------------------------------------------------------------------
22
+ # constructore
23
+
24
+ def initialize( options={} )
25
+ @describes = {}
26
+ # init logger
27
+ logger(options).info "initialized"
28
+ end
29
+
30
+ # ------------------------------------------------------------------
31
+ # Restforce api authentication
32
+
33
+ # Create connection object to sales force
34
+ #
35
+ # @return [Restforce] client object
36
+ def connect( )
37
+ @client ||= Restforce.new :username => Config.configuration.username,
38
+ :password => Config.configuration.password,
39
+ :security_token => Config.configuration.security_token,
40
+ :client_id => Config.configuration.client_id,
41
+ :client_secret => Config.configuration.client_secret,
42
+ :api_version => Config.configuration.api_version # "34.0"
43
+ end
44
+
45
+
46
+ def client
47
+ connect
48
+ end
49
+
50
+ # ------------------------------------------------------------------
51
+ # cache api queries
52
+
53
+ # cache 'sObject' describes, use '&blk' to read if given
54
+ # (allow file cache)
55
+ def describe( sObject, &blk )
56
+
57
+ logger.info "#{__method__}: sObject=#{sObject}"
58
+
59
+ # object cache?
60
+ return describes[ sObject] unless describes[ sObject].nil?
61
+ logger.info "#{__method__}: not cached - sObject=#{sObject}"
62
+
63
+ # try file cachke if block_ive
64
+ if block_given?
65
+ logger.info "#{__method__}: block given, does yield have a cache for sObject=#{sObject}"
66
+ describes[ sObject] = yield( sObject )
67
+ end
68
+ if describes[sObject]
69
+ logger.info "#{__method__}: yield did return cached object=#{sObject}"
70
+ return describes[ sObject]
71
+ end
72
+
73
+ # access salesforce
74
+ logger.info "#{__method__}: not cached - read start=#{sObject}"
75
+ describes[ sObject] = client.describe( sObject )
76
+ logger.info "#{__method__}: not cached - read done=#{sObject}"
77
+ return describes[ sObject]
78
+ end
79
+
80
+ end
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,195 @@
1
+ # loader.rb
2
+
3
+
4
+ module Sbuilder
5
+
6
+ module Interfaces
7
+
8
+ module Salesforce
9
+
10
+ class SalesforceLoader
11
+
12
+ attr_reader :facade # [Sbuilder::ApiLoader]
13
+ # integration with sbuilder
14
+
15
+ attr_reader :reader # [Sbuilder::Interfaces::Salesforce::Reader]
16
+
17
+ # logger received from sbuilder facade
18
+ attr_reader :logger
19
+
20
+ # ------------------------------------------------------------------
21
+ # constructore
22
+
23
+ def initialize( options={} )
24
+ @reader = Sbuilder::Interfaces::Salesforce::Reader.new( options )
25
+ # init logger
26
+ # logger(options).info "initialized salesforce api"
27
+ end
28
+
29
+ # Configure sbuilder facade object implementing services, which
30
+ # allows loader to integrate with sbuilder
31
+
32
+ def setFacade( facade )
33
+ @facade = facade
34
+ @logger = facade.createLogger( Config.configuration.app )
35
+ # create logger method replaced with facade version
36
+ Config.configuration.logger = lambda do |progname|
37
+ facade.createLogger( progname )
38
+ end
39
+ end
40
+
41
+ # ------------------------------------------------------------------
42
+ # class method to confiugre
43
+
44
+ # @param configuration [Hash] properties to configure
45
+
46
+ def self.configure( configuration )
47
+ configuration.each do |k,v|
48
+ begin
49
+ Config.configuration.method( "#{k}=".to_sym ).call( v )
50
+ rescue NameError => e
51
+ msg = <<-EOS
52
+ #{e.message}
53
+
54
+ in salesforce loader configuration #{configuration.to_yaml}
55
+ EOS
56
+ raise NameError, msg, e.backtrace
57
+ end
58
+ end
59
+ end
60
+ # ------------------------------------------------------------------
61
+ # Sbuilder intergration
62
+ # @param yamlFileUri [String] path or url to YAML file to process
63
+
64
+ def load( yamlFileUri )
65
+ logger.info( "#{__method__} yamlFileUri=#{yamlFileUri}" )
66
+
67
+ # use facade services to read lines in
68
+ yaml_lines = readLines( yamlFileUri )
69
+
70
+ # YAML parse & schema validation
71
+ config_hash = YAML.load( yaml_lines )
72
+
73
+ # and extract interfaces && definitions
74
+ doLoadDefinitions( config_hash )
75
+ doLoadInterfaces( config_hash )
76
+
77
+ end
78
+
79
+ # @param yamlFileUri [String] path or url to YAML file to process
80
+ private def readLines( yamlFileUri )
81
+ yaml_lines = facade.read_cached( yamlFileUri )
82
+ return yaml_lines
83
+ end
84
+
85
+
86
+ # Iterate 'definitions': for each definition create
87
+ # 'newDefinition', for each field 'newParameter', add new
88
+ # definition to 'facadade'
89
+
90
+ def doLoadDefinitions( config_hash )
91
+
92
+ # load unique 'sObjects' in operation defs
93
+ definitions = config_hash.map { |operationDef| operationDef['sObject']}.uniq
94
+
95
+ definitions.each do |definition|
96
+ paramSet = facade.newDefinition( definition )
97
+ isArray = false
98
+ fields = fields( definition ).each do |field|
99
+ parameter = facade.newParameter( field[:name], isArray )
100
+ paramSet.addParameter( parameter )
101
+ end
102
+ facade.modelDefinition( paramSet )
103
+ end
104
+ end
105
+
106
+ # Iterate 'path': for each path create 'newInterface'
107
+ def doLoadInterfaces( config_hash )
108
+
109
+ operationDefs = config_hash
110
+
111
+ operationDefs.each do |operationDef|
112
+
113
+ validateProperties( operationDef, ['action', 'sObject', 'request', 'reply', 'path' ] , [] )
114
+
115
+ # read salesforce [Hash:Array] of :path
116
+ valid_urls = read_urls( operationDef['sObject'] )
117
+
118
+ request = facade.newInterface( operationDef['path'], operationDef['action'] )
119
+
120
+ input = facade.newParameterReference( operationDef['request'], operationDef['sObject'], false )
121
+ request.addParameter( input )
122
+
123
+ # add fully configured pojo to model
124
+ facade.modelInterface( request )
125
+ # end # ops
126
+ # end # urls
127
+
128
+ end
129
+ end
130
+
131
+ private
132
+
133
+ # @param sObject [String] name of sObject to read from salesforce
134
+ def read( sObject )
135
+ ret = facade.get_cached( sObject )
136
+ return YAML.load ret unless ret.nil?
137
+
138
+ ret = reader.describe( sObject )
139
+ facade.cache_line( ret.to_yaml, sObject )
140
+ ret
141
+ end
142
+
143
+ # ------------------------------------------------------------------
144
+ # Fields in 'sObject'
145
+ #
146
+ # @param sObject [String] name of sObject from which fields
147
+ # are retrieved
148
+ #
149
+ # @return [Hash:Array] of :name, -properties in 'sObject'
150
+ def fields( sObject )
151
+ return nil unless o = read( sObject )
152
+ return nil unless o['fields']
153
+ o['fields'].map do |f|
154
+ {
155
+ :name => f['name']
156
+ }
157
+ end
158
+ end
159
+
160
+ # # Extract urls for sObject metadata
161
+ # #
162
+ # # @return [Hash:Array] with :name,:url -propertes
163
+ def read_urls( sObject )
164
+ return [] unless read( sObject ) && read( sObject )['urls']
165
+ read( sObject )['urls'].map do |name,url|
166
+ {
167
+ :name => name,
168
+ :url => url,
169
+ }
170
+ end
171
+ end
172
+
173
+
174
+
175
+ # ------------------------------------------------------------------
176
+ # validate 'defintionHash' all 'required'/only 'allowed' props set
177
+ private def validateProperties( defintionHash, required, allowed=[] )
178
+
179
+ allowed = allowed + required
180
+
181
+
182
+ missingProps = required - defintionHash.keys
183
+ raise LoaderException, "Missing properties #{missingProps} in #{defintionHash} - required #{required}" if missingProps.any?
184
+
185
+ invalidProps = defintionHash.keys - allowed
186
+ raise LoaderException, "Unknown properties #{invalidProps} in #{defintionHash} - allowed #{allowed}" if invalidProps.any?
187
+
188
+ end
189
+
190
+
191
+ end # classs
192
+ end
193
+ end
194
+ end
195
+
@@ -0,0 +1,8 @@
1
+
2
+ require_relative 'sbuilder/logger'
3
+ require_relative 'sbuilder/extend'
4
+ require_relative 'sbuilder/config'
5
+ require_relative 'sbuilder/reader'
6
+ require_relative 'sbuilder/sales_force_loader'
7
+
8
+
@@ -0,0 +1,38 @@
1
+ # -*- encoding: utf-8; mode: ruby -*-
2
+
3
+ lib = File.expand_path('../lib/', __FILE__)
4
+ $:.unshift lib unless $:.include?(lib)
5
+
6
+
7
+ # http://guides.rubygems.org/make-your-own-gem/
8
+
9
+ Gem::Specification.new do |s|
10
+
11
+ # version = "0.0.1.pre"
12
+ version = File.open( "VERSION", "r" ) { |f| f.read }.strip.gsub( "-SNAPSHOT", ".pre" )
13
+
14
+ s.name = 'tla-sbuilder-salesforce'
15
+ s.version = version
16
+ s.date = Time.now.strftime( "%Y-%m-%d" ) #'2014-09-10'
17
+ s.summary = "Sbuilder Salesforce API loader"
18
+ s.description = <<EOF
19
+
20
+ Sbuilder Salasforce API loader interfaces with Salesforce Rest API,
21
+ extracts object definitions in Force.com platforms, and creates
22
+ interface operations and parameter definitions into Sbuilder.
23
+
24
+ EOF
25
+ s.authors = ["jarjuk"]
26
+ s.files = ["README.md", "VERSION", "#{s.name}.gemspec" ] | Dir.glob("lib/**/*")
27
+ s.require_paths = [ "lib" ]
28
+ # s.executables = [ "sbuilder.rb" ]
29
+ s.license = 'MIT'
30
+
31
+ # s.homepage = "https://github.com/jarjuk/tla-sbuilder"
32
+
33
+ s.required_ruby_version = '~> 2'
34
+
35
+ s.add_runtime_dependency 'restforce', '~>2.1', ">=2.1.3"
36
+ s.add_runtime_dependency 'tla-sbuilder', '~>0.1', ">=0.1.1.pre"
37
+
38
+ end
metadata ADDED
@@ -0,0 +1,98 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: tla-sbuilder-salesforce
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - jarjuk
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-04-06 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: restforce
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2.1'
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: 2.1.3
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - "~>"
28
+ - !ruby/object:Gem::Version
29
+ version: '2.1'
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: 2.1.3
33
+ - !ruby/object:Gem::Dependency
34
+ name: tla-sbuilder
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '0.1'
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ version: 0.1.1.pre
43
+ type: :runtime
44
+ prerelease: false
45
+ version_requirements: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - "~>"
48
+ - !ruby/object:Gem::Version
49
+ version: '0.1'
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: 0.1.1.pre
53
+ description: |2+
54
+
55
+ Sbuilder Salasforce API loader interfaces with Salesforce Rest API,
56
+ extracts object definitions in Force.com platforms, and creates
57
+ interface operations and parameter definitions into Sbuilder.
58
+
59
+ email:
60
+ executables: []
61
+ extensions: []
62
+ extra_rdoc_files: []
63
+ files:
64
+ - README.md
65
+ - VERSION
66
+ - lib/sbuilder/config.rb
67
+ - lib/sbuilder/extend.rb
68
+ - lib/sbuilder/logger.rb
69
+ - lib/sbuilder/reader.rb
70
+ - lib/sbuilder/sales_force_loader.rb
71
+ - lib/tla-sbuilder-salesforce.rb
72
+ - tla-sbuilder-salesforce.gemspec
73
+ homepage:
74
+ licenses:
75
+ - MIT
76
+ metadata: {}
77
+ post_install_message:
78
+ rdoc_options: []
79
+ require_paths:
80
+ - lib
81
+ required_ruby_version: !ruby/object:Gem::Requirement
82
+ requirements:
83
+ - - "~>"
84
+ - !ruby/object:Gem::Version
85
+ version: '2'
86
+ required_rubygems_version: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - ">="
89
+ - !ruby/object:Gem::Version
90
+ version: '0'
91
+ requirements: []
92
+ rubyforge_project:
93
+ rubygems_version: 2.2.2
94
+ signing_key:
95
+ specification_version: 4
96
+ summary: Sbuilder Salesforce API loader
97
+ test_files: []
98
+ has_rdoc: