Skip to content

Commit

Permalink
add machine owners
Browse files Browse the repository at this point in the history
  • Loading branch information
ezekg committed Jan 12, 2024
1 parent 0a2fc30 commit 5762d93
Show file tree
Hide file tree
Showing 60 changed files with 1,763 additions and 577 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ class MachinesController < Api::V1::BaseController
authorize :group

def index
machines = apply_pagination(authorized_scope(apply_scopes(group.machines), with: Groups::MachinePolicy).preload(:product, :policy, :license, :owner))
machines = apply_pagination(authorized_scope(apply_scopes(group.machines), with: Groups::MachinePolicy).preload(:product, :policy, :owner, license: %i[owner]))
authorize! machines,
with: Groups::MachinePolicy

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ class MachinesController < Api::V1::BaseController
authorize :license

def index
machines = apply_pagination(authorized_scope(apply_scopes(license.machines)).preload(:product, :policy, :owner))
machines = apply_pagination(authorized_scope(apply_scopes(license.machines)).preload(:product, :policy, :owner, license: %i[owner]))
authorize! machines,
with: Licenses::MachinePolicy

Expand Down
24 changes: 24 additions & 0 deletions app/controllers/api/v1/machines/relationships/owners_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,30 @@ def show
render jsonapi: owner
end

typed_params {
format :jsonapi

param :data, type: :hash, allow_nil: true do
param :type, type: :string, inclusion: { in: %w[user users] }
param :id, type: :uuid
end
}
def update
owner = machine.owner
authorize! owner,
with: Machines::OwnerPolicy

machine.update!(owner_id: owner_params[:id])

BroadcastEventService.call(
event: 'machine.owner.updated',
account: current_account,
resource: machine,
)

render jsonapi: machine
end

private

attr_reader :machine
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ class UsersController < Api::V1::BaseController
authorize :machine

def show
user = machine.owner
user = machine.license.owner
authorize! user,
with: Machines::V1x5::UserPolicy

Expand Down
10 changes: 9 additions & 1 deletion app/controllers/api/v1/machines_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ class MachinesController < Api::V1::BaseController
before_action :set_machine, only: [:show, :update, :destroy]

def index
machines = apply_pagination(authorized_scope(apply_scopes(current_account.machines)).preload(:product, :policy, :license, :owner))
machines = apply_pagination(authorized_scope(apply_scopes(current_account.machines)).preload(:product, :policy, :owner, license: %i[owner]))
authorize! machines

render jsonapi: machines
Expand Down Expand Up @@ -54,6 +54,14 @@ def show
param :id, type: :uuid
end
end

param :owner, type: :hash, optional: true do
param :data, type: :hash do
param :type, type: :string, inclusion: { in: %w[user users] }
param :id, type: :uuid
end
end

param :group, type: :hash, optional: true, if: -> { current_bearer&.has_role?(:admin, :developer, :sales_agent, :support_agent, :product, :environment) } do
param :data, type: :hash, allow_nil: true do
param :type, type: :string, inclusion: { in: %w[group groups] }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ class MachinesController < Api::V1::BaseController
authorize :product

def index
machines = apply_pagination(authorized_scope(apply_scopes(product.machines)).preload(:product, :policy, :license, :owner))
machines = apply_pagination(authorized_scope(apply_scopes(product.machines)).preload(:product, :policy, :owner, license: %i[owner]))
authorize! machines,
with: Products::MachinePolicy

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ class MachinesController < Api::V1::BaseController
authorize :user

def index
machines = apply_pagination(authorized_scope(apply_scopes(user.machines)).preload(:product, :policy, :license, :owner))
machines = apply_pagination(authorized_scope(apply_scopes(user.machines)).preload(:product, :policy, :owner, license: %i[owner]))
authorize! machines,
with: Users::MachinePolicy

Expand Down
38 changes: 38 additions & 0 deletions app/migrations/add_user_relationship_to_machine_migration.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# frozen_string_literal: true

class AddUserRelationshipToMachineMigration < BaseMigration
description %(adds user relationship to a Machine)

migrate if: -> body { body in data: { ** } } do |body|
body => data:

case data
in type: /\Amachines\z/, id: machine_id, relationships: { account: { data: { id: account_id } }, license: { data: { id: license_id } } } => rels
license_owner_id, * = License.where(id: license_id, account_id:)
.limit(1)
.pluck(:user_id)

rels[:user] = {
data: license_owner_id.present? ? { type: :users, id: license_owner_id } : nil,
links: {
related: v1_account_machine_v1_5_user_path(account_id, machine_id),
},
}

rels.delete(:owner)
else
end
end

response if: -> res { res.status < 400 && res.request.params in controller: 'api/v1/machines' | 'api/v1/machines/actions/v1x0/proofs' | 'api/v1/machines/actions/heartbeats' | 'api/v1/machines/relationships/groups' |
'api/v1/licenses/relationships/machines' | 'api/v1/products/relationships/machines' | 'api/v1/policies/relationships/machines' |
'api/v1/users/relationships/machines' | 'api/v1/groups/relationships/machines' | 'api/v1/machine_components/relationships/machines' |
'api/v1/machine_processes/relationships/machines',
action: 'show' | 'create' | 'update' | 'ping' } do |res|
body = JSON.parse(res.body, symbolize_names: true)

