layervault 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (64) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +17 -0
  3. data/Gemfile +4 -0
  4. data/LICENSE.txt +22 -0
  5. data/README.md +151 -0
  6. data/Rakefile +1 -0
  7. data/layervault.gemspec +29 -0
  8. data/lib/layervault.rb +16 -0
  9. data/lib/layervault/client.rb +62 -0
  10. data/lib/layervault/client/files.rb +64 -0
  11. data/lib/layervault/client/folders.rb +27 -0
  12. data/lib/layervault/client/organizations.rb +9 -0
  13. data/lib/layervault/client/projects.rb +25 -0
  14. data/lib/layervault/client/revisions.rb +23 -0
  15. data/lib/layervault/client/users.rb +9 -0
  16. data/lib/layervault/default_options.rb +38 -0
  17. data/lib/layervault/file.rb +11 -0
  18. data/lib/layervault/folder.rb +27 -0
  19. data/lib/layervault/middleware/raise_error.rb +15 -0
  20. data/lib/layervault/mixins/authentication.rb +7 -0
  21. data/lib/layervault/mixins/configurable.rb +40 -0
  22. data/lib/layervault/mixins/connection.rb +15 -0
  23. data/lib/layervault/model.rb +41 -0
  24. data/lib/layervault/organization.rb +19 -0
  25. data/lib/layervault/project.rb +28 -0
  26. data/lib/layervault/response/error.rb +111 -0
  27. data/lib/layervault/revision.rb +23 -0
  28. data/lib/layervault/user.rb +11 -0
  29. data/lib/layervault/version.rb +3 -0
  30. data/spec/cassettes/Files/Basic_operations/_create_file/creates_the_File.json +1 -0
  31. data/spec/cassettes/Files/Basic_operations/_delete_file/deletes_the_File.json +1 -0
  32. data/spec/cassettes/Files/Basic_operations/_file/returns_the_File_info.json +1 -0
  33. data/spec/cassettes/Files/_move_file/moves_the_File.json +1 -0
  34. data/spec/cassettes/Files/_sync_check/performs_a_sync_check_on_the_path.json +1 -0
  35. data/spec/cassettes/Folders/_change_folder_color/changes_the_folder_color.json +1 -0
  36. data/spec/cassettes/Folders/_create_folder/creates_the_Folder.json +1 -0
  37. data/spec/cassettes/Folders/_delete_folder/deletes_the_folder.json +1 -0
  38. data/spec/cassettes/Folders/_folder/returns_the_Folder_info.json +1 -0
  39. data/spec/cassettes/Folders/_move_folder/moves_the_folder.json +1 -0
  40. data/spec/cassettes/Organizations/_organization/returns_the_Organization_info.json +1 -0
  41. data/spec/cassettes/Projects/_change_project_folder_color/changes_the_folder_color.json +1 -0
  42. data/spec/cassettes/Projects/_create_project/Creates_a_new_project.json +1 -0
  43. data/spec/cassettes/Projects/_delete_project/Deletes_the_project.json +1 -0
  44. data/spec/cassettes/Projects/_move_project/Moves_the_project.json +1 -0
  45. data/spec/cassettes/Projects/_project/returns_the_Organization_info.json +1 -0
  46. data/spec/cassettes/Revisions/_meta/returns_the_Meta_info.json +1 -0
  47. data/spec/cassettes/Revisions/_previews/returns_the_Preview_info.json +1 -0
  48. data/spec/cassettes/Revisions/_revision/returns_the_Revision_info.json +1 -0
  49. data/spec/cassettes/Revisions/_revisions/returns_the_Revisions_info.json +1 -0
  50. data/spec/cassettes/Users/_me/returns_the_User_info_as_JSON.json +1 -0
  51. data/spec/fixtures/Test.psd +0 -0
  52. data/spec/fixtures/dhh.png +0 -0
  53. data/spec/layervault/client/files_spec.rb +58 -0
  54. data/spec/layervault/client/folder_spec.rb +54 -0
  55. data/spec/layervault/client/organizations_spec.rb +16 -0
  56. data/spec/layervault/client/projects_spec.rb +51 -0
  57. data/spec/layervault/client/revisions_spec.rb +39 -0
  58. data/spec/layervault/client/user_spec.rb +16 -0
  59. data/spec/layervault/client_spec.rb +15 -0
  60. data/spec/layervault/error.rb +69 -0
  61. data/spec/layervault_spec.rb +12 -0
  62. data/spec/spec.opts +5 -0
  63. data/spec/spec_helper.rb +72 -0
  64. metadata +256 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: b3b3c597c5f9ea686f645318bfc78bfbfbe1e5b0
