classic-api 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 155a57a8e9118c5d979aaa22812da02f45c8972d
4
+ data.tar.gz: 3b6afa61788364fbcec9c7118d3e77872cd7ee5b
5
+ SHA512:
6
+ metadata.gz: c9a159499fa15fbfc30cc29de487a00aa7b8128239cd197c9bcd07c0e425d8a97d926f079828c3660f641b92e9b06ae76fcf36c670c7acafaa25ed07470c0873
7
+ data.tar.gz: 0b7151809900549f34b602cc84ff2efe88ecf95e30521b5adf74631b88f4120ecd282b79d47c4fecaaf59a8d5fe7a01afb9a3ae254777dbc8d108a736a857971
data/.gitignore ADDED
@@ -0,0 +1 @@
1
+ /images/*
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'http://rubygems.org'
2
+
3
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,149 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ classic-api (1.0.0)
5
+ actionpack (>= 4.0)
6
+ activesupport (>= 4.0)
7
+ bundler (>= 1.13, < 2.0)
8
+ google-cloud-storage (~> 0.23)
9
+ json_api_client (~> 1.4)
10
+ mime-types (>= 1.6)
11
+ thor (~> 0.19)
12
+
13
+ GEM
14
+ remote: http://rubygems.org/
15
+ specs:
16
+ actionpack (5.0.1)
17
+ actionview (= 5.0.1)
18
+ activesupport (= 5.0.1)
19
+ rack (~> 2.0)
20
+ rack-test (~> 0.6.3)
21
+ rails-dom-testing (~> 2.0)
22
+ rails-html-sanitizer (~> 1.0, >= 1.0.2)
23
+ actionview (5.0.1)
24
+ activesupport (= 5.0.1)
25
+ builder (~> 3.1)
26
+ erubis (~> 2.7.0)
27
+ rails-dom-testing (~> 2.0)
28
+ rails-html-sanitizer (~> 1.0, >= 1.0.2)
29
+ activemodel (5.0.1)
30
+ activesupport (= 5.0.1)
31
+ activesupport (5.0.1)
32
+ concurrent-ruby (~> 1.0, >= 1.0.2)
33
+ i18n (~> 0.7)
34
+ minitest (~> 5.1)
35
+ tzinfo (~> 1.1)
36
+ addressable (2.5.0)
37
+ public_suffix (~> 2.0, >= 2.0.2)
38
+ builder (3.2.3)
39
+ coderay (1.1.1)
40
+ concurrent-ruby (1.0.4)
41
+ diff-lcs (1.3)
42
+ digest-crc (0.4.1)
43
+ erubis (2.7.0)
44
+ faraday (0.11.0)
45
+ multipart-post (>= 1.2, < 3)
46
+ faraday_middleware (0.11.0.1)
47
+ faraday (>= 0.7.4, < 1.0)
48
+ google-api-client (0.9.26)
49
+ addressable (~> 2.3)
50
+ googleauth (~> 0.5)
51
+ httpclient (~> 2.7)
52
+ hurley (~> 0.1)
53
+ memoist (~> 0.11)
54
+ mime-types (>= 1.6)
55
+ representable (~> 2.3.0)
56
+ retriable (~> 2.0)
57
+ google-cloud-core (0.21.1)
58
+ googleauth (~> 0.5.1)
59
+ google-cloud-storage (0.23.1)
60
+ digest-crc (~> 0.4)
61
+ google-api-client (~> 0.9.11)
62
+ google-cloud-core (~> 0.21.0)
63
+ googleauth (0.5.1)
64
+ faraday (~> 0.9)
65
+ jwt (~> 1.4)
66
+ logging (~> 2.0)
67
+ memoist (~> 0.12)
68
+ multi_json (~> 1.11)
69
+ os (~> 0.9)
70
+ signet (~> 0.7)
71
+ httpclient (2.8.3)
72
+ hurley (0.2)
73
+ i18n (0.8.0)
74
+ json_api_client (1.4.0)
75
+ activemodel (>= 3.2.0)
76
+ activesupport (>= 3.2.0)
77
+ addressable (~> 2.2)
78
+ faraday (~> 0.9)
79
+ faraday_middleware (~> 0.9)
80
+ jwt (1.5.6)
81
+ little-plugger (1.1.4)
82
+ logging (2.1.0)
83
+ little-plugger (~> 1.1)
84
+ multi_json (~> 1.10)
85
+ loofah (2.0.3)
86
+ nokogiri (>= 1.5.9)
87
+ memoist (0.15.0)
88
+ method_source (0.8.2)
89
+ mime-types (3.1)
90
+ mime-types-data (~> 3.2015)
91
+ mime-types-data (3.2016.0521)
92
+ mini_portile2 (2.1.0)
93
+ minitest (5.10.1)
94
+ multi_json (1.12.1)
95
+ multipart-post (2.0.0)
96
+ nokogiri (1.7.0.1)
97
+ mini_portile2 (~> 2.1.0)
98
+ os (0.9.6)
99
+ pry (0.10.3)
100
+ coderay (~> 1.1.0)
101
+ method_source (~> 0.8.1)
102
+ slop (~> 3.4)
103
+ public_suffix (2.0.5)
104
+ rack (2.0.1)
105
+ rack-test (0.6.3)
106
+ rack (>= 1.0)
107
+ rails-dom-testing (2.0.2)
108
+ activesupport (>= 4.2.0, < 6.0)
109
+ nokogiri (~> 1.6)
110
+ rails-html-sanitizer (1.0.3)
111
+ loofah (~> 2.0)
112
+ representable (2.3.0)
113
+ uber (~> 0.0.7)
114
+ retriable (2.1.0)
115
+ rspec (3.5.0)
116
+ rspec-core (~> 3.5.0)
117
+ rspec-expectations (~> 3.5.0)
118
+ rspec-mocks (~> 3.5.0)
119
+ rspec-core (3.5.4)
120
+ rspec-support (~> 3.5.0)
121
+ rspec-expectations (3.5.0)
122
+ diff-lcs (>= 1.2.0, < 2.0)
123
+ rspec-support (~> 3.5.0)
124
+ rspec-mocks (3.5.0)
125
+ diff-lcs (>= 1.2.0, < 2.0)
126
+ rspec-support (~> 3.5.0)
127
+ rspec-support (3.5.0)
128
+ signet (0.7.3)
129
+ addressable (~> 2.3)
130
+ faraday (~> 0.9)
131
+ jwt (~> 1.5)
132
+ multi_json (~> 1.10)
133
+ slop (3.6.0)
134
+ thor (0.19.4)
135
+ thread_safe (0.3.5)
136
+ tzinfo (1.2.2)
137
+ thread_safe (~> 0.1)
138
+ uber (0.0.15)
139
+
140
+ PLATFORMS
141
+ ruby
142
+
143
+ DEPENDENCIES
144
+ classic-api!
145
+ pry (~> 0.10)
146
+ rspec (~> 3.5)
147
+
148
+ BUNDLED WITH
149
+ 1.13.7
data/README.md ADDED
@@ -0,0 +1 @@
1
+ # Classic API
data/bin/classic ADDED
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env ruby
2
+ $:.push("lib/")
3
+ require "thor"
4
+ require "classic_api"
5
+ require "classic_api/command/base"
6
+ require "classic_api/command/images"
7
+ require "classic_api/command/users"
8
+ require "classic_api/command/main"
9
+
10
+ ClassicApi.load_config("~/.config/classic.api.yml")
11
+ ClassicApi.config.debug = !!ENV["DEBUG"]
12
+ ClassicApi.login
13
+ ClassicApi::Command::Main.start
@@ -0,0 +1,29 @@
1
+ lib = File.expand_path('../lib', __FILE__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+ require 'classic_api/version'
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "classic-api"
7
+ s.version = ClassicApi::VERSION
8
+ s.licenses = ["BSD-3-Clause"]
9
+ s.platform = Gem::Platform::RUBY
10
+ s.authors = ["Tobias Strebitzer"]
11
+ s.email = ["tobias.strebitzer@magloft.com"]
12
+ s.homepage = "https://github.com/MagLoft/classic-api"
13
+ s.summary = "Classic API Client"
14
+ s.description = "Classic API Client to communicate with Classic Server"
15
+ s.required_ruby_version = '~> 2.3'
16
+ s.required_rubygems_version = '~> 2.5'
17
+ s.add_dependency "bundler", '>= 1.13', '< 2.0'
18
+ s.add_runtime_dependency "thor", "~> 0.19"
19
+ s.add_runtime_dependency 'actionpack', '>= 4.0'
20
+ s.add_runtime_dependency 'activesupport', '>= 4.0'
21
+ s.add_runtime_dependency 'mime-types', '>= 1.6'
22
+ s.add_runtime_dependency "google-cloud-storage", "~> 0.23"
23
+ s.add_runtime_dependency "json_api_client", "~> 1.4"
24
+ s.add_development_dependency "rspec", "~> 3.5"
25
+ s.add_development_dependency "pry", "~> 0.10"
26
+ s.files = `git ls-files`.split("\n")
27
+ s.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
28
+ s.require_path = 'lib'
29
+ end
@@ -0,0 +1,37 @@
1
+ module ClassicApi
2
+ module Command
3
+ class Base < Thor
4
+ class << self
5
+ def start(given_args = ARGV, config = {})
6
+ config[:shell] ||= Thor::Base.shell.new
7
+ dispatch(nil, given_args.dup, nil, config)
8
+ rescue StandardError => e
9
+ Util::Format.show_error(e)
10
+ exit(1)
11
+ end
12
+
13
+ def handle_argument_error(command, error, args, arity) #:nodoc:
14
+ message = "#{basename} #{command.name} was called with "
15
+ message << "no arguments" if args.empty?
16
+ message << "arguments " << args.inspect unless args.empty?
17
+ Util::Format.print_error("Invalid Arguments", message)
18
+ Util::Format.print_info("USAGE", banner(command).inspect)
19
+ end
20
+
21
+ def api
22
+ ClassicApi
23
+ end
24
+ end
25
+
26
+ no_commands do
27
+ delegate :print_header, to: ClassicApi::Util::Format
28
+ delegate :print_error, to: ClassicApi::Util::Format
29
+ delegate :print_info, to: ClassicApi::Util::Format
30
+ delegate :print_success, to: ClassicApi::Util::Format
31
+ delegate :ask_input, to: ClassicApi::Util::Format
32
+ delegate :present, to: ClassicApi::Util::Format
33
+ delegate :api, to: :class
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,72 @@
1
+ module ClassicApi
2
+ module Command
3
+ class Images < Base
4
+ desc "list", "List Images"
5
+ def list
6
+ images = api.images.all
7
+ present(images)
8
+ end
9
+
10
+ desc "get ID", "Get Image"
11
+ method_option(:open, aliases: "-o", type: :boolean, default: false)
12
+ def get(id)
13
+ image = api.images.find(id)
14
+ system "open #{image.url}" if options.open
15
+ present(image)
16
+ if image.children
17
+ image.children.each do|child|
18
+ present(child, 1)
19
+ end
20
+ end
21
+ end
22
+
23
+ desc "transform ID STRATEGY [WIDTH] [HEIGHT]", "Get Image"
24
+ method_option(:open, aliases: "-o", type: :boolean, default: false)
25
+ def transform(id, strategy, width=nil, height=nil)
26
+ transformation = api.images.find(id).transform(strategy: strategy, width: width, height: height)
27
+ system "open #{transformation.url}" if options.open
28
+ present transformation
29
+ end
30
+
31
+ desc "upload FILE [PATH]", "Upload Image"
32
+ method_option(:force, aliases: "-f", type: :boolean, default: false)
33
+ method_option(:open, aliases: "-o", type: :boolean, default: false)
34
+ method_option(:transformations, aliases: "-t", type: :array, default: [])
35
+ def upload(file, path=nil)
36
+ # Validate arguments
37
+ local_path = File.absolute_path(file)
38
+ raise "File #{file} does not exist!" unless File.exists?(local_path)
39
+ path = file if path.nil?
40
+
41
+ # Create policy
42
+ policy = api.policies.create!(path: path)
43
+
44
+ # Throw override error if not forced
45
+ if policy.image.stored_at and !options.force
46
+ print_error("An image file already exists with path '#{path}'.")
47
+ print_info("USAGE", "Use --force to overwrite.")
48
+ exit(1)
49
+ end
50
+
51
+ # Upload image via policy
52
+ result = policy.upload_file(local_path)
53
+
54
+ # Prepare transforms
55
+ transformations = options.transformations.map do |transformation_string|
56
+ (strategy,width,height) = transformation_string.split("/")
57
+ {strategy: strategy, width: width&.to_i, height: height&.to_i}
58
+ end
59
+
60
+ # Update image
61
+ policy.image.update_attributes(stored_at: DateTime.now, transformations: transformations)
62
+
63
+ # Open image if requested
64
+ system "open #{policy.image.url}" if options.open
65
+
66
+ # Respond with image
67
+ print_success("Image successfully uploaded")
68
+ present policy.image
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,42 @@
1
+ module ClassicApi
2
+ module Command
3
+ class Main < Base
4
+ desc "images SUBCOMMAND ...ARGS", "manage images"
5
+ subcommand "images", Images
6
+
7
+ desc "users SUBCOMMAND ...ARGS", "manage users"
8
+ subcommand "users", Users
9
+
10
+ desc "auth [SITE] [API_KEY]", "Authenticate with Classic Server"
11
+ def auth(site=nil, api_key=nil)
12
+ print_header("AUTHENTICATE")
13
+
14
+ # Capture arguments
15
+ site = ask_input("SITE", api.config.site) if site.nil?
16
+ api_key = ask_input("API KEY", api.config.api_key) if api_key.nil?
17
+
18
+ # Validate input
19
+ if !site or !api_key
20
+ print_error("Missing SITE or API_KEY.")
21
+ exit(1)
22
+ end
23
+
24
+ # Update auth information
25
+ api.login(site, api_key)
26
+
27
+ # Validate login
28
+ user = api.users.find(:current)
29
+
30
+ # Save auth information
31
+ if user.valid?
32
+ api.save_config("~/.config/classic.api.yml")
33
+ present(user)
34
+ print_success("Authentication successful")
35
+ else
36
+ print_error("Invalid user login!")
37
+ exit(1)
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,50 @@
1
+ module ClassicApi
2
+ module Command
3
+ class Users < Base
4
+ desc "list", "List Users"
5
+ def list
6
+ users = api.users.all
7
+ present(users)
8
+ end
9
+
10
+ desc "get ID", "Get User"
11
+ def get(id)
12
+ user = api.users.find(id)
13
+ present(user)
14
+ end
15
+
16
+ desc "create", "Create User"
17
+ method_option(:name, aliases: "-n", type: :string)
18
+ method_option(:role, aliases: "-r", type: :string, default: "default")
19
+ method_option(:root_path, aliases: "-p", type: :string)
20
+ def create()
21
+ # Validate arguments
22
+ raise ArgumentError, "invalid name '#{options.name}'. May only contain lowercase and digit characters." if !(options.name =~ /^[a-z0-9]+$/)
23
+ raise ArgumentError, "invalid role '#{options.role}'. May only contain values 'admin' or 'default'." if !["admin", "default"].include?(options.role)
24
+ raise ArgumentError, "invalid root path '#{options.root_path}'. Must be a path pattern with trailing slash." if options.root_path and !(options.root_path =~ %r{\A[a-z0-9/_-]+\/\z})
25
+
26
+ # Create user
27
+ user = api.users.create!(options)
28
+ present user
29
+ end
30
+
31
+ desc "update ID", "Create User"
32
+ method_option(:name, aliases: "-n", type: :string)
33
+ method_option(:role, aliases: "-r", type: :string)
34
+ method_option(:root_path, aliases: "-p", type: :string)
35
+ def update(id)
36
+ # Validate arguments
37
+ raise ArgumentError, "invalid name '#{options.name}'. May only contain lowercase and digit characters." if options.name and !(options.name =~ /^[a-z0-9]+$/)
38
+ raise ArgumentError, "invalid role '#{options.role}'. May only contain values 'admin' or 'default'." if options.role and !["admin", "default"].include?(options.role)
39
+ raise ArgumentError, "invalid root path '#{options.root_path}'. Must be a path pattern with trailing slash." if options.root_path and !(options.root_path =~ %r{\A[a-z0-9/_-]+\/\z})
40
+
41
+ # Find user
42
+ user = api.users.find(id)
43
+
44
+ # Update user
45
+ user.update_attributes(options)
46
+ present user
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,182 @@
1
+ module ClassicApi
2
+ module Query
3
+ class Builder
4
+ attr_reader :klass
5
+ # delegate :key_formatter, to: :klass
6
+
7
+ def initialize(klass)
8
+ @klass = klass
9
+ @primary_key = nil
10
+ @pagination_params = {}
11
+ @path_params = {}
12
+ @additional_params = {}
13
+ @filters = {}
14
+ @includes = []
15
+ @orders = []
16
+ @fields = []
17
+ end
18
+
19
+ def where(conditions = {})
20
+ # pull out any path params here
21
+ @path_params.merge!(conditions.slice(*klass.prefix_params))
22
+ @filters.merge!(conditions.except(*klass.prefix_params))
23
+ self
24
+ end
25
+
26
+ def order(*args)
27
+ @orders += parse_orders(*args)
28
+ self
29
+ end
30
+
31
+ def includes(*tables)
32
+ @includes += parse_related_links(*tables)
33
+ self
34
+ end
35
+
36
+ def select(*fields)
37
+ fields = Array(fields).flatten
38
+ fields = fields.map { |i| i.to_s.split(",") }.flatten
39
+
40
+ @fields += fields.map(&:strip)
41
+
42
+ self
43
+ end
44
+
45
+ def paginate(conditions = {})
46
+ scope = self
47
+ scope = scope.page(conditions[:page]) if conditions[:page]
48
+ scope = scope.per(conditions[:per_page]) if conditions[:per_page]
49
+ scope
50
+ end
51
+
52
+ def page(number)
53
+ @pagination_params[:number] = number
54
+ self
55
+ end
56
+
57
+ def per(size)
58
+ @pagination_params[:size] = size
59
+ self
60
+ end
61
+
62
+ def with_params(more_params)
63
+ @additional_params.merge!(more_params)
64
+ self
65
+ end
66
+
67
+ def first
68
+ paginate(page: 1, per_page: 1).to_a.first
69
+ end
70
+
71
+ def last
72
+ paginate(page: 1, per_page: 1).pages.last.to_a.last
73
+ end
74
+
75
+ def build
76
+ klass.new(params)
77
+ end
78
+
79
+ def params
80
+ filter_params
81
+ .merge(pagination_params)
82
+ .merge(includes_params)
83
+ .merge(order_params)
84
+ .merge(select_params)
85
+ .merge(primary_key_params)
86
+ .merge(path_params)
87
+ .merge(additional_params)
88
+ end
89
+
90
+ def to_a
91
+ @to_a ||= find
92
+ end
93
+ alias all to_a
94
+
95
+ def find(args = {})
96
+ case args
97
+ when Hash
98
+ where(args)
99
+ klass.requestor.get(params)
100
+ else
101
+ @primary_key = args
102
+ klass.requestor.get(params).first
103
+ end
104
+ end
105
+
106
+ def method_missing(method_name, *args, &block)
107
+ to_a.send(method_name, *args, &block)
108
+ end
109
+
110
+ private
111
+
112
+ def path_params
113
+ @path_params.empty? ? {} : {path: @path_params}
114
+ end
115
+
116
+ def additional_params
117
+ @additional_params
118
+ end
119
+
120
+ def primary_key_params
121
+ return {} unless @primary_key
122
+
123
+ @primary_key.is_a?(Array) ?
124
+ {klass.primary_key.to_s.pluralize.to_sym => @primary_key.join(",")} :
125
+ {klass.primary_key => @primary_key}
126
+ end
127
+
128
+ def pagination_params
129
+ @pagination_params.empty? ? {} : {page: @pagination_params}
130
+ end
131
+
132
+ def includes_params
133
+ @includes.empty? ? {} : {include: @includes.join(",")}
134
+ end
135
+
136
+ def filter_params
137
+ @filters.empty? ? {} : {filter: @filters}
138
+ end
139
+
140
+ def order_params
141
+ @orders.empty? ? {} : {sort: @orders.join(",")}
142
+ end
143
+
144
+ def select_params
145
+ @fields.empty? ? {} : {fields: {klass.table_name => @fields.join(",")}}
146
+ end
147
+
148
+ def parse_related_links(*tables)
149
+ tables.map do |table|
150
+ case table
151
+ when Hash
152
+ table.map do |k, v|
153
+ parse_related_links(*v).map do |sub|
154
+ "#{k}.#{sub}"
155
+ end
156
+ end
157
+ when Array
158
+ table.map do |v|
159
+ parse_related_links(*v)
160
+ end
161
+ else
162
+ key_formatter.format(table)
163
+ end
164
+ end.flatten
165
+ end
166
+
167
+ def parse_orders(*args)
168
+ args.map do |arg|
169
+ case arg
170
+ when Hash
171
+ arg.map do |k, v|
172
+ operator = (v == :desc ? "-" : "")
173
+ "#{operator}#{k}"
174
+ end
175
+ else
176
+ "#{arg}"
177
+ end
178
+ end.flatten
179
+ end
180
+ end
181
+ end
182
+ end
@@ -0,0 +1,74 @@
1
+ module ClassicApi
2
+ module Query
3
+ class Requestor
4
+ extend Forwardable
5
+
6
+ def initialize(klass)
7
+ @klass = klass
8
+ end
9
+
10
+ # expects a record
11
+ def create(record)
12
+ request(:post, klass.path(record.attributes), {
13
+ data: record.as_json_api
14
+ })
15
+ end
16
+
17
+ def update(record)
18
+ request(:patch, resource_path(record.attributes), {
19
+ data: record.as_json_api
20
+ })
21
+ end
22
+
23
+ def get(params = {})
24
+ path = resource_path(params)
25
+ params.delete(klass.primary_key)
26
+ request(:get, path, params)
27
+ end
28
+
29
+ def destroy(record)
30
+ request(:delete, resource_path(record.attributes), {})
31
+ end
32
+
33
+ def linked(path)
34
+ request(:get, path, {})
35
+ end
36
+
37
+ def custom(method_name, options, params)
38
+ path = resource_path(params)
39
+ params.delete(klass.primary_key)
40
+ path = File.join(path, method_name.to_s)
41
+
42
+ request(options.fetch(:request_method, :get), path, params)
43
+ end
44
+
45
+ protected
46
+
47
+ attr_reader :klass
48
+ def_delegators :klass, :connection
49
+
50
+ def resource_path(parameters)
51
+ if resource_id = parameters[klass.primary_key]
52
+ File.join(klass.path(parameters), encoded(resource_id))
53
+ else
54
+ klass.path(parameters)
55
+ end
56
+ end
57
+
58
+ def encoded(part)
59
+ Addressable::URI.encode_component(part, Addressable::URI::CharacterClasses::UNRESERVED)
60
+ end
61
+
62
+ def request(type, path, params)
63
+ response = connection.run(type, path, params, klass.custom_headers)
64
+ if ClassicApi.config.debug
65
+ require "thor"
66
+ require "classic_api/command/base"
67
+ ClassicApi::Util::Format.print_info(response.status.to_s, response.body.to_json)
68
+ end
69
+ klass.parser.parse(klass, response)
70
+ end
71
+
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,25 @@
1
+ module ClassicApi
2
+ module Resource
3
+ class Base < JsonApiClient::Resource
4
+ self.query_builder = ClassicApi::Query::Builder
5
+ self.site = nil
6
+ self.requestor_class = ClassicApi::Query::Requestor
7
+
8
+ class << self
9
+ def create!(attributes = {})
10
+ record = create(attributes)
11
+ raise APIEntityException.new(record, record.errors) if record.errors.present?
12
+ record
13
+ end
14
+ end
15
+
16
+ def api
17
+ ClassicApi
18
+ end
19
+
20
+ def insist!
21
+
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,18 @@
1
+ module ClassicApi
2
+ module Resource
3
+ class Image < Base
4
+ property :stored_at, type: :datetime
5
+ has_many :transformations
6
+ has_many :children
7
+
8
+ def transform(options={})
9
+ api.transformations.create(
10
+ strategy: options[:strategy],
11
+ width: options[:width],
12
+ height: options[:height],
13
+ image_id: self.id
14
+ )
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,20 @@
1
+ module ClassicApi
2
+ module Resource
3
+ class Policy < Base
4
+ has_one :image
5
+
6
+ def upload_file(path)
7
+ connection = Faraday.new(url: url) do |builder|
8
+ builder.request :url_encoded
9
+ builder.response :json, :content_type => /\bjson$/
10
+ builder.adapter Faraday.default_adapter
11
+ end
12
+ response = connection.put do |req|
13
+ req.headers = headers
14
+ req.body = File.read(path)
15
+ end
16
+ (response.status == 200)
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,7 @@
1
+ module ClassicApi
2
+ module Resource
3
+ class Transformation < Base
4
+ has_one :image
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,6 @@
1
+ module ClassicApi
2
+ module Resource
3
+ class User < Base
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,121 @@
1
+ module ClassicApi
2
+ module Util
3
+ class Format
4
+ COLOR_BOLD = "\e[1m"
5
+ COLOR_RED = "\e[31m"
6
+ COLOR_RED_BG = "\e[41m"
7
+ COLOR_WHITE = "\e[97m"
8
+ COLOR_GREY = "\e[38;5;245m"
9
+ COLOR_GREY_BG = "\e[48;5;255m"
10
+ COLOR_END = "\e[0m"
11
+ COLOR_BLUE = "\e[34m"
12
+ COLOR_BLUE_BG = "\e[44m"
13
+ COLOR_GREEN = "\e[32m"
14
+ COLOR_GREEN_BG = "\e[42m"
15
+ COLOR_MAGENTA = "\e[35m"
16
+ COLOR_MAGENTA_BG = "\e[45m"
17
+ ARROW = " \e[37m⇢\e[0m "
18
+ QUOTE = "\e[38;5;15m\"\e[0m"
19
+
20
+ class << self
21
+ def show_error(e)
22
+ case e
23
+ when Errno::EPIPE
24
+ nil
25
+ when Thor::Error
26
+ print_error(e.message)
27
+ when ClassicApi::APIEntityException
28
+ print_error("Could not save #{e.entity.class.resource_name}:")
29
+ e.errors.messages.each do |key, value|
30
+ value.each do |v|
31
+ print_key_value(key, v)
32
+ end
33
+ end
34
+ when JsonApiClient::Errors::ConnectionError
35
+ print_error("Connection Error", "Could not connect to '#{e.env.url}'")
36
+ when JsonApiClient::Errors::NotFound
37
+ print_error("Not Found", e.message)
38
+ when JsonApiClient::Errors::ServerError
39
+ print_error(e.env.status.to_s, e.env.body.to_json)
40
+ when JsonApiClient::Errors::AccessDenied, JsonApiClient::Errors::NotAuthorized
41
+ print_error(e.env.reason_phrase, e.env.body["error"])
42
+ when RuntimeError, ArgumentError
43
+ print_error(e.class.name, e.message)
44
+ else
45
+ raise e
46
+ end
47
+ end
48
+
49
+ def present(entity, indent=0)
50
+ padding = " " * indent * 2
51
+ if entity == true
52
+ puts "#{padding}#{ARROW+COLOR_GREEN+COLOR_BOLD}success#{COLOR_END}"
53
+ elsif entity == false
54
+ puts "#{padding}#{ARROW+COLOR_RED+COLOR_BOLD}error#{COLOR_END}"
55
+ elsif entity.is_a?(JsonApiClient::ResultSet)
56
+ entity.each do |e|
57
+ present(e, indent)
58
+ end
59
+ else
60
+ puts "#{padding}#{(indent.zero? ? COLOR_BLUE_BG : COLOR_MAGENTA_BG)+COLOR_WHITE+COLOR_BOLD} #{entity.class.resource_name.upcase}##{entity.id} #{COLOR_END}"
61
+ entity.attributes.each do |key, value|
62
+ puts "#{padding}#{ARROW+COLOR_MAGENTA+COLOR_BOLD}#{key}:#{COLOR_END} #{format_value(value)}"
63
+ end
64
+ end
65
+ end
66
+
67
+ def print_error(title, message=nil)
68
+ if message.nil?
69
+ puts "#{COLOR_RED_BG+COLOR_BOLD+COLOR_WHITE} ERROR #{COLOR_END} #{COLOR_BOLD}#{title}#{COLOR_END}"
70
+ else
71
+ puts "#{COLOR_RED_BG+COLOR_BOLD+COLOR_WHITE} ERROR #{COLOR_END} #{COLOR_BOLD}#{title}: #{COLOR_RED}#{message}#{COLOR_END}"
72
+ end
73
+ end
74
+
75
+ def print_key_value(key, value)
76
+ puts "#{ARROW+COLOR_MAGENTA+COLOR_BOLD}#{key}:#{COLOR_END} #{value}"
77
+ end
78
+
79
+
80
+ def ask_input(label, default=nil)
81
+ if default.nil?
82
+ default_text = COLOR_WHITE+default+COLOR_END
83
+ else
84
+ default_text = COLOR_GREEN+default+COLOR_END
85
+ end
86
+ result = Thor::LineEditor.readline("#{ARROW}#{COLOR_BOLD}#{label}#{COLOR_END} #{default_text}: ")
87
+ result.strip!
88
+ if default && result.blank?
89
+ default
90
+ else
91
+ result
92
+ end
93
+ end
94
+
95
+ def print_header(title, color=COLOR_BLUE_BG)
96
+ puts "#{color+COLOR_BOLD+COLOR_WHITE} #{title} #{COLOR_END}"
97
+ end
98
+
99
+ def print_info(title, message)
100
+ puts "#{COLOR_BLUE_BG+COLOR_BOLD+COLOR_WHITE} #{title} #{COLOR_END} #{COLOR_BLUE+COLOR_BOLD}#{message}#{COLOR_END}"
101
+ end
102
+
103
+ def print_success(message)
104
+ puts "#{COLOR_GREEN_BG+COLOR_BOLD+COLOR_WHITE} SUCCESS #{COLOR_END} #{COLOR_GREEN+COLOR_BOLD}#{message}#{COLOR_END}"
105
+ end
106
+
107
+ def format_value(value)
108
+ if value.is_a?(Fixnum) or value.is_a?(Float)
109
+ COLOR_BLUE+value.to_s+COLOR_END
110
+ elsif value.is_a?(String)
111
+ QUOTE+value+QUOTE
112
+ elsif value.is_a?(DateTime)
113
+ COLOR_GREEN+value.strftime("%d.%m.%Y %H:%M")+COLOR_END
114
+ else
115
+ value
116
+ end
117
+ end
118
+ end
119
+ end
120
+ end
121
+ end
@@ -0,0 +1,3 @@
1
+ module ClassicApi
2
+ VERSION = "1.0.0"
3
+ end
@@ -0,0 +1,82 @@
1
+ require "active_support"
2
+ require "yaml"
3
+ require "json_api_client"
4
+ require "ext/json_api_client/schema/property"
5
+ require "classic_api/version"
6
+ require "classic_api/query/builder"
7
+ require "classic_api/query/requestor"
8
+ require "classic_api/util/format"
9
+ require "classic_api/resource/base"
10
+ require "classic_api/resource/user"
11
+ require "classic_api/resource/image"
12
+ require "classic_api/resource/transformation"
13
+ require "classic_api/resource/policy"
14
+
15
+ module ClassicApi
16
+ include ActiveSupport::Configurable
17
+ config_accessor :site, instance_reader: true, instrance_writer: true
18
+ config_accessor :api_key, instance_reader: true, instrance_writer: true
19
+ config_accessor :debug, instance_reader: true, instrance_writer: true
20
+
21
+ def self.load_config(path)
22
+ path = File.expand_path(path)
23
+ yaml_config = YAML.load_file(path)
24
+ config.site = yaml_config["classic"]["site"]
25
+ config.api_key = yaml_config["classic"]["api_key"]
26
+ config
27
+ end
28
+
29
+ def self.save_config(path)
30
+ path = File.expand_path(path)
31
+ FileUtils.mkdir_p(File.dirname(path)) if !File.directory?(File.dirname(path))
32
+ yaml_config = YAML.load_file(path)
33
+ yaml_config["classic"]["site"] = config.site
34
+ yaml_config["classic"]["api_key"] = config.api_key
35
+ File.open(File.expand_path(path), "w") {|file| file << YAML.dump(yaml_config)}
36
+ true
37
+ end
38
+
39
+ def self.login(site=nil, api_key=nil)
40
+ config.site = site unless site.nil?
41
+ config.api_key = api_key unless api_key.nil?
42
+ Resource::Base.site = config.site
43
+ Resource::Base.connection(true) do |c|
44
+ c.faraday.headers["X-Classic-Api-Key"] = config.api_key
45
+ end
46
+ self
47
+ end
48
+
49
+ def self.users
50
+ ClassicApi::Resource::User
51
+ end
52
+
53
+ def self.transformations
54
+ ClassicApi::Resource::Transformation
55
+ end
56
+
57
+ def self.images
58
+ ClassicApi::Resource::Image
59
+ end
60
+
61
+ def self.policies
62
+ ClassicApi::Resource::Policy
63
+ end
64
+
65
+ class ValidationError < StandardError
66
+ attr_reader :errors, :entity
67
+
68
+ def initialize(entity, errors)
69
+ @entity = entity
70
+ @errors = errors
71
+ end
72
+ end
73
+
74
+ class APIEntityException < StandardError
75
+ attr_reader :errors, :entity
76
+
77
+ def initialize(entity, errors)
78
+ @entity = entity
79
+ @errors = errors
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,31 @@
1
+ class JsonApiClient::Schema::Property
2
+ def cast(value)
3
+ return nil if value.nil?
4
+ return value if type.nil?
5
+ case type.to_sym
6
+ when :int, :integer
7
+ value.to_i
8
+ when :string
9
+ value.to_s
10
+ when :float
11
+ value.to_f
12
+ when :datetime
13
+ value.is_a?(DateTime) || nil ? value : DateTime.parse(value)
14
+ when :time
15
+ value.is_a?(Time) || nil ? value : Time.parse(value)
16
+ when :decimal
17
+ value.nil? ? nil : BigDecimal.new(value)
18
+ when :boolean
19
+ case value
20
+ when "false", "0", 0, false
21
+ false
22
+ when "true", "1", 1, true
23
+ true
24
+ else
25
+ default
26
+ end
27
+ else
28
+ value
29
+ end
30
+ end
31
+ end
metadata ADDED
@@ -0,0 +1,198 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: classic-api
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Tobias Strebitzer
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-02-06 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '1.13'
20
+ - - "<"
21
+ - !ruby/object:Gem::Version
22
+ version: '2.0'
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ version: '1.13'
30
+ - - "<"
31
+ - !ruby/object:Gem::Version
32
+ version: '2.0'
33
+ - !ruby/object:Gem::Dependency
34
+ name: thor
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '0.19'
40
+ type: :runtime
41
+ prerelease: false
42
+ version_requirements: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: '0.19'
47
+ - !ruby/object:Gem::Dependency
48
+ name: actionpack
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: '4.0'
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: '4.0'
61
+ - !ruby/object:Gem::Dependency
62
+ name: activesupport
63
+ requirement: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: '4.0'
68
+ type: :runtime
69
+ prerelease: false
70
+ version_requirements: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ version: '4.0'
75
+ - !ruby/object:Gem::Dependency
76
+ name: mime-types
77
+ requirement: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ version: '1.6'
82
+ type: :runtime
83
+ prerelease: false
84
+ version_requirements: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ version: '1.6'
89
+ - !ruby/object:Gem::Dependency
90
+ name: google-cloud-storage
91
+ requirement: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - "~>"
94
+ - !ruby/object:Gem::Version
95
+ version: '0.23'
96
+ type: :runtime
97
+ prerelease: false
98
+ version_requirements: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - "~>"
101
+ - !ruby/object:Gem::Version
102
+ version: '0.23'
103
+ - !ruby/object:Gem::Dependency
104
+ name: json_api_client
105
+ requirement: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - "~>"
108
+ - !ruby/object:Gem::Version
109
+ version: '1.4'
110
+ type: :runtime
111
+ prerelease: false
112
+ version_requirements: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - "~>"
115
+ - !ruby/object:Gem::Version
116
+ version: '1.4'
117
+ - !ruby/object:Gem::Dependency
118
+ name: rspec
119
+ requirement: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - "~>"
122
+ - !ruby/object:Gem::Version
123
+ version: '3.5'
124
+ type: :development
125
+ prerelease: false
126
+ version_requirements: !ruby/object:Gem::Requirement
127
+ requirements:
128
+ - - "~>"
129
+ - !ruby/object:Gem::Version
130
+ version: '3.5'
131
+ - !ruby/object:Gem::Dependency
132
+ name: pry
133
+ requirement: !ruby/object:Gem::Requirement
134
+ requirements:
135
+ - - "~>"
136
+ - !ruby/object:Gem::Version
137
+ version: '0.10'
138
+ type: :development
139
+ prerelease: false
140
+ version_requirements: !ruby/object:Gem::Requirement
141
+ requirements:
142
+ - - "~>"
143
+ - !ruby/object:Gem::Version
144
+ version: '0.10'
145
+ description: Classic API Client to communicate with Classic Server
146
+ email:
147
+ - tobias.strebitzer@magloft.com
148
+ executables:
149
+ - classic
150
+ extensions: []
151
+ extra_rdoc_files: []
152
+ files:
153
+ - ".gitignore"
154
+ - Gemfile
155
+ - Gemfile.lock
156
+ - README.md
157
+ - bin/classic
158
+ - classic_api.gemspec
159
+ - lib/classic_api.rb
160
+ - lib/classic_api/command/base.rb
161
+ - lib/classic_api/command/images.rb
162
+ - lib/classic_api/command/main.rb
163
+ - lib/classic_api/command/users.rb
164
+ - lib/classic_api/query/builder.rb
165
+ - lib/classic_api/query/requestor.rb
166
+ - lib/classic_api/resource/base.rb
167
+ - lib/classic_api/resource/image.rb
168
+ - lib/classic_api/resource/policy.rb
169
+ - lib/classic_api/resource/transformation.rb
170
+ - lib/classic_api/resource/user.rb
171
+ - lib/classic_api/util/format.rb
172
+ - lib/classic_api/version.rb
173
+ - lib/ext/json_api_client/schema/property.rb
174
+ homepage: https://github.com/MagLoft/classic-api
175
+ licenses:
176
+ - BSD-3-Clause
177
+ metadata: {}
178
+ post_install_message:
179
+ rdoc_options: []
180
+ require_paths:
181
+ - lib
182
+ required_ruby_version: !ruby/object:Gem::Requirement
183
+ requirements:
184
+ - - "~>"
185
+ - !ruby/object:Gem::Version
186
+ version: '2.3'
187
+ required_rubygems_version: !ruby/object:Gem::Requirement
188
+ requirements:
189
+ - - "~>"
190
+ - !ruby/object:Gem::Version
191
+ version: '2.5'
192
+ requirements: []
193
+ rubyforge_project:
194
+ rubygems_version: 2.5.2
195
+ signing_key:
196
+ specification_version: 4
197
+ summary: Classic API Client
198
+ test_files: []