migrate!(body)

res.body = JSON.generate(body)
end
end
79 changes: 79 additions & 0 deletions app/migrations/add_user_relationship_to_machines_migration.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# frozen_string_literal: true

class AddUserRelationshipToMachinesMigration < BaseMigration
description %(adds user relationship to Machines)

migrate if: -> body { body in included: [*] } do |body|
case body
in included: [
*,
{ type: /\Amachines\z/, relationships: { account: { data: { type: /\Aaccounts\z/, id: _ } } } },
*
] => includes
account_ids = includes.collect { _1[:relationships][:account][:data][:id] }.compact.uniq
machine_ids = includes.collect { _1[:id] }.compact.uniq

licenses = License.joins(:machines)
.where(account_id: account_ids, machines: { id: machine_ids })
.select(:id, :user_id)
.group_by(&:id)

includes.each do |record|
case record
in type: /\Amachines\z/, id: machine_id, relationships: { account: { data: { id: account_id } }, license: { data: { id: license_id } } } => rels
license = licenses[license_id]&.first

rels[:user] = {
data: license.owner_id? ? { type: :users, id: license.owner_id } : nil,
links: {
related: v1_account_machine_v1_5_user_path(account_id, machine_id),
},
}
else
end
end
else
end
end

migrate if: -> body { body in data: [*] } do |body|
case body
in data: [*, { type: /\Amachines\z/, relationships: { account: { data: { type: /\Aaccounts\z/, id: _ } } } }, *] => data
account_ids = data.collect { _1[:relationships][:account][:data][:id] }.compact.uniq
machine_ids = data.collect { _1[:id] }.compact.uniq

licenses = License.joins(:machines)
.where(account_id: account_ids, machines: { id: machine_ids })
.select(:id, :user_id)
.group_by(&:id)

data.each do |machine|
case machine
in type: /\Amachines\z/, id: machine_id, relationships: { account: { data: { id: account_id } }, license: { data: { id: license_id } } } => rels
license = licenses[license_id]&.first

rels[:user] = {
data: license.owner_id? ? { type: :users, id: license.owner_id } : nil,
links: {
related: v1_account_machine_v1_5_user_path(account_id, machine_id),
},
}

rels.delete(:owner)
else
end
end
else
end
end

response if: -> res { res.status < 400 && res.request.params in controller: 'api/v1/machines' | 'api/v1/licenses/relationships/machines' | 'api/v1/products/relationships/machines' |
'api/v1/policies/relationships/machines' | 'api/v1/users/relationships/machines' | 'api/v1/groups/relationships/machines',
action: 'index' } do |res|
body = JSON.parse(res.body, symbolize_names: true)

migrate!(body)

res.body = JSON.generate(body)
end
end

This file was deleted.

This file was deleted.

32 changes: 30 additions & 2 deletions app/models/machine.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,13 @@ class ResurrectionExpiredError < StandardError; end
HEARTBEAT_TTL = 10.minutes

belongs_to :license, counter_cache: true
belongs_to :owner,
class_name: User.name,
optional: true
belongs_to :group,
optional: true
has_one :product, through: :license
has_one :policy, through: :license
has_one :owner, through: :license
has_many :users, through: :license
has_many :processes,
class_name: 'MachineProcess',
Expand All @@ -42,11 +44,18 @@ class ResurrectionExpiredError < StandardError; end
accepts_nested_attributes_for :components, limit: 20, reject_if: :reject_associated_records_for_components
tracks_nested_attributes_for :components

# Machines automatically inherit their license's group ID
# Machines firstly automatically inherit their license's group ID.
before_validation -> { self.group_id = license.group_id },
if: -> { license.present? && group_id.nil? },
on: %i[create]

# Machines secondly automatically inherit their owner's group ID. We're using before_validation
# instead of before_create so that this can be run when the owner is changed as well,
# and so that we can keep our group limit validations in play.
before_validation -> { self.group_id = owner.group_id },
if: -> { owner_id_changed? && owner.present? && group_id.nil? },
on: %i[create update]

# Set initial heartbeat if heartbeat is required
before_validation -> { self.last_heartbeat_at ||= Time.current },
if: :heartbeat_from_creation?,
Expand All @@ -64,6 +73,13 @@ class ResurrectionExpiredError < StandardError; end
validates :license,
scope: { by: :account_id }

validates :owner,
presence: { message: 'must exist' },
scope: { by: :account_id },
unless: -> {
owner_id_before_type_cast.nil?
}

validates :group,
presence: { message: 'must exist' },
scope: { by: :account_id },
Expand Down Expand Up @@ -181,6 +197,18 @@ class ResurrectionExpiredError < StandardError; end
errors.add :group, :machine_limit_exceeded, message: "machine count has exceeded maximum allowed by current group (#{group.max_machines})"
end

validate on: %i[create update] do
next unless
owner_id_changed?

next unless
owner.present?

unless license.users.exists?(owner.id)
errors.add :owner, :invalid, message: 'must be a valid license user'
end
end

scope :search_id, -> (term) {
identifier = term.to_s
return none if
Expand Down
Loading

0 comments on commit 5762d93

Please sign in to comment.