classic-api 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml 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: []