sp-duh 2.0.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +661 -0
- data/README.md +2 -0
- data/Rakefile +32 -0
- data/config/i18n/i18n.xlsx +0 -0
- data/config/initializers/active_record/connection_adapters_postgre_sql_adapter.rb +165 -0
- data/config/initializers/active_record/migration_without_transaction.rb +4 -0
- data/config/initializers/active_record/migrator.rb +34 -0
- data/config/initializers/rails/generators.rb +13 -0
- data/config/jsonapi/settings.yml +14 -0
- data/config/locales/pt.yml +15 -0
- data/lib/generators/accounting_migration/accounting_migration_generator.rb +10 -0
- data/lib/generators/accounting_migration/templates/migration.rb +42 -0
- data/lib/generators/accounting_payroll_migration/accounting_payroll_migration_generator.rb +10 -0
- data/lib/generators/accounting_payroll_migration/templates/migration.rb +73 -0
- data/lib/generators/sharded_migration/sharded_migration_generator.rb +10 -0
- data/lib/generators/sharded_migration/templates/migration.rb +45 -0
- data/lib/sp-duh.rb +32 -0
- data/lib/sp/duh.rb +180 -0
- data/lib/sp/duh/adapters/pg/text_decoder/json.rb +15 -0
- data/lib/sp/duh/adapters/pg/text_encoder/json.rb +15 -0
- data/lib/sp/duh/db/transfer/backup.rb +71 -0
- data/lib/sp/duh/db/transfer/restore.rb +89 -0
- data/lib/sp/duh/engine.rb +35 -0
- data/lib/sp/duh/exceptions.rb +70 -0
- data/lib/sp/duh/i18n/excel_loader.rb +26 -0
- data/lib/sp/duh/jsonapi/adapters/base.rb +168 -0
- data/lib/sp/duh/jsonapi/adapters/db.rb +36 -0
- data/lib/sp/duh/jsonapi/adapters/raw_db.rb +77 -0
- data/lib/sp/duh/jsonapi/configuration.rb +167 -0
- data/lib/sp/duh/jsonapi/doc/apidoc_documentation_format_generator.rb +286 -0
- data/lib/sp/duh/jsonapi/doc/generator.rb +32 -0
- data/lib/sp/duh/jsonapi/doc/schema_catalog_helper.rb +97 -0
- data/lib/sp/duh/jsonapi/doc/victor_pinus_metadata_format_parser.rb +374 -0
- data/lib/sp/duh/jsonapi/exceptions.rb +56 -0
- data/lib/sp/duh/jsonapi/model/base.rb +25 -0
- data/lib/sp/duh/jsonapi/model/concerns/attributes.rb +94 -0
- data/lib/sp/duh/jsonapi/model/concerns/model.rb +42 -0
- data/lib/sp/duh/jsonapi/model/concerns/persistence.rb +221 -0
- data/lib/sp/duh/jsonapi/model/concerns/serialization.rb +59 -0
- data/lib/sp/duh/jsonapi/parameters.rb +44 -0
- data/lib/sp/duh/jsonapi/resource_publisher.rb +28 -0
- data/lib/sp/duh/jsonapi/service.rb +110 -0
- data/lib/sp/duh/migrations.rb +47 -0
- data/lib/sp/duh/migrations/migrator.rb +41 -0
- data/lib/sp/duh/repl.rb +193 -0
- data/lib/sp/duh/version.rb +25 -0
- data/lib/tasks/db_utils.rake +98 -0
- data/lib/tasks/doc.rake +27 -0
- data/lib/tasks/i18n.rake +23 -0
- data/lib/tasks/oauth.rake +29 -0
- data/lib/tasks/transfer.rake +48 -0
- data/lib/tasks/xls2jrxml.rake +15 -0
- data/test/jsonapi/server.rb +67 -0
- data/test/tasks/test.rake +10 -0
- metadata +170 -0
@@ -0,0 +1,56 @@
|
|
1
|
+
module SP
|
2
|
+
module Duh
|
3
|
+
module JSONAPI
|
4
|
+
module Exceptions
|
5
|
+
|
6
|
+
# JSONAPI service and configuration errors
|
7
|
+
|
8
|
+
class ServiceSetupError < SP::Duh::Exceptions::GenericError ; ; end
|
9
|
+
class ServiceProtocolError < SP::Duh::Exceptions::GenericDetailedError ; ; end
|
10
|
+
class InvalidResourceConfigurationError < SP::Duh::Exceptions::GenericDetailedError ; ; end
|
11
|
+
class InvalidResourcePublisherError < SP::Duh::Exceptions::GenericDetailedError ; ; end
|
12
|
+
class DuplicateResourceError < SP::Duh::Exceptions::GenericDetailedError ; ; end
|
13
|
+
class SaveConfigurationError < SP::Duh::Exceptions::GenericError ; ; end
|
14
|
+
class InvalidJSONAPIKeyError < SP::Duh::Exceptions::GenericDetailedError ; ; end
|
15
|
+
|
16
|
+
# JSONAPI model querying errors
|
17
|
+
|
18
|
+
class GenericModelError < SP::Duh::Exceptions::GenericError
|
19
|
+
|
20
|
+
attr_reader :id
|
21
|
+
attr_reader :status
|
22
|
+
attr_reader :result
|
23
|
+
|
24
|
+
def initialize(result, nested = $!)
|
25
|
+
@result = result
|
26
|
+
errors = get_result_errors()
|
27
|
+
@status = (errors.map { |error| error[:status].to_i }.max) || 403
|
28
|
+
message = errors.first[:detail]
|
29
|
+
super(message, nested)
|
30
|
+
end
|
31
|
+
|
32
|
+
def internal_error
|
33
|
+
errors = get_result_errors()
|
34
|
+
if errors.length != 1
|
35
|
+
@result.to_json
|
36
|
+
else
|
37
|
+
errors.first[:meta]['internal-error'] if errors.first[:meta]
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def inspect()
|
42
|
+
description = super()
|
43
|
+
description = description + " (#{internal_error})" if internal_error
|
44
|
+
description
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def get_result_errors() ; (result.is_a?(Hash) ? result : HashWithIndifferentAccess.new(JSON.parse(result)))[:errors] ; end
|
50
|
+
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'sp/duh/jsonapi/model/concerns/model'
|
2
|
+
|
3
|
+
module SP
|
4
|
+
module Duh
|
5
|
+
module JSONAPI
|
6
|
+
module Model
|
7
|
+
|
8
|
+
class Base
|
9
|
+
include ::ActiveRecord::Validations
|
10
|
+
include Concerns::Model
|
11
|
+
|
12
|
+
def self.inherited(child)
|
13
|
+
child.resource_name = child.name.demodulize.underscore.pluralize
|
14
|
+
child.autogenerated_id = true
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.i18n_scope
|
18
|
+
:activerecord
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
module SP
|
2
|
+
module Duh
|
3
|
+
module JSONAPI
|
4
|
+
module Model
|
5
|
+
module Concerns
|
6
|
+
module Attributes
|
7
|
+
extend ::ActiveSupport::Concern
|
8
|
+
|
9
|
+
included do
|
10
|
+
end
|
11
|
+
|
12
|
+
module ClassMethods
|
13
|
+
|
14
|
+
def attributes
|
15
|
+
if !@attributes && superclass.respond_to?(:attributes)
|
16
|
+
@attributes = []
|
17
|
+
@attributes += superclass.attributes
|
18
|
+
end
|
19
|
+
@attributes = [] if !@attributes
|
20
|
+
@attributes
|
21
|
+
end
|
22
|
+
|
23
|
+
def attr_accessible(name)
|
24
|
+
attributes << name if !attributes.include?(name)
|
25
|
+
attr_accessor name
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def initialize(new_attributes = nil)
|
30
|
+
assign_attributes(new_attributes) if new_attributes
|
31
|
+
yield self if block_given?
|
32
|
+
end
|
33
|
+
|
34
|
+
def attributes=(new_attributes)
|
35
|
+
return unless new_attributes.is_a?(Hash)
|
36
|
+
assign_attributes(new_attributes)
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def assign_attributes(new_attributes)
|
42
|
+
return if new_attributes.blank?
|
43
|
+
|
44
|
+
new_attributes = new_attributes.stringify_keys
|
45
|
+
nested_parameter_attributes = []
|
46
|
+
|
47
|
+
new_attributes.each do |k, v|
|
48
|
+
if respond_to?("#{k}=")
|
49
|
+
if v.is_a?(Hash)
|
50
|
+
nested_parameter_attributes << [ k, v ]
|
51
|
+
else
|
52
|
+
send("#{k}=", v)
|
53
|
+
end
|
54
|
+
# else
|
55
|
+
# raise(ActiveRecord::UnknownAttributeError, "unknown attribute: #{k}")
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# Assign any deferred nested attributes after the base attributes have been set
|
60
|
+
nested_parameter_attributes.each do |k,v|
|
61
|
+
send("#{k}=", v)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# Returns an <tt>#inspect</tt>-like string for the value of the
|
66
|
+
# attribute +attr_name+. String attributes are truncated upto 50
|
67
|
+
# characters, and Date and Time attributes are returned in the
|
68
|
+
# <tt>:db</tt> format. Other attributes return the value of
|
69
|
+
# <tt>#inspect</tt> without modification.
|
70
|
+
#
|
71
|
+
# person = Person.create!(:name => "David Heinemeier Hansson " * 3)
|
72
|
+
#
|
73
|
+
# person.attribute_for_inspect(:name)
|
74
|
+
# # => '"David Heinemeier Hansson David Heinemeier Hansson D..."'
|
75
|
+
#
|
76
|
+
# person.attribute_for_inspect(:created_at)
|
77
|
+
# # => '"2009-01-12 04:48:57"'
|
78
|
+
def attribute_for_inspect(name)
|
79
|
+
value = self.send(name)
|
80
|
+
if value.is_a?(String) && value.length > 50
|
81
|
+
"#{value[0..50]}...".inspect
|
82
|
+
elsif value.is_a?(Date) || value.is_a?(Time)
|
83
|
+
%("#{value.to_s(:db)}")
|
84
|
+
else
|
85
|
+
value.inspect
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'active_support'
|
2
|
+
require 'active_record'
|
3
|
+
|
4
|
+
require 'sp/duh/jsonapi/model/concerns/attributes'
|
5
|
+
require 'sp/duh/jsonapi/model/concerns/serialization'
|
6
|
+
require 'sp/duh/jsonapi/model/concerns/persistence'
|
7
|
+
|
8
|
+
module SP
|
9
|
+
module Duh
|
10
|
+
module JSONAPI
|
11
|
+
module Model
|
12
|
+
module Concerns
|
13
|
+
module Model
|
14
|
+
extend ::ActiveSupport::Concern
|
15
|
+
|
16
|
+
include Attributes
|
17
|
+
include Serialization
|
18
|
+
include Persistence
|
19
|
+
|
20
|
+
included do
|
21
|
+
end
|
22
|
+
|
23
|
+
module ClassMethods
|
24
|
+
|
25
|
+
def inspect
|
26
|
+
"#{super}(#{self.attributes.join(', ')})"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# Returns the contents of the record as a nicely formatted string.
|
31
|
+
def inspect
|
32
|
+
# attrs = self.class.attributes
|
33
|
+
inspection = self.class.attributes.collect { |name| "#{name}: #{attribute_for_inspect(name)}" }.compact.join(", ")
|
34
|
+
"#<#{self.class} #{inspection}>"
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,221 @@
|
|
1
|
+
module SP
|
2
|
+
module Duh
|
3
|
+
module JSONAPI
|
4
|
+
module Model
|
5
|
+
module Concerns
|
6
|
+
module Persistence
|
7
|
+
extend ::ActiveSupport::Concern
|
8
|
+
|
9
|
+
included do
|
10
|
+
|
11
|
+
# Idem for data adapter configuration...
|
12
|
+
# In a similar way to ActiveRecord::Base.connection, the adapter should be defined at the base level and is inherited by all subclasses
|
13
|
+
class_attribute :adapter, instance_reader: false, instance_writer: false
|
14
|
+
|
15
|
+
self.autogenerated_id = true
|
16
|
+
|
17
|
+
attr_accessible :id
|
18
|
+
end
|
19
|
+
|
20
|
+
module ClassMethods
|
21
|
+
|
22
|
+
# Define resource configuration accessors at the class (and subclass) level (static).
|
23
|
+
# These attribute values are NOT inherited by subclasses, each subclass MUST define their own.
|
24
|
+
# Instances can access these attributes at the class level only.
|
25
|
+
attr_accessor :resource_name
|
26
|
+
attr_accessor :autogenerated_id
|
27
|
+
|
28
|
+
def find!(id, conditions = nil) ; get(id, conditions) ; end
|
29
|
+
|
30
|
+
def find_explicit!(exp_accounting_schema, exp_accounting_prefix, id, conditions = nil)
|
31
|
+
get_explicit(exp_accounting_schema, exp_accounting_prefix, id, conditions)
|
32
|
+
end
|
33
|
+
|
34
|
+
def find(id, conditions = nil)
|
35
|
+
begin
|
36
|
+
get(id, conditions)
|
37
|
+
rescue Exception => e
|
38
|
+
return nil
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def query!(condition) ; get_all(condition) ; end
|
43
|
+
def query_explicit!(exp_accounting_schema, exp_accounting_prefix, condition) ; get_all_explicit(exp_accounting_schema, exp_accounting_prefix, condition) ; end
|
44
|
+
|
45
|
+
def query(condition)
|
46
|
+
begin
|
47
|
+
get_all(condition)
|
48
|
+
rescue Exception => e
|
49
|
+
nil
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def first!(condition = "")
|
54
|
+
condition += (condition.blank? ? "" : "&") + "page[size]=1"
|
55
|
+
get_all(condition).first
|
56
|
+
end
|
57
|
+
|
58
|
+
def first(condition = "")
|
59
|
+
begin
|
60
|
+
condition += (condition.blank? ? "" : "&") + "page[size]=1"
|
61
|
+
get_all(condition).first
|
62
|
+
rescue Exception => e
|
63
|
+
nil
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def all! ; get_all("") ; end
|
68
|
+
|
69
|
+
def all
|
70
|
+
begin
|
71
|
+
get_all("")
|
72
|
+
rescue Exception => e
|
73
|
+
nil
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
private
|
78
|
+
|
79
|
+
def get_explicit(exp_accounting_schema, exp_accounting_prefix, id, conditions = nil)
|
80
|
+
result = self.adapter.get_explicit!(exp_accounting_schema, exp_accounting_prefix, "#{self.resource_name}/#{id.to_s}", conditions)
|
81
|
+
jsonapi_result_to_instance(result[:data], result)
|
82
|
+
end
|
83
|
+
|
84
|
+
def get(id, conditions = nil)
|
85
|
+
result = self.adapter.get("#{self.resource_name}/#{id.to_s}", conditions)
|
86
|
+
jsonapi_result_to_instance(result[:data], result)
|
87
|
+
end
|
88
|
+
|
89
|
+
def get_all(condition)
|
90
|
+
got = []
|
91
|
+
result = self.adapter.get(self.resource_name, condition)
|
92
|
+
if result
|
93
|
+
got = result[:data].map do |item|
|
94
|
+
data = { data: item }
|
95
|
+
data.merge(included: result[:included]) if result[:included]
|
96
|
+
jsonapi_result_to_instance(item, data)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
got
|
100
|
+
end
|
101
|
+
|
102
|
+
def get_all_explicit(exp_accounting_schema, exp_accounting_prefix, condition)
|
103
|
+
got = []
|
104
|
+
result = self.adapter.get_explicit!(exp_accounting_schema, exp_accounting_prefix, self.resource_name, condition)
|
105
|
+
if result
|
106
|
+
got = result[:data].map do |item|
|
107
|
+
data = { data: item }
|
108
|
+
data.merge(included: result[:included]) if result[:included]
|
109
|
+
jsonapi_result_to_instance(item, data)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
got
|
113
|
+
end
|
114
|
+
|
115
|
+
def jsonapi_result_to_instance(result, data)
|
116
|
+
if result
|
117
|
+
instance = self.new(result.merge(result[:attributes]).except(:attributes))
|
118
|
+
instance.send :_data=, data
|
119
|
+
end
|
120
|
+
instance
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
# Instance methods
|
125
|
+
|
126
|
+
def new_record?
|
127
|
+
if self.class.autogenerated_id || self.id.nil?
|
128
|
+
self.id.nil?
|
129
|
+
else
|
130
|
+
self.class.find(self.id).nil?
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
def save!
|
135
|
+
if new_record?
|
136
|
+
create!
|
137
|
+
else
|
138
|
+
update!
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
def save_explicit!(exp_accounting_schema, exp_accounting_prefix)
|
143
|
+
if new_record?
|
144
|
+
create!(exp_accounting_schema, exp_accounting_prefix)
|
145
|
+
else
|
146
|
+
update!(exp_accounting_schema, exp_accounting_prefix)
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
def destroy!
|
151
|
+
if !new_record?
|
152
|
+
self.class.adapter.delete(path_for_id)
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
def destroy_explicit!(exp_accounting_schema, exp_accounting_prefix)
|
157
|
+
if !new_record?
|
158
|
+
self.class.adapter.delete_explicit!(exp_accounting_schema, exp_accounting_prefix, path_for_id)
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
alias :delete! :destroy!
|
163
|
+
|
164
|
+
def create!(exp_accounting_schema = nil, exp_accounting_prefix = nil)
|
165
|
+
if self.class.autogenerated_id
|
166
|
+
params = {
|
167
|
+
data: {
|
168
|
+
type: self.class.resource_name,
|
169
|
+
attributes: get_persistent_json.reject { |k,v| k == :id || v.nil? }
|
170
|
+
}
|
171
|
+
}
|
172
|
+
else
|
173
|
+
params = {
|
174
|
+
data: {
|
175
|
+
type: self.class.resource_name,
|
176
|
+
attributes: get_persistent_json.reject { |k,v| v.nil? }
|
177
|
+
}
|
178
|
+
}
|
179
|
+
end
|
180
|
+
result = if !exp_accounting_schema.blank? || !exp_accounting_prefix.blank?
|
181
|
+
self.class.adapter.post_explicit!(exp_accounting_schema, exp_accounting_prefix, self.class.resource_name, params)
|
182
|
+
else
|
183
|
+
self.class.adapter.post(self.class.resource_name, params)
|
184
|
+
end
|
185
|
+
# Set the id to the newly created id
|
186
|
+
self.id = result[:data][:id]
|
187
|
+
end
|
188
|
+
|
189
|
+
def update!(exp_accounting_schema = nil, exp_accounting_prefix = nil)
|
190
|
+
params = {
|
191
|
+
data: {
|
192
|
+
type: self.class.resource_name,
|
193
|
+
id: self.id.to_s,
|
194
|
+
attributes: get_persistent_json.reject { |k,v| k == :id }
|
195
|
+
}
|
196
|
+
}
|
197
|
+
result = if !exp_accounting_schema.blank? || !exp_accounting_prefix.blank?
|
198
|
+
self.class.adapter.patch_explicit!(exp_accounting_schema, exp_accounting_prefix, path_for_id, params)
|
199
|
+
else
|
200
|
+
self.class.adapter.patch(path_for_id, params)
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
def get_persistent_json
|
205
|
+
as_json.reject { |k| !k.in?(self.class.attributes) }
|
206
|
+
end
|
207
|
+
|
208
|
+
protected
|
209
|
+
|
210
|
+
attr_accessor :_data
|
211
|
+
|
212
|
+
private
|
213
|
+
|
214
|
+
def path_for_id ; "#{self.class.resource_name}/#{self.id.to_s}" ; end
|
215
|
+
|
216
|
+
end
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module SP
|
2
|
+
module Duh
|
3
|
+
module JSONAPI
|
4
|
+
module Model
|
5
|
+
module Concerns
|
6
|
+
module Serialization
|
7
|
+
extend ::ActiveSupport::Concern
|
8
|
+
|
9
|
+
included do
|
10
|
+
end
|
11
|
+
|
12
|
+
def as_json(options = {})
|
13
|
+
root = ActiveRecord::Base.include_root_in_json
|
14
|
+
root = options[:root] if options.try(:key?, :root)
|
15
|
+
if root
|
16
|
+
root = self.class.name.underscore.gsub('/','_').to_sym
|
17
|
+
{ root => serializable_hash(options) }
|
18
|
+
else
|
19
|
+
serializable_hash(options)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def from_json(json)
|
24
|
+
root = ActiveRecord::Base.include_root_in_json
|
25
|
+
hash = ActiveSupport::JSON.decode(json)
|
26
|
+
hash = hash.values.first if root
|
27
|
+
self.attributes = hash
|
28
|
+
self
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
alias :read_attribute_for_serialization :send
|
34
|
+
|
35
|
+
def serializable_hash(options = {})
|
36
|
+
|
37
|
+
attribute_names = self.class.attributes.sort
|
38
|
+
if only = options[:only]
|
39
|
+
attribute_names &= Array.wrap(only).map(&:to_s)
|
40
|
+
elsif except = options[:except]
|
41
|
+
attribute_names -= Array.wrap(except).map(&:to_s)
|
42
|
+
end
|
43
|
+
|
44
|
+
hash = {}
|
45
|
+
attribute_names.each { |n| hash[n] = read_attribute_for_serialization(n) }
|
46
|
+
|
47
|
+
method_names = Array.wrap(options[:methods]).select { |n| respond_to?(n) }
|
48
|
+
method_names.each { |n| hash[n] = send(n) }
|
49
|
+
|
50
|
+
hash
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|