boar 1.0.1
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/.gitignore +7 -0
- data/.travis-gemfile +13 -0
- data/.travis.yml +10 -0
- data/.yardopts +1 -0
- data/Gemfile +20 -0
- data/README.md +19 -0
- data/Rakefile +11 -0
- data/app/controllers/boar/application_controller.rb +16 -0
- data/app/controllers/boar/downloads_controller.rb +17 -0
- data/app/controllers/boar/pages_controller.rb +13 -0
- data/app/models/boar/handlers/authentication.rb +38 -0
- data/app/models/boar/handlers/downloads/base.rb +56 -0
- data/app/models/boar/handlers/downloads/box_net.rb +67 -0
- data/app/models/boar/handlers/downloads/dropbox.rb +59 -0
- data/app/models/boar/handlers/downloads/google_drive.rb +48 -0
- data/app/models/boar/handlers/downloads/local.rb +34 -0
- data/app/models/boar/handlers/downloads/remote.rb +24 -0
- data/app/models/boar/handlers/downloads/s3.rb +54 -0
- data/app/models/boar/handlers/downloads/sky_drive.rb +45 -0
- data/app/models/boar/handlers/generic.rb +25 -0
- data/app/models/boar/handlers/hosts.rb +28 -0
- data/app/models/boar/handlers/locale.rb +25 -0
- data/app/models/boar/handlers/path_mapper.rb +15 -0
- data/app/models/boar/handlers/root.rb +25 -0
- data/app/models/boar/handlers/views.rb +25 -0
- data/app/models/boar/services/downloads.rb +118 -0
- data/app/models/boar/services/generic.rb +78 -0
- data/app/models/boar/services/pages.rb +62 -0
- data/app/models/boar/utils/basic.rb +27 -0
- data/boar.gemspec +41 -0
- data/lib/boar.rb +31 -0
- data/lib/boar/configuration.rb +77 -0
- data/lib/boar/engine.rb +16 -0
- data/lib/boar/exceptions.rb +46 -0
- data/lib/boar/providers/base.rb +28 -0
- data/lib/boar/providers/box_net.rb +27 -0
- data/lib/boar/providers/dropbox.rb +26 -0
- data/lib/boar/providers/google_drive.rb +87 -0
- data/lib/boar/providers/sky_drive.rb +80 -0
- data/lib/boar/router.rb +68 -0
- data/lib/boar/version.rb +25 -0
- data/lib/tasks/boar.rake +102 -0
- data/spec/boar/controllers/application_controller_spec.rb +11 -0
- data/spec/boar/controllers/pages_controller_spec.rb +11 -0
- data/spec/boar/routes_spec.rb +11 -0
- data/spec/boar_spec.rb +7 -0
- data/spec/coverage_helper.rb +17 -0
- data/spec/spec_helper.rb +15 -0
- metadata +264 -0
@@ -0,0 +1,62 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
#
|
3
|
+
# This file is part of the boar gem. Copyright (C) 2013 and above Shogun <shogun_panda@me.com>.
|
4
|
+
# Licensed under the MIT license, which can be found at http://www.opensource.org/licenses/mit-license.php.
|
5
|
+
#
|
6
|
+
|
7
|
+
module Boar
|
8
|
+
module Services
|
9
|
+
class Pages < Boar::Services::Generic
|
10
|
+
def main
|
11
|
+
# Get the path
|
12
|
+
path = @controller.params[:path]
|
13
|
+
|
14
|
+
# Map the path
|
15
|
+
path = self.handler_for(:pages_mapper, options).call(self, path)
|
16
|
+
|
17
|
+
# Handle ACL for the path
|
18
|
+
self.handle_authentication(path, @options)
|
19
|
+
|
20
|
+
@controller.render(file: search_page(path)) if !@controller.performed?
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
def search_page(path, partial = false, options = nil)
|
25
|
+
# Setup path and options
|
26
|
+
path = "index" if path.blank?
|
27
|
+
options ||= @options
|
28
|
+
|
29
|
+
# Get the template file
|
30
|
+
template = (self.handler_for(:root, options).call + path).to_s
|
31
|
+
|
32
|
+
begin
|
33
|
+
perform_search(template, partial, options)
|
34
|
+
|
35
|
+
# We return the template if search didn't fail, which means that the #render method will success.
|
36
|
+
template
|
37
|
+
rescue ActionView::MissingTemplate
|
38
|
+
raise Boar::Exceptions::NotFound.new(template)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def perform_search(template, partial, options)
|
43
|
+
# Get the context
|
44
|
+
context = @controller.view_renderer.lookup_context
|
45
|
+
|
46
|
+
# Setup the locale
|
47
|
+
context.locale = options[:add_locale] ? current_locale(options) : nil
|
48
|
+
|
49
|
+
# Perform the search
|
50
|
+
context.with_fallbacks { context.find_template(template, nil, partial, [], {}) }
|
51
|
+
end
|
52
|
+
|
53
|
+
def current_root(options = nil)
|
54
|
+
self.handler_for(:root, options).call
|
55
|
+
end
|
56
|
+
|
57
|
+
def current_locale(options = nil)
|
58
|
+
self.handler_for(:locale, options).call
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
#
|
3
|
+
# This file is part of the boar gem. Copyright (C) 2013 and above Shogun <shogun_panda@me.com>.
|
4
|
+
# Licensed under the MIT license, which can be found at http://www.opensource.org/licenses/mit-license.php.
|
5
|
+
#
|
6
|
+
|
7
|
+
module Boar
|
8
|
+
module Utils
|
9
|
+
module Basic
|
10
|
+
def ensure_hash(obj, default = {})
|
11
|
+
HashWithIndifferentAccess.new(obj.is_a?(Hash) ? obj : default)
|
12
|
+
end
|
13
|
+
|
14
|
+
def get_option(options, key, default = nil)
|
15
|
+
begin
|
16
|
+
options.symbolize_keys.fetch(key.to_sym)
|
17
|
+
rescue Exception => _
|
18
|
+
block_given? ? yield(key) : default
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def interpolate(template, args)
|
23
|
+
Mustache.render(template, args)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
data/boar.gemspec
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
#
|
3
|
+
# This file is part of the boar gem. Copyright (C) 2013 and above Shogun <shogun_panda@me.com>.
|
4
|
+
# Licensed under the MIT license, which can be found at http://www.opensource.org/licenses/mit-license.php.
|
5
|
+
#
|
6
|
+
|
7
|
+
require File.expand_path('../lib/boar/version', __FILE__)
|
8
|
+
|
9
|
+
Gem::Specification.new do |gem|
|
10
|
+
gem.name = "boar"
|
11
|
+
gem.version = Boar::Version::STRING
|
12
|
+
gem.homepage = "http://github.com/ShogunPanda/boar"
|
13
|
+
gem.summary = "A Rails engine to handle local static pages and downloads on the cloud."
|
14
|
+
gem.description = "A Rails engine to handle local static pages and downloads on the cloud."
|
15
|
+
gem.rubyforge_project = "boar"
|
16
|
+
|
17
|
+
gem.authors = ["Shogun"]
|
18
|
+
gem.email = ["shogun_panda@me.com"]
|
19
|
+
|
20
|
+
gem.files = `git ls-files`.split($\)
|
21
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
22
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
23
|
+
gem.require_paths = ["lib"]
|
24
|
+
|
25
|
+
gem.required_ruby_version = ">= 1.9.3"
|
26
|
+
|
27
|
+
gem.add_dependency("rails", ">= 3.2.12")
|
28
|
+
gem.add_dependency("mustache", "~> 0.99.4")
|
29
|
+
gem.add_dependency("mbrao", "~> 1.1.1")
|
30
|
+
gem.add_dependency("redis", "~> 3.0.3")
|
31
|
+
gem.add_dependency("oj", "~> 2.0.10")
|
32
|
+
gem.add_dependency("elephas", "~> 3.0.0")
|
33
|
+
gem.add_dependency("clavem", "~> 1.2.2")
|
34
|
+
|
35
|
+
# Downloads gem
|
36
|
+
gem.add_dependency("dropbox-sdk", "~> 1.5.1")
|
37
|
+
gem.add_dependency("google-api-client", "~> 0.6.3")
|
38
|
+
gem.add_dependency("aws-sdk", "~> 1.9.5")
|
39
|
+
gem.add_dependency("ruby-box", "~> 1.2.0")
|
40
|
+
gem.add_dependency("skydrive", "~> 0.1.0")
|
41
|
+
end
|
data/lib/boar.rb
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
#
|
3
|
+
# This file is part of the boar gem. Copyright (C) 2013 and above Shogun <shogun_panda@me.com>.
|
4
|
+
# Licensed under the MIT license, which can be found at http://www.opensource.org/licenses/mit-license.php.
|
5
|
+
#
|
6
|
+
|
7
|
+
# TODO: Simplify code in ALL classes.
|
8
|
+
# TODO: Rename #call to #perform everywhere.
|
9
|
+
|
10
|
+
require "mbrao"
|
11
|
+
require "mustache"
|
12
|
+
require "mime-types"
|
13
|
+
require "redis"
|
14
|
+
require "oj"
|
15
|
+
require "clavem"
|
16
|
+
require "dropbox_sdk"
|
17
|
+
require "google/api_client"
|
18
|
+
require "aws-sdk"
|
19
|
+
require "ruby-box"
|
20
|
+
require "skydrive"
|
21
|
+
|
22
|
+
require "boar/version" if !defined?(Boar::Version)
|
23
|
+
require "boar/configuration"
|
24
|
+
require "boar/router"
|
25
|
+
require "boar/engine"
|
26
|
+
require "boar/exceptions"
|
27
|
+
require "boar/providers/base"
|
28
|
+
require "boar/providers/dropbox"
|
29
|
+
require "boar/providers/google_drive"
|
30
|
+
require "boar/providers/box_net"
|
31
|
+
require "boar/providers/sky_drive"
|
@@ -0,0 +1,77 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
#
|
3
|
+
# This file is part of the boar gem. Copyright (C) 2013 and above Shogun <shogun_panda@me.com>.
|
4
|
+
# Licensed under the MIT license, which can be found at http://www.opensource.org/licenses/mit-license.php.
|
5
|
+
#
|
6
|
+
|
7
|
+
# TODO: Embed the specific configuration here, and then use a get("key.subkey") interface
|
8
|
+
# TODO: Reorganize all options
|
9
|
+
|
10
|
+
module Boar
|
11
|
+
class Configuration
|
12
|
+
def initialize(app)
|
13
|
+
@data = {
|
14
|
+
app: app,
|
15
|
+
backend: Redis.new,
|
16
|
+
skip_cache_param: "sc",
|
17
|
+
handlers: {
|
18
|
+
root: Boar::Handlers::Root,
|
19
|
+
authentication: Boar::Handlers::Authentication,
|
20
|
+
locale: Boar::Handlers::Locale,
|
21
|
+
views: Boar::Handlers::Views,
|
22
|
+
hosts: Boar::Handlers::Hosts
|
23
|
+
}
|
24
|
+
}
|
25
|
+
|
26
|
+
initialize_pages()
|
27
|
+
initialize_downloads()
|
28
|
+
end
|
29
|
+
|
30
|
+
def backend_key(key, _, request)
|
31
|
+
host = self.handlers[:hosts].new.call(request)
|
32
|
+
"boar[#{host}]:#{key}"
|
33
|
+
end
|
34
|
+
|
35
|
+
def method_missing(method, *args, &block)
|
36
|
+
key = method.to_sym
|
37
|
+
|
38
|
+
if @data.has_key?(key) then
|
39
|
+
rv = @data[key]
|
40
|
+
rv = HashWithIndifferentAccess.new(rv) if rv.is_a?(Hash)
|
41
|
+
rv
|
42
|
+
else
|
43
|
+
super
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
def initialize_pages
|
49
|
+
@data.merge!({
|
50
|
+
pages_root: "",
|
51
|
+
pages_directory: "{{root}}/app/pages",
|
52
|
+
locale_param: :locale,
|
53
|
+
add_locale: true,
|
54
|
+
default_locale: @data[:app].config.i18n.default_locale || "en",
|
55
|
+
locale_regexp: /[a-z]{2}((_([a-zA-Z]{2}))?)/,
|
56
|
+
views: {
|
57
|
+
authentication: "authentication",
|
58
|
+
error: "errors/{{code}}"
|
59
|
+
}
|
60
|
+
})
|
61
|
+
|
62
|
+
@data[:handlers].merge!({pages_mapper: Boar::Handlers::PathMapper})
|
63
|
+
end
|
64
|
+
|
65
|
+
def initialize_downloads
|
66
|
+
@data.merge!({
|
67
|
+
downloads_root: "downloads",
|
68
|
+
downloads_directory: "{{root}}/files",
|
69
|
+
config_file: "{{root}}/config/downloads.yml",
|
70
|
+
credentials_file: "{{root}}/config/credentials.yml",
|
71
|
+
default_provider: :local
|
72
|
+
})
|
73
|
+
|
74
|
+
@data[:handlers].merge!({downloads_mapper: Boar::Handlers::PathMapper})
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
data/lib/boar/engine.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
#
|
3
|
+
# This file is part of the boar gem. Copyright (C) 2013 and above Shogun <shogun_panda@me.com>.
|
4
|
+
# Licensed under the MIT license, which can be found at http://www.opensource.org/licenses/mit-license.php.
|
5
|
+
#
|
6
|
+
|
7
|
+
module Boar
|
8
|
+
class Engine < ::Rails::Engine
|
9
|
+
isolate_namespace Boar
|
10
|
+
mattr_accessor :backend
|
11
|
+
|
12
|
+
initializer "boar.configuration" do |app|
|
13
|
+
app.config.boar = Boar::Configuration.new(app)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
#
|
3
|
+
# This file is part of the boar gem. Copyright (C) 2013 and above Shogun <shogun_panda@me.com>.
|
4
|
+
# Licensed under the MIT license, which can be found at http://www.opensource.org/licenses/mit-license.php.
|
5
|
+
#
|
6
|
+
|
7
|
+
module Boar
|
8
|
+
module Exceptions
|
9
|
+
class Error < ::Exception
|
10
|
+
attr_reader :code
|
11
|
+
|
12
|
+
def initialize(code, message = nil)
|
13
|
+
super(message)
|
14
|
+
@code = code
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.must_raise?(exception)
|
18
|
+
exception.is_a?(Lazier::Exceptions::Dump) && Rails.env.development?
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
class ServerError < Error
|
23
|
+
def initialize(message)
|
24
|
+
super(500, message)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
class UnImplemented < Error
|
29
|
+
def initialize(message = nil)
|
30
|
+
super(501, message)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
class AuthorizationFailed < Error
|
35
|
+
def initialize(message = nil)
|
36
|
+
super(403, message)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
class NotFound < Error
|
41
|
+
def initialize(message = nil)
|
42
|
+
super(404, message)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
#
|
3
|
+
# This file is part of the boar gem. Copyright (C) 2013 and above Shogun <shogun_panda@me.com>.
|
4
|
+
# Licensed under the MIT license, which can be found at http://www.opensource.org/licenses/mit-license.php.
|
5
|
+
#
|
6
|
+
|
7
|
+
module Boar
|
8
|
+
module Providers
|
9
|
+
class Base
|
10
|
+
def redirect_for_authentication(_, _)
|
11
|
+
raise Boar::Exceptions::UnImplemented
|
12
|
+
end
|
13
|
+
|
14
|
+
def get_credentials(_, _, _)
|
15
|
+
raise Boar::Exceptions::UnImplemented
|
16
|
+
end
|
17
|
+
|
18
|
+
def update_credentials(credentials, params)
|
19
|
+
params[:all][params[:host]][params[:name]] = params[:single].merge(credentials).to_hash
|
20
|
+
open(params[:path], "w") {|f| f.write(params[:all].to_yaml) }
|
21
|
+
end
|
22
|
+
|
23
|
+
def search_file(_, _)
|
24
|
+
false
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
#
|
3
|
+
# This file is part of the boar gem. Copyright (C) 2013 and above Shogun <shogun_panda@me.com>.
|
4
|
+
# Licensed under the MIT license, which can be found at http://www.opensource.org/licenses/mit-license.php.
|
5
|
+
#
|
6
|
+
|
7
|
+
module Boar
|
8
|
+
module Providers
|
9
|
+
class BoxNet < Base
|
10
|
+
def redirect_for_authentication(authorizer, configuration)
|
11
|
+
@session = RubyBox::Session.new({client_id: configuration[:client_id], client_secret: configuration[:client_secret]})
|
12
|
+
@session.authorize_url(authorizer.callback_url)
|
13
|
+
end
|
14
|
+
|
15
|
+
def get_credentials(authorizer, request, response)
|
16
|
+
begin
|
17
|
+
raise Clavem::Exceptions::AuthorizationDenied if request.query["error"].present?
|
18
|
+
|
19
|
+
token = @session.get_access_token(request.query["code"])
|
20
|
+
{access_token: token.token, refresh_token: token.refresh_token}
|
21
|
+
rescue RuntimeError
|
22
|
+
nil
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
#
|
3
|
+
# This file is part of the boar gem. Copyright (C) 2013 and above Shogun <shogun_panda@me.com>.
|
4
|
+
# Licensed under the MIT license, which can be found at http://www.opensource.org/licenses/mit-license.php.
|
5
|
+
#
|
6
|
+
|
7
|
+
module Boar
|
8
|
+
module Providers
|
9
|
+
class Dropbox < Base
|
10
|
+
def redirect_for_authentication(authorizer, configuration)
|
11
|
+
@session = DropboxSession.new(configuration[:app_key], configuration[:app_secret])
|
12
|
+
@session.get_request_token
|
13
|
+
@session.get_authorize_url + "&oauth_callback=#{authorizer.callback_url}"
|
14
|
+
end
|
15
|
+
|
16
|
+
def get_credentials(authorizer, request, response)
|
17
|
+
begin
|
18
|
+
@session.get_access_token
|
19
|
+
{session: @session.serialize}
|
20
|
+
rescue DropboxAuthError
|
21
|
+
nil
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
#
|
3
|
+
# This file is part of the boar gem. Copyright (C) 2013 and above Shogun <shogun_panda@me.com>.
|
4
|
+
# Licensed under the MIT license, which can be found at http://www.opensource.org/licenses/mit-license.php.
|
5
|
+
#
|
6
|
+
|
7
|
+
module Boar
|
8
|
+
module Providers
|
9
|
+
class GoogleDrive < Base
|
10
|
+
NAME = "boar"
|
11
|
+
VERSION = "1.0.0"
|
12
|
+
SCOPES = ["https://www.googleapis.com/auth/drive"]
|
13
|
+
|
14
|
+
def self.client(configuration)
|
15
|
+
rv = Google::APIClient.new(authorization: :oauth_2, application_name: Boar::Providers::GoogleDrive::NAME, application_version: Boar::Providers::GoogleDrive::VERSION)
|
16
|
+
rv.authorization.client_id = configuration[:client_id]
|
17
|
+
rv.authorization.client_secret = configuration[:client_secret]
|
18
|
+
rv.authorization.scope = Boar::Providers::GoogleDrive::SCOPES
|
19
|
+
rv
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.authorized_client(configuration)
|
23
|
+
authorizer = Clavem::Authorizer.new(configuration.fetch(:authorizer_host, "localhost"), configuration.fetch(:authorizer_port, "2501"))
|
24
|
+
|
25
|
+
client = Boar::Providers::GoogleDrive.client(configuration)
|
26
|
+
client.authorization.redirect_uri = authorizer.callback_url
|
27
|
+
client.authorization.refresh_token = configuration["token"]
|
28
|
+
client.authorization.fetch_access_token!
|
29
|
+
|
30
|
+
client
|
31
|
+
end
|
32
|
+
|
33
|
+
def redirect_for_authentication(authorizer, configuration)
|
34
|
+
@client = Boar::Providers::GoogleDrive.client(configuration)
|
35
|
+
@client.authorization.redirect_uri = authorizer.callback_url
|
36
|
+
@client.authorization.authorization_uri
|
37
|
+
end
|
38
|
+
|
39
|
+
def get_credentials(authorizer, request, response)
|
40
|
+
begin
|
41
|
+
raise Clavem::Exceptions::AuthorizationDenied if request.query["error"].present? || request.query["code"].blank?
|
42
|
+
|
43
|
+
@client.authorization.code = request.query["code"].to_s
|
44
|
+
@client.authorization.fetch_access_token!
|
45
|
+
{token: @client.authorization.refresh_token}
|
46
|
+
rescue RuntimeError
|
47
|
+
nil
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def search_file(path, params)
|
52
|
+
rv = nil
|
53
|
+
|
54
|
+
# Split path
|
55
|
+
Lazier.load_pathname
|
56
|
+
filename = File.basename(path)
|
57
|
+
tree = Pathname.new(File.dirname(path)).components
|
58
|
+
|
59
|
+
# Initialize client
|
60
|
+
client = Boar::Providers::GoogleDrive.authorized_client(params[:single])
|
61
|
+
api = client.discovered_api('drive', 'v2')
|
62
|
+
|
63
|
+
# Find the last folder
|
64
|
+
parent = "root"
|
65
|
+
tree.each do |folder|
|
66
|
+
list = client.execute(api_method: api.children.list, parameters: {"folderId" => parent, "q" => "title='#{folder}'", "maxResults" => 1})
|
67
|
+
parent = validate_file_entry(list)
|
68
|
+
break if !parent
|
69
|
+
end
|
70
|
+
|
71
|
+
# We have the container, query for the file
|
72
|
+
if parent then
|
73
|
+
list = client.execute(api_method: api.files.list, parameters: {"folderId" => parent, "q" => "title='#{filename}'", "maxResults" => 1})
|
74
|
+
rv = validate_file_entry(list)
|
75
|
+
end
|
76
|
+
|
77
|
+
rv ? {"id" => rv} : nil
|
78
|
+
end
|
79
|
+
|
80
|
+
private
|
81
|
+
def validate_file_entry(results)
|
82
|
+
items = results.data.items
|
83
|
+
items.present? ? items[0]["id"] : nil
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|