springcm-sdk 0.5.0 → 0.6.0

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: d6147bd5e61e56858a0c7bd91d8aab95a718b580e32e42f1fe7de34a2599742a
4
- data.tar.gz: 84260d0c7050d3675765f7d481da6ca261e98ad900c272731de26daa8d1792a5
3
+ metadata.gz: 82c89b40586e83b4ac5e875be284b7f399be97beeeba634e3e500dd8e98fcff2
4
+ data.tar.gz: b0584226929818e8088afc36c9172cdc21a4d894217fd1e877eb126da1d731a7
5
5
  SHA512:
6
- metadata.gz: 3ed35a3f5b082bf08b7e4162f8a6491f8a27d8f4d6ffb9a88de4116dd2b1c078cb564f161d683c557c0eda46b9251dfb23f7ffee6aec592d64ca804cde7af254
7
- data.tar.gz: cf064b9ea38ac974f63f87d67e548aab6e58fed69d545bff2e84da68e30be373b9c1924954c16ee6a5ad582cd58eb1a6930e56a0f4b37f2b4657b31fa4b9c3ea
6
+ metadata.gz: f09f4b7a972bdda28ca3c48653acec5da3e9f8fe77170ff46b7973b0c66d4a911fecb376308377661a1e00680d110412353cf8d2adcc8e3134e39930fc470048
7
+ data.tar.gz: 661b3763c4fdeace47f06e4bf0cd731960809dd6b6454da7a2cf908bd83190b6ac74ee687efaa5a489b30b8bb79760aa90ae156181368a6d27fa244f3d9b3a6e
data/CHANGELOG.md CHANGED
@@ -4,6 +4,21 @@ All notable changes to springcm-sdk will be documented in this file.
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ## [0.6.0] - 2020-01-13
8
+ ### Added
9
+ * Document versions via #versions method. Document versions behave much like
10
+ normal Documents, but have some different behaviors.
11
+ * Document#download method.
12
+ * Resource#reload! method, to reload a resource object in-place.
13
+ * Security setting updates for Folders.
14
+ * Users and Groups.
15
+
16
+ ### Changed
17
+ * Path is expanded when retrieving individual Folders.
18
+
19
+ ### Removed
20
+ * `springcm` executable. Will be provided via the springcm-cli gem.
21
+
7
22
  ## [0.5.0] - 2020-01-03
8
23
  ### Added
9
24
  * #patch and #put for Resources.
@@ -124,3 +139,4 @@ All notable changes to springcm-sdk will be documented in this file.
124
139
  [0.3.6]: https://github.com/paulholden2/springcm-sdk/releases/tag/0.3.6
125
140
  [0.4.0]: https://github.com/paulholden2/springcm-sdk/releases/tag/0.4.0
126
141
  [0.5.0]: https://github.com/paulholden2/springcm-sdk/releases/tag/0.5.0
142
+ [0.6.0]: https://github.com/paulholden2/springcm-sdk/releases/tag/0.6.0
@@ -22,6 +22,7 @@ module Springcm
22
22
  # you can call #all_attribute_groups instead, as attribute group
23
23
  # configurations do not frequently change.
24
24
  def attribute_groups(offset: 0, limit: 20)
25
+ Helpers.validate_offset_limit!(offset, limit)
25
26
  conn = @client.authorized_connection(url: @client.object_api_url)
26
27
  res = conn.get do |req|
27
28
  req.url "accounts/current/attributegroups"
