From 24033bec7a2a87685ab006ac18b51f77bf39388b Mon Sep 17 00:00:00 2001 From: Urban Rotnik Date: Mon, 16 Sep 2019 09:31:30 +0200 Subject: [PATCH] Add API endpoint for Task creation --- app/controllers/api/v1/tasks_controller.rb | 18 ++++ app/models/my_module.rb | 3 +- config/routes.rb | 4 +- spec/factories/my_modules.rb | 2 +- spec/models/experiment_spec.rb | 2 +- spec/requests/api/v1/tasks_controller_spec.rb | 99 ++++++++++++++++++- .../test_experiment_data/experiment.json | 2 +- 7 files changed, 122 insertions(+), 8 deletions(-) diff --git a/app/controllers/api/v1/tasks_controller.rb b/app/controllers/api/v1/tasks_controller.rb index 94250cb0b..db6e02348 100644 --- a/app/controllers/api/v1/tasks_controller.rb +++ b/app/controllers/api/v1/tasks_controller.rb @@ -23,6 +23,16 @@ module Api render jsonapi: @task, serializer: TaskSerializer end + def create + raise PermissionError.new(MyModule, :create) unless can_manage_experiment?(@experiment) + + my_module = @experiment.my_modules.create!(my_module_params) + + render jsonapi: my_module, + serializer: TaskSerializer, + status: :created + end + def activities activities = ActivitiesService.my_module_activities(@task) .page(params.dig(:page, :number)) @@ -34,6 +44,14 @@ module Api private + def my_module_params + raise TypeError unless params.require(:data).require(:type) == 'tasks' + + attr_list = %i(name x y) + params.require(:data).require(:attributes).require(attr_list) + params.require(:data).require(:attributes).permit(attr_list + [:description]) + end + # Made the method below because its more elegant than changing parameters # in routes file, and here. It exists because when we call input or output # for a task, the "id" that used to be task id is now an id for the output diff --git a/app/models/my_module.rb b/app/models/my_module.rb index 1cfe8f49c..482e538ee 100644 --- a/app/models/my_module.rb +++ b/app/models/my_module.rb @@ -17,7 +17,6 @@ class MyModule < ApplicationRecord validates :experiment, presence: true validates :my_module_group, presence: true, if: proc { |mm| !mm.my_module_group_id.nil? } - validates_uniqueness_of :x, scope: %i(y experiment_id), message: :not_unique belongs_to :created_by, @@ -427,7 +426,7 @@ class MyModule < ApplicationRecord description: self.description, x: self.x, y: self.y) - clone.save + clone.save(validate: false) # Remove the automatically generated protocol, # & clone the protocol instead diff --git a/config/routes.rb b/config/routes.rb index 85555c776..496ebac90 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -620,7 +620,7 @@ Rails.application.routes.draw do namespace :api, defaults: { format: 'json' } do get 'health', to: 'api#health' get 'status', to: 'api#status' - if Api.configuration.core_api_v1_enabled + if Api.configuration.core_api_v1_enabled || Rails.env.development? namespace :v1 do resources :teams, only: %i(index show) do resources :inventories, @@ -655,7 +655,7 @@ Rails.application.routes.draw do resources :experiments, only: %i(index show) do resources :task_groups, only: %i(index show) resources :connections, only: %i(index show) - resources :tasks, only: %i(index show) do + resources :tasks, only: %i(index show create) do resources :task_inventory_items, only: %i(index show), path: 'items', as: :items diff --git a/spec/factories/my_modules.rb b/spec/factories/my_modules.rb index 55d1b14f4..dca7990f4 100644 --- a/spec/factories/my_modules.rb +++ b/spec/factories/my_modules.rb @@ -4,7 +4,7 @@ FactoryBot.define do factory :my_module do sequence(:name) { |n| "Task-#{n}" } x { Faker::Number.between(from: 1, to: 100) } - y { Faker::Number.between(from: 1, to: 100) } + sequence(:y) { |n| n } workflow_order { MyModule.where(experiment_id: experiment.id).count + 1 } experiment my_module_group { create :my_module_group, experiment: experiment } diff --git a/spec/models/experiment_spec.rb b/spec/models/experiment_spec.rb index e83629855..602990bbe 100644 --- a/spec/models/experiment_spec.rb +++ b/spec/models/experiment_spec.rb @@ -120,7 +120,7 @@ describe Experiment, type: :model do { id: 'n' + i.to_s, name: t.name + '_new', x: 50, - y: 50 } + y: 50 + i } end end let(:to_clone) do diff --git a/spec/requests/api/v1/tasks_controller_spec.rb b/spec/requests/api/v1/tasks_controller_spec.rb index b1e0173ab..3059aab09 100644 --- a/spec/requests/api/v1/tasks_controller_spec.rb +++ b/spec/requests/api/v1/tasks_controller_spec.rb @@ -2,7 +2,7 @@ require 'rails_helper' -RSpec.describe "Api::V1::TasksController", type: :request do +RSpec.describe 'Api::V1::TasksController', type: :request do before :all do @user = create(:user) @teams = create_list(:team, 2, created_by: @user) @@ -139,4 +139,101 @@ RSpec.describe "Api::V1::TasksController", type: :request do expect(hash_body['errors'][0]).to include('status': 404) end end + + describe 'POST tasks, #create' do + before :all do + create :user_project, :normal_user, user: @user, project: @valid_project + @valid_headers['Content-Type'] = 'application/json' + end + + let(:request_body) do + { + data: { + type: 'tasks', + attributes: { + name: 'task name', + x: 1, + y: 4 + } + } + } + end + + context 'when has valid params' do + let(:action) do + post(api_v1_team_project_experiment_tasks_path( + team_id: @teams.first.id, + project_id: @valid_project.id, + experiment_id: @valid_experiment.id + ), + params: request_body.to_json, + headers: @valid_headers) + end + + it 'creates new my module' do + expect { action }.to change { MyModule.count }.by(1) + end + + it 'returns status 201' do + action + + expect(response).to have_http_status 201 + end + + it 'returns well formated response' do + action + + expect(json).to match( + hash_including( + data: hash_including( + type: 'tasks', + attributes: hash_including(name: 'task name'), + relationships: hash_including(outputs: { data: [] }, inputs: { data: [] }) + ) + ) + ) + end + end + + context 'when has not valid params' do + it 'renders 404 when project not found' do + post(api_v1_team_project_experiment_tasks_path( + team_id: @teams.first.id, + project_id: -1, + experiment_id: @valid_experiment.id + ), + params: request_body.to_json, + headers: @valid_headers) + + expect(response).to have_http_status(404) + end + + it 'renders 403 when user is not member of the team' do + post(api_v1_team_project_experiment_tasks_path( + team_id: @teams.second.id, + project_id: @valid_project.id, + experiment_id: @valid_experiment.id + ), + params: request_body.to_json, + headers: @valid_headers) + + expect(response).to have_http_status(403) + end + + it 'renders 403 for use with view permissions' do + up = UserProject.where(user: @user, project: @valid_project).first + up.update_attribute(:role, :viewer) + + post(api_v1_team_project_experiment_tasks_path( + team_id: @teams.first.id, + project_id: @valid_project.id, + experiment_id: @valid_experiment.id + ), + params: request_body.to_json, + headers: @valid_headers) + + expect(response).to have_http_status(403) + end + end + end end diff --git a/spec/services/model_importers/test_experiment_data/experiment.json b/spec/services/model_importers/test_experiment_data/experiment.json index 03a6380a3..4e6c2033a 100644 --- a/spec/services/model_importers/test_experiment_data/experiment.json +++ b/spec/services/model_importers/test_experiment_data/experiment.json @@ -53,7 +53,7 @@ "updated_at": "2019-01-21T13:32:46.278Z", "workflow_order": -1, "x": 64, - "y": 46 + "y": 22 }, "my_module_repository_rows": [], "my_module_tags": [],