4
+ data.tar.gz: bc04212e9747011fe20fb05a8fe88948a957ff64
5
+ SHA512:
6
+ metadata.gz: 6831520fc7291294398f9435fd204aa560e8fd1837681733ca7462eab46e9eb8bb7f2648e5907d313978c593700562710395dbe0a5f0aa4179fc3262e227d21c
7
+ data.tar.gz: 4292f9681e83d9369c9a0f2e6d10da1bc65394961a5f0671c38dbd9944b8d86d15ebefdf887999ed6a73dddd10be1d2bcc91fecbe236bf6ad78c9ca5bbec4656
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in layervault_ruby_client.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 John McDowall
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,151 @@
1
+ # LayerVault Ruby API Client
2
+
3
+ This is the Ruby client library that wraps the [LayerVault API](https://github.com/layervault/api). It assumes you have used another oAuth 2 library to obtain and manage a valid access token.
4
+
5
+ ## Installation
6
+
7
+ Put this in your Gemfile and smoke it:
8
+
9
+ ```ruby
10
+ gem 'layervault'
11
+ ```
12
+
13
+ Or install it:
14
+
15
+ ```shell
16
+ $ gem install layervault
17
+ ```
18
+
19
+ ## Supported oAuth flows
20
+
21
+ Currently only [Resource Owner Credentials](https://github.com/applicake/doorkeeper/wiki/Using-Resource-Owner-Password-Credentials-flow) and [Client Credentials](https://github.com/applicake/doorkeeper/wiki/Client-Credentials-flow) are supported. Client credentials are pretty much useless as 99% of the API requires a User as context for the operations.
22
+
23
+ ## Requesting an Access Token
24
+
25
+ 1. Register or choose an application from https://layervault.com/oauth/applications/ and note the `client_id` and `client_secret`.
26
+ 2. Plug those values into the following curl snippet, including your LayerVault username and password:
27
+
28
+ ```
29
+ curl -i https://api.layervault.com/oauth/token \
30
+ -F grant_type="password" \
31
+ -F username="<username_goes_here>" \
32
+ -F password="<password_goes_here>" \
33
+ -F client_id="<client_id_goes_here>" \
34
+ -F client_secret="<client_secret_goes_here>"
35
+ ```
36
+ 3. You now have an access token. You can make API requests by calling via CURL like so:
37
+
38
+ ```
39
+ curl -H 'Authorization: Bearer <your access token>' \
40
+ 'https://api.layervault.com/api/v1/me'
41
+ ```
42
+
43
+ ## Initializing the Client
44
+
45
+ You can initialize the client via Environment Variables or by passing configurations options into the client when you create it, like this:
46
+
47
+ @client = LayerVault::Client.new(access_token: 'your_access_token', api_endpoint: 'your_api_endpoint')
48
+
49
+ Or you can also say:
50
+
51
+ LayerVault.client.access_token = 'access_token'
52
+ LayerVault.client.api_endpoint = 'api_endpoint'
53
+
54
+ ### Environment Variables
55
+
56
+ ENV['LAYERVAULT_ACCESS_TOKEN'] - You LayerVault API access token
57
+ ENV['LAYERVAULT_API_ENDPOINT'] - The API Endpoint you wish to target calls against (defaults to `https://api.layervault.com/api/v1/`)
58
+ ENV['LAYERVAULT_USER_AGENT'] - Defaults to `LayerVault Ruby Gem #{LayerVault::VERSION}`
59
+
60
+ ### The User Agent
61
+
62
+ You should set the User agent to include your email address so that in the event your client does something wrong we can contact you.
63
+
64
+ ## Making API calls
65
+
66
+ You can use the `LayerVault.client.<api_operation>` methods to call the API to perform actions. Alternatively, each API object has simple object model that allows you to say:
67
+
68
+ ```
69
+ LayerVault.client.access_token = 'access_token'
70
+ p = LayerVault::Organization.for('layervault')
71
+ p.create_project('my new project')
72
+ ```
73
+
74
+ And so on.
75
+
76
+ ### Simple Object Model
77
+
78
+ There's a very simple object model provided by classes that implement [Hashie](https://github.com/intridea/hashie) objects that wrap the JSON responses from the ```LayerVault.client``` interface. The objects mostly all follow a ```.for``` pattern that accepts the appropriate number of arguments for the level of nesting the object represents. For example,
79
+
80
+ - Organizations require the name of the organization: ```Organization.for('layervault')```
81
+ - Projects require the name of the organization and the project name: ```Project.for('layervault', 'Designer News')```
82
+ - ... and so on ...
83
+
84
+ #### Associations
85
+
86
+ When using the simple object model, associations will be hydrated into the correct child objects allowing a simple level of traversal down the object model hierarchy.
87
+
88
+ ## Access Tokens
89
+
90
+ Access Tokens are valid for two hours only. When you request a token, you are also told how long the token is valid for, in seconds, as part of the token response:
91
+
92
+ {
93
+ "access_token": "aec9c670cf5e673bfedf83d055d2a2e0e5f37e52d3b41cffcf7874f73a7458bf",
94
+ "token_type": "bearer",
95
+ "expires_in": 7200,
96
+ "refresh_token": "afe9c670cf5e673bfedf83d055d2a2e0e5f37e52d3b41cffcf7874f73a7458bf",
97
+ "scope": "user"
98
+ }
99
+
100
+ Here we can see that the Token is valid for 7200 more seconds, or two hours.
101
+
102
+ The API implements [Refresh Tokens](https://github.com/applicake/doorkeeper/wiki/Enable-Refresh-Token-Credentials) which allow you to request new tokens without re-authenticating the user through the Web credentials flow. You can see in the access token response above, a refresh_token was given for use in future calls. Store it, use it.
103
+
104
+ ## Omniauth Strategy
105
+
106
+ If you're looking for something that makes a Rails integration more easy, we've made an [Omniauth Authentication Strategy](https://github.com/layervault/omniauth-layervault).
107
+
108
+ ## Running the test suite.
109
+
110
+ You should create a test project called ```api-playground``` in your Organization. and make sure ```TEST_ORG``` and ```TEST_PROJECT``` environment variables are set correctly in the call below:
111
+
112
+ ```TEST_ORG='layervault-test' TEST_PROJECT='api-playground' LAYERVAULT_API_ENDPOINT='https://api.layervault.com/api/v1/' LAYERVAULT_ACCESS_TOKEN=<your_access_token> be rspec spec/layervault/client ```
113
+
114
+ The test suite uses VCR to save making requests against the server. You always have the option of deleting the contents of the ```spec/cassettes``` folder to make real calls against the LayerVault servers.
115
+
116
+ ## Client Methods Summary
117
+
118
+ ### General
119
+ - LayerVault.client.me
120
+
121
+ ### Organizations
122
+
123
+ - LayerVault.client.organization( organization_name )
124
+
125
+ ### Projects
126
+ - LayerVault.client.project( organization_name, project )
127
+ - LayerVault.client.create_project( organization_name, project )
128
+ - LayerVault.client.delete_project( organization_name, project )
129
+ - LayerVault.client.move_project( organization_name, project, to )
130
+ - LayerVault.client.change_project_folder_color( organization_name, project, color )
131
+
132
+ ### Folders
133
+ - LayerVault.client.folder( organization_name, project, folder_path )
134
+ - LayerVault.client.create_folder( organization_name, project, folder_path )
135
+ - LayerVault.client.delete_folder( organization_name, project, folder_path )
136
+ - LayerVault.client.move_folder( organization_name, project, folder_path, new_folder )
137
+ - LayerVault.client.change_folder_color( organization_name, project, folder_path, color )
138
+
139
+ ### Files
140
+ - LayerVault.client.file( organization_name, project, folder_path, file_name )
141
+ - LayerVault.client.create_file( organization_name, project, folder_path, file_name, options )
142
+ - LayerVault.client.delete_file( organization_name, project, folder_path, file_name )
143
+ - LayerVault.client.move_file( organization_name, project, folder_path, new_folder, new_filename )
144
+ - LayerVault.client.sync_check( organization_name, project, folder_path, options )
145
+
146
+ ### Revisions
147
+ - LayerVault.client.revision( organization_name, project, folder_path, file_name, revision )
148
+ - LayerVault.client.previews( organization_name, project, folder_path, file_name, revision, options )
149
+ - LayerVault.client.revisions( organization_name, project, folder_path, file_name, revision, options )
150
+ - LayerVault.client.meta( organization_name, project, folder_path, file_name, revision )
151
+
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,29 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'layervault/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "layervault"
8
+ spec.version = LayerVault::VERSION
9
+ spec.authors = ["John McDowall", "Ryan LeFevre", "Kelly Sutton"]
10
+ spec.email = ["john@mcdowall.info", "ryan@layervault.com", "kelly@layervault.com"]
11
+ spec.description = %q{The LayerVault Ruby API client.}
12
+ spec.summary = %q{Provides Ruby native wrappers for the LayerVault API.}
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency "faraday"
22
+ spec.add_dependency "hashie"
23
+ spec.add_development_dependency "bundler", "~> 1.3"
24
+ spec.add_development_dependency "rake"
25
+ spec.add_development_dependency "rspec"
26
+ spec.add_development_dependency "multi_json"
27
+ spec.add_development_dependency "vcr"
28
+ spec.add_development_dependency "webmock", "1.13"
29
+ end
@@ -0,0 +1,16 @@
1
+ require "layervault/version"
2
+ require "layervault/client"
3
+ require "layervault/mixins/configurable"
4
+ require "layervault/default_options"
5
+
6
+ module LayerVault
7
+ class << self
8
+ include LayerVault::Configurable
9
+
10
+ def client
11
+ @client ||= LayerVault::Client.new(options)
12
+ end
13
+ end
14
+ end
15
+
16
+ LayerVault.setup
@@ -0,0 +1,62 @@
1
+ require 'faraday'
2
+ require 'multi_json'
3
+
4
+ require 'layervault/mixins/authentication'
5
+ require 'layervault/mixins/configurable'
6
+ require 'layervault/mixins/connection'
7
+
8
+ require 'layervault/client/users'
9
+ require 'layervault/client/organizations'
10
+ require 'layervault/client/projects'
11
+ require 'layervault/client/folders'
12
+ require 'layervault/client/files'
13
+ require 'layervault/client/revisions'
14
+
15
+ require 'layervault/model'
16
+ require 'layervault/organization'
17
+ require 'layervault/user'
18
+ require 'layervault/project'
19
+ require 'layervault/folder'
20
+ require 'layervault/file'
21
+ require 'layervault/revision'
22
+
23
+ module LayerVault
24
+
25
+ class ClientParamsError < Exception ; end
26
+
27
+ class Client
28
+ include LayerVault::Authentication
29
+ include LayerVault::Configurable
30
+ include LayerVault::Connection
31
+
32
+ include LayerVault::Client::Users
33
+ include LayerVault::Client::Organizations
34
+ include LayerVault::Client::Projects
35
+ include LayerVault::Client::Folders
36
+ include LayerVault::Client::Files
37
+ include LayerVault::Client::Revisions
38
+
39
+ def initialize(options={})
40
+ LayerVault::Configurable.keys.each do |key|
41
+ instance_variable_set(:"@#{key}", options[key] || LayerVault.instance_variable_get(:"@#{key}"))
42
+ end
43
+ end
44
+
45
+ def get(url, options = {})
46
+ request :get, url, options
47
+ end
48
+
49
+ def post(url, options = {})
50
+ request :post, url, options
51
+ end
52
+
53
+ def put(url, options = {})
54
+ request :put, url, options
55
+ end
56
+
57
+ def delete(url, options = {})
58
+ request :delete, url, options
59
+ end
60
+
61
+ end
62
+ end
@@ -0,0 +1,64 @@
1
+ require 'multi_json'
2
+ require 'digest/md5'
3
+ require 'uri'
4
+
5
+ module LayerVault
6
+ class Client
7
+ module Files
8
+ def file(organization_name, project_name, path, file_name)
9
+ get "#{organization_name}/#{project_name}/#{path}/#{file_name}"
10
+ end
11
+
12
+ def delete_file(organization_name, project_name, path, file_name, options={})
13
+ raise ClientParamsError.new("You must specify the md5 option of the file you are trying to delete.") unless options.fetch(:md5, nil)
14
+ delete "#{organization_name}/#{project_name}/#{path}/#{file_name}", options
15
+ end
16
+
17
+ def create_file(organization_name, project_name, path, file_name, options={} )
18
+ raise ClientParamsError.new("You must specify the local_file_path option to the file you want to upload.") unless options.fetch(:local_file_path, nil)
19
+ raise ClientParamsError.new("You must specify the content_type option to the content type of the file you are uploading.") unless options.fetch(:content_type, nil)
20
+
21
+ local_file_path = options.fetch(:local_file_path, nil)
22
+ content_type = options.fetch(:content_type, nil)
23
+
24
+ md5 = Digest::MD5.hexdigest(::File.read(local_file_path))
25
+ options = {md5: md5}
26
+
27
+ json_response = put("#{organization_name}/#{project_name}/#{path}/#{file_name}", options)
28
+
29
+ s3_response = MultiJson.decode(json_response)
30
+ s3_response.merge!( "Content-Type" => content_type)
31
+
32
+ payload = s3_response.merge({ file: Faraday::UploadIO.new(local_file_path, content_type) })
33
+
34
+ conn = Faraday.new('https://omnivore-scratch.s3.amazonaws.com') do |f|
35
+ f.request :multipart
36
+ f.request :url_encoded
37
+ f.adapter :net_http # This is what ended up making it work
38
+ end
39
+
40
+ response_from_s3 = conn.post('/', payload)
41
+ redirect = response_from_s3[:location]
42
+
43
+ # Add the access_token to the query
44
+ uri = URI.parse(redirect)
45
+ new_query_ar = URI.decode_www_form(uri.query) << ["access_token", LayerVault.client.access_token]
46
+ uri.query = URI.encode_www_form(new_query_ar)
47
+
48
+ re = conn.post( uri.to_s )
49
+ end
50
+
51
+ def move_file(organization_name, project_name, path, file_name, options={})
52
+ raise ClientParamsError.new("You must specify the new_folder option to specify the destination folder") unless options.fetch(:new_folder, nil)
53
+ raise ClientParamsError.new("You must specify the new_filename option to specify the destination folder") unless options.fetch(:new_file_name, nil)
54
+
55
+ post "#{organization_name}/#{project_name}/#{path}/#{file_name}/move", options
56
+ end
57
+
58
+ def sync_check(organization_name, project_name, path, file_name, options={})
59
+ raise ClientParamsError.new("You must specify the md5 option of the file you are trying to sync check.") unless options.fetch(:md5, nil)
60
+ get "#{organization_name}/#{project_name}/#{path}/#{file_name}/sync_check", options
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,27 @@
1
+ module LayerVault
2
+ class Client
3
+ module Folders
4
+ def folder(organization_name, project_name, path)
5
+ get "#{organization_name}/#{project_name}/#{path}"
6
+ end
7
+
8
+ def delete_folder(organization_name, project_name, path)
9
+ delete "#{organization_name}/#{project_name}/#{path}"
10
+ end
11
+
12
+ def create_folder(organization_name, project_name, path)
13
+ post "#{organization_name}/#{project_name}/#{path}"
14
+ end
15
+
16
+ def move_folder(organization_name, project_name, path, options={})
17
+ raise ClientParamsError.new("You must specify the :to option for the destination folder.") unless options.fetch(:to, nil)
18
+ post "#{organization_name}/#{project_name}/#{path}/move", options
19
+ end
20
+
21
+ def change_folder_color(organization_name, project_name, path, options={})
22
+ raise ClientParamsError.new("You must specify the color option for the new color to apply to the folder.") unless options.fetch(:color, nil)
23
+ put "#{organization_name}/#{project_name}/#{path}", options
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,9 @@
1
+ module LayerVault
2
+ class Client
3
+ module Organizations
4
+ def organization(name)
5
+ get "#{name}"
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,25 @@
1
+ module LayerVault
2
+ class Client
3
+ module Projects
4
+ def project(organization_name, project_name)
5
+ get "#{organization_name}/#{project_name}"
6
+ end
7
+
8
+ def create_project(organization_name, project_name)
9
+ post "#{organization_name}/#{project_name}"
10
+ end
11
+
12
+ def delete_project(organization_name, project_name)
13
+ delete "#{organization_name}/#{project_name}"
14
+ end
15
+
16
+ def move_project(organization_name, project_name, options={})
17
+ post "#{organization_name}/#{project_name}/move", options
18
+ end
19
+
20
+ def change_project_folder_color(organization_name, project_name, options={})
21
+ put "#{organization_name}/#{project_name}", options
22
+ end
23
+ end
24
+ end
25
+ end