@@ -0,0 +1,27 @@
1
+ require "springcm-sdk/resource"
2
+
3
+ module Springcm
4
+ class ChangeSecurityTask < Resource
5
+ def await!(interval: 1, tries: 10, backoff: 2)
6
+ while tries > 0
7
+ return true if complete?
8
+ sleep(interval)
9
+ interval *= backoff
10
+ tries -= 1
11
+ end
12
+ raise Springcm::ChangeSecurityTaskAwaitTimeout.new
13
+ end
14
+
15
+ def await(interval: 1, tries: 10, backoff: 2)
16
+ begin
17
+ await!(interval: interval, tries: tries, backoff: backoff)
18
+ rescue Springcm::ChangeSecurityTaskAwaitTimeout => timeout
19
+ return false
20
+ end
21
+ end
22
+
23
+ def complete?
24
+ get.status == "Success"
25
+ end
26
+ end
27
+ end
@@ -3,6 +3,7 @@ require "json"
3
3
  require "springcm-sdk/account"
4
4
  require "springcm-sdk/folder"
5
5
  require "springcm-sdk/document"
6
+ require "springcm-sdk/group"
6
7
  require "springcm-sdk/middleware"
7
8
 
8
9
  module Springcm
@@ -159,6 +160,38 @@ module Springcm
159
160
  end
160
161
  end
161
162
 
163
+ def groups(offset: 0, limit: 20)
164
+ Helpers.validate_offset_limit!(offset, limit)
165
+ conn = authorized_connection(url: object_api_url)
166
+ res = conn.get do |req|
167
+ req.url "groups"
168
+ req.params["offset"] = offset
169
+ req.params["limit"] = limit
170
+ end
171
+ if res.success?
172
+ data = JSON.parse(res.body)
173
+ ResourceList.new(data, self, Group, self)
174
+ else
175
+ nil
176
+ end
177
+ end
178
+
179
+ def users(offset: 0, limit: 20)
180
+ Helpers.validate_offset_limit!(offset, limit)
181
+ conn = authorized_connection(url: object_api_url)
182
+ res = conn.get do |req|
183
+ req.url "users"
184
+ req.params["offset"] = offset
185
+ req.params["limit"] = limit
186
+ end
187
+ if res.success?
188
+ data = JSON.parse(res.body)
189
+ ResourceList.new(data, self, User, self)
190
+ else
191
+ nil
192
+ end
193
+ end
194
+
162
195
  # Check if client is successfully authenticated
163
196
  # @return [Boolean] Whether a valid, unexpired access token is held.
164
197
  def authenticated?
@@ -1,6 +1,7 @@
1
1
  require "springcm-sdk/resource"
2
2
  require "springcm-sdk/mixins/access_level"
3
3
  require "springcm-sdk/mixins/attributes"
4
+ require "springcm-sdk/mixins/parent_folder"
4
5
  require "springcm-sdk/history_item"
5
6
 
6
7
  module Springcm
@@ -15,6 +16,23 @@ module Springcm
15
16
  }
16
17
  end
17
18
 
19
+ def download
20
+ io = StringIO.new
21
+ conn = @client.authorized_connection(url: @client.download_api_url)
22
+ res = conn.get do |req|
23
+ req.url resource_uri
24
+ req.options.on_data = Proc.new do |chunk, total_bytes|
25
+ io << chunk
26
+ end
27
+ end
28
+ if res.success?
29
+ io.rewind
30
+ io
31
+ else
32
+ nil
33
+ end
34
+ end
35
+
18
36
  def history(offset: 0, limit: 20)
19
37
  Helpers.validate_offset_limit!(offset, limit)
20
38
  conn = @client.authorized_connection(url: @client.object_api_url)
@@ -31,6 +49,22 @@ module Springcm
31
49
  end
32
50
  end
33
51
 
52
+ def versions(offset: 0, limit: 20)
53
+ Helpers.validate_offset_limit!(offset, limit)
54
+ conn = @client.authorized_connection(url: @client.object_api_url)
55
+ res = conn.get do |req|
56
+ req.url "#{resource_uri}/versions"
57
+ req.params["offset"] = offset
58
+ req.params["limit"] = limit
59
+ end
60
+ if res.success?
61
+ data = JSON.parse(res.body)
62
+ ResourceList.new(data, self, Document, @client, method_override: :versions)
63
+ else
64
+ nil
65
+ end
66
+ end
67
+
34
68
  def delete!
