katello 4.0.0 → 4.0.2

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of katello might be problematic. Click here for more details.

Files changed (58) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/katello/api/registry/registry_proxies_controller.rb +5 -7
  3. data/app/controllers/katello/api/v2/repositories_controller.rb +4 -3
  4. data/app/lib/actions/katello/agent_action.rb +26 -17
  5. data/app/lib/actions/katello/bulk_agent_action.rb +18 -5
  6. data/app/lib/actions/katello/capsule_content/sync.rb +1 -1
  7. data/app/lib/actions/katello/capsule_content/sync_capsule.rb +44 -6
  8. data/app/lib/actions/katello/content_view/promote.rb +25 -0
  9. data/app/lib/actions/katello/content_view/publish.rb +29 -0
  10. data/app/lib/actions/katello/content_view_version/incremental_update.rb +14 -3
  11. data/app/lib/actions/katello/jail_concern/content_view.rb +30 -0
  12. data/app/lib/actions/katello/jail_concern/organization.rb +30 -0
  13. data/app/lib/actions/katello/orphan_cleanup/remove_orphans.rb +2 -2
  14. data/app/lib/actions/katello/repository/check_matching_content.rb +3 -4
  15. data/app/lib/actions/katello/repository/sync.rb +59 -0
  16. data/app/lib/actions/pulp3/content_guard/refresh.rb +6 -10
  17. data/app/lib/actions/pulp3/orchestration/orphan_cleanup/remove_orphans.rb +1 -1
  18. data/app/lib/katello/agent/client_message_handler.rb +12 -3
  19. data/app/lib/katello/errors.rb +1 -1
  20. data/app/lib/katello/event_daemon/monitor.rb +1 -0
  21. data/app/lib/katello/event_daemon/services/agent_event_receiver.rb +6 -10
  22. data/app/lib/katello/util/hostgroup_facets_helper.rb +126 -0
  23. data/app/models/katello/agent/dispatch_history.rb +2 -0
  24. data/app/models/katello/authorization/content_view.rb +12 -0
  25. data/app/models/katello/authorization/repository.rb +18 -0
  26. data/app/models/katello/concerns/hostgroup_extensions.rb +4 -2
  27. data/app/models/katello/concerns/redhat_extensions.rb +23 -18
  28. data/app/models/katello/concerns/smart_proxy_extensions.rb +23 -0
  29. data/app/models/katello/content_view.rb +13 -7
  30. data/app/models/katello/glue/pulp/repo.rb +0 -19
  31. data/app/models/katello/ping.rb +3 -10
  32. data/app/models/katello/repository.rb +16 -2
  33. data/app/models/katello/root_repository.rb +1 -1
  34. data/app/services/cert/certs.rb +4 -0
  35. data/app/services/katello/agent/dispatcher.rb +18 -24
  36. data/app/services/katello/applicability/applicable_content_helper.rb +8 -6
  37. data/app/services/katello/managed_content_medium_provider.rb +3 -3
  38. data/app/services/katello/pulp3/api/content_guard.rb +39 -5
  39. data/app/services/katello/pulp3/migration.rb +4 -3
  40. data/app/services/katello/pulp3/repository.rb +32 -4
  41. data/app/services/katello/pulp3/repository/apt.rb +1 -2
  42. data/app/services/katello/pulp3/repository/yum.rb +1 -1
  43. data/app/services/katello/ui_notifications/pulp/proxy_disk_space.rb +1 -0
  44. data/app/views/overrides/smart_proxies/_download_policy.erb +1 -1
  45. data/db/migrate/20200514092553_move_katello_fields_from_hostgroups.katello.rb +1 -1
  46. data/db/migrate/20210119162528_delete_puppet_and_ostree_repos.rb +25 -22
  47. data/db/migrate/20210512192745_fix_red_hat_root_repository_arch.rb +11 -0
  48. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/bulk/views/products-bulk-advanced-sync-modal.html +2 -2
  49. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/details/repositories/new/views/new-repository.html +3 -4
  50. data/lib/katello.rb +1 -1
  51. data/lib/katello/engine.rb +11 -11
  52. data/lib/katello/plugin.rb +5 -2
  53. data/lib/katello/tasks/clean_backend_objects.rake +0 -25
  54. data/lib/katello/tasks/fix_hostgroup_facets.rake +8 -0
  55. data/lib/katello/version.rb +1 -1
  56. data/lib/proxy_api/container_gateway.rb +22 -11
  57. metadata +28 -10
  58. data/app/lib/actions/pulp3/capsule_content/refresh_content_guard.rb +0 -17
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a68ac9c26daead6a548c835d225d8dbebe0ce33ca5b6315b6505ef64660d9ee6
4
- data.tar.gz: '09d66a70cef13109aff1e38a9e409862c5cae6263c5c7aea415399c7cd0e08b0'
3
+ metadata.gz: 77e02e169ae9d1f8c31a49999ac7f6fcc900e5a3c9338beb825d19e3ed8598a4
4
+ data.tar.gz: 7049ec270dcd1db4d3335d33f4665306178289888b7ba33e283f25d69a72768d
5
5
  SHA512:
