[arvados] created: 2.7.0-6627-g55009aa6c2
git repository hosting
git at public.arvados.org
Thu May 30 15:23:49 UTC 2024
at 55009aa6c287dc7ef98e68d2225a3469a601c674 (commit)
commit 55009aa6c287dc7ef98e68d2225a3469a601c674
Author: Tom Clegg <tom at curii.com>
Date: Thu May 30 11:23:16 2024 -0400
20640: Add computed permissions API.
Arvados-DCO-1.1-Signed-off-by: Tom Clegg <tom at curii.com>
diff --git a/lib/controller/federation/conn.go b/lib/controller/federation/conn.go
index c2cbfec008..51f7a21c7d 100644
--- a/lib/controller/federation/conn.go
+++ b/lib/controller/federation/conn.go
@@ -408,6 +408,10 @@ func (conn *Conn) CollectionUntrash(ctx context.Context, options arvados.Untrash
return conn.chooseBackend(options.UUID).CollectionUntrash(ctx, options)
}
+func (conn *Conn) ComputedPermissionList(ctx context.Context, options arvados.ListOptions) (arvados.ComputedPermissionList, error) {
+ return conn.local.ComputedPermissionList(ctx, options)
+}
+
func (conn *Conn) ContainerList(ctx context.Context, options arvados.ListOptions) (arvados.ContainerList, error) {
return conn.generated_ContainerList(ctx, options)
}
diff --git a/lib/controller/router/router.go b/lib/controller/router/router.go
index 39c7d871d8..b3c7b115ff 100644
--- a/lib/controller/router/router.go
+++ b/lib/controller/router/router.go
@@ -184,6 +184,13 @@ func (rtr *router) addRoutes() {
return rtr.backend.CollectionUntrash(ctx, *opts.(*arvados.UntrashOptions))
},
},
+ {
+ arvados.EndpointComputedPermissionList,
+ func() interface{} { return &arvados.ListOptions{} },
+ func(ctx context.Context, opts interface{}) (interface{}, error) {
+ return rtr.backend.ComputedPermissionList(ctx, *opts.(*arvados.ListOptions))
+ },
+ },
{
arvados.EndpointContainerCreate,
func() interface{} { return &arvados.CreateOptions{} },
diff --git a/lib/controller/rpc/conn.go b/lib/controller/rpc/conn.go
index 3125ae29be..899c5ce7de 100644
--- a/lib/controller/rpc/conn.go
+++ b/lib/controller/rpc/conn.go
@@ -341,6 +341,13 @@ func (conn *Conn) CollectionUntrash(ctx context.Context, options arvados.Untrash
return resp, err
}
+func (conn *Conn) ComputedPermissionList(ctx context.Context, options arvados.ListOptions) (arvados.ComputedPermissionList, error) {
+ ep := arvados.EndpointComputedPermissionList
+ var resp arvados.ComputedPermissionList
+ err := conn.requestAndDecode(ctx, &resp, ep, nil, options)
+ return resp, err
+}
+
func (conn *Conn) ContainerCreate(ctx context.Context, options arvados.CreateOptions) (arvados.Container, error) {
ep := arvados.EndpointContainerCreate
var resp arvados.Container
diff --git a/sdk/go/arvados/api.go b/sdk/go/arvados/api.go
index d2e2b2088c..2c932531ae 100644
--- a/sdk/go/arvados/api.go
+++ b/sdk/go/arvados/api.go
@@ -42,6 +42,7 @@ var (
EndpointCollectionDelete = APIEndpoint{"DELETE", "arvados/v1/collections/{uuid}", ""}
EndpointCollectionTrash = APIEndpoint{"POST", "arvados/v1/collections/{uuid}/trash", ""}
EndpointCollectionUntrash = APIEndpoint{"POST", "arvados/v1/collections/{uuid}/untrash", ""}
+ EndpointComputedPermissionList = APIEndpoint{"GET", "arvados/v1/computed_permissions", ""}
EndpointContainerCreate = APIEndpoint{"POST", "arvados/v1/containers", "container"}
EndpointContainerUpdate = APIEndpoint{"PATCH", "arvados/v1/containers/{uuid}", "container"}
EndpointContainerPriorityUpdate = APIEndpoint{"POST", "arvados/v1/containers/{uuid}/update_priority", "container"}
@@ -291,6 +292,7 @@ type API interface {
CollectionDelete(ctx context.Context, options DeleteOptions) (Collection, error)
CollectionTrash(ctx context.Context, options DeleteOptions) (Collection, error)
CollectionUntrash(ctx context.Context, options UntrashOptions) (Collection, error)
+ ComputedPermissionList(ctx context.Context, options ListOptions) (ComputedPermissionList, error)
ContainerCreate(ctx context.Context, options CreateOptions) (Container, error)
ContainerUpdate(ctx context.Context, options UpdateOptions) (Container, error)
ContainerPriorityUpdate(ctx context.Context, options UpdateOptions) (Container, error)
diff --git a/sdk/go/arvados/link.go b/sdk/go/arvados/link.go
index 7df6b84d60..3bf5bcb8dd 100644
--- a/sdk/go/arvados/link.go
+++ b/sdk/go/arvados/link.go
@@ -32,3 +32,15 @@ type LinkList struct {
Offset int `json:"offset"`
Limit int `json:"limit"`
}
+
+type ComputedPermission struct {
+ UserUUID string `json:"user_uuid"`
+ TargetUUID string `json:"target_uuid"`
+ PermLevel string `json:"perm_level"`
+}
+
+type ComputedPermissionList struct {
+ Items []ComputedPermission `json:"items"`
+ ItemsAvailable int `json:"items_available"`
+ Limit int `json:"limit"`
+}
diff --git a/sdk/go/arvadostest/api.go b/sdk/go/arvadostest/api.go
index 658874c6d7..5da69eb22c 100644
--- a/sdk/go/arvadostest/api.go
+++ b/sdk/go/arvadostest/api.go
@@ -108,6 +108,10 @@ func (as *APIStub) CollectionUntrash(ctx context.Context, options arvados.Untras
as.appendCall(ctx, as.CollectionUntrash, options)
return arvados.Collection{}, as.Error
}
+func (as *APIStub) ComputedPermissionList(ctx context.Context, options arvados.ListOptions) (arvados.ComputedPermissionList, error) {
+ as.appendCall(ctx, as.ComputedPermissionList, options)
+ return arvados.ComputedPermissionList{}, as.Error
+}
func (as *APIStub) ContainerCreate(ctx context.Context, options arvados.CreateOptions) (arvados.Container, error) {
as.appendCall(ctx, as.ContainerCreate, options)
return arvados.Container{}, as.Error
diff --git a/sdk/python/arvados-v1-discovery.json b/sdk/python/arvados-v1-discovery.json
index 0dcec6bb20..2c9d1a73f6 100644
--- a/sdk/python/arvados-v1-discovery.json
+++ b/sdk/python/arvados-v1-discovery.json
@@ -1539,6 +1539,70 @@
}
}
},
+ "computed_permissions": {
+ "methods": {
+ "list": {
+ "id": "arvados.computed_permissions.list",
+ "path": "computed_permissions",
+ "httpMethod": "GET",
+ "description": "List ComputedPermissions.\n\n The <code>list</code> method returns a\n <a href=\"/api/resources.html\">resource list</a> of\n matching ComputedPermissions. For example:\n\n <pre>\n {\n \"kind\":\"arvados#computedPermissionList\",\n \"etag\":\"\",\n \"self_link\":\"\",\n \"next_page_token\":\"\",\n \"next_link\":\"\",\n \"items\":[\n ...\n ],\n \"items_available\":745,\n \"_profile\":{\n \"request_time\":0.157236317\n }\n </pre>",
+ "parameters": {
+ "filters": {
+ "type": "array",
+ "required": false,
+ "description": "",
+ "location": "query"
+ },
+ "where": {
+ "type": "object",
+ "required": false,
+ "description": "",
+ "location": "query"
+ },
+ "order": {
+ "type": "array",
+ "required": false,
+ "description": "",
+ "location": "query"
+ },
+ "select": {
+ "type": "array",
+ "description": "Attributes of each object to return in the response.",
+ "required": false,
+ "location": "query"
+ },
+ "distinct": {
+ "type": "boolean",
+ "required": false,
+ "default": "false",
+ "description": "",
+ "location": "query"
+ },
+ "limit": {
+ "type": "integer",
+ "required": false,
+ "default": "100",
+ "description": "",
+ "location": "query"
+ },
+ "count": {
+ "type": "string",
+ "required": false,
+ "default": "exact",
+ "description": "",
+ "location": "query"
+ }
+ },
+ "response": {
+ "$ref": "ComputedPermissionList"
+ },
+ "scopes": [
+ "https://api.arvados.org/auth/arvados",
+ "https://api.arvados.org/auth/arvados.readonly"
+ ]
+ }
+ }
+ },
"containers": {
"methods": {
"get": {
@@ -5919,6 +5983,57 @@
}
}
},
+ "ComputedPermissionList": {
+ "id": "ComputedPermissionList",
+ "description": "ComputedPermission list",
+ "type": "object",
+ "properties": {
+ "kind": {
+ "type": "string",
+ "description": "Object type. Always arvados#computedPermissionList.",
+ "default": "arvados#computedPermissionList"
+ },
+ "etag": {
+ "type": "string",
+ "description": "List version."
+ },
+ "items": {
+ "type": "array",
+ "description": "The list of ComputedPermissions.",
+ "items": {
+ "$ref": "ComputedPermission"
+ }
+ },
+ "next_link": {
+ "type": "string",
+ "description": "A link to the next page of ComputedPermissions."
+ },
+ "next_page_token": {
+ "type": "string",
+ "description": "The page token for the next page of ComputedPermissions."
+ },
+ "selfLink": {
+ "type": "string",
+ "description": "A link back to this list."
+ }
+ }
+ },
+ "ComputedPermission": {
+ "id": "ComputedPermission",
+ "description": "ComputedPermission",
+ "type": "object",
+ "properties": {
+ "user_uuid": {
+ "type": "string"
+ },
+ "target_uuid": {
+ "type": "string"
+ },
+ "perm_level": {
+ "type": "integer"
+ }
+ }
+ },
"ContainerList": {
"id": "ContainerList",
"description": "Container list",
diff --git a/sdk/python/tests/test_computed_permissions.py b/sdk/python/tests/test_computed_permissions.py
new file mode 100644
index 0000000000..7f0eee2014
--- /dev/null
+++ b/sdk/python/tests/test_computed_permissions.py
@@ -0,0 +1,18 @@
+# Copyright (C) The Arvados Authors. All rights reserved.
+#
+# SPDX-License-Identifier: Apache-2.0
+
+import arvados
+from . import run_test_server
+
+class ComputedPermissionTest(run_test_server.TestCaseWithServers):
+ def test_computed_permission(self):
+ run_test_server.authorize_with('admin')
+ api_client = arvados.api('v1')
+ active_user_uuid = run_test_server.fixture('users')['active']['uuid']
+ resp = api_client.computed_permissions().list(
+ filters=[['user_uuid', '=', active_user_uuid]],
+ ).execute()
+ assert len(resp['items']) > 0
+ for item in resp['items']:
+ assert item['user_uuid'] == active_user_uuid
diff --git a/services/api/app/controllers/arvados/v1/computed_permissions_controller.rb b/services/api/app/controllers/arvados/v1/computed_permissions_controller.rb
new file mode 100644
index 0000000000..9291a37ba6
--- /dev/null
+++ b/services/api/app/controllers/arvados/v1/computed_permissions_controller.rb
@@ -0,0 +1,7 @@
+# Copyright (C) The Arvados Authors. All rights reserved.
+#
+# SPDX-License-Identifier: AGPL-3.0
+
+class Arvados::V1::ComputedPermissionsController < ApplicationController
+ before_action :admin_required
+end
diff --git a/services/api/app/controllers/arvados/v1/schema_controller.rb b/services/api/app/controllers/arvados/v1/schema_controller.rb
index dd7a7a759e..0c50db2b64 100644
--- a/services/api/app/controllers/arvados/v1/schema_controller.rb
+++ b/services/api/app/controllers/arvados/v1/schema_controller.rb
@@ -397,6 +397,19 @@ class Arvados::V1::SchemaController < ApplicationController
end
end
+ # The computed_permissions controller does not offer all of the
+ # usual methods and attributes. Modify discovery doc accordingly.
+ discovery[:resources]['computed_permissions'][:methods].select! do |method|
+ method == :list
+ end
+ discovery[:resources]['computed_permissions'][:methods][:list][:parameters].select! do |param|
+ ![:cluster_id, :bypass_federation, :offset].include?(param)
+ end
+ discovery[:schemas]['ComputedPermission'].delete(:uuidPrefix)
+ discovery[:schemas]['ComputedPermission'][:properties].select! do |prop|
+ ![:uuid, :etag].include?(prop)
+ end
+
# The 'replace_files' option is implemented in lib/controller,
# not Rails -- we just need to add it here so discovery-aware
# clients know how to validate it.
diff --git a/services/api/app/models/arvados_model.rb b/services/api/app/models/arvados_model.rb
index 9ee2cca410..8316a30a4e 100644
--- a/services/api/app/models/arvados_model.rb
+++ b/services/api/app/models/arvados_model.rb
@@ -170,10 +170,6 @@ class ArvadosModel < ApplicationRecord
end.map(&:name)
end
- def self.attribute_column attr
- self.columns.select { |col| col.name == attr.to_s }.first
- end
-
def self.attributes_required_columns
# This method returns a hash. Each key is the name of an API attribute,
# and it's mapped to a list of database columns that must be fetched
@@ -564,18 +560,6 @@ class ArvadosModel < ApplicationRecord
"to_tsvector('english', substr(#{parts.join(" || ' ' || ")}, 0, 8000))"
end
- def self.apply_filters query, filters
- ft = record_filters filters, self
- if not ft[:cond_out].any?
- return query
- end
- ft[:joins].each do |t|
- query = query.joins(t)
- end
- query.where('(' + ft[:cond_out].join(') AND (') + ')',
- *ft[:param_out])
- end
-
@_add_uuid_to_name = false
def add_uuid_to_make_unique_name
@_add_uuid_to_name = true
diff --git a/services/api/app/models/computed_permission.rb b/services/api/app/models/computed_permission.rb
new file mode 100644
index 0000000000..af89eadf1f
--- /dev/null
+++ b/services/api/app/models/computed_permission.rb
@@ -0,0 +1,58 @@
+# Copyright (C) The Arvados Authors. All rights reserved.
+#
+# SPDX-License-Identifier: AGPL-3.0
+
+require 'record_filters'
+
+class ComputedPermission < ApplicationRecord
+ self.table_name = 'materialized_permissions'
+ include CurrentApiClient
+ include CommonApiTemplate
+ extend RecordFilters
+
+ PERM_LEVEL_S = ['none', 'can_read', 'can_write', 'can_manage']
+
+ api_accessible :user do |t|
+ t.add :user_uuid
+ t.add :target_uuid
+ t.add :perm_level_s, as: :perm_level
+ end
+
+ protected
+
+ def perm_level_s
+ PERM_LEVEL_S[perm_level]
+ end
+
+ def self.default_orders
+ ["#{table_name}.user_uuid", "#{table_name}.target_uuid"]
+ end
+
+ def self.readable_by(*args)
+ self
+ end
+
+ def self.searchable_columns(operator)
+ if !operator.match(/[<=>]/) && !operator.in?(['in', 'not in'])
+ []
+ else
+ ['user_uuid', 'target_uuid']
+ end
+ end
+
+ def self.limit_index_columns_read
+ []
+ end
+
+ def self.selectable_attributes
+ %w(user_uuid target_uuid perm_level)
+ end
+
+ def self.columns_for_attributes(select_attributes)
+ select_attributes
+ end
+
+ def self.serialized_attributes
+ {}
+ end
+end
diff --git a/services/api/app/models/group.rb b/services/api/app/models/group.rb
index d4c81fe9d1..d159b73c94 100644
--- a/services/api/app/models/group.rb
+++ b/services/api/app/models/group.rb
@@ -231,7 +231,7 @@ insert into frozen_groups (uuid) select uuid from temptable where is_frozen on c
def before_ownership_change
if owner_uuid_changed? and !self.owner_uuid_was.nil?
- MaterializedPermission.where(user_uuid: owner_uuid_was, target_uuid: uuid).delete_all
+ ComputedPermission.where(user_uuid: owner_uuid_was, target_uuid: uuid).delete_all
update_permissions self.owner_uuid_was, self.uuid, REVOKE_PERM
end
end
@@ -243,7 +243,7 @@ insert into frozen_groups (uuid) select uuid from temptable where is_frozen on c
end
def clear_permissions_trash_frozen
- MaterializedPermission.where(target_uuid: uuid).delete_all
+ ComputedPermission.where(target_uuid: uuid).delete_all
ActiveRecord::Base.connection.exec_delete(
"delete from trashed_groups where group_uuid=$1",
"Group.clear_permissions_trash_frozen",
diff --git a/services/api/app/models/materialized_permission.rb b/services/api/app/models/materialized_permission.rb
deleted file mode 100644
index 24ba6737ae..0000000000
--- a/services/api/app/models/materialized_permission.rb
+++ /dev/null
@@ -1,6 +0,0 @@
-# Copyright (C) The Arvados Authors. All rights reserved.
-#
-# SPDX-License-Identifier: AGPL-3.0
-
-class MaterializedPermission < ApplicationRecord
-end
diff --git a/services/api/app/models/user.rb b/services/api/app/models/user.rb
index c104ac6fda..824610b234 100644
--- a/services/api/app/models/user.rb
+++ b/services/api/app/models/user.rb
@@ -171,7 +171,7 @@ SELECT 1 FROM #{PERMISSION_VIEW}
def before_ownership_change
if owner_uuid_changed? and !self.owner_uuid_was.nil?
- MaterializedPermission.where(user_uuid: owner_uuid_was, target_uuid: uuid).delete_all
+ ComputedPermission.where(user_uuid: owner_uuid_was, target_uuid: uuid).delete_all
update_permissions self.owner_uuid_was, self.uuid, REVOKE_PERM
end
end
@@ -183,7 +183,7 @@ SELECT 1 FROM #{PERMISSION_VIEW}
end
def clear_permissions
- MaterializedPermission.where("user_uuid = ? and target_uuid != ?", uuid, uuid).delete_all
+ ComputedPermission.where("user_uuid = ? and target_uuid != ?", uuid, uuid).delete_all
end
def forget_cached_group_perms
@@ -191,7 +191,7 @@ SELECT 1 FROM #{PERMISSION_VIEW}
end
def remove_self_from_permissions
- MaterializedPermission.where("target_uuid = ?", uuid).delete_all
+ ComputedPermission.where("target_uuid = ?", uuid).delete_all
check_permissions_against_full_refresh
end
diff --git a/services/api/config/routes.rb b/services/api/config/routes.rb
index df3c057b57..910e6a3f29 100644
--- a/services/api/config/routes.rb
+++ b/services/api/config/routes.rb
@@ -50,7 +50,6 @@ Rails.application.routes.draw do
end
resources :links
resources :logs
- resources :workflows
resources :user_agreements do
get 'signatures', on: :collection
post 'sign', on: :collection
@@ -68,6 +67,8 @@ Rails.application.routes.draw do
get 'logins', on: :member
get 'get_all_logins', on: :collection
end
+ resources :workflows
+ get '/computed_permissions', to: 'computed_permissions#index'
get '/permissions/:uuid', to: 'links#get_permissions'
end
end
diff --git a/services/api/lib/can_be_an_owner.rb b/services/api/lib/can_be_an_owner.rb
index 995f6f334c..f2d4d7c051 100644
--- a/services/api/lib/can_be_an_owner.rb
+++ b/services/api/lib/can_be_an_owner.rb
@@ -24,6 +24,7 @@ module CanBeAnOwner
'jobs',
'job_tasks',
'keep_disks',
+ 'materialized_permissions',
'nodes',
'pipeline_instances',
'pipeline_templates',
diff --git a/services/api/lib/record_filters.rb b/services/api/lib/record_filters.rb
index e51223254f..41a9201677 100644
--- a/services/api/lib/record_filters.rb
+++ b/services/api/lib/record_filters.rb
@@ -293,4 +293,19 @@ module RecordFilters
{:cond_out => conds_out, :param_out => param_out, :joins => joins}
end
+ def apply_filters query, filters
+ ft = record_filters filters, self
+ if not ft[:cond_out].any?
+ return query
+ end
+ ft[:joins].each do |t|
+ query = query.joins(t)
+ end
+ query.where('(' + ft[:cond_out].join(') AND (') + ')',
+ *ft[:param_out])
+ end
+
+ def attribute_column attr
+ self.columns.select { |col| col.name == attr.to_s }.first
+ end
end
diff --git a/services/api/test/functional/arvados/v1/computed_permissions_controller_test.rb b/services/api/test/functional/arvados/v1/computed_permissions_controller_test.rb
new file mode 100644
index 0000000000..6c89e90b63
--- /dev/null
+++ b/services/api/test/functional/arvados/v1/computed_permissions_controller_test.rb
@@ -0,0 +1,90 @@
+# Copyright (C) The Arvados Authors. All rights reserved.
+#
+# SPDX-License-Identifier: AGPL-3.0
+
+require 'test_helper'
+
+class Arvados::V1::ComputedPermissionsControllerTest < ActionController::TestCase
+ test "require auth" do
+ get :index, params: {}
+ assert_response 401
+ end
+
+ test "require admin" do
+ authorize_with :active
+ get :index, params: {}
+ assert_response 403
+ end
+
+ test "index with no options" do
+ authorize_with :admin
+ get :index, params: {}
+ assert_response :success
+ assert_operator 0, :<, json_response['items'].length
+
+ last_user = ''
+ last_target = ''
+ json_response['items'].each do |item|
+ assert_not_empty item['user_uuid']
+ assert_not_empty item['target_uuid']
+ assert_not_empty item['perm_level']
+ # check default ordering
+ assert_operator last_user, :<=, item['user_uuid']
+ if last_user == item['user_uuid']
+ assert_operator last_target, :<=, item['target_uuid']
+ end
+ last_user = item['user_uuid']
+ last_target = item['target_uuid']
+ end
+ end
+
+ test "index with limit" do
+ authorize_with :admin
+ get :index, params: {limit: 10}
+ assert_response :success
+ assert_equal 10, json_response['items'].length
+ end
+
+ test "index with filter on user_uuid" do
+ user_uuid = users(:active).uuid
+ authorize_with :admin
+ get :index, params: {filters: [['user_uuid', '=', user_uuid]]}
+ assert_response :success
+ assert_not_equal 0, json_response['items'].length
+ json_response['items'].each do |item|
+ assert_equal user_uuid, item['user_uuid']
+ end
+ end
+
+ test "index with filter on user_uuid and target_uuid" do
+ user_uuid = users(:active).uuid
+ target_uuid = groups(:aproject).uuid
+ authorize_with :admin
+ get :index, params: {filters: [
+ ['user_uuid', '=', user_uuid],
+ ['target_uuid', '=', target_uuid],
+ ]}
+ assert_response :success
+ assert_equal([{"user_uuid" => user_uuid,
+ "target_uuid" => target_uuid,
+ "perm_level" => "can_manage",
+ }],
+ json_response['items'])
+ end
+
+ test "index with disallowed filters" do
+ authorize_with :admin
+ get :index, params: {filters: [['perm_level', '=', 'can_manage']]}
+ assert_response 422
+ end
+
+ %w(user_uuid target_uuid perm_level).each do |attr|
+ test "select only #{attr}" do
+ authorize_with :admin
+ get :index, params: {select: [attr], limit: 1}
+ assert_response :success
+ assert_operator 0, :<, json_response['items'][0][attr].length
+ assert_equal([{attr => json_response['items'][0][attr]}], json_response['items'])
+ end
+ end
+end
diff --git a/services/api/test/integration/computed_permissions_test.rb b/services/api/test/integration/computed_permissions_test.rb
new file mode 100644
index 0000000000..803c7fef68
--- /dev/null
+++ b/services/api/test/integration/computed_permissions_test.rb
@@ -0,0 +1,28 @@
+# Copyright (C) The Arvados Authors. All rights reserved.
+#
+# SPDX-License-Identifier: AGPL-3.0
+
+require 'test_helper'
+
+class ComputedPermissionsTest < ActionDispatch::IntegrationTest
+ include DbCurrentTime
+ fixtures :users, :groups, :api_client_authorizations, :collections
+
+ test "non-admin forbidden" do
+ get "/arvados/v1/computed_permissions",
+ params: {:format => :json},
+ headers: auth(:active)
+ assert_response 403
+ end
+
+ test "admin get permission for specified user" do
+ get "/arvados/v1/computed_permissions",
+ params: {
+ :format => :json,
+ :filters => [['user_uuid', '=', users(:active).uuid]].to_json,
+ },
+ headers: auth(:admin)
+ assert_response :success
+ assert_equal users(:active).uuid, json_response['items'][0]['user_uuid']
+ end
+end
diff --git a/services/api/test/integration/discovery_document_test.rb b/services/api/test/integration/discovery_document_test.rb
index 37e7750297..e29c4416b3 100644
--- a/services/api/test/integration/discovery_document_test.rb
+++ b/services/api/test/integration/discovery_document_test.rb
@@ -49,10 +49,9 @@ class DiscoveryDocumentTest < ActionDispatch::IntegrationTest
if expected_json != actual_json
File.open(out_path, "w") { |f| f.write(actual_json) }
end
- assert_equal(expected_json, actual_json, [
- "#{src_path} did not match the live discovery document",
- "Current live version saved to #{out_path}",
- "Commit that to #{src_path} to regenerate documentation",
- ].join(". "))
+ assert_equal(expected_json, actual_json,
+ "Live discovery document did not match the expected version (#{src_path}). " +
+ "If the live version is correct, copy it to the git working directory by running:\n" +
+ "cp #{out_path} #{src_path}\n")
end
end
-----------------------------------------------------------------------
hooks/post-receive
--
More information about the arvados-commits
mailing list