sfrest 0.0.8

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 7045fc0312fa3b50c38c0605a27b900a9a50a08e
4
+ data.tar.gz: 8f31a1b7ecc1e6d1bb13a47e36d7fc20c05bbdc4
5
+ SHA512:
6
+ metadata.gz: c1273edd11d4687be24897f2d76e7210abde28982f8377fcc54acf909af4f7890abedbc9aba15cb4b5c4f9453886243d5bdee90ac0a404d5ed3089ba1e16ae50
7
+ data.tar.gz: 5021fe6693f4af1bd6624cf9956c9568b5373e24a5bbe61ee483bdde5c08c8ec982f9dcfee990d934ea6699407516bc56fe3d79026c99299ac98cdb414dec06a
@@ -0,0 +1,16 @@
1
+ module SFRest
2
+ # Get all the audit devents.
3
+ class Audit
4
+ # @param [SFRest::Connection] conn
5
+ def initialize(conn)
6
+ @conn = conn
7
+ end
8
+
9
+ # Lists audit events.
10
+ # @return [Hash{'count' => Integer, 'changes' => [Hash, Hash]}]
11
+ def list_audit_events
12
+ current_path = '/api/v1/audit'
13
+ @conn.get(current_path)
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,46 @@
1
+ module SFRest
2
+ # Backup a site or restore onto that site
3
+ class Backup
4
+ # @param [SFRest::Connection] conn
5
+ def initialize(conn)
6
+ @conn = conn
7
+ end
8
+
9
+ # cool stuff goes here
10
+ # @param [Integer] site_id the node id of the site node
11
+ # @return [Hash]
12
+ def get_backups(site_id, datum = nil)
13
+ current_path = "/api/v1/sites/#{site_id}/backups"
14
+ pb = SFRest::Pathbuilder.new
15
+ @conn.get URI.parse(URI.encode(pb.build_url_query(current_path, datum))).to_s
16
+ end
17
+
18
+ # Deletes a site backup.
19
+ # @param [Integer] site_id Node id of site
20
+ # @param [Integer] backup_id Id of backup to delete
21
+ def delete_backup(site_id, backup_id)
22
+ current_path = "/api/v1/sites/#{site_id}/backups/#{backup_id}"
23
+ @conn.delete(current_path)
24
+ end
25
+
26
+ # Backs up a site.
27
+ # @param [Integer] site_id
28
+ # @param [Hash] datum Options to the backup
29
+ # @option datum [String] 'label'
30
+ # @option datum [Url] 'callback_url'
31
+ # @option datum [String] 'callback_method' GET|POST
32
+ # @option datum [Json] 'caller_data' json encoded string
33
+ def create_backup(site_id, datum = nil)
34
+ current_path = "/api/v1/sites/#{site_id}/backup"
35
+ @conn.post(current_path, datum.to_json)
36
+ end
37
+
38
+ # Gets a url to download a backup
39
+ # @param [Integer] site_id Node id of site
40
+ # @param [Integer] backup_id Id of backup to delete
41
+ # @param [Integer] lifetime TTL of the url
42
+ def backup_url(site_id, backup_id, lifetime = 60)
43
+ @conn.get("/api/v1/sites/#{site_id}/backups/#{backup_id}/url?lifetime=#{lifetime}")
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,173 @@
1
+ module SFRest
2
+ # Generic http methods
3
+ # accessors for all the sub classes.
4
+ class Connection
5
+ attr_accessor :base_url, :username, :password
6
+
7
+ # @param [String] url base url of the SF endpoint e.g. https://www.sfdev.acsitefactory.com
8
+ # @param [String] user api user
9
+ # @param [String] password api password
10
+ def initialize(url, user, password)
11
+ @base_url = url
12
+ @username = user
13
+ @password = password
14
+ end
15
+
16
+ # http request via get
17
+ # @param [string] uri
18
+ # @return [Object] ruby representation of the json response
19
+ # if the reponsebody does not parse, returns
20
+ # the non-parsed body
21
+ def get(uri)
22
+ headers = { 'Content-Type' => 'application/json' }
23
+ res = Excon.get(@base_url + uri.to_s,
24
+ headers: headers,
25
+ user: username,
26
+ password: password,
27
+ ssl_verify_peer: false)
28
+ begin
29
+ access_check JSON(res.body)
30
+ rescue JSON::ParserError
31
+ res.body
32
+ end
33
+ end
34
+
35
+ # http request via get
36
+ # @param [string] uri
37
+ # @return [Integer, Object] http status and the ruby representation
38
+ # of the json response if the reponse body
39
+ # does not parse, returns the non-parsed body
40
+ def get_with_status(uri)
41
+ headers = { 'Content-Type' => 'application/json' }
42
+ res = Excon.get(@base_url + uri.to_s,
43
+ headers: headers,
44
+ user: username,
45
+ password: password,
46
+ ssl_verify_peer: false)
47
+ begin
48
+ data = access_check JSON(res.body)
49
+ return res.status, data
50
+
51
+ rescue JSON::ParserError
52
+ return res.status, res.body
53
+ end
54
+ end
55
+
56
+ # http request via post
57
+ # @param [string] uri
58
+ # @return [Object] ruby representation of the json response
59
+ # if the reponsebody does not parse, returns
60
+ # the non-parsed body
61
+ def post(uri, payload)
62
+ headers = { 'Content-Type' => 'application/json' }
63
+ res = Excon.post(@base_url + uri.to_s,
64
+ headers: headers,
65
+ user: username,
66
+ password: password,
67
+ ssl_verify_peer: false,
68
+ body: payload)
69
+ begin
70
+ access_check JSON(res.body)
71
+ rescue JSON::ParserError
72
+ res.body
73
+ end
74
+ end
75
+
76
+ # http request via put
77
+ # @param [string] uri
78
+ # @return [Object] ruby representation of the json response
79
+ # if the reponsebody does not parse, returns
80
+ # the non-parsed body
81
+ def put(uri, payload)
82
+ headers = { 'Content-Type' => 'application/json' }
83
+ res = Excon.put(@base_url + uri.to_s,
84
+ headers: headers,
85
+ user: username,
86
+ password: password,
87
+ ssl_verify_peer: false,
88
+ body: payload)
89
+ begin
90
+ access_check JSON(res.body)
91
+ rescue JSON::ParserError
92
+ res.body
93
+ end
94
+ end
95
+
96
+ # http request via delete
97
+ # @param [string] uri
98
+ # @return [Object] ruby representation of the json response
99
+ # if the reponsebody does not parse, returns
100
+ # the non-parsed body
101
+ def delete(uri)
102
+ headers = { 'Content-Type' => 'application/json' }
103
+ res = Excon.delete(@base_url + uri.to_s,
104
+ headers: headers,
105
+ user: username,
106
+ password: password,
107
+ ssl_verify_peer: false)
108
+ begin
109
+ access_check JSON(res.body)
110
+ rescue JSON::ParserError
111
+ res.body
112
+ end
113
+ end
114
+
115
+ # Throws an SFRest exception for requests that have problems
116
+ # @param [Object] data JSON parsed http reponse of the SFApi
117
+ # @return [Object] the data object if there are no issues
118
+ # @raise [SFRest::AccessDeniedError] if Authentication fails
119
+ # @raise [SFRest::ActionForbiddenError] if the users role cannot perform the request
120
+ # @raise [SFRest::BadRequestError] if there is something malformed in the request
121
+ #
122
+ def access_check(data)
123
+ return data unless data.is_a?(Hash) # if there is an error message, it will be in a hash.
124
+ unless data['message'].nil?
125
+ raise SFRest::AccessDeniedError, data['message'] if data['message'] =~ /Access denied/
126
+ raise SFRest::ActionForbiddenError, data['message'] if data['message'] =~ /Forbidden: /
127
+ raise SFRest::BadRequestError, data['message'] if data['message'] =~ /Bad Request:/
128
+ end
129
+ data
130
+ end
131
+
132
+ # pings the SF api as an authenticated user
133
+ # responds with a pong
134
+ def ping
135
+ get('/api/v1/ping')
136
+ end
137
+
138
+ # Pings to retrieve a service response.
139
+ def service_response
140
+ ping
141
+ end
142
+
143
+ # define the other class accessor methods.
144
+ # this will instantiate the class with the set creds
145
+ # and make it possible to do
146
+ # sfa = SFRest.new url, user, password
147
+ # sfa.ping
148
+ # sfa.site.first_site_id
149
+ #
150
+ # If a new class is added, add the accessor to this list.
151
+ # NOTE: accessor == Class_name.to_lower
152
+ REST_METHODS = %w(audit
153
+ backup
154
+ domains
155
+ group
156
+ role
157
+ site
158
+ stage
159
+ task
160
+ theme
161
+ update
162
+ user
163
+ variable).freeze
164
+
165
+ REST_METHODS.each do |m|
166
+ define_method(m) do
167
+ m.capitalize!
168
+ sfrest_klass = "SFRest::#{m}"
169
+ Object.const_get(sfrest_klass).new(self)
170
+ end
171
+ end
172
+ end
173
+ end
@@ -0,0 +1,63 @@
1
+ module SFRest
2
+ # Find Staging envs and stage a set of sites
3
+ class Domains
4
+ # @param [SFRest::Connection] conn
5
+ def initialize(conn)
6
+ @conn = conn
7
+ end
8
+
9
+ # Get the domains information on a node
10
+ # @param [Integer] node_id The id of the node.
11
+ #
12
+ # @return [Hash] { "node_id" => 4966, "node_type" => "site",
13
+ # "time" => "2016-11-18T20:09:55+00:00",
14
+ # "domains" => { "protected_domains" =>[ "it252garden4.utest.sfdev.acquia-test.co" ],
15
+ # "custom_domains" => [ "it252coll3.utest.sfdev.acquia-test.co", "sc1.nikgregory.us" ] } }
16
+ def get(node_id)
17
+ current_path = "/api/v1/domains/#{node_id}"
18
+ @conn.get(current_path)
19
+ end
20
+
21
+ # Get the custom domains on a node
22
+ # @param [Integer] node_id The id of the node.
23
+ #
24
+ # @return [Array] custom(removable) domains on a node
25
+ def custom_domains(node_id)
26
+ get(node_id)['domains']['custom_domains']
27
+ end
28
+
29
+ # Get the protetect domains on a node
30
+ # @param [Integer] node_id The id of the node.
31
+ #
32
+ # @return [Array] protected (non-removable) domains on a node
33
+ def protected_domains(node_id)
34
+ get(node_id)['domains']['protected_domains']
35
+ end
36
+
37
+ # Add a domain
38
+ # @param [Integer] node_id The id of the node to which add a domain
39
+ # @param [String] domain_name domain to add. e.g. www.example.com
40
+ #
41
+ # @return [Hash] { "node_type": "site_collection",
42
+ # "domain": "www.example.com",
43
+ # "added": true,
44
+ # "messages": [ "Your domain name was successfully added to the site collection."] }
45
+ def add(node_id, domain_name)
46
+ payload = { 'domain_name' => domain_name }.to_json
47
+ @conn.post("/api/v1/domains/#{node_id}/add", payload)
48
+ end
49
+
50
+ # Remove a domain
51
+ # @param [Integer] node_id The id of the node to which remove a domain
52
+ # @param [String] domain_name domain to remove. e.g. www.example.com
53
+ #
54
+ # @return [Hash] { "node_type": "site_collection",
55
+ # "domain": "www.example.com",
56
+ # "removed": true,
57
+ # "messages": [ "Your domain name was successfully removed from the site collection." ] }
58
+ def remove(node_id, domain_name)
59
+ payload = { 'domain_name' => domain_name }.to_json
60
+ @conn.post("/api/v1/domains/#{node_id}/remove", payload)
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,19 @@
1
+ module SFRest
2
+ # Error classes for SFRest
3
+
4
+ # Extends StandardError so we can catch SFRest::SFError
5
+ class SFError < StandardError; end
6
+
7
+ # Throw this when a user cannot successfuly authenticate
8
+ class AccessDeniedError < SFRest::SFError; end
9
+
10
+ # Throw this when a user does not have permission to perform an action
11
+ class ActionForbiddenError < SFRest::SFError; end
12
+
13
+ # Throw this when the request is incomplete or otherwise cannot be processed by
14
+ # the factory
15
+ class BadRequestError < SFRest::SFError; end
16
+
17
+ # Throw when a task appears to be running too long
18
+ class TaskNotDoneError < SFRest::SFError; end
19
+ end
@@ -0,0 +1,54 @@
1
+ module SFRest
2
+ # SF Group management
3
+ class Group
4
+ # @param [SFRest::Connection] conn
5
+ def initialize(conn)
6
+ @conn = conn
7
+ end
8
+
9
+ # Creates a site group with specified group name.
10
+ # This currently will only create a group in the root
11
+ # @param [String] groupname Name of the group to be created
12
+ def create_group(groupname)
13
+ current_path = '/api/v1/groups'
14
+ payload = { 'group_name' => groupname }.to_json
15
+ @conn.post(current_path, payload)
16
+ end
17
+
18
+ # Gets a site group with a specified group id.
19
+ # @param [Integer] group_id Id of the group to fetch
20
+ # @return [Hash] group object from the SF Api
21
+ def get_group(group_id = 0)
22
+ current_path = '/api/v1/groups/' << group_id.to_s
23
+ @conn.get(current_path)
24
+ end
25
+
26
+ # Gets a list of all site groups.
27
+ # @return [Hash] all the groups on the factory plus a count
28
+ # {'count' => count, 'groups' => Hash }
29
+ # this will iterate through the group pages
30
+ def group_list
31
+ page = 1
32
+ not_done = true
33
+ count = 0
34
+ while not_done
35
+ current_path = '/api/v1/groups?page=' << page.to_s
36
+ res = @conn.get(current_path)
37
+ if res['groups'] == []
38
+ not_done = false
39
+ elsif !res['message'].nil?
40
+ return { 'message' => res['message'] }
41
+ elsif page == 1
42
+ count = res['count']
43
+ groups = res['groups']
44
+ else
45
+ res['groups'].each do |group|
46
+ groups << group
47
+ end
48
+ end
49
+ page += 1
50
+ end
51
+ { 'count' => count, 'groups' => groups }
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,24 @@
1
+ module SFRest
2
+ # make a url querypath
3
+ # so that if the are multiple items in a get request
4
+ # we can get a path like /api/v1/foo?bar=boo&bat=gah ...
5
+ class Pathbuilder
6
+ # build a get query
7
+ # @param [String] current_path the uri like /api/v1/foo
8
+ # @param [Hash] datum k,v hash of get query param and value
9
+ def build_url_query(current_path, datum = nil)
10
+ unless datum.nil?
11
+ current_path += '?'
12
+ datum.each do |key, value|
13
+ current_path += '&' if needs_new_parameter? current_path
14
+ current_path += key.to_s + '=' + value.to_s
15
+ end
16
+ end
17
+ current_path
18
+ end
19
+
20
+ private def needs_new_parameter?(path)
21
+ path[-1, 1] != '?' # if path has '?' then we need to create a new parameter
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,101 @@
1
+ module SFRest
2
+ # create, delete, update, roles
3
+ class Role
4
+ # @param [SFRest::Connection] conn
5
+ def initialize(conn)
6
+ @conn = conn
7
+ end
8
+
9
+ # roles
10
+ # Gets the complete list of roles
11
+ # @return [Hash] all the roles on the factory plus a count
12
+ # {'count' => count, 'roles' => Hash }
13
+ # this will iterate through the roles pages
14
+ def role_list
15
+ page = 1
16
+ not_done = true
17
+ count = 0
18
+ while not_done
19
+ current_path = '/api/v1/roles?page=' << page.to_s
20
+ res = @conn.get(current_path)
21
+ if res['roles'] == []
22
+ not_done = false
23
+ elsif !res['message'].nil?
24
+ return { 'message' => res['message'] }
25
+ elsif page == 1
26
+ count = res['count']
27
+ roles = res['roles']
28
+ else
29
+ res['roles'].each do |roleid, rolename|
30
+ roles[roleid] = rolename
31
+ end
32
+ end
33
+ page += 1
34
+ end
35
+ { 'count' => count, 'roles' => roles }
36
+ end
37
+
38
+ # gets the role ID for the role named rolename
39
+ # will page through all the roles available searching for the site
40
+ # @param [String] rolename the name of the role to find
41
+ # @return [Integer] the id of rolename
42
+ # this will iterate through the roles pages
43
+ def get_role_id(rolename)
44
+ pglimit = 100
45
+ res = @conn.get('/api/v1/roles&limit=' + pglimit.to_s)
46
+ rolecount = res['count'].to_i
47
+ id = role_data_from_results(res, rolename)
48
+ return id if id
49
+ pages = (rolecount / pglimit) + 1
50
+ 2.upto(pages) do |i|
51
+ res = @conn.get('/api/v1/roles&limit=' + pglimit.to_s + '?page=' + i.to_s)
52
+ id = role_data_from_results(res, rolename)
53
+ return id if id
54
+ end
55
+ nil
56
+ end
57
+
58
+ # Extract the role data for rolename based on the role result object
59
+ # @param [Hash] res result from a request to /roles
60
+ # @param [String] rolename
61
+ # @return [Object] Integer, String, Array, Hash depending on the user data
62
+ def role_data_from_results(res, rolename)
63
+ roles = res['roles']
64
+ roles.each do |role|
65
+ return role[0].to_i if role[1] == rolename
66
+ end
67
+ nil
68
+ end
69
+
70
+ # Gets role data for a specific role id
71
+ # @param [Integer] id the role id
72
+ # @return [Hash] the role
73
+ def role_data(id)
74
+ @conn.get('/api/v1/roles/' + id.to_s)
75
+ end
76
+
77
+ # Creates a role.
78
+ # @param [String] name name of the role to create
79
+ def create_role(name)
80
+ current_path = '/api/v1/roles'
81
+ payload = { 'name' => name }.to_json
82
+ @conn.post(current_path, payload)
83
+ end
84
+
85
+ # Updates a role (changes the name).
86
+ # @param [Integer] id the id of the role to rename
87
+ # @param [String] name the new role name
88
+ def update_role(id, name)
89
+ current_path = "/api/v1/roles/#{id}/update"
90
+ payload = { 'new_name' => name }.to_json
91
+ @conn.put(current_path, payload)
92
+ end
93
+
94
+ # Delete a role.
95
+ # @param [Integer] id the role id of the role to delete
96
+ def delete_role(id)
97
+ current_path = "/api/v1/roles/#{id}"
98
+ @conn.delete(current_path)
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,99 @@
1
+ module SFRest
2
+ # Find sites, create a site,
3
+ class Site
4
+ # @param [SFRest::Connection] conn
5
+ def initialize(conn)
6
+ @conn = conn
7
+ end
8
+
9
+ # gets the site ID for the site named sitename
10
+ # will page through all the sites available searching for the site
11
+ # @param [String] sitename the name of the site
12
+ # @return [Integer] the id of sitename
13
+ def get_site_id(sitename)
14
+ pglimit = 100
15
+ res = @conn.get('/api/v1/sites&limit=' + pglimit.to_s)
16
+ sitecount = res['count'].to_i
17
+ id = site_data_from_results(res, sitename, 'id')
18
+ return id if id
19
+ pages = (sitecount / pglimit) + 1
20
+ 2.upto(pages) do |i|
21
+ res = @conn.get('/api/v1/sites&limit=' + pglimit.to_s + '?page=' + i.to_s)
22
+ id = site_data_from_results(res, sitename, 'id')
23
+ return id if id
24
+ end
25
+ nil
26
+ end
27
+
28
+ # Extract the site data for 'key' based on the site result object
29
+ # @param [Hash] res result from a request to /sites
30
+ # @param [String] sitename
31
+ # @param [String] key one of the user data returned (id, site, domain...)
32
+ # @return [Object] Integer, String, Array, Hash depending on the site data
33
+ def site_data_from_results(res, sitename, key)
34
+ sites = res['sites']
35
+ sites.each do |site|
36
+ return site[key] if site['site'] == sitename
37
+ end
38
+ nil
39
+ end
40
+
41
+ # Gets the site data for a specific site id
42
+ # @param [Integer] site_id the site id
43
+ # @return [Hash]
44
+ def get_site_data(site_id)
45
+ @conn.get('/api/v1/sites/' + site_id.to_s)
46
+ end
47
+
48
+ # gets the site id of the 1st one found using the api
49
+ # @return [Integer] the site id
50
+ def first_site_id
51
+ res = @conn.get('/api/v1/sites')
52
+ res['sites'].first['id']
53
+ end
54
+
55
+ # Gets the complete list of users
56
+ # Makes multiple requests to the factory to get all the sites on the factory
57
+ # @return [Hash{'count' => Integer, 'sites' => Hash}]
58
+ def site_list
59
+ page = 1
60
+ not_done = true
61
+ count = 0
62
+ while not_done
63
+ current_path = '/api/v1/sites?page=' << page.to_s
64
+ res = @conn.get(current_path)
65
+ if res['sites'] == []
66
+ not_done = false
67
+ elsif !res['message'].nil?
68
+ return { 'message' => res['message'] }
69
+ elsif page == 1
70
+ count = res['count']
71
+ sites = res['sites']
72
+ else
73
+ res['sites'].each do |site|
74
+ sites << site
75
+ end
76
+ end
77
+ page += 1
78
+ end
79
+ { 'count' => count, 'sites' => sites }
80
+ end
81
+
82
+ # Creates a site.
83
+ # @param [String] sitename The name of the site to create
84
+ # @param [Integer] group_id The Id of the group the site is to be a member of
85
+ # @param [String] install_profile The install profile to use when creating the site
86
+ def create_site(sitename, group_id, install_profile = nil)
87
+ current_path = '/api/v1/sites'
88
+ payload = { 'site_name' => sitename, 'group_ids' => [group_id],
89
+ 'install_profile' => install_profile }.to_json
90
+ @conn.post(current_path, payload)
91
+ end
92
+
93
+ # accessors for backups/restore
94
+ # so that you can do site.backup.list_backups
95
+ def backup
96
+ @conn.backup
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,31 @@
1
+ module SFRest
2
+ # Find Staging envs and stage a set of sites
3
+ class Stage
4
+ # @param [SFRest::Connection] conn
5
+ def initialize(conn)
6
+ @conn = conn
7
+ end
8
+
9
+ # Stage a site
10
+ # @param [String] to_env the name of of target env. defaults to test
11
+ # @param [Array] sites Array of site nids to stage
12
+ # @param [Boolean] email_site_status send an email about the staging status of each site
13
+ # @param [Boolean] skip_gardener skip staging the gardener and only stage the sites
14
+ #
15
+ # @return [Integer] Id of the staging task created.
16
+ def stage(to_env = 'test', sites = nil, email_site_status = false, skip_gardener = false)
17
+ payload = { 'to_env' => to_env, 'sites' => sites,
18
+ 'detailed_status' => email_site_status,
19
+ 'skip_gardener' => skip_gardener }.to_json
20
+ @conn.post('/api/v1/stage', payload)
21
+ end
22
+
23
+ # Query for available staging environments
24
+ #
25
+ # @return environments
26
+ def list_staging_environments
27
+ current_path = '/api/v1/stage'
28
+ @conn.get(current_path)
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,261 @@
1
+ module SFRest
2
+ # Deal with tasks, find them, pause them...
3
+ class Task
4
+ STATUS_NOT_STARTED = 1 # Task has been added to queue but not picked up
5
+ STATUS_RESTARTED = 2 # Task has been re-added to the queue but not picked up
6
+ STATUS_TO_BE_RUN = 3 # Restarted + not started
7
+ STATUS_IN_PROCESS = 4 # A wip process is actively processing a task
8
+ STATUS_WAITING = 8 # Task has been released from active processing
9
+ STATUS_RUNNING = 12 # Running = in process + waiting to be processed
10
+ STATUS_COMPLETED = 16 # Task is successfully processed (exited with success)
11
+ STATUS_ERROR = 32 # Task unsccuessfully completed (exited with failure)
12
+ STATUS_KILLED = 64 # Task is terminated
13
+ STATUS_WARNING = 144 # Completed bit + 128 bit(warning).
14
+ STATUS_DONE = 240 # Completed + error + killed + 128 bit(warning)
15
+
16
+ # @param [SFRest::Connection] conn
17
+ def initialize(conn)
18
+ @conn = conn
19
+ end
20
+
21
+ # Returns true only if the status evaluates to completed
22
+ # @param [Integer] status
23
+ # @return [Boolean]
24
+ def status_completed?(status)
25
+ return true if status.to_i == STATUS_COMPLETED
26
+ false
27
+ end
28
+
29
+ # Returns true if the status evaluates to either
30
+ # waiting or in process
31
+ # @param [Integer] status
32
+ # @return [Boolean]
33
+ def status_running?(status)
34
+ return true if (status.to_i & STATUS_RUNNING) > 0
35
+ false
36
+ end
37
+
38
+ # Returns true only if the status evaluates to errored
39
+ # @param [Integer] status
40
+ # @return [Boolean]
41
+ def status_error?(status)
42
+ return true if status.to_i == STATUS_ERROR
43
+ false
44
+ end
45
+
46
+ # Returns true only if the status evaluates to killed
47
+ # @param [Integer] status
48
+ # @return [Boolean]
49
+ def status_killed?(status)
50
+ return true if status.to_i == STATUS_KILLED
51
+ false
52
+ end
53
+
54
+ # Returns true if the status evaluates to a state that is
55
+ # considered done
56
+ # @param [Integer] status
57
+ # @return [Boolean]
58
+ def status_done?(status)
59
+ return true if (status.to_i & STATUS_DONE) > 0
60
+ false
61
+ end
62
+
63
+ # Returns true only if WIP reports the status as running
64
+ # @param [Integer] task_id
65
+ # @return [Boolean]
66
+ def task_running?(task_id)
67
+ task_path = "/api/v1/wip/task/#{task_id}/status"
68
+
69
+ res = @conn.get task_path
70
+ status = res['wip_task']['status']
71
+ status_running?(status)
72
+ end
73
+
74
+ # Returns true only if WIP reports the status as completed
75
+ # @param [Integer] task_id
76
+ # @return [Boolean]
77
+ def task_completed?(task_id)
78
+ task_path = "/api/v1/wip/task/#{task_id}/status"
79
+
80
+ res = @conn.get task_path
81
+ status = res['wip_task']['status']
82
+ status_completed?(status)
83
+ end
84
+
85
+ # Returns true if WIP reports the status in a state that is
86
+ # considered done
87
+ # @param [Integer] task_id
88
+ # @return [Boolean]
89
+ def task_done?(task_id)
90
+ task_path = "/api/v1/wip/task/#{task_id}/status"
91
+
92
+ res = @conn.get task_path
93
+ status = res['wip_task']['status']
94
+ status_done?(status)
95
+ end
96
+
97
+ # Returns true only if WIP reports the status as killed
98
+ # @param [Integer] task_id
99
+ # @return [Boolean]
100
+ def task_killed?(task_id)
101
+ task_path = "/api/v1/wip/task/#{task_id}/status"
102
+
103
+ res = @conn.get task_path
104
+ status = res['wip_task']['status']
105
+ status_killed?(status)
106
+ end
107
+
108
+ # Returns true only if WIP reports the status as errored
109
+ # @param [Integer] task_id
110
+ # @return [Boolean]
111
+ def task_errored?(task_id)
112
+ task_path = "/api/v1/wip/task/#{task_id}/status"
113
+
114
+ res = @conn.get task_path
115
+ status = res['wip_task']['status']
116
+ status_error?(status)
117
+ end
118
+
119
+ # Find a set of task ids.
120
+ # @param [Integer] limit max amount of results to return per request
121
+ # @param [Integer] page page of request
122
+ # @param [String] group task group
123
+ # @param [String] klass task class
124
+ # @param [Integer] status Integerish the status of the task
125
+ # see SFRest::Task::STATUS_*
126
+ # @return [Array[Integer]]
127
+ def find_task_ids(limit = nil, page = nil, group = nil, klass = nil, status = nil)
128
+ res = find_tasks limit: limit, page: page, group: group, klass: klass, status: status
129
+ task_ids = []
130
+ i = 0
131
+ res.each do |task|
132
+ task_ids[i] = task['id']
133
+ i += 1
134
+ end
135
+ task_ids
136
+ end
137
+
138
+ # Find a set of tasks.
139
+ # @param [Hash] datum Hash of filters
140
+ # @option datum [Integer] :limit max amount of results to return per request
141
+ # @option datum [Integer] :page page of request
142
+ # @option datum [String] :group task group
143
+ # @option datum [String] :class task class
144
+ # @option datum [Integer] :status Integerish the status of the task
145
+ # see SFRest::Task::STATUS_*
146
+ def find_tasks(datum = nil)
147
+ current_path = '/api/v1/tasks'
148
+ pb = SFRest::Pathbuilder.new
149
+ @conn.get URI.parse(URI.encode(pb.build_url_query(current_path, datum))).to_s
150
+ end
151
+
152
+ # Looks for a task with a specific name
153
+ # @param [String] name display name of the task
154
+ # @param [String] group task group filter
155
+ # @param [String] klass task class filter
156
+ # @param [String] status task status filter
157
+ def get_task_id(name, group = nil, klass = nil, status = nil)
158
+ page_size = 100
159
+ page = 0
160
+ loop do
161
+ tasks = find_tasks(limit: page_size, page: page, group: group, class: klass, status: status)
162
+ tasks.each do |task|
163
+ return task['id'].to_i if task['name'] =~ /#{name}/
164
+ page += 1
165
+ end
166
+ break if tasks.size < page_size
167
+ end
168
+ nil
169
+ end
170
+
171
+ # Pauses all tasks.
172
+ def pause_all_tasks
173
+ current_path = '/api/v1/pause'
174
+ payload = { 'paused' => true }.to_json
175
+ @conn.post(current_path, payload)
176
+ end
177
+
178
+ # Resumes all tasks.
179
+ def resume_all_tasks
180
+ current_path = '/api/v1/pause'
181
+ payload = { 'paused' => false }.to_json
182
+ @conn.post(current_path, payload)
183
+ end
184
+
185
+ # Get a specific task's logs
186
+ # @param [Integer] task_id
187
+ def get_task_logs(task_id)
188
+ current_path = '/api/v1/tasks/' << task_id.to_s << '/logs'
189
+ @conn.get(current_path)
190
+ end
191
+
192
+ # Gets the value of a vairable
193
+ # @TODO: this is missnamed becasue it does not check the global paused variable
194
+ # @param [String] variable_name
195
+ # @return [Object]
196
+ def globally_paused?(variable_name)
197
+ current_path = "/api/v1/variables?name=#{variable_name}"
198
+ res = @conn.get(current_path)
199
+ res[variable_name]
200
+ end
201
+
202
+ # Pauses a specific task identified by its task id.
203
+ # This can pause either the task and it's children or just the task
204
+ # @param [Integer] task_id
205
+ # @param [String] level family|task
206
+ def pause_task(task_id, level = 'family')
207
+ current_path = '/api/v1/pause/' << task_id.to_s
208
+ payload = { 'paused' => true, 'level' => level }.to_json
209
+ @conn.post(current_path, payload)
210
+ end
211
+
212
+ # Resumes a specific task identified by its task id.
213
+ # This can resume either the task and it's children or just the task
214
+ # @param [Integer] task_id
215
+ # @param [String] level family|task
216
+ def resume_task(task_id, level = 'family')
217
+ current_path = '/api/v1/pause/' << task_id.to_s
218
+ payload = { 'paused' => false, 'level' => level }.to_json
219
+ @conn.post(current_path, payload)
220
+ end
221
+
222
+ # returns the classes that are either softpaused or softpause-for-update
223
+ # @param [String] type softpaused | softpause-for-update
224
+ # @return [Array] Array of wip classes
225
+ def get_task_class_info(type = '')
226
+ current_path = '/api/v1/classes/' << type
227
+ @conn.get(current_path)
228
+ end
229
+
230
+ # Get the status of a wip task by id.
231
+ # @param [Integer] task_id
232
+ # @return [Hash{"wip_task" => {"id" => Integer, "status" => Integer}, "time" => timestamp}]
233
+ def get_wip_task_status(task_id)
234
+ current_path = "/api/v1/wip/task/#{task_id}/status"
235
+ @conn.get(current_path)
236
+ end
237
+
238
+ # Blocks until a task is done
239
+ # @param [Integer] task_id
240
+ # @param [Integer] max_nap seconds to try before giving up
241
+ def wait_until_done(task_id, max_nap = 600)
242
+ wait_until_state(task_id, 'done', max_nap)
243
+ end
244
+
245
+ # Blocks until a task reaches a specific status
246
+ # @param [Integer] task_id
247
+ # @param [String] state state to reach
248
+ # @param [Integer] max_nap seconds to try before giving up
249
+ def wait_until_state(task_id, state, max_nap)
250
+ blink_time = 5 # wake up and scan
251
+ nap_start = Time.now
252
+ state_method = method("task_#{state}?".to_sym)
253
+ loop do
254
+ break if state_method.call(task_id)
255
+ raise TaskNotDoneError, "Task: #{task_id} has taken too long to complete!" if Time.new > (nap_start + max_nap)
256
+ sleep blink_time
257
+ end
258
+ task_id
259
+ end
260
+ end
261
+ end
@@ -0,0 +1,23 @@
1
+ module SFRest
2
+ # Tell the Factory that there is theme work to do
3
+ class Theme
4
+ # @param [SFRest::Connection] conn
5
+ def initialize(conn)
6
+ @conn = conn
7
+ end
8
+
9
+ # Sends a theme notification.
10
+ def send_theme_notification(scope = 'site', event = 'modify', nid = 0, theme = '')
11
+ current_path = '/api/v1/theme/notification'
12
+ payload = { 'scope' => scope, 'event' => event, 'nid' => nid, 'theme' => theme }.to_json
13
+ @conn.post(current_path, payload)
14
+ end
15
+
16
+ # Processes a theme notification.
17
+ def process_theme_notification(sitegroup_id = 0)
18
+ current_path = '/api/v1/theme/process'
19
+ payload = { 'sitegroup_id' => sitegroup_id }.to_json
20
+ @conn.post(current_path, payload)
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,78 @@
1
+ module SFRest
2
+ # Drive updates on the Site Factory
3
+ class Update
4
+ # @param [SFRest::Connection] conn
5
+ def initialize(conn)
6
+ @conn = conn
7
+ end
8
+
9
+ # Gets the status information.
10
+ def status_info
11
+ current_path = '/api/v1/status'
12
+ @conn.get(current_path)
13
+ end
14
+
15
+ # Modifies the status information.
16
+ def modify_status(site_creation, site_duplication, domain_management, bulk_operations)
17
+ current_path = '/api/v1/status'
18
+ payload = { 'site_creation' => site_creation,
19
+ 'site_duplication' => site_duplication,
20
+ 'domain_management' => domain_management,
21
+ 'bulk_operations' => bulk_operations }.to_json
22
+ @conn.put(current_path, payload)
23
+ end
24
+
25
+ # Lists vcs refs.
26
+ def list_vcs_refs(type = 'sites')
27
+ current_path = '/api/v1/vcs?type=' << type
28
+ @conn.get(current_path)
29
+ end
30
+
31
+ # Starts an update.
32
+ def start_update(ref)
33
+ update_data = { scope: 'sites', sites_type: 'code, db', sites_ref: ref }
34
+ update(update_data)
35
+ end
36
+
37
+ # Starts an update. The rest api supports the following
38
+ # scope: sites|factory|both (defaults to 'sites')
39
+ # start_time:
40
+ # sites_type: code|code, db| code, db, registry (defaults to 'code, db')
41
+ # factory_type: code|code, db (defaults to 'code, db')
42
+ # sites_ref:
43
+ # factory_ref:
44
+ # This method does not filter or validate so that it can be used for
45
+ # negative cases. (missing data)
46
+ def update(datum)
47
+ current_path = '/api/v1/update'
48
+ payload = datum.to_json
49
+ @conn.post(current_path, payload)
50
+ end
51
+
52
+ # Gets the list of updates.
53
+ def update_list
54
+ current_path = '/api/v1/update'
55
+ @conn.get(current_path)
56
+ end
57
+
58
+ # Gets the progress of an update by id.
59
+ def update_progress(update_id)
60
+ current_path = '/api/v1/update/' + update_id.to_s + '/status'
61
+ @conn.get(current_path)
62
+ end
63
+
64
+ # Pauses current update.
65
+ def pause_update
66
+ current_path = '/api/v1/update/pause'
67
+ payload = { 'pause' => true }.to_json
68
+ @conn.post(current_path, payload)
69
+ end
70
+
71
+ # Resumes current update.
72
+ def resume_update
73
+ current_path = '/api/v1/update/pause'
74
+ payload = { 'pause' => false }.to_json
75
+ @conn.post(current_path, payload)
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,106 @@
1
+ module SFRest
2
+ # Do User actions within the Site Factory
3
+ class User
4
+ # @param [SFRest::Connection] conn
5
+ def initialize(conn)
6
+ @conn = conn
7
+ end
8
+
9
+ # Gets the complete list of users
10
+ # Makes multiple requests to the factory to get all the users on the factory
11
+ # @return [Hash{'count' => Integer, 'users' => Hash}]
12
+ def user_list
13
+ page = 1
14
+ not_done = true
15
+ count = 0
16
+ while not_done
17
+ current_path = '/api/v1/users?page=' << page.to_s
18
+ res = @conn.get(current_path)
19
+ if res['users'] == []
20
+ not_done = false
21
+ elsif !res['message'].nil?
22
+ return { 'message' => res['message'] }
23
+ elsif page == 1
24
+ count = res['count']
25
+ users = res['users']
26
+ else
27
+ res['users'].each do |user|
28
+ users << user
29
+ end
30
+ end
31
+ page += 1
32
+ end
33
+ { 'count' => count, 'users' => users }
34
+ end
35
+
36
+ # gets the site ID for the site named sitename
37
+ # will page through all the sites available searching for the site
38
+ # @param [String] username drupal username (not email)
39
+ # @return [Integer] the uid of the drupal user
40
+ def get_user_id(username)
41
+ pglimit = 100
42
+ res = @conn.get('/api/v1/users&limit=' + pglimit.to_s)
43
+ usercount = res['count'].to_i
44
+ id = user_data_from_results(res, username, 'uid')
45
+ return id if id
46
+ pages = (usercount / pglimit) + 1
47
+ 2.upto(pages) do |i|
48
+ res = @conn.get('/api/v1/users&limit=' + pglimit.to_s + '?page=' + i.to_s)
49
+ id = user_data_from_results(res, username, 'uid')
50
+ return id if id
51
+ end
52
+ nil
53
+ end
54
+
55
+ # Extract the user data for 'key' based on the user result object
56
+ # @param [Hash] res result from a request to /users
57
+ # @param [String] username
58
+ # @param [String] key one of the user data returned (uid, mail, tfa_status...)
59
+ # @return [Object] Integer, String, Array, Hash depending on the user data
60
+ def user_data_from_results(res, username, key)
61
+ users = res['users']
62
+ users.each do |user|
63
+ return user[key] if user['name'] == username
64
+ end
65
+ nil
66
+ end
67
+
68
+ # Gets the data for user UID
69
+ # @param [int] uid site id
70
+ # @return [Hash]
71
+ def get_user_data(uid)
72
+ @conn.get('/api/v1/users/' + uid.to_s)
73
+ end
74
+
75
+ # Creates a user.
76
+ # @param [String] name
77
+ # @param [String] email
78
+ # @param [Hash] datum hash with elements :pass => string,
79
+ # :status => 0|1,
80
+ # :roles => Array
81
+ def create_user(name, email, datum = nil)
82
+ current_path = '/api/v1/users'
83
+ payload = { name: name, mail: email }
84
+ payload.merge!(datum) unless datum.nil?
85
+ @conn.post(current_path, payload.to_json)
86
+ end
87
+
88
+ # Updates a user.
89
+ # @param [Integer] uid user id of the drupal user to update
90
+ # @param [Hash] datum hash with elements :name => string, :pass => string,
91
+ # :status => 0|1, :roles => Array,
92
+ # :mail => string@string, :tfa_status => 0|1
93
+ def update_user(uid, datum = nil)
94
+ current_path = "/api/v1/users/#{uid}/update"
95
+ payload = datum.to_json unless datum.nil?
96
+ @conn.put(current_path, payload)
97
+ end
98
+
99
+ # Delete a user.
100
+ # @param [integer] uid Uid of the user to be deleted
101
+ def delete_user(uid)
102
+ current_path = "/api/v1/users/#{uid}"
103
+ @conn.delete(current_path)
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,28 @@
1
+ module SFRest
2
+ # Perform actions against variables in the Factory
3
+ class Variable
4
+ # @param [SFRest::Connection] conn
5
+ def initialize(conn)
6
+ @conn = conn
7
+ end
8
+
9
+ # Gets the list of variables.
10
+ def variable_list
11
+ current_path = '/api/v1/variables'
12
+ @conn.get(current_path)
13
+ end
14
+
15
+ # Gets the value of a specific variable.
16
+ def get_variable(name)
17
+ current_path = '/api/v1/variables?name=' << name
18
+ @conn.get(current_path)
19
+ end
20
+
21
+ # Sets the key and value of a variable.
22
+ def set_variable(name, value)
23
+ current_path = '/api/v1/variables'
24
+ payload = { 'name' => name, 'value' => value }.to_json
25
+ @conn.put(current_path, payload)
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,3 @@
1
+ module SFRest
2
+ VERSION = '0.0.8'.freeze
3
+ end
data/lib/sfrest.rb ADDED
@@ -0,0 +1,43 @@
1
+ # Simple wrappper around RestClient.Resource
2
+ $LOAD_PATH.unshift(File.dirname(__FILE__)) unless
3
+ $LOAD_PATH.include?(File.dirname(__FILE__)) ||
4
+ $LOAD_PATH.include?(File.expand_path(File.dirname(__FILE__)))
5
+
6
+ require 'excon'
7
+ require 'json'
8
+
9
+ require 'sfrest/audit'
10
+ require 'sfrest/backup'
11
+ require 'sfrest/connection'
12
+ require 'sfrest/domains'
13
+ require 'sfrest/error'
14
+ require 'sfrest/group'
15
+ require 'sfrest/pathbuilder'
16
+ require 'sfrest/role'
17
+ require 'sfrest/site'
18
+ require 'sfrest/stage'
19
+ require 'sfrest/task'
20
+ require 'sfrest/theme'
21
+ require 'sfrest/update'
22
+ require 'sfrest/user'
23
+ require 'sfrest/variable'
24
+
25
+ # Base Class for SF rest API sdk
26
+ module SFRest
27
+ # Class set to work as an sdk for the Site Factory Rest api
28
+ # most of the interesting pieces happen in the connection class and others
29
+ class << self
30
+ attr_accessor :base_url, :user, :password, :conn
31
+
32
+ # returns a connection object to the SF Rest api for a specific factory
33
+ # @param [String] url Base url of the Site Factory
34
+ # @param [String] user username of a user on the factory
35
+ # @param [String] password api password for the user on the factory
36
+ def new(url, user, password)
37
+ @base_url = url
38
+ @user = user
39
+ @password = password
40
+ @conn = SFRest::Connection.new(@base_url, @user, @password)
41
+ end
42
+ end
43
+ end
metadata ADDED
@@ -0,0 +1,130 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sfrest
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.8
5
+ platform: ruby
6
+ authors:
7
+ - ACSF Engineering
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-12-22 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: excon
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: simplecov
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '0.11'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '0.11'
69
+ - !ruby/object:Gem::Dependency
70
+ name: webmock
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '1.24'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '1.24'
83
+ description: Wrapper methods around the ACSF Rest API.
84
+ email:
85
+ executables: []
86
+ extensions: []
87
+ extra_rdoc_files: []
88
+ files:
89
+ - lib/sfrest.rb
90
+ - lib/sfrest/audit.rb
91
+ - lib/sfrest/backup.rb
92
+ - lib/sfrest/connection.rb
93
+ - lib/sfrest/domains.rb
94
+ - lib/sfrest/error.rb
95
+ - lib/sfrest/group.rb
96
+ - lib/sfrest/pathbuilder.rb
97
+ - lib/sfrest/role.rb
98
+ - lib/sfrest/site.rb
99
+ - lib/sfrest/stage.rb
100
+ - lib/sfrest/task.rb
101
+ - lib/sfrest/theme.rb
102
+ - lib/sfrest/update.rb
103
+ - lib/sfrest/user.rb
104
+ - lib/sfrest/variable.rb
105
+ - lib/sfrest/version.rb
106
+ homepage: http://github.com/acquia/sf-sdk-ruby
107
+ licenses:
108
+ - MIT
109
+ metadata: {}
110
+ post_install_message:
111
+ rdoc_options: []
112
+ require_paths:
113
+ - lib
114
+ required_ruby_version: !ruby/object:Gem::Requirement
115
+ requirements:
116
+ - - ">="
117
+ - !ruby/object:Gem::Version
118
+ version: '0'
119
+ required_rubygems_version: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - ">="
122
+ - !ruby/object:Gem::Version
123
+ version: '0'
124
+ requirements: []
125
+ rubyforge_project:
126
+ rubygems_version: 2.5.1
127
+ signing_key:
128
+ specification_version: 4
129
+ summary: Acquia Site Factory Rest API.
130
+ test_files: []