6
- metadata.gz: cd9715ea64ef37bf05df3ae49a209e15f0ffe0c51d8f69d4b3fcba119909349fea1fe1ab4f464240515057e1529304850176ec5a48cbbfb25cd04fb1ee788137
7
- data.tar.gz: 6ab0c5c117c51017b37ca1366fc05f64efcc7a17375e2bb4ac9ee3633990baedf68ba505cc29292295f5a01e31ac6fd4bef209d9272e2f5cfce0f3f9c7f29a65
6
+ metadata.gz: e4282384c1c10065f6d731af1e45a006cc4b5a294e1d906954fad02fd81db19faf772a3c8bcd81aca33dcf7bb2c779f740ea3a2155cefb84d9af041f6e55f2d6
7
+ data.tar.gz: 87a57f7437ebfdf7f567efbae8e24f60d1c5655aa18944085cc9ce141483b61b92637bf1f10999c5ea00d35fe16ec98f8e98fd0e9bb244bf294efe33ef84407c
@@ -106,7 +106,7 @@ module Katello
106
106
  return nil unless params[:repository]
107
107
  repository = Repository.docker_type.find_by(container_repository_name: params[:repository])
108
108
  if require_user_authorization?(repository)
109
- repository = readable_repositories.docker_type.find_by(container_repository_name: params[:repository])
109
+ repository = Repository.readable_docker_catalog.find_by(container_repository_name: params[:repository])
110
110
  end
111
111
  repository
112
112
  end
@@ -167,11 +167,9 @@ module Katello
167
167
  end
168
168
 
169
169
  if (manifest_response = redirect_client { Resources::Registry::Proxy.get(@_request.fullpath, headers) })
170
- #when pulp 2 is removed, this should no longer be needed, and all clients should be redirected
171
- logger.debug filter_sensitive_data(manifest_response)
170
+ #for some requests, we get a redirect, but for others we get the actual manifest in response
172
171
  results = JSON.parse(manifest_response)
173
-
174
- response.header['Docker-Content-Digest'] = "sha256:#{Digest::SHA256.hexdigest(manifest_response)}"
172
+ response.header['Docker-Content-Digest'] = manifest_response.headers[:docker_content_digest]
175
173
  # https://docs.docker.com/registry/spec/manifest-v2-2/
176
174
  # If its v2 schema 2 only the mediaType attribute will be present in the manifest
177
175
  media_type = results['mediaType']
@@ -333,7 +331,7 @@ module Katello
333
331
  params[:per_page] = params[:n] || 25
334
332
  params[:search] = params[:q]
335
333
 
