diff --git a/app/controllers/api/v1/base_controller.rb b/app/controllers/api/v1/base_controller.rb index 4c43813e9..96744578e 100644 --- a/app/controllers/api/v1/base_controller.rb +++ b/app/controllers/api/v1/base_controller.rb @@ -5,12 +5,12 @@ module Api class BaseController < ApiController rescue_from ActionController::ParameterMissing do |e| logger.error e.message - render json: {}, status: :bad_request + render jsonapi: {}, status: :bad_request end rescue_from ActiveRecord::RecordNotFound do |e| logger.error e.message - render json: {}, status: :not_found + render jsonapi: {}, status: :not_found end end end diff --git a/app/controllers/api/v1/inventories_controller.rb b/app/controllers/api/v1/inventories_controller.rb index 136801caa..e8c8ad6b4 100644 --- a/app/controllers/api/v1/inventories_controller.rb +++ b/app/controllers/api/v1/inventories_controller.rb @@ -9,18 +9,18 @@ module Api def index inventories = @team.repositories.page(params[:page]).per(params[:page_size]) - render json: inventories, each_serializer: InventorySerializer + render jsonapi: inventories, each_serializer: InventorySerializer end def show - render json: @inventory, serializer: InventorySerializer + render jsonapi: @inventory, serializer: InventorySerializer end private def load_team @team = Team.find(params.require(:team_id)) - return render json: {}, status: :forbidden unless can_read_team?(@team) + return render jsonapi: {}, status: :forbidden unless can_read_team?(@team) end def load_inventory diff --git a/app/controllers/api/v1/inventory_items_controller.rb b/app/controllers/api/v1/inventory_items_controller.rb index f22f1462f..0971c1d78 100644 --- a/app/controllers/api/v1/inventory_items_controller.rb +++ b/app/controllers/api/v1/inventory_items_controller.rb @@ -5,6 +5,7 @@ module Api class InventoryItemsController < BaseController before_action :load_team before_action :load_inventory + before_action :check_manage_permissions, only: %i(create) def index items = @@ -12,23 +13,73 @@ module Api .includes(repository_cells: :repository_column) .includes( repository_cells: Extends::REPOSITORY_SEARCH_INCLUDES - ).page(params[:page]) - .per(params[:page_size]) - render json: items, + ).page(params.dig(:page, :number)) + .per(params.dig(:page, :size)) + render jsonapi: items, each_serializer: InventoryItemSerializer, include: :inventory_cells end + def create + attributes = inventory_item_params.merge( + created_by: current_user, + last_modified_by: current_user + ) + if inventory_cells_params.present? + inventory_cells_params + .each { |p| p.require(:attributes).require(%i(column_id value)) } + item = @inventory.repository_rows.new(attributes) + item.transaction do + item.save! + inventory_cells_params.each do |cell_params| + cell_attributes = cell_params[:attributes] + column = + @inventory.repository_columns.find(cell_attributes[:column_id]) + RepositoryCell.create_with_value( + item, column, cell_attributes[:value], current_user + ) + end + end + else + item = @inventory.repository_rows.create!(attributes) + end + render jsonapi: item, + serializer: InventoryItemSerializer, + include: :inventory_cells, + status: :created + end + private def load_team @team = Team.find(params.require(:team_id)) - return render json: {}, status: :forbidden unless can_read_team?(@team) + return render jsonapi: {}, status: :forbidden unless can_read_team?(@team) end def load_inventory @inventory = @team.repositories.find(params.require(:inventory_id)) end + + def check_manage_permissions + unless can_manage_repository_rows?(@team) + render body: nil, status: :forbidden + end + end + + def inventory_item_params + unless params.require(:data).require(:type) == 'inventory_items' + raise ActionController::BadRequest, + 'Wrong object type within parameters' + end + params.require(:data).require(:attributes) + params.permit(data: { attributes: %i(name uid) })[:data] + end + + # Partially implement sideposting draft + # https://github.com/json-api/json-api/pull/1197 + def inventory_cells_params + params[:included]&.select { |el| el[:type] == 'inventory_cells' } + end end end end diff --git a/app/controllers/api/v1/teams_controller.rb b/app/controllers/api/v1/teams_controller.rb index fdf00d14e..2c8630098 100644 --- a/app/controllers/api/v1/teams_controller.rb +++ b/app/controllers/api/v1/teams_controller.rb @@ -5,18 +5,18 @@ module Api def index teams = current_user.teams.page(params[:page]).per(params[:page_size]) - render json: teams, each_serializer: TeamSerializer + render jsonapi: teams, each_serializer: TeamSerializer end def show - render json: @team, serializer: TeamSerializer + render jsonapi: @team, serializer: TeamSerializer end private def load_team @team = Team.find(params.require(:id)) - return render json: {}, status: :forbidden unless can_read_team?(@team) + return render jsonapi: {}, status: :forbidden unless can_read_team?(@team) end end end diff --git a/app/models/repository_asset_value.rb b/app/models/repository_asset_value.rb index c9f564eb1..7e58168af 100644 --- a/app/models/repository_asset_value.rb +++ b/app/models/repository_asset_value.rb @@ -22,4 +22,19 @@ class RepositoryAssetValue < ApplicationRecord def data asset.file_file_name end + + def self.new_with_payload(payload, attributes) + value = new(attributes) + team = value.repository_cell.repository_column.repository.team + file = Paperclip.io_adapters.for(payload[:file_data]) + file.original_filename = payload[:file_name] + value.asset = Asset.create!( + file: file, + created_by: value.created_by, + last_modified_by: value.created_by, + team: team + ) + value.asset.post_process_file(team) + value + end end diff --git a/app/models/repository_cell.rb b/app/models/repository_cell.rb index ab2759573..3bd9ad3f5 100644 --- a/app/models/repository_cell.rb +++ b/app/models/repository_cell.rb @@ -31,12 +31,28 @@ class RepositoryCell < ActiveRecord::Base end), optional: true, foreign_key: :value_id + validates_inclusion_of :repository_column, + in: (lambda do |cell| + cell.repository_row.repository.repository_columns + end) validates :repository_column, presence: true validate :repository_column_data_type validates :repository_row, uniqueness: { scope: :repository_column }, unless: :importing + def self.create_with_value(row, column, data, user) + cell = new(repository_row: row, repository_column: column) + cell.transaction do + value_klass = column.data_type.constantize + value = value_klass.new_with_payload(data, repository_cell: cell, + created_by: user, + last_modified_by: user) + cell.value = value + value.save! + end + end + private def repository_column_data_type diff --git a/app/models/repository_date_value.rb b/app/models/repository_date_value.rb index ed1609d83..232d914eb 100644 --- a/app/models/repository_date_value.rb +++ b/app/models/repository_date_value.rb @@ -17,4 +17,10 @@ class RepositoryDateValue < ApplicationRecord def formatted data end + + def self.new_with_payload(payload, attributes) + value = new(attributes) + value.data = payload + value + end end diff --git a/app/models/repository_list_value.rb b/app/models/repository_list_value.rb index c53856809..72d61390c 100644 --- a/app/models/repository_list_value.rb +++ b/app/models/repository_list_value.rb @@ -20,4 +20,13 @@ class RepositoryListValue < ApplicationRecord return nil unless repository_list_item repository_list_item.data end + + def self.new_with_payload(payload, attributes) + value = new(attributes) + value.repository_list_item = value.repository_cell + .repository_column + .repository_list_items + .find(payload) + value + end end diff --git a/app/models/repository_text_value.rb b/app/models/repository_text_value.rb index 194e809dd..c3524aad4 100644 --- a/app/models/repository_text_value.rb +++ b/app/models/repository_text_value.rb @@ -18,4 +18,10 @@ class RepositoryTextValue < ApplicationRecord def formatted data end + + def self.new_with_payload(payload, attributes) + value = new(attributes) + value.data = payload + value + end end diff --git a/app/serializers/api/v1/inventory_cell_serializer.rb b/app/serializers/api/v1/inventory_cell_serializer.rb index ff478c9d9..b615f456c 100644 --- a/app/serializers/api/v1/inventory_cell_serializer.rb +++ b/app/serializers/api/v1/inventory_cell_serializer.rb @@ -4,16 +4,16 @@ module Api module V1 class InventoryCellSerializer < ActiveModel::Serializer type :inventory_cells - attributes :id, :data_type, :data + attributes :id, :value_type, :value attribute :repository_column_id, key: :column_id - def data_type + def value_type type_id = RepositoryColumn .data_types[object.repository_column.data_type] I18n.t("api.v1.inventory_data_types.t#{type_id}") end - def data + def value value = case object.value_type when 'RepositoryTextValue' @@ -23,7 +23,7 @@ module Api when 'RepositoryListValue' object.repository_list_value when 'RepositoryAssetValue' - object.repository_list_value + object.repository_asset_value end ActiveModelSerializers::SerializableResource.new( value, diff --git a/app/serializers/api/v1/inventory_item_serializer.rb b/app/serializers/api/v1/inventory_item_serializer.rb index 05a1b3d90..3df5dc3f8 100644 --- a/app/serializers/api/v1/inventory_item_serializer.rb +++ b/app/serializers/api/v1/inventory_item_serializer.rb @@ -4,7 +4,7 @@ module Api module V1 class InventoryItemSerializer < ActiveModel::Serializer type :inventory_items - attributes :id, :name + attributes :name has_many :repository_cells, key: :inventory_cells, serializer: InventoryCellSerializer, class_name: 'RepositoryCell' diff --git a/app/serializers/api/v1/repository_list_value_serializer.rb b/app/serializers/api/v1/repository_list_value_serializer.rb index 6360b529d..a073ca300 100644 --- a/app/serializers/api/v1/repository_list_value_serializer.rb +++ b/app/serializers/api/v1/repository_list_value_serializer.rb @@ -3,8 +3,8 @@ module Api module V1 class RepositoryListValueSerializer < ActiveModel::Serializer - attribute :formatted, key: :value attribute :repository_list_item_id, key: :inventory_list_item_id + attribute :formatted, key: :inventory_list_item_name end end end diff --git a/app/serializers/api/v1/repository_text_value_serializer.rb b/app/serializers/api/v1/repository_text_value_serializer.rb index ea1a51da0..62dab6b2b 100644 --- a/app/serializers/api/v1/repository_text_value_serializer.rb +++ b/app/serializers/api/v1/repository_text_value_serializer.rb @@ -3,7 +3,7 @@ module Api module V1 class RepositoryTextValueSerializer < ActiveModel::Serializer - attribute :formatted, key: :value + attribute :formatted, key: :text end end end diff --git a/config/initializers/active_model_serializer.rb b/config/initializers/active_model_serializer.rb index 692906d54..0258f46a7 100644 --- a/config/initializers/active_model_serializer.rb +++ b/config/initializers/active_model_serializer.rb @@ -1,4 +1,6 @@ # frozen_string_literal: true +require 'active_model_serializers/register_jsonapi_renderer' + ActiveModelSerializers.config.adapter = :json_api ActiveModelSerializers.config.key_transform = :unaltered diff --git a/config/initializers/api.rb b/config/initializers/api.rb index cad116bbf..8510bec09 100644 --- a/config/initializers/api.rb +++ b/config/initializers/api.rb @@ -10,6 +10,7 @@ Api.configure do |config| end config.core_api_v1_preview = true if ENV['CORE_API_V1_PREVIEW'] + Paperclip::DataUriAdapter.register if ENV['CORE_API_V1_PREVIEW'] vars = ENV.select { |name, _| name =~ /^[[:alnum:]]*_AZURE_AD_APP_ID/ } vars.each do |name, value| diff --git a/config/initializers/mime_types.rb b/config/initializers/mime_types.rb index 72b582a0a..dc1899682 100644 --- a/config/initializers/mime_types.rb +++ b/config/initializers/mime_types.rb @@ -2,4 +2,3 @@ # Add new mime types for use in respond_to blocks: # Mime::Type.register "text/richtext", :rtf -Mime::Type.register 'application/vnd.api+json', :json diff --git a/config/routes.rb b/config/routes.rb index a3a1d412f..5c3819723 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -545,11 +545,15 @@ Rails.application.routes.draw do resources :teams, only: %i(index show) do resources :inventories, only: %i(index show) do get 'columns', to: 'inventory_columns#index' - get 'items', to: 'inventory_items#index' + resources :inventory_items, + only: %i(index create), + path: 'items', + as: :items end end resources :users, only: %i(show) do - resources :user_identities, only: %i(index create show update destroy) + resources :user_identities, + only: %i(index create show update destroy) end end end diff --git a/spec/factories/repository_rows.rb b/spec/factories/repository_rows.rb index c4d83a5f4..b5a113f2f 100644 --- a/spec/factories/repository_rows.rb +++ b/spec/factories/repository_rows.rb @@ -2,6 +2,7 @@ FactoryBot.define do factory :repository_row do name 'Custom row' created_by { User.first || association(:user) } + repository { Repository.first || create(:repository) } last_modified_by { User.first || association(:user) } end end diff --git a/spec/requests/api/v1/inventory_items_controller_spec.rb b/spec/requests/api/v1/inventory_items_controller_spec.rb index 80e28fc80..5da4f1cae 100644 --- a/spec/requests/api/v1/inventory_items_controller_spec.rb +++ b/spec/requests/api/v1/inventory_items_controller_spec.rb @@ -75,7 +75,7 @@ RSpec.describe 'Api::V1::InventoryItemsController', type: :request do get api_v1_team_inventory_items_path( team_id: @teams.first.id, inventory_id: @teams.first.repositories.first.id - ), params: { page_size: 100 }, headers: @valid_headers + ), params: { page: { size: 100 } }, headers: @valid_headers expect { hash_body = json }.not_to raise_exception expect(hash_body[:data]).to match( ActiveModelSerializers::SerializableResource