springcm-sdk 0.5.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 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"