attache-api 0.2.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 +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +33 -0
- data/Rakefile +11 -0
- data/lib/attache/api.rb +4 -0
- data/lib/attache/api/model.rb +43 -0
- data/lib/attache/api/test.rb +13 -0
- data/lib/attache/api/utils.rb +16 -0
- data/lib/attache/api/v1.rb +81 -0
- data/lib/attache/api/version.rb +5 -0
- metadata +137 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 8931e55f0ecea4268085000b50136ef7b10fd0ac
|
4
|
+
data.tar.gz: d4aa35b5cfb27dab0c89cfbdcef04863a0e8d853
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 9fd0d41f14b5dd23285b5b2e4e8ae05f16ce614f7cc581c6dc1a1d88130dc8c1972505e3899fb22e67f88fb34a22ab303b59a306eb99a3dc8c35f41bcb6a6c72
|
7
|
+
data.tar.gz: 1971d934bffa4ad420271b82a75a14ffe404514a759e9fb5c7a762737260f98fac24ecbd8819fcf53b139bcc1d4c6ac95643c751cb3ac7451e4d3884f1d81c8a
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2015 choonkeat
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
# Attache/API
|
2
|
+
|
3
|
+
[](https://travis-ci.org/choonkeat/attache-api)
|
4
|
+
|
5
|
+
Core library and tests to integrate with an [attache](https://github.com/choonkeat/attache) server; to be leveraged by client libraries e.g. [attache_rails](https://github.com/choonkeat/attache_rails) which integrates with ActiveRecord
|
6
|
+
|
7
|
+
## Testing against an attache-compatible server
|
8
|
+
|
9
|
+
Test suite will interact with a server when `ATTACHE_URL` is explicitly set. This can conveniently check if a server at a given URL is attache-compatible. To run such compatibility check, execute the test suite with `ATTACHE_URL` and `ATTACHE_SECRET_KEY` set to the correct values
|
10
|
+
|
11
|
+
```
|
12
|
+
ATTACHE_URL=http://localhost:9292 ATTACHE_SECRET_KEY=topsecret rake
|
13
|
+
```
|
14
|
+
|
15
|
+
NOTE: the test suite will upload a small jpg file and delete it immediately, for several iterations.
|
16
|
+
|
17
|
+
## Environment variables
|
18
|
+
|
19
|
+
Important variables:
|
20
|
+
|
21
|
+
- `ATTACHE_URL` url pointing to the attache server instance, default `http://localhost:9292`
|
22
|
+
- `ATTACHE_SECRET_KEY` optional shared secret with attache server, default no secret
|
23
|
+
|
24
|
+
Optional variables:
|
25
|
+
|
26
|
+
- `ATTACHE_UPLOAD_DURATION` browser upload signature expiration duration, default 3 hours; used in conjunction with `ATTACHE_SECRET_KEY`
|
27
|
+
- `ATTACHE_UPLOAD_URL`, `ATTACHE_DOWNLOAD_URL`, `ATTACHE_DELETE_URL` specific urls pointing to upload, download, delete API end points, default `{ATTACHE_URL}/upload`, `{ATTACHE_URL}/view`, `{ATTACHE_URL}/delete`
|
28
|
+
- `ATTACHE_DISCARD_FAILURE_RAISE_ERROR` when set, raises an error if deleting of files causes an error. by default, deletion errors are discarded
|
29
|
+
|
30
|
+
## License
|
31
|
+
|
32
|
+
MIT
|
33
|
+
|
data/Rakefile
ADDED
data/lib/attache/api.rb
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
module Attache
|
2
|
+
module API
|
3
|
+
module Model
|
4
|
+
def attache_field_options(attr_value, geometry, options = {})
|
5
|
+
V1.attache_options(geometry, attache_field_attributes(attr_value, geometry), options)
|
6
|
+
end
|
7
|
+
|
8
|
+
def attache_field_urls(attr_value, geometry)
|
9
|
+
attache_field_attributes(attr_value, geometry).collect {|attrs| attrs['url']}
|
10
|
+
end
|
11
|
+
|
12
|
+
def attache_field_attributes(attr_value, geometry)
|
13
|
+
Utils.array(attr_value).inject([]) do |sum, obj|
|
14
|
+
sum + Utils.array(obj && obj.tap {|attrs|
|
15
|
+
attrs['url'] = V1.attache_url_for(attrs['path'], geometry)
|
16
|
+
})
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def attache_field_set(array)
|
21
|
+
new_value = Utils.array(array).inject([]) {|sum,value|
|
22
|
+
hash = value.respond_to?(:read) && V1.attache_upload(value) || value
|
23
|
+
hash = JSON.parse(hash.to_s) rescue Hash(error: $!) unless hash.kind_of?(Hash)
|
24
|
+
okay = hash.respond_to?(:[]) && (hash['path'] || hash[:path])
|
25
|
+
okay ? sum + [hash] : sum
|
26
|
+
}
|
27
|
+
Utils.array(new_value)
|
28
|
+
end
|
29
|
+
|
30
|
+
def attache_mark_for_discarding(old_value, new_value, attaches_discarded)
|
31
|
+
obsoleted = Utils.array(old_value).collect {|x| x['path'] } - Utils.array(new_value).collect {|x| x['path'] }
|
32
|
+
obsoleted.each {|path| attaches_discarded.push(path) unless path.nil? || path == "" }
|
33
|
+
end
|
34
|
+
|
35
|
+
def attaches_discard!(files)
|
36
|
+
files.reject! {|x| x.nil? || x == "" }
|
37
|
+
V1.attache_delete(*files.uniq) unless files.empty?
|
38
|
+
rescue Exception
|
39
|
+
raise if ENV['ATTACHE_DISCARD_FAILURE_RAISE_ERROR']
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
require 'securerandom'
|
2
|
+
require "httpclient"
|
3
|
+
require 'openssl'
|
4
|
+
require 'uri'
|
5
|
+
require 'cgi'
|
6
|
+
|
7
|
+
module Attache
|
8
|
+
module API
|
9
|
+
module V1
|
10
|
+
class HTTPClient < ::HTTPClient; end # local reference
|
11
|
+
|
12
|
+
ATTACHE_URL = ENV.fetch('ATTACHE_URL') { "http://localhost:9292" }
|
13
|
+
ATTACHE_UPLOAD_URL = ENV.fetch('ATTACHE_UPLOAD_URL') { "#{ATTACHE_URL}/upload" }
|
14
|
+
ATTACHE_DOWNLOAD_URL = ENV.fetch('ATTACHE_DOWNLOAD_URL') { "#{ATTACHE_URL}/view" }
|
15
|
+
ATTACHE_DELETE_URL = ENV.fetch('ATTACHE_DELETE_URL') { "#{ATTACHE_URL}/delete" }
|
16
|
+
ATTACHE_UPLOAD_DURATION = ENV.fetch('ATTACHE_UPLOAD_DURATION') { 3*3600 }.to_i # expires signed upload form
|
17
|
+
ATTACHE_SECRET_KEY = ENV['ATTACHE_SECRET_KEY'] # unset to test password-less interaction
|
18
|
+
|
19
|
+
def attache_upload(readable)
|
20
|
+
uri = URI.parse(ATTACHE_UPLOAD_URL)
|
21
|
+
original_filename =
|
22
|
+
readable.respond_to?(:original_filename) && readable.original_filename ||
|
23
|
+
readable.respond_to?(:path) && File.basename(readable.path) ||
|
24
|
+
'noname'
|
25
|
+
uri.query = { file: original_filename, **attache_auth_options }.collect {|k,v|
|
26
|
+
CGI.escape(k.to_s) + "=" + CGI.escape(v.to_s)
|
27
|
+
}.join('&')
|
28
|
+
res = attache_retry_doing(3) { HTTPClient.post(uri, readable, {'Content-Type' => 'binary/octet-stream'}) }
|
29
|
+
res.body
|
30
|
+
end
|
31
|
+
|
32
|
+
def attache_url_for(path, geometry)
|
33
|
+
prefix, basename = File.split(path)
|
34
|
+
[ATTACHE_DOWNLOAD_URL, prefix, CGI.escape(geometry), CGI.escape(basename)].join('/')
|
35
|
+
end
|
36
|
+
|
37
|
+
def attache_delete(*paths)
|
38
|
+
HTTPClient.post_content(
|
39
|
+
URI.parse(ATTACHE_DELETE_URL),
|
40
|
+
attache_auth_options.merge(paths: paths.join("\n"))
|
41
|
+
)
|
42
|
+
end
|
43
|
+
|
44
|
+
def attache_auth_options
|
45
|
+
if ATTACHE_SECRET_KEY
|
46
|
+
uuid = SecureRandom.uuid
|
47
|
+
expiration = (Time.now + ATTACHE_UPLOAD_DURATION).to_i
|
48
|
+
hmac = OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha1'), ATTACHE_SECRET_KEY, "#{uuid}#{expiration}")
|
49
|
+
{ uuid: uuid, expiration: expiration, hmac: hmac }
|
50
|
+
else
|
51
|
+
{}
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def attache_options(geometry, current_value, auth_options: true, placeholder: nil, data: {})
|
56
|
+
{
|
57
|
+
data: {
|
58
|
+
geometry: geometry,
|
59
|
+
value: [*current_value],
|
60
|
+
placeholder: [*placeholder],
|
61
|
+
uploadurl: ATTACHE_UPLOAD_URL,
|
62
|
+
downloadurl: ATTACHE_DOWNLOAD_URL,
|
63
|
+
}.merge(data || {}).merge(auth_options == false ? {} : attache_auth_options),
|
64
|
+
}
|
65
|
+
end
|
66
|
+
|
67
|
+
def attache_retry_doing(max_retries, retries = 0, exception_class = Exception)
|
68
|
+
yield
|
69
|
+
rescue exception_class
|
70
|
+
if (retries += 1) <= max_retries
|
71
|
+
max_sleep_seconds = Float(2 ** retries)
|
72
|
+
sleep rand(0..max_sleep_seconds)
|
73
|
+
retry
|
74
|
+
end
|
75
|
+
raise
|
76
|
+
end
|
77
|
+
|
78
|
+
self.extend(self)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
metadata
ADDED
@@ -0,0 +1,137 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: attache-api
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.2.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- choonkeat
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-11-08 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: httpclient
|
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: rake
|
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: fastimage
|
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: minitest
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: guard
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: guard-minitest
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
description:
|
98
|
+
email:
|
99
|
+
- choonkeat@gmail.com
|
100
|
+
executables: []
|
101
|
+
extensions: []
|
102
|
+
extra_rdoc_files: []
|
103
|
+
files:
|
104
|
+
- MIT-LICENSE
|
105
|
+
- README.md
|
106
|
+
- Rakefile
|
107
|
+
- lib/attache/api.rb
|
108
|
+
- lib/attache/api/model.rb
|
109
|
+
- lib/attache/api/test.rb
|
110
|
+
- lib/attache/api/utils.rb
|
111
|
+
- lib/attache/api/v1.rb
|
112
|
+
- lib/attache/api/version.rb
|
113
|
+
homepage: https://github.com/choonkeat/attache-api
|
114
|
+
licenses:
|
115
|
+
- MIT
|
116
|
+
metadata: {}
|
117
|
+
post_install_message:
|
118
|
+
rdoc_options: []
|
119
|
+
require_paths:
|
120
|
+
- lib
|
121
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
122
|
+
requirements:
|
123
|
+
- - ">="
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: '0'
|
126
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
127
|
+
requirements:
|
128
|
+
- - ">="
|
129
|
+
- !ruby/object:Gem::Version
|
130
|
+
version: '0'
|
131
|
+
requirements: []
|
132
|
+
rubyforge_project:
|
133
|
+
rubygems_version: 2.4.8
|
134
|
+
signing_key:
|
135
|
+
specification_version: 4
|
136
|
+
summary: API for client lib to integrate with attache server
|
137
|
+
test_files: []
|