35
69
  unsafe_delete
36
70
  end
@@ -39,7 +73,7 @@ module Springcm
39
73
  private :unsafe_delete
40
74
 
41
75
  def delete
42
- reload
76
+ reload!
43
77
  if path.start_with?("/#{@client.account.name}/Trash")
44
78
  raise Springcm::DeleteRefusedError.new(path)
45
79
  end
@@ -1,9 +1,12 @@
1
1
  require "springcm-sdk/resource"
2
+ require "springcm-sdk/document"
2
3
  require "springcm-sdk/resource_list"
4
+ require "springcm-sdk/change_security_task"
3
5
  require "springcm-sdk/mixins/access_level"
4
6
  require "springcm-sdk/mixins/parent_folder"
5
7
  require "springcm-sdk/mixins/documents"
6
8
  require "springcm-sdk/helpers"
9
+ require "uri"
7
10
 
8
11
  module Springcm
9
12
  class Folder < Resource
@@ -13,7 +16,7 @@ module Springcm
13
16
 
14
17
  def self.resource_params
15
18
  {
16
- "expand" => "attributegroups"
19
+ "expand" => "attributegroups,path"
17
20
  }
18
21
  end
19
22
 
@@ -71,5 +74,63 @@ module Springcm
71
74
  nil
72
75
  end
73
76
  end
77
+
78
+ def upload(name:, file:, type: :binary)
79
+ file_types = {
80
+ binary: "application/octet-stream",
81
+ pdf: "application/pdf",
82
+ csv: "text/csv",
83
+ txt: "text/plain"
84
+ }
85
+ if !type.nil? && !file_types.keys.include?(type)
86
+ raise ArgumentError.new("File type must be one of: nil, #{file_types.map(&:inspect).join(", ")}")
87
+ end
88
+ conn = @client.authorized_connection(url: @client.upload_api_url)
89
+ res = conn.post do |req|
90
+ req.headers["Content-Type"] = file_types[type]
91
+ req.headers["Content-Length"] = file.size.to_s
92
+ req.url "folders/#{uid}/documents?name=#{URI.escape(name)}"
93
+ req.body = file
94
+ end
95
+ if res.success?
96
+ data = JSON.parse(res.body)
97
+ Document.new(data, @client)
98
+ else
99
+ nil
100
+ end
101
+ end
102
+
103
+ def update_security(group:, access:)
104
+ Helpers.validate_access!(access)
105
+ if !group.is_a?(Springcm::Group)
106
+ raise ArgumentError.new("Invalid group; must be a Springcm::Group")
107
+ end
108
+ if group.group_type != "Security"
109
+ raise ArgumentError.new("Invalid group type; must be a security group")
110
+ end
111
+ conn = @client.authorized_connection(url: @client.object_api_url)
112
+ res = conn.post do |req|
113
+ req.headers["Content-Type"] = "application/json"
114
+ req.url "changesecuritytasks"
115
+ req.body = {
116
+ "Status" => "",
117
+ "Security" => {
118
+ "Groups" => [
119
+ {
120
+ "Item" => group.raw,
121
+ "AccessType" => access.to_s.split('_').map(&:capitalize).join
122
+ }
123
+ ]
124
+ },
125
+ "Folder" => raw
126
+ }.to_json
127
+ end
128
+ if res.success?
129
+ data = JSON.parse(res.body)
130
+ ChangeSecurityTask.new(data, @client)
131
+ else
132
+ nil
133
+ end
134
+ end
74
135
  end
75
136
  end
