diff --git a/.ruby-version b/.ruby-version new file mode 100644 index 0000000..bea438e --- /dev/null +++ b/.ruby-version @@ -0,0 +1 @@ +3.3.1 diff --git a/backend/Gemfile b/backend/Gemfile index 9b3267b..fecaf66 100644 --- a/backend/Gemfile +++ b/backend/Gemfile @@ -144,6 +144,7 @@ group :development do # Speed up commands on slow machines / big apps [https://github.com/rails/spring] gem "spring" + gem 'graphiql-rails' # Annotate Rails classes with schema and routes info gem "annotate" end diff --git a/backend/app/controllers/api/v1/handle_file_controller.rb b/backend/app/controllers/api/v1/handle_file_controller.rb new file mode 100644 index 0000000..9ce50e5 --- /dev/null +++ b/backend/app/controllers/api/v1/handle_file_controller.rb @@ -0,0 +1,12 @@ +module Api + module V1 + class HandleFileController < Api::ApplicationController + include HandleFileHelper + + def import + rows = set_rows_to_json(params[:file].tempfile) + render json: {file_rows: rows}, status: :ok + end + end + end +end \ No newline at end of file diff --git a/backend/app/helpers/handle_file_helper.rb b/backend/app/helpers/handle_file_helper.rb new file mode 100644 index 0000000..df74648 --- /dev/null +++ b/backend/app/helpers/handle_file_helper.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true +require 'csv' + +module HandleFileHelper + def set_rows_to_json(file) + file_parsed = parse_file_csv(file) + set_response(file_parsed) + end + + def parse_file_csv(file) + CSV.parse(file.read) + end + + def encode_string(string) + string.force_encoding("ISO-8859-1").encode("UTF-8") + end + + def set_response(parsed_file) + json_response = {} + parsed_file.each_with_index { |row,index| json_response.merge!("row_#{index + 1}": encode_string(row.join(','))) } + + json_response + end +end diff --git a/backend/config/application.rb b/backend/config/application.rb index 8d2e009..4e9eaf9 100644 --- a/backend/config/application.rb +++ b/backend/config/application.rb @@ -3,6 +3,7 @@ require_relative "boot" require "rails/all" +require "csv" # Require the gems listed in Gemfile, including any gems # you've limited to :test, :development, or :production. diff --git a/backend/config/routes.rb b/backend/config/routes.rb index aa9596d..51497c9 100644 --- a/backend/config/routes.rb +++ b/backend/config/routes.rb @@ -18,6 +18,9 @@ namespace :api, defaults: { format: :json } do namespace :v1 do + get "/public_method", to: "hello_world#public_method" + get "/private_method", to: "hello_world#private_method" + post "/import", to: "handle_file#import" resources :users, only: %i[index create] resources :schools, only: %i[index] end diff --git a/backend/spec/fixtures/files/empty_file.csv b/backend/spec/fixtures/files/empty_file.csv new file mode 100644 index 0000000..e69de29 diff --git a/backend/spec/fixtures/files/non_csv_file.txt b/backend/spec/fixtures/files/non_csv_file.txt new file mode 100644 index 0000000..e69de29 diff --git a/backend/spec/fixtures/files/valid_file.csv b/backend/spec/fixtures/files/valid_file.csv new file mode 100644 index 0000000..cfa01ee --- /dev/null +++ b/backend/spec/fixtures/files/valid_file.csv @@ -0,0 +1,3 @@ +name,age +Alice,30 +Bob,25 \ No newline at end of file diff --git a/backend/spec/requests/api/v1/handle_file_spec.rb b/backend/spec/requests/api/v1/handle_file_spec.rb new file mode 100644 index 0000000..06dd747 --- /dev/null +++ b/backend/spec/requests/api/v1/handle_file_spec.rb @@ -0,0 +1,49 @@ +require "rails_helper" + +RSpec.describe "HandleFiles" do + describe 'POST api/v1/import' do + context 'when CSV file is valid' do + let(:csv_file) { fixture_file_upload(Rails.root.join('spec', 'fixtures', 'files', 'valid_file.csv'), 'text/csv') } + + it 'returns status ok' do + post '/api/v1/import', params: { file: csv_file } + expect(response).to have_http_status(:ok) + end + + it 'returns the correct JSON response with file rows' do + post '/api/v1/import', params: { file: csv_file } + expect(JSON.parse(response.body)['file_rows']).to be_a(Hash) + end + end + + + context 'when CSV file is empty' do + let(:empty_csv_file) { fixture_file_upload(Rails.root.join('spec', 'fixtures', 'files', 'empty_file.csv'), 'text/csv') } + + it 'returns status ok' do + post '/api/v1/import', params: { file: empty_csv_file } + expect(response).to have_http_status(:ok) + end + + it 'returns empty file rows in JSON' do + post '/api/v1/import', params: { file: empty_csv_file } + expect(JSON.parse(response.body)['file_rows']).to be_empty + end + end + + context "when CSV file is valid" do + let(:csv_file) { fixture_file_upload(Rails.root.join('spec', 'fixtures', 'files', 'valid_file.csv'), 'text/csv') } + + it "returns status ok" do + post '/api/v1/import', params: { file: csv_file } + expect(response).to have_http_status(:ok) + end + + it "returns the correct JSON response with file rows" do + post '/api/v1/import', params: { file: csv_file } + expect(response.body).to include('file_rows') + end + end + end +end + diff --git a/backend/spec/requests/api/v1/hello_word_request_spec.rb b/backend/spec/requests/api/v1/hello_word_request_spec.rb index 93df43b..d0022a1 100644 --- a/backend/spec/requests/api/v1/hello_word_request_spec.rb +++ b/backend/spec/requests/api/v1/hello_word_request_spec.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require "rails_helper" +require 'rails_helper' RSpec.describe "HelloWorlds" do describe "GET /api/v1/public_method" do diff --git a/config/routes.rb b/config/routes.rb new file mode 100644 index 0000000..52b6b14 --- /dev/null +++ b/config/routes.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +require "sidekiq/web" + +Rails.application.routes.draw do + mount GraphiQL::Rails::Engine, at: "/graphiql", graphql_path: "/graphql" if Rails.env.development? + post "/graphql", to: "graphql#execute" + mount Sidekiq::Web => "/sidekiq" + mount Rswag::Ui::Engine => "/api-docs" + mount Rswag::Api::Engine => "/api-docs" + devise_for :users + # Define your application routes per the DSL in https://guides.rubyonrails.org/routing.html + + # Defines the root path route ("/") + # root "articles#index" + + get "/public_method", to: "hello_world#public_method" + get "/private_method", to: "hello_world#private_method" + get "/search", to: "hello_world#search" + + namespace :api, defaults: { format: :json } do + namespace :v1 do + get "/public_method", to: "hello_world#public_method" + get "/private_method", to: "hello_world#private_method" + post "/import", to: "handle_file#import" + + resources :users, only: [:index] + end + end +end