336
- search_results = scoped_search(readable_repositories.docker_type.distinct,
334
+ search_results = scoped_search(Repository.readable_docker_catalog.distinct,
337
335
  :container_repository_name, :asc, options)
338
336
 
339
337
  results = {
@@ -347,7 +345,7 @@ module Katello
347
345
  end
348
346
 
349
347
  def catalog
350
- repositories = readable_repositories.docker_type.collect do |repository|
348
+ repositories = Repository.readable_docker_catalog.collect do |repository|
351
349
  repository.container_repository_name
352
350
  end
353
351
  render json: { repositories: repositories }
@@ -52,9 +52,9 @@ module Katello
52
52
  param :upstream_password, String, :desc => N_("Password of the upstream repository user used for authentication")
53
53
  param :ostree_upstream_sync_policy, ::Katello::RootRepository::OSTREE_UPSTREAM_SYNC_POLICIES, :desc => N_("policies for syncing upstream ostree repositories")
54
54
  param :ostree_upstream_sync_depth, :number, :desc => N_("if a custom sync policy is chosen for ostree repositories then a 'depth' value must be provided")
55
- param :deb_releases, String, :desc => N_("comma-separated list of releases to be synced from deb-archive")
56
- param :deb_components, String, :desc => N_("comma-separated list of repo components to be synced from deb-archive")
57
- param :deb_architectures, String, :desc => N_("comma-separated list of architectures to be synced from deb-archive")
55
+ param :deb_releases, String, :desc => N_("whitespace-separated list of releases to be synced from deb-archive")
56
+ param :deb_components, String, :desc => N_("whitespace-separated list of repo components to be synced from deb-archive")
57
+ param :deb_architectures, String, :desc => N_("whitespace-separated list of architectures to be synced from deb-archive")
58
58
  param :ignorable_content, Array, :desc => N_("List of content units to ignore while syncing a yum repository. Must be subset of %s") % RootRepository::IGNORABLE_CONTENT_UNIT_TYPES.join(",")
59
59
  param :ansible_collection_requirements, String, :desc => N_("Contents of requirement yaml file to sync from URL")
60
60
  param :http_proxy_policy, ::Katello::RootRepository::HTTP_PROXY_POLICIES, :desc => N_("policies for HTTP proxy for content sync")
@@ -95,6 +95,7 @@ module Katello
95
95
  :required => false
96
96
  param :with_content, RepositoryTypeManager.enabled_content_types, :desc => N_("only repositories having at least one of the specified content type ex: rpm , erratum")
97
97
  param :download_policy, ::Runcible::Models::YumImporter::DOWNLOAD_POLICIES, :desc => N_("limit to only repositories with this download policy")
98
+ param :username, String, :desc => N_("only show the repositories readable by this user with this username")
98
99
  param_group :search, Api::V2::ApiController
99
100
  add_scoped_search_description_for(Repository)
100
101
  def index
@@ -15,22 +15,15 @@ module Actions
15
15
  def plan(host, options)
16
16
  action_subject(host, :hostname => host.name, :content => options[:content])
17
17
 
18
- # if already dispatched by bulk action use the provided history ID
19
- dispatch_history_id = options.dig(:dispatch_histories, host.id.to_s)
20
-
21
- unless dispatch_history_id
22
- histories = ::Katello::Agent::Dispatcher.dispatch(
23
- self.class.agent_message,
24
- [host.id],
25
- content: options[:content]
26
- )
27
-
28
- dispatch_history_id = histories.first.id
29
- end
18
+ dispatch_history_id = options.dig(:dispatch_histories, host.id.to_s) || ::Katello::Agent::Dispatcher.create_histories(
19
+ host_ids: [host.id]
20
+ ).first.id
30
21
 
31
22
  plan_self(
32
23
  host_id: host.id,
33
- dispatch_history_id: dispatch_history_id
24
+ dispatch_history_id: dispatch_history_id,
25
+ content: options[:content],
26
+ bulk: options[:bulk]
34
27
  )
35
28
  end
36
29
 
@@ -38,20 +31,23 @@ module Actions
38
31
  case event
39
32
  when nil
40
33
  history = dispatch_history
34
+ timeout = accept_timeout
41
35
 
42
36
  if history.finished?
43
37
  fail_on_errors
44
38
  return
45
39
  elsif history.accepted?
46
- schedule_timeout(finish_timeout)
47
- else
48
- schedule_timeout(accept_timeout)
40
+ timeout = finish_timeout
49
41
  end
50
42
 
51
43
  suspend do |suspended_action|
52
44
  history.dynflow_execution_plan_id = suspended_action.execution_plan_id
53
45
  history.dynflow_step_id = suspended_action.step_id
54
46
  history.save!
47
+
48
+ dispatch_message(history) unless input[:bulk]
49
+
50
+ schedule_timeout(timeout)
55
51
  end
56
52
  when Dynflow::Action::Timeouts::Timeout
57
53
  process_timeout
@@ -63,6 +59,14 @@ module Actions
63
59
  end
64
60
  end
65
61
 
62
+ def dispatch_message(history)
63
+ ::Katello::Agent::Dispatcher.dispatch(
64
+ self.class.agent_message,
65
+ [history],
66
+ content: input[:content]
67
+ )
68
+ end
69
+
66
70
  def accept_timeout
67
71
  Setting['content_action_accept_timeout']
68
72
  end
@@ -79,7 +83,12 @@ module Actions
79
83
  end
80
84
 
81
85
  unless history.finished?
82
- fail _("Host did not finish content action in %s seconds. The task has been cancelled.") % finish_timeout
86
+ # we could be processing the accept_timeout here
87
+ # only fail for finish_timeout unless the actual duration has elapsed
88
+ finish_limit = history.accepted_at + finish_timeout
89
+ if finish_limit < DateTime.now
90
+ fail _("Host did not finish content action in %s seconds. The task has been cancelled.") % finish_timeout
91
+ end
83
92
  end
84
93
  end
85
94
 
@@ -3,19 +3,32 @@ module Actions
3
3
  class BulkAgentAction < Actions::BulkAction
4
4
  def plan(agent_action, hosts, args)
5
5
  host_ids = hosts.map(&:id)
6
- dispatch_args = {
7
- content: args[:content]
8
- }
9
- histories = ::Katello::Agent::Dispatcher.dispatch(agent_action.agent_message, host_ids, dispatch_args)
6
+
7
+ histories = ::Katello::Agent::Dispatcher.create_histories(
8
+ host_ids: host_ids
9
+ )
10
10
 
11
11
  grouped_histories = {}
12
12
  histories.each { |h| grouped_histories[h.host_id] = h.id }
13
13
  options = {
14
14
  dispatch_histories: grouped_histories,
15
- content: args[:content]
15
+ type: agent_action.agent_message,
16
+ content: args[:content],
17
+ bulk: true
16
18
  }
17
19
  super(agent_action, hosts, options)
18
20
  end
21
+
22
+ def spawn_plans
23
+ args = input[:args].first
24
+ histories = ::Katello::Agent::DispatchHistory.where(id: args[:dispatch_histories].slice(*current_batch.map(&:to_s)).values)
25
+ ::Katello::Agent::Dispatcher.dispatch(
26
+ args[:type].to_sym,
27
+ histories,
28
+ content: args[:content]
29
+ )
30
+ super
31
+ end
19
32
  end
20
33
  end
21
34
  end
@@ -39,7 +39,7 @@ module Actions
39
39
  end
40
40
 
41
41
  if smart_proxy.has_feature?(SmartProxy::PULP3_FEATURE)
42
- plan_action(Actions::Pulp3::CapsuleContent::RefreshContentGuard, smart_proxy)
42
+ plan_action(Actions::Pulp3::ContentGuard::Refresh, smart_proxy)
43
43
  plan_action(Actions::Pulp3::Orchestration::Repository::RefreshRepos, smart_proxy, refresh_options)
44
44
  end
45
45
  plan_action(SyncCapsule, smart_proxy, refresh_options)
@@ -35,15 +35,53 @@ module Actions
35
35
  end
36
36
  end
37
37
  end
38
- update_unauthenticated_repo_list(smart_proxy) if smart_proxy.has_feature?("Container_Gateway")
38
+ sync_container_gateway(smart_proxy)
39
39
  end
40
40
 
41
- def update_unauthenticated_repo_list(smart_proxy)
42
- unauthenticated_repo_list =
43
- ::Katello::SmartProxyHelper.new(smart_proxy).combined_repos_available_to_capsule.select do |repo|
44
- repo.docker? && repo.environment.registry_unauthenticated_pull
41
+ def sync_container_gateway(smart_proxy)
42
+ if smart_proxy.has_feature?(::SmartProxy::CONTAINER_GATEWAY_FEATURE)
43
+ update_container_repo_list(smart_proxy)
44
+ users = smart_proxy.container_gateway_users
45
+ update_user_container_repo_mapping(smart_proxy, users) if users.any?
46
+ end
47
+ end
48
+
49
+ def unauthenticated_container_repositories
50
+ ::Katello::Repository.joins(:environment).where("#{::Katello::KTEnvironment.table_name}.registry_unauthenticated_pull" => true).select(:id).pluck(:id)
51
+ end
52
+
53
+ def update_container_repo_list(smart_proxy)
54
+ # [{ repository: "repoA", auth_required: false }]
55
+ repo_list = []
56
+ ::Katello::SmartProxyHelper.new(smart_proxy).combined_repos_available_to_capsule.each do |repo|
57
+ if repo.docker? && !repo.container_repository_name.nil?
58
+ repo_list << { repository: repo.container_repository_name,
59
+ auth_required: !unauthenticated_container_repositories.include?(repo.id) }
45
60
  end
46
- smart_proxy.update_unauthenticated_repo_list(unauthenticated_repo_list.map(&:container_repository_name))
61
+ end
62
+ smart_proxy.update_container_repo_list(repo_list)
63
+ end
64
+
65
+ def update_user_container_repo_mapping(smart_proxy, users)
66
+ # Example user-repo mapping:
67
+ # { users:
68
+ # [
69
+ # 'user a' => [{ repository: 'repo 1', auth_required: true }]
70
+ # ]
71
+ # }
72
+
73
+ user_repo_map = { users: [] }
74
+ users.each do |user|
75
+ inner_repo_list = []
76
+ repositories = ::Katello::Repository.readable_docker_catalog_as(user)
77
+ repositories.each do |repo|
78
+ next if repo.container_repository_name.nil?
79
+ inner_repo_list << { repository: repo.container_repository_name,
80
+ auth_required: !unauthenticated_container_repositories.include?(repo.id) }
81
+ end
82
+ user_repo_map[:users] << { user.login => inner_repo_list }
83
+ end
84
+ smart_proxy.update_user_container_repo_mapping(user_repo_map)
47
85
  end
48
86
 
49
87
  def repos_to_sync(smart_proxy, environment, content_view, repository, skip_metatadata_check = false)
@@ -2,6 +2,9 @@ module Actions
2
2
  module Katello
3
3
  module ContentView
4
4
  class Promote < Actions::EntryAction
5
+ extend ApipieDSL::Class
6
+ include ::Actions::ObservableAction
7
+
5
8
  def plan(version, environments, is_force = false, description = nil, incremental_update = false)
6
9
  action_subject(version.content_view)
7
10
  version.check_ready_to_promote!(environments)
@@ -18,6 +21,28 @@ module Actions
18
21
  end
19
22
  end
20
23
  end
24
+
25
+ def environments
26
+ input['environments']
27
+ end
28
+
29
+ apipie :class, "A class representing #{self} object" do
30
+ desc 'This object is available as **@object** variable in
31
+ webhook templates when a corresponding event occures.
32
+ The following properties can be used to retrieve the needed information.'
33
+ name "#{class_scope}"
34
+ refs "#{class_scope}"
35
+ sections only: %w[all webhooks]
36
+ property :task, object_of: 'Task', desc: 'Returns the task to which this action belongs'
37
+ property :environments, array_of: String, desc: 'Returns the list of environments the content view was promoted to'
38
+ end
39
+ include Actions::Katello::JailConcern::Organization
40
+ include Actions::Katello::JailConcern::ContentView
41
+ class Jail < ::Actions::ObservableAction::Jail
42
+ allow :organization_id, :organization_name, :organization_label,
43
+ :content_view_id, :content_view_name, :content_view_label,
44
+ :environments
45
+ end
21
46
  end
22
47
  end
23
48
  end
@@ -3,7 +3,9 @@ module Actions
3
3
  module Katello
4
4
  module ContentView
5
5
  class Publish < Actions::EntryAction
6
+ extend ApipieDSL::Class
6
7
  include ::Katello::ContentViewHelper
8
+ include ::Actions::ObservableAction
7
9
  attr_accessor :version
8
10
  # rubocop:disable Metrics/MethodLength,Metrics/AbcSize,Metrics/CyclomaticComplexity
9
11
  def plan(content_view, description = "", options = {importing: false}) # rubocop:disable Metrics/PerceivedComplexity
@@ -145,6 +147,14 @@ module Actions
145
147
  rescue ::Katello::Errors::CapsuleCannotBeReached # skip any capsules that cannot be connected to
146
148
  end
147
149
 
150
+ def content_view_version_id
151
+ input['content_view_version_id']
152
+ end
153
+
154
+ def content_view_version_name
155
+ input['content_view_version_name']
156
+ end
157
+
148
158
  private
149
159
 
150
160
  def include_other_components(override_components, content_view)
@@ -197,6 +207,25 @@ module Actions
197
207
  end
198
208
  end
199
209
  end
210
+
211
+ apipie :class, "A class representing #{self} object" do
212
+ desc 'This object is available as **@object** variable in
213
+ webhook templates when a corresponding event occures.
214
+ The following properties can be used to retrieve the needed information.'
215
+ name "#{class_scope}"
216
+ refs "#{class_scope}"
217
+ sections only: %w[all webhooks]
218
+ property :task, object_of: 'Task', desc: 'Returns the task to which this action belongs'
219
+ property :content_view_version_id, Integer, desc: 'Returns published content view version id'
220
+ property :content_view_version_name, String, desc: 'Returns published content view version name'
221
+ end
222
+ include Actions::Katello::JailConcern::Organization
223
+ include Actions::Katello::JailConcern::ContentView
224
+ class Jail < ::Actions::ObservableAction::Jail
225
+ allow :organization_id, :organization_name, :organization_label,
226
+ :content_view_id, :content_view_name, :content_view_label,
227
+ :content_view_version_id, :content_view_version_name
228
+ end
200
229
  end
201
230
  end
202
231
  end
@@ -19,7 +19,8 @@ module Actions
19
19
 
20
20
  # rubocop:disable Metrics/MethodLength
21
21
  # rubocop:disable Metrics/AbcSize
22
- def plan(old_version, environments, options = {}) # rubocop:disable Metrics/CyclomaticComplexity
22
+ # rubocop:disable Metrics/CyclomaticComplexity
23
+ def plan(old_version, environments, options = {})
23
24
  dep_solve = options.fetch(:resolve_dependencies, true)
24
25
  description = options.fetch(:description, '')
25
26
  content = options.fetch(:content, {})
@@ -72,8 +73,18 @@ module Actions
72
73
  unit_map = pulp3_content_mapping(content)
73
74
 
74
75
  unless extended_repo_mapping.empty? || unit_map.values.flatten.empty?
75
- copy_action_outputs << plan_action(Pulp3::Repository::MultiCopyUnits, extended_repo_mapping, unit_map,
76
- dependency_solving: dep_solve).output
76
+ sequence do
77
+ copy_action_outputs << plan_action(Pulp3::Repository::MultiCopyUnits, extended_repo_mapping, unit_map,
78
+ dependency_solving: dep_solve).output
79
+ repos_to_clone.each do |source_repos|
80
+ if separated_repo_map[:pulp3_yum].keys.include?(source_repos)
81
+ copy_repos(repository_mapping[source_repos],
82
+ new_content_view_version,
83
+ content,
84
+ dep_solve)
85
+ end
86
+ end
87
+ end
77
88
  end
78
89
  end
79
90
 
@@ -0,0 +1,30 @@
1
+ module Actions
2
+ module Katello
3
+ module JailConcern
4
+ module ContentView
5
+ def content_view_id
6
+ input['content_view']['id']
7
+ end
8
+
9
+ def content_view_name
10
+ input['content_view']['name']
11
+ end
12
+
13
+ def content_view_label
14
+ input['content_view']['label']
15
+ end
16
+
17
+ def self.included(base)
18
+ super
19
+ base.instance_eval do
20
+ apipie :class do
21
+ property :content_view_id, Integer, desc: 'Returns the id of the content view'
22
+ property :content_view_name, String, desc: 'Returns the name of the content view'
23
+ property :content_view_label, String, desc: 'Returns the label of the content view'
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,30 @@
1
+ module Actions
2
+ module Katello
3
+ module JailConcern
4
+ module Organization
5
+ def organization_id
6
+ input['organization']['id']
7
+ end
8
+
9
+ def organization_name
10
+ input['organization']['name']
11
+ end
12
+
13
+ def organization_label
14
+ input['organization']['label']
15
+ end
16
+
17
+ def self.included(base)
18
+ super
19
+ base.instance_eval do
20
+ apipie :class do
21
+ property :organization_id, Integer, desc: 'Returns the id of the organization'
22
+ property :organization_name, String, desc: 'Returns the name of the organization'
23
+ property :organization_label, String, desc: 'Returns the label of the organization'
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end