@@ -0,0 +1,23 @@
1
+ require "springcm-sdk/resource"
2
+ require "springcm-sdk/user"
3
+
4
+ module Springcm
5
+ class Group < Resource
6
+ # @return [ResourceList] Users that are a member of the specified Group.
7
+ def users(offset: 0, limit: 20)
8
+ Helpers.validate_offset_limit!(offset, limit)
9
+ conn = @client.authorized_connection(url: @client.object_api_url)
10
+ res = conn.get do |req|
11
+ req.url "#{resource_uri}/groupmembers"
12
+ req.params["offset"] = offset
13
+ req.params["limit"] = limit
14
+ end
15
+ if res.success?
16
+ data = JSON.parse(res.body)
17
+ ResourceList.new(data, self, User, @client)
18
+ else
19
+ nil
20
+ end
21
+ end
22
+ end
23
+ end
@@ -13,6 +13,7 @@ module Springcm
13
13
 
14
14
  def self.serialize_field(field_config, value)
15
15
  type = field_config.type
16
+ name = field_config.name
16
17
  repeating = field_config.repeating_attribute?
17
18
  serialized = {
18
19
  "AttributeType" => type,
@@ -46,5 +47,20 @@ module Springcm
46
47
  value
47
48
  end
48
49
  end
50
+
51
+ def self.validate_access!(access)
52
+ access_types = [
53
+ :inherit_from_parent_folder,
54
+ :no_access,
55
+ :view,
56
+ :view_create,
57
+ :view_edit,
58
+ :view_edit_delete,
59
+ :view_edit_delete_set_access
60
+ ]
61
+ if !access_types.include?(access)
62
+ raise ArgumentError.new("Access must be one of: #{access_types.map(&:inspect).join(", ")}")
63
+ end
64
+ end
49
65
  end
50
66
  end
@@ -10,6 +10,7 @@ module Springcm
10
10
 
11
11
  def call(env)
12
12
  @app.call(env).on_complete do |response_env|
13
+ return response_env if response_env[:response].env.response_headers["Content-Type"] != "application/json"
13
14
  body = JSON.parse(response_env[:body])
14
15
  if body.fetch("Error", {}).fetch("ErrorCode", nil) == 103
15
16
  raise Springcm::RateLimitExceededError.new
@@ -8,7 +8,26 @@ module Springcm
8
8
  @data["AttributeGroups"] = value
9
9
  end
10
10
 
11
- def set_attribute(group:, field:, index: nil, value:)
11
+ # TODO: Clean this whole mess up. This pattern sucks. Better:
12
+ # doc = client.document(...)
13
+ # group = doc.attribute_group("My Data")
14
+ # attr = group.attribute("My Field")
15
+ # attr.type
16
+ # attr.repeating?
17
+ # attr.value
18
+ # attr.value = 2
19
+ # attr.value = [1, 2, 3]
20
+ # attr.value[0] = 3
21
+ # attr.insert(at: 0, value: 5)
22
+ # doc.patch
23
+
24
+ def set_attribute(group:, field:, index: nil, value:, mode: :insert)
25
+ if ![:insert, :replace].include?(mode)
26
+ raise ArgumentError.new("Set attribute mode must be one of: :insert, :replace")
27
+ end
28
+ if mode == :replace && (index.nil? || index < 0)
29
+ raise ArgumentError.new("When set attribute mode is :replace, index must be a non-negative integer")
30
+ end
12
31
  group_config = @client.account.all_attribute_groups.select { |g|
13
32
  g.name == group
14
33
  }.first
@@ -24,10 +43,28 @@ module Springcm
24
43
  if !field_set_config.nil? && field_set_config["RepeatingAttribute"]
25
44
  set_name = field_set_config["Name"]
26
45
  set_data = group_data.fetch(set_name, {})
27
- set_data["Items"].insert(index || -1, Springcm::Helpers.serialize_field(field_config, value))
46
+ field_data = {
47
+ "#{field}" => Springcm::Helpers.serialize_field(field_config, value)
48
+ }
49
+ if mode == :insert
50
+ set_data["Items"].insert(index || -1, field_data)
51
+ elsif
52
+ set_data["Items"][index] = field_data
53
+ end
54
+ set_data["Items"].reject!(&:nil?)
28
55
  group_data[set_name] = set_data
29
56
  else
30
- group_data[field] = Springcm::Helpers.serialize_field(field_config, value)
57
+ serialized = Springcm::Helpers.serialize_field(field_config, value)
58
+ if field_config.repeating_attribute?
59
+ if mode == :insert
60
+ group_data[field]["Value"].insert(index || -1, serialized["Value"])
61
+ else
62
+ group_data[field]["Value"][index] = serialized["Value"]
63
+ end
64
+ group_data[field]["Value"].reject!(&:nil?)
65
+ else
66
+ group_data[field] = serialized
67
+ end
31
68
  end
32
69
  groups[group] = group_data
33
70
  attribute_groups = groups
@@ -13,6 +13,13 @@ module Springcm
13
13
  get
14
14
  end
15
15
 
16
+ # Resend a request to the API for this resource and modify the data for
17
+ # the current object in-place.
18
+ def reload!
19
+ @data = reload.raw
20
+ self
21
+ end
22
+
16
23
  # Send a GET request for this resource.
17
24
  def get
18
25
  conn = @client.authorized_connection(url: @client.object_api_url)
@@ -4,9 +4,10 @@ module Springcm
4
4
  # that are retrieved in this manner are attached to a parent object, e.g.
5
5
  # the account for attribute groups, or a folder for documents.
6
6
  class ResourceList < Object
7
- def initialize(data, parent_object, kind, client)
7
+ def initialize(data, parent_object, kind, client, method_override: nil)
8
8
  @parent_object = parent_object
9
9
  @kind = kind
10
+ @method_override = method_override
10
11
  super(data, client)
11
12
  end
12
13
 
@@ -45,9 +46,13 @@ module Springcm
45
46
  @parent_object.send(method, offset: offset, limit: limit)
46
47
  end
47
48
 
49
+ # TODO: Better pattern for generating related links
50
+ # Possibly a Springcm::DocumentVersion object?
48
51
  def method_for_kind!(kind)
49
52
  method = nil
50
- if kind == Springcm::Folder
53
+ if !@method_override.nil?
54
+ method = @method_override
55
+ elsif kind == Springcm::Folder
51
56
  method = :folders
52
57
  elsif kind == Springcm::Document
53
58
  method = :documents
@@ -55,8 +60,12 @@ module Springcm
55
60
  method = :attribute_groups
56
61
  elsif kind == Springcm::HistoryItem
57
62
  method = :history
63
+ elsif kind == Springcm::Group
64
+ method = :groups
65
+ elsif kind == Springcm::User
66
+ method = :users
58
67
  else
59
- raise ArgumentError.new("Resource kind must be one of: Springcm::Document, Springcm::Folder, Springcm::AttributeGroup, Springcm::HistoryItem.")
68
+ raise ArgumentError.new("Resource kind must be one of: Springcm::Document, Springcm::Folder, Springcm::AttributeGroup, Springcm::HistoryItem, Springcm::User, Springcm::Group.")
60
69
  end
61
70
  return method
62
71
  end
@@ -0,0 +1,23 @@
1
+ require "springcm-sdk/resource"
2
+ require "springcm-sdk/group"
3
+
4
+ module Springcm
5
+ class User < Resource
6
+ # @return [ResourceList] Groups that the specified User is a member of.
7
+ def groups(offset: 0, limit: 20)
8
+ Helpers.validate_offset_limit!(offset, limit)
9
+ conn = @client.authorized_connection(url: @client.object_api_url)
10
+ res = conn.get do |req|
11
+ req.url "#{resource_uri}/groups"
12
+ req.params["offset"] = offset
13
+ req.params["limit"] = limit
14
+ end
15
+ if res.success?
16
+ data = JSON.parse(res.body)
17
+ ResourceList.new(data, self, Group, @client)
18
+ else
19
+ nil
20
+ end
21
+ end
22
+ end
23
+ end
@@ -1,3 +1,3 @@
1
1
  module Springcm
2
- VERSION = "0.5.0"
2
+ VERSION = "0.6.0"
3
3
  end
data/lib/springcm-sdk.rb CHANGED
@@ -50,4 +50,10 @@ module Springcm
50
50
  super("Refused to delete #{path}, use #delete! instead.")
51
51
  end
52
52
  end
53
+
54
+ class ChangeSecurityTaskAwaitTimeout < Error
55
+ def initialize
56
+ super("Timed out while awaiting ChangeSecurityTask to complete.")
57
+ end
58
+ end
53
59
  end
data/springcm-sdk.gemspec CHANGED
@@ -34,10 +34,10 @@ Gem::Specification.new do |spec|
34
34
  `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
35
35
  end
36
36
  spec.bindir = "exe"
37
- spec.executables = ["springcm"]
37
+ spec.executables = []
38
38
  spec.require_paths = ["lib"]
39
39
 
40
- spec.add_dependency "faraday", "~> 0.17.1"
40
+ spec.add_dependency "faraday", "~> 1.0"
41
41
 
42
42
  spec.add_development_dependency "bundler", "~> 2.0"
43
43
  spec.add_development_dependency "rake", "~> 13.0"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: springcm-sdk
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Paul Holden
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-01-03 00:00:00.000000000 Z
11
+ date: 2020-01-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faraday
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: 0.17.1
19
+ version: '1.0'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: 0.17.1
26
+ version: '1.0'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: bundler
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -140,8 +140,7 @@ description: A library for working with the SpringCM REST API and associated obj
140
140
  in Ruby applications.
141
141
  email:
142
142
  - pholden@stria.com
143
- executables:
144
- - springcm
143
+ executables: []
145
144
  extensions: []
146
145
  extra_rdoc_files: []
147
146
  files:
@@ -157,14 +156,15 @@ files:
157
156
  - bin/.gitignore
158
157
  - bin/console
159
158
  - bin/setup
160
- - exe/springcm
161
159
  - lib/springcm-sdk.rb
162
160
  - lib/springcm-sdk/account.rb
163
161
  - lib/springcm-sdk/attribute.rb
164
162
  - lib/springcm-sdk/attribute_group.rb
163
+ - lib/springcm-sdk/change_security_task.rb
165
164
  - lib/springcm-sdk/client.rb
166
165
  - lib/springcm-sdk/document.rb
167
166
  - lib/springcm-sdk/folder.rb
167
+ - lib/springcm-sdk/group.rb
168
168
  - lib/springcm-sdk/helpers.rb
169
169
  - lib/springcm-sdk/history_item.rb
170
170
  - lib/springcm-sdk/middleware.rb
@@ -178,6 +178,7 @@ files:
178
178
  - lib/springcm-sdk/object.rb
179
179
  - lib/springcm-sdk/resource.rb
180
180
  - lib/springcm-sdk/resource_list.rb
181
+ - lib/springcm-sdk/user.rb
181
182
  - lib/springcm-sdk/version.rb
182
183
  - springcm-sdk.gemspec
183
184
  homepage: https://github.com/paulholden2/springcm-sdk
@@ -187,7 +188,7 @@ metadata:
187
188
  allowed_push_host: https://rubygems.org
188
189
  homepage_uri: https://github.com/paulholden2/springcm-sdk
189
190
  source_code_uri: https://github.com/paulholden2/springcm-sdk
190
- documentation_uri: https://rubydoc.info/github/paulholden2/springcm-sdk/0.5.0
191
+ documentation_uri: https://rubydoc.info/github/paulholden2/springcm-sdk/0.6.0
191
192
  changelog_uri: https://github.com/paulholden2/springcm-sdk/blob/master/CHANGELOG.md
192
193
  post_install_message:
193
194
  rdoc_options: []
data/exe/springcm DELETED
@@ -1,3 +0,0 @@
1
- #!/usr/bin/env ruby
2
-
3
- require "springcm-sdk"