Feature #3818 ยป 3818-try1.patch
apps/workbench/app/models/arvados_base.rb | ||
---|---|---|
62 | 62 |
schema = arvados_api_client.discovery[:schemas][self.to_s.to_sym] |
63 | 63 |
return @columns if schema.nil? |
64 | 64 |
schema[:properties].each do |k, coldef| |
65 |
case k |
|
66 |
when :etag, :kind |
|
65 |
if coldef[:readonly] or (k == :etag) or (k == :kind) |
|
67 | 66 |
attr_reader k |
68 | 67 |
else |
69 | 68 |
if coldef[:type] == coldef[:type].downcase |
... | ... | |
329 | 328 |
(current_user.is_admin or |
330 | 329 |
current_user.uuid == self.owner_uuid or |
331 | 330 |
new_record? or |
332 |
(respond_to?(:writable_by) ?
|
|
331 |
((respond_to?(:writable_by) and !writable_by.nil?) ?
|
|
333 | 332 |
writable_by.include?(current_user.uuid) : |
334 | 333 |
(ArvadosBase.find(owner_uuid).writable_by.include? current_user.uuid rescue false)))) or false |
335 | 334 |
end |
services/api/app/controllers/arvados/v1/schema_controller.rb | ||
---|---|---|
95 | 95 |
next |
96 | 96 |
end |
97 | 97 |
object_properties = {} |
98 |
k.columns.
|
|
99 |
select { |col| col.name != 'id' }.
|
|
100 |
collect do |col|
|
|
101 |
if k.serialized_attributes.has_key? col.name
|
|
102 |
object_properties[col.name] = {
|
|
103 |
type: k.serialized_attributes[col.name].object_class.to_s
|
|
104 |
}
|
|
98 |
attr_types = k.attributes_types
|
|
99 |
k.all_api_accessible_attributes.each_key.map(&:to_s).each do |attr_name|
|
|
100 |
attr_schema = {}
|
|
101 |
if attr_types.include?(attr_name)
|
|
102 |
attr_schema[:type] = attr_types[attr_name]
|
|
103 |
elsif k.serialized_attributes.include?(attr_name)
|
|
104 |
attr_schema[:type] = k.serialized_attributes[attr_name].object_class.to_s
|
|
105 | 105 |
else |
106 |
object_properties[col.name] = { |
|
107 |
type: col.type |
|
108 |
} |
|
106 |
attr_schema[:type] = k.columns_hash[attr_name].type |
|
107 |
end |
|
108 |
unless k.columns_hash.include?(attr_name) |
|
109 |
attr_schema[:readonly] = true |
|
109 | 110 |
end |
111 |
object_properties[attr_name] = attr_schema |
|
110 | 112 |
end |
111 | 113 |
discovery[:schemas][k.to_s + 'List'] = { |
112 | 114 |
id: k.to_s + 'List', |
services/api/app/models/api_client_authorization.rb | ||
---|---|---|
22 | 22 | |
23 | 23 |
UNLOGGED_CHANGES = ['last_used_at', 'last_used_by_ip_address', 'updated_at'] |
24 | 24 | |
25 |
def self.attributes_types |
|
26 |
super.merge({ "uuid" => columns_hash["api_token"].type, |
|
27 |
"owner_uuid" => "string", |
|
28 |
"modified_by_client_uuid" => "string", |
|
29 |
"modified_by_user_uuid" => "string", |
|
30 |
"modified_at" => "timestamp", |
|
31 |
}) |
|
32 |
end |
|
33 | ||
25 | 34 |
def assign_random_api_token |
26 | 35 |
self.api_token ||= rand(2**256).to_s(36) |
27 | 36 |
end |
services/api/app/models/arvados_model.rb | ||
---|---|---|
81 | 81 |
self.columns.select { |col| col.name == attr.to_s }.first |
82 | 82 |
end |
83 | 83 | |
84 |
def self.all_api_accessible_attributes |
|
85 |
# Return a hash that maps attributes accessible through the API to the |
|
86 |
# methods that generate them (all symbols). |
|
87 |
# The result includes attributes accessible through any API template. |
|
88 |
# If the same attribute is generated by different methods in different |
|
89 |
# templates, the value for that attribute key is undefined. |
|
90 |
result = {} |
|
91 |
methods.grep(/^api_accessible_\w+$/).each do |method_name| |
|
92 |
next if method_name == :api_accessible_attributes |
|
93 |
result.merge!(send(method_name)) |
|
94 |
end |
|
95 |
result |
|
96 |
end |
|
97 | ||
98 |
def self.attributes_types |
|
99 |
# Return a hash that maps attribute name strings to type strings. |
|
100 |
# Subclasses should override this to provide type information for the |
|
101 |
# discovery document for any attributes that aren't database-backed. |
|
102 |
{ "etag" => "string", |
|
103 |
"href" => "string", |
|
104 |
"kind" => "string", |
|
105 |
} |
|
106 |
end |
|
107 | ||
84 | 108 |
def self.attributes_required_columns |
85 | 109 |
# This method returns a hash. Each key is the name of an API attribute, |
86 | 110 |
# and it's mapped to a list of database columns that must be fetched |
... | ... | |
91 | 115 |
# specific columns from the database. |
92 | 116 |
all_columns = columns.map(&:name) |
93 | 117 |
api_column_map = Hash.new { |hash, key| hash[key] = [] } |
94 |
methods.grep(/^api_accessible_\w+$/).each do |method_name| |
|
95 |
next if method_name == :api_accessible_attributes |
|
96 |
send(method_name).each_pair do |api_attr_name, col_name| |
|
97 |
col_name = col_name.to_s |
|
98 |
if all_columns.include?(col_name) |
|
99 |
api_column_map[api_attr_name.to_s] |= [col_name] |
|
100 |
end |
|
118 |
all_api_accessible_attributes.each_pair do |api_attr_name, source_name| |
|
119 |
source_name = source_name.to_s |
|
120 |
if all_columns.include?(source_name) |
|
121 |
api_column_map[api_attr_name.to_s] |= [source_name] |
|
101 | 122 |
end |
102 | 123 |
end |
103 | 124 |
api_column_map |
services/api/app/models/group.rb | ||
---|---|---|
16 | 16 |
t.add :writable_by |
17 | 17 |
end |
18 | 18 | |
19 |
def self.attributes_types |
|
20 |
super.merge({"writable_by" => "Array"}) |
|
21 |
end |
|
22 | ||
19 | 23 |
def maybe_invalidate_permissions_cache |
20 | 24 |
if uuid_changed? or owner_uuid_changed? |
21 | 25 |
# This can change users' permissions on other groups as well as |
services/api/app/models/job.rb | ||
---|---|---|
62 | 62 |
(Complete = 'Complete'), |
63 | 63 |
] |
64 | 64 | |
65 |
def self.attributes_types |
|
66 |
super.merge({ "queue_position" => "integer", |
|
67 |
"node_uuids" => "Array", |
|
68 |
}) |
|
69 |
end |
|
70 | ||
65 | 71 |
def assert_finished |
66 | 72 |
update_attributes(finished_at: finished_at || db_current_time, |
67 | 73 |
success: success.nil? ? false : success, |
services/api/app/models/keep_disk.rb | ||
---|---|---|
23 | 23 |
t.add :ping_secret |
24 | 24 |
end |
25 | 25 | |
26 |
def self.attributes_types |
|
27 |
proxy_attrs = {} |
|
28 |
%w(service_host service_port service_ssl_flag).each do |attr_name| |
|
29 |
proxy_attrs[attr_name] = KeepService.columns_hash[attr_name].type |
|
30 |
end |
|
31 |
super.merge(proxy_attrs) |
|
32 |
end |
|
33 | ||
26 | 34 |
def foreign_key_attributes |
27 | 35 |
super.reject { |a| a == "filesystem_uuid" } |
28 | 36 |
end |
services/api/app/models/link.rb | ||
---|---|---|
21 | 21 |
t.add :properties |
22 | 22 |
end |
23 | 23 | |
24 |
def self.attributes_types |
|
25 |
result = super |
|
26 |
kind_type = result["kind"] |
|
27 |
result.merge({"head_kind" => kind_type, "tail_kind" => kind_type}) |
|
28 |
end |
|
29 | ||
24 | 30 |
def properties |
25 | 31 |
@properties ||= Hash.new |
26 | 32 |
super |
services/api/app/models/log.rb | ||
---|---|---|
18 | 18 |
t.add :properties |
19 | 19 |
end |
20 | 20 | |
21 |
def self.attributes_types |
|
22 |
super.merge({"object_kind" => "string"}) |
|
23 |
end |
|
24 | ||
21 | 25 |
def object_kind |
22 | 26 |
if k = ArvadosModel::resource_class_for_uuid(object_uuid) |
23 | 27 |
k.kind |
services/api/app/models/node.rb | ||
---|---|---|
39 | 39 |
t.add lambda { |x| @@nameservers }, :as => :nameservers |
40 | 40 |
end |
41 | 41 | |
42 |
def self.attributes_types |
|
43 |
super.merge({ "crunch_worker_state" => "string", |
|
44 |
"nameservers" => "Array", |
|
45 |
"status" => "string", |
|
46 |
}) |
|
47 |
end |
|
48 | ||
42 | 49 |
def domain |
43 | 50 |
super || @@domain |
44 | 51 |
end |
services/api/app/models/repository.rb | ||
---|---|---|
19 | 19 |
super.merge({"push_url" => ["name"], "fetch_url" => ["name"]}) |
20 | 20 |
end |
21 | 21 | |
22 |
def self.attributes_types |
|
23 |
super.merge({"fetch_url" => "string", "push_url" => "string"}) |
|
24 |
end |
|
25 | ||
22 | 26 |
def push_url |
23 | 27 |
"git@git.%s.arvadosapi.com:%s.git" % [Rails.configuration.uuid_prefix, name] |
24 | 28 |
end |
services/api/app/models/user.rb | ||
---|---|---|
57 | 57 | |
58 | 58 |
ALL_PERMISSIONS = {read: true, write: true, manage: true} |
59 | 59 | |
60 |
def self.attributes_types |
|
61 |
super.merge({ "full_name" => "string", |
|
62 |
"is_invited" => "boolean", |
|
63 |
"writable_by" => "Array", |
|
64 |
}) |
|
65 |
end |
|
66 | ||
60 | 67 |
def full_name |
61 | 68 |
"#{first_name} #{last_name}".strip |
62 | 69 |
end |
services/api/test/functional/arvados/v1/schema_controller_test.rb | ||
---|---|---|
20 | 20 |
assert_includes discovery_doc, 'defaultTrashLifetime' |
21 | 21 |
assert_equal discovery_doc['defaultTrashLifetime'], Rails.application.config.default_trash_lifetime |
22 | 22 |
end |
23 | ||
24 |
test "discovery document includes types for all API-accessible attributes" do |
|
25 |
get :index |
|
26 |
assert_response :success |
|
27 |
schemas = json_response["schemas"] |
|
28 |
ActiveRecord::Base.descendants.reject(&:abstract_class?).each do |klass| |
|
29 |
klass_name = klass.to_s |
|
30 |
next if klass_name.start_with?("Commit") |
|
31 |
klass_schema = schemas[klass_name].andand["properties"] |
|
32 |
refute_nil(klass_schema, "no schema for #{klass}") |
|
33 |
attr_names = klass.methods. |
|
34 |
grep(/^api_accessible_\w+$/). |
|
35 |
flat_map do |method_name| |
|
36 |
if method_name == :api_accessible_attributes |
|
37 |
[] |
|
38 |
else |
|
39 |
klass.send(method_name).keys |
|
40 |
end |
|
41 |
end.uniq.map(&:to_s).sort |
|
42 |
schema_keys = klass_schema.keys.sort |
|
43 |
assert_equal(attr_names, schema_keys, |
|
44 |
"#{klass_name} has wrong attributes list") |
|
45 |
assert_empty(klass_schema.keys. |
|
46 |
select { |key| klass_schema[key]["type"].nil? }, |
|
47 |
"#{klass_name} attributes missing type information") |
|
48 |
writable_attrs = attr_names.reject { |n| klass.columns_hash[n].nil? } |
|
49 |
assert_empty(writable_attrs.select { |k| klass_schema[k]["readonly"] }, |
|
50 |
"writable attribute(s) marked readonly") |
|
51 |
readonly_attrs = attr_names - writable_attrs |
|
52 |
assert_empty(readonly_attrs.reject { |k| klass_schema[k]["readonly"] }, |
|
53 |
"readonly attribute(s) not marked readonly") |
|
54 |
end |
|
55 |
end |
|
23 | 56 |
end |