layervault 0.1.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.
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