diff --git a/Gemfile.lock b/Gemfile.lock index 22c5b1e14..53b350008 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -125,7 +125,6 @@ GEM crass (1.0.6) cssbundling-rails (1.4.0) railties (>= 6.0.0) - daemons (1.4.1) database_cleaner (2.0.2) database_cleaner-active_record (>= 2, < 3) database_cleaner-active_record (2.1.0) @@ -134,11 +133,6 @@ GEM database_cleaner-core (2.0.1) date (3.3.4) deep_merge (1.2.2) - delayed_job (4.1.11) - activesupport (>= 3.0, < 8.0) - delayed_job_active_record (4.1.8) - activerecord (>= 3.0, < 8.0) - delayed_job (>= 3.0, < 5) devise (4.9.4) bcrypt (~> 3.0) orm_adapter (~> 0.1) @@ -484,9 +478,7 @@ DEPENDENCIES config countries cssbundling-rails - daemons database_cleaner - delayed_job_active_record devise dotenv faker diff --git a/app/domain/ptime/assign_employee_ids.rb b/app/domain/ptime/assign_employee_ids.rb index ea4416952..b916ad6fc 100644 --- a/app/domain/ptime/assign_employee_ids.rb +++ b/app/domain/ptime/assign_employee_ids.rb @@ -25,25 +25,25 @@ def map_employees(should_map) mapped_people_count = 0 @ptime_employees.each do |ptime_employee| - ptime_employee_firstname = ptime_employee['attributes']['firstname'] - ptime_employee_lastname = ptime_employee['attributes']['lastname'] + ptime_employee_firstname = ptime_employee[:attributes][:firstname] + ptime_employee_lastname = ptime_employee[:attributes][:lastname] ptime_employee_name = "#{ptime_employee_firstname} #{ptime_employee_lastname}" - ptime_employee_email = ptime_employee['attributes']['email'] + ptime_employee_email = ptime_employee[:attributes][:email] matched_skills_people = Person.where(ptime_employee_id: nil) .where(name: ptime_employee_name) .where(email: ptime_employee_email) if matched_skills_people.empty? - unmatched_entries << { name: ptime_employee_name, id: ptime_employee['id'] } + unmatched_entries << { name: ptime_employee_name, id: ptime_employee[:id] } elsif matched_skills_people.one? if should_map matched_skills_person = matched_skills_people.first - matched_skills_person.ptime_employee_id = ptime_employee['id'] + matched_skills_person.ptime_employee_id = ptime_employee[:id] matched_skills_person.save! end mapped_people_count += 1 else - ambiguous_entries << { name: ptime_employee_name, id: ptime_employee['id'] } + ambiguous_entries << { name: ptime_employee_name, id: ptime_employee[:id] } end end @@ -60,7 +60,7 @@ def map_employees(should_map) def fetch_data puts 'Fetching required data...' - @ptime_employees = Ptime::Client.new.get('employees', { per_page: 1000 })['data'] + @ptime_employees = Ptime::Client.new.request(:get, 'employees', { per_page: 1000 }) @skills_people = Person.all puts 'Successfully fetched data' end diff --git a/app/domain/ptime/client.rb b/app/domain/ptime/client.rb index def0d8c5c..254305b17 100644 --- a/app/domain/ptime/client.rb +++ b/app/domain/ptime/client.rb @@ -9,38 +9,41 @@ def initialize @base_url = "#{ENV.fetch('PTIME_BASE_URL')}/api/v1/" end - def get(endpoint, params = {}) - request(:get, @base_url + endpoint, params) + def request(method, endpoint, params = {}) + path = @base_url + endpoint + + if last_ptime_error_more_than_5_minutes_ago? + send_request_and_parse_response(method, path, params) + else + raise CustomExceptions::PTimeClientError, 'Error' + end end private - def request(method, path, params = nil) - path = "#{path}?#{URI.encode_www_form(params)}" - response = RestClient.send(method, path, headers) - JSON.parse(response.body) - rescue RestClient::BadRequest => e - msg = response_error_message(e) - e.message = msg if msg.present? - raise e - end + def last_ptime_error_more_than_5_minutes_ago? + last_request_time = ENV.fetch('LAST_PTIME_ERROR', nil) + return true if last_request_time.nil? - def response_error_message(exception) - JSON.parse(exception.response.body).dig('error', 'message') - rescue JSON::ParserError # rescue only JSON parsing errors - nil + last_request_time.to_datetime <= 5.minutes.ago end - def headers - { - authorization: "Basic #{basic_token}", - content_type: :json, - accept: :json - } + def ptime_request(method, url) + RestClient::Request.new( + :method => method, + :url => url, + :user => ENV.fetch('PTIME_API_USERNAME'), + :password => ENV.fetch('PTIME_API_PASSWORD'), + :headers => { :accept => :json, :content_type => :json } + ) end - def basic_token - Base64.encode64("#{ENV.fetch('PTIME_API_USERNAME')}:#{ENV.fetch('PTIME_API_PASSWORD')}") + def send_request_and_parse_response(method, url, params) + url += "?#{params.to_query}" if method == :get && params.present? + response = ptime_request(method, url).execute + JSON.parse(response.body, symbolize_names: true)[:data] + rescue RestClient::ExceptionWithResponse + raise CustomExceptions::PTimeClientError, 'Error' end end end diff --git a/app/domain/ptime/update_people_data.rb b/app/domain/ptime/update_people_data.rb index db885ea87..b785f1f0e 100644 --- a/app/domain/ptime/update_people_data.rb +++ b/app/domain/ptime/update_people_data.rb @@ -7,18 +7,18 @@ class UpdatePeopleData # rubocop:disable Metrics def run - ptime_employees = Ptime::Client.new.get('employees', { per_page: 1000 })['data'] + ptime_employees = Ptime::Client.new.request(:get, 'employees', { per_page: 1000 }) ptime_employees.each do |ptime_employee| - ptime_employee_firstname = ptime_employee['attributes']['firstname'] - ptime_employee_lastname = ptime_employee['attributes']['lastname'] + ptime_employee_firstname = ptime_employee[:attributes][:firstname] + ptime_employee_lastname = ptime_employee[:attributes][:lastname] ptime_employee_name = "#{ptime_employee_firstname} #{ptime_employee_lastname}" - skills_person = Person.find_by(ptime_employee_id: ptime_employee['id']) + skills_person = Person.find_by(ptime_employee_id: ptime_employee[:id]) skills_person ||= Person.new skills_person.name = ptime_employee_name - skills_person.ptime_employee_id ||= ptime_employee['id'] - ptime_employee['attributes'].each do |key, value| + skills_person.ptime_employee_id ||= ptime_employee[:id] + ptime_employee[:attributes].each do |key, value| if key.to_sym.in?(ATTRIBUTE_MAPPING.keys) skills_person[ATTRIBUTE_MAPPING[key.to_sym]] = value end diff --git a/app/exceptions/custom_exceptions.rb b/app/exceptions/custom_exceptions.rb new file mode 100644 index 000000000..b59a9ef87 --- /dev/null +++ b/app/exceptions/custom_exceptions.rb @@ -0,0 +1,5 @@ +module CustomExceptions + + class PTimeClientError < StandardError; end + +end diff --git a/app/helpers/auth_helper.rb b/app/helpers/auth_helper.rb index 2d98e159f..c5cfbcdfc 100644 --- a/app/helpers/auth_helper.rb +++ b/app/helpers/auth_helper.rb @@ -21,4 +21,8 @@ def find_person_by_auth_user def devise? AuthConfig.keycloak? || Rails.env.test? end + + def ptime_available? + ActiveModel::Type::Boolean.new.cast(ENV.fetch('PTIME_API_ACCESSIBLE', true)) + end end diff --git a/app/helpers/person_helper.rb b/app/helpers/person_helper.rb index 6d2bcd6b3..3c6dde47b 100644 --- a/app/helpers/person_helper.rb +++ b/app/helpers/person_helper.rb @@ -76,4 +76,37 @@ def not_rated_default_skills(person) certificate: false, core_competence: false }) end end + + def sorted_people + fetch_ptime_or_skills_data.sort_by { |e| e.first.downcase } + end + + def fetch_ptime_or_skills_data + all_skills_people = Person.all.map { |p| [p.name, person_path(p)] } + return all_skills_people unless ptime_available? + + ptime_employees = Ptime::Client.new.request(:get, 'employees', { per_page: 1000 }) + build_dropdown_data(ptime_employees) + rescue CustomExceptions::PTimeClientError + ENV['LAST_PTIME_ERROR'] = DateTime.current.to_s + all_skills_people + end + + def build_dropdown_data(ptime_employees) + ptime_employees.map do |ptime_employee| + ptime_employee_name = append_ptime_employee_name(ptime_employee) + skills_person = Person.find_by(ptime_employee_id: ptime_employee[:id]) + ptime_employee_id = ptime_employee[:id] + already_exists = ptime_employee_id.in?(Person.pluck(:ptime_employee_id)) + path = new_person_path(ptime_employee_id: ptime_employee_id) + path = person_path(skills_person) if already_exists + + [ptime_employee_name, path] + end + end + + # Once https://github.com/puzzle/skills/issues/744 is merged there should be no need for this + def append_ptime_employee_name(ptime_employee) + "#{ptime_employee[:attributes][:firstname]} #{ptime_employee[:attributes][:lastname]}" + end end diff --git a/app/helpers/select_helper.rb b/app/helpers/select_helper.rb index fa69b73b2..5183f0401 100644 --- a/app/helpers/select_helper.rb +++ b/app/helpers/select_helper.rb @@ -2,8 +2,8 @@ module SelectHelper def select_when_availabale(obj) - selected = obj ? obj.id : '' - prompt = obj ? false : true + selected = obj.present? ? polymorphic_path(obj) : '' + prompt = obj.nil? { selected: selected, prompt: prompt, disabled: '' } end diff --git a/app/javascript/controllers/dropdown_controller.js b/app/javascript/controllers/dropdown_controller.js index 5639ce0ac..e9a88982f 100644 --- a/app/javascript/controllers/dropdown_controller.js +++ b/app/javascript/controllers/dropdown_controller.js @@ -11,6 +11,6 @@ export default class extends Controller { } handleChange(event) { - window.location.href = event.target.dataset.value + event.target.value; + window.location.href = event.target.value; } } diff --git a/app/views/people/_search.html.haml b/app/views/people/_search.html.haml index be4edee70..ebc4c114a 100644 --- a/app/views/people/_search.html.haml +++ b/app/views/people/_search.html.haml @@ -1,7 +1,7 @@ %div.d-flex.align-items-center.justify-content-between %div.d-flex.col-9.gap-3 %span.col-6{"data-controller": "dropdown"} - = collection_select :person_id, :person, Person.all.sort_by {|p| p.name.downcase }, :id, :name, select_when_availabale(person), {data:{"dropdown-target": "dropdown" , action: "change->dropdown#handleChange", value: "/people/"}} + = select :person_id, :person, options_for_select(sorted_people, select_when_availabale(person)), {prompt: person.nil?}, {data: { "dropdown-target" => "dropdown", action: "change->dropdown#handleChange"}} %div.d-flex.align-items-center.text-gray = "#{t 'profile.updated_at'}: #{@person&.last_updated_at.strftime("%d.%m.%Y")}" if @person&.last_updated_at %a.d-flex.justify-content-between#new-person-button{href: "/people/new"} diff --git a/bin/delayed_job b/bin/delayed_job deleted file mode 100755 index edf195985..000000000 --- a/bin/delayed_job +++ /dev/null @@ -1,5 +0,0 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.join(File.dirname(__FILE__), '..', 'config', 'environment')) -require 'delayed/command' -Delayed::Command.new(ARGV).daemonize diff --git a/config/application.rb b/config/application.rb index 0a6ebec84..dee2a1781 100644 --- a/config/application.rb +++ b/config/application.rb @@ -37,7 +37,5 @@ class Application < Rails::Application config.assets.enabled = true config.assets.paths << Rails.root.join("uploads") - - config.active_job.queue_adapter = :delayed_job end end diff --git a/db/migrate/20240701085558_create_delayed_jobs.rb b/db/migrate/20240701085558_create_delayed_jobs.rb deleted file mode 100644 index c3ce5afc9..000000000 --- a/db/migrate/20240701085558_create_delayed_jobs.rb +++ /dev/null @@ -1,22 +0,0 @@ -class CreateDelayedJobs < ActiveRecord::Migration[7.0] - def self.up - create_table :delayed_jobs do |table| - table.integer :priority, default: 0, null: false # Allows some jobs to jump to the front of the queue - table.integer :attempts, default: 0, null: false # Provides for retries, but still fail eventually. - table.text :handler, null: false # YAML-encoded string of the object that will do work - table.text :last_error # reason for last failure (See Note below) - table.datetime :run_at # When to run. Could be Time.zone.now for immediately, or sometime in the future. - table.datetime :locked_at # Set when a client is working on this object - table.datetime :failed_at # Set when all retries have failed (actually, by default, the record is deleted instead) - table.string :locked_by # Who is working on this object (if locked) - table.string :queue # The name of the queue this job is in - table.timestamps null: true - end - - add_index :delayed_jobs, [:priority, :run_at], name: "delayed_jobs_priority" - end - - def self.down - drop_table :delayed_jobs - end -end diff --git a/db/schema.rb b/db/schema.rb index cfb6306f6..038f495fa 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.0].define(version: 2024_07_01_085558) do +ActiveRecord::Schema[7.0].define(version: 2024_06_24_122411) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -77,21 +77,6 @@ t.datetime "updated_at", precision: nil, null: false end - create_table "delayed_jobs", force: :cascade do |t| - t.integer "priority", default: 0, null: false - t.integer "attempts", default: 0, null: false - t.text "handler", null: false - t.text "last_error" - t.datetime "run_at" - t.datetime "locked_at" - t.datetime "failed_at" - t.string "locked_by" - t.string "queue" - t.datetime "created_at" - t.datetime "updated_at" - t.index ["priority", "run_at"], name: "delayed_jobs_priority" - end - create_table "departments", force: :cascade do |t| t.string "name", null: false t.datetime "created_at", null: false diff --git a/docker-compose.yml b/docker-compose.yml index 8568cb03a..c54c7c2ce 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -41,7 +41,7 @@ services: /bin/bash -c " curl -fsSL https://deb.nodesource.com/setup_18.x | bash - && apt-get install -y nodejs && - npm install -g yarn && bin/assets && + npm install -g yarn && yarn add nodemon esbuild && bin/assets && sleep infinity" volumes: - ./:/myapp diff --git a/package.json b/package.json index 41be726a2..1316e6270 100644 --- a/package.json +++ b/package.json @@ -8,8 +8,8 @@ "autoprefixer": "^10.4.17", "bootstrap": "^5.3.2", "bootstrap-icons": "^1.11.3", - "esbuild": "^0.20.0", - "nodemon": "^3.0.3", + "esbuild": "^0.23.0", + "nodemon": "^3.1.4", "postcss": "^8.4.33", "postcss-cli": "^11.0.0", "sass": "^1.70.0", diff --git a/spec/domain/ptime/assign_employee_ids_spec.rb b/spec/domain/ptime/assign_employee_ids_spec.rb index 1e7e7a462..3e8f01a6c 100644 --- a/spec/domain/ptime/assign_employee_ids_spec.rb +++ b/spec/domain/ptime/assign_employee_ids_spec.rb @@ -1,74 +1,8 @@ require 'rails_helper' -ptime_base_test_url = "www.ptime.example.com" -ptime_api_test_username = "test username" -ptime_api_test_password = "test password" -ENV["PTIME_BASE_URL"] = ptime_base_test_url -ENV["PTIME_API_USERNAME"] = ptime_api_test_username -ENV["PTIME_API_PASSWORD"] = ptime_api_test_password - describe Ptime::AssignEmployeeIds do - employees_json = { - 'data': [ - { - 'id': 33, - 'type': 'employee', - 'attributes': { - 'shortname': 'LSM', - 'firstname': 'Longmax', - 'lastname': 'Smith', - 'email': 'longmax@example.com', - 'marital_status': 'single', - 'nationalities': [ - 'ZW' - ], - 'graduation': 'BSc in Architecture', - 'department_shortname': 'SYS', - 'employment_roles': [] - } - }, - { - 'id': 21, - 'type': 'employee', - 'attributes': { - 'shortname': 'AMA', - 'firstname': 'Alice', - 'lastname': 'Mante', - 'email': 'alice@example.com', - 'marital_status': 'single', - 'nationalities': [ - 'AU' - ], - 'graduation': 'MSc in writing', - 'department_shortname': 'SYS', - 'employment_roles': [] - } - }, - { - 'id': 45, - 'type': 'employee', - 'attributes': { - 'shortname': 'CFO', - 'firstname': 'Charlie', - 'lastname': 'Ford', - 'email': 'charlie@example.com', - 'marital_status': 'married', - 'nationalities': [ - 'GB' - ], - 'graduation': 'MSc in networking', - 'department_shortname': 'SYS', - 'employment_roles': [] - } - }, - ] - }.to_json it 'should map people with the correct puzzletime id' do - stub_request(:get, "#{ptime_base_test_url}/api/v1/employees?per_page=1000"). - to_return(body: employees_json, headers: { 'content-type': "application/vnd.api+json; charset=utf-8" }, status: 200) - .with(basic_auth: [ptime_api_test_username, ptime_api_test_password]) - person_longmax = people(:longmax) person_alice = people(:alice) person_charlie = people(:charlie) @@ -81,12 +15,12 @@ end it 'should not map people that are not found' do - parsed_employees_json = JSON.parse(employees_json) - parsed_employees_json['data'].first["attributes"]["firstname"] = "Rochus" - parsed_employees_json['data'].second["attributes"]["firstname"] = "Melchior" - stub_request(:get, "#{ptime_base_test_url}/api/v1/employees?per_page=1000"). - to_return(body: parsed_employees_json.to_json, headers: { 'content-type': "application/vnd.api+json; charset=utf-8" }, status: 200) - .with(basic_auth: [ptime_api_test_username, ptime_api_test_password]) + parsed_employees_json = ptime_employees + parsed_employees_json[:data].first[:attributes][:firstname] = "Rochus" + parsed_employees_json[:data].second[:attributes][:firstname] = "Melchior" + + + stub_ptime_request(parsed_employees_json.to_json) person_longmax = people(:longmax) person_alice = people(:alice) @@ -100,10 +34,6 @@ end it 'should not map people that are ambiguous' do - stub_request(:get, "#{ptime_base_test_url}/api/v1/employees?per_page=1000"). - to_return(body: employees_json, headers: { 'content-type': "application/vnd.api+json; charset=utf-8" }, status: 200) - .with(basic_auth: [ptime_api_test_username, ptime_api_test_password]) - person_longmax = people(:longmax) person_alice = people(:alice) person_charlie = people(:charlie) @@ -120,10 +50,6 @@ end it 'should not map people on dry run' do - stub_request(:get, "#{ptime_base_test_url}/api/v1/employees?per_page=1000"). - to_return(body: employees_json, headers: { 'content-type': "application/vnd.api+json; charset=utf-8" }, status: 200) - .with(basic_auth: [ptime_api_test_username, ptime_api_test_password]) - person_longmax = people(:longmax) person_alice = people(:alice) person_charlie = people(:charlie) diff --git a/spec/domain/ptime/client_spec.rb b/spec/domain/ptime/client_spec.rb index bdcc6b9c7..42d727b07 100644 --- a/spec/domain/ptime/client_spec.rb +++ b/spec/domain/ptime/client_spec.rb @@ -1,85 +1,29 @@ require 'rails_helper' -ptime_base_test_url = "www.ptime.example.com" -ptime_api_test_username = "test username" -ptime_api_test_password = "test password" -ENV["PTIME_BASE_URL"] = ptime_base_test_url -ENV["PTIME_API_USERNAME"] = ptime_api_test_username -ENV["PTIME_API_PASSWORD"] = ptime_api_test_password - describe Ptime::Client do - employees_json = { - 'data': [ - { - 'id': 33, - 'type': 'employee', - 'attributes': { - 'shortname': 'LSM', - 'firstname': 'Longmax', - 'lastname': 'Smith', - 'email': 'longmax@example.com', - 'marital_status': 'single', - 'nationalities': [ - 'ZW' - ], - 'graduation': 'BSc in Architecture', - 'department_shortname': 'SYS', - 'employment_roles': [] - } - }, - { - 'id': 21, - 'type': 'employee', - 'attributes': { - 'shortname': 'AMA', - 'firstname': 'Alice', - 'lastname': 'Mante', - 'email': 'alice@example.com', - 'marital_status': 'single', - 'nationalities': [ - 'AU' - ], - 'graduation': 'MSc in writing', - 'department_shortname': 'SYS', - 'employment_roles': [] - } - }, - { - 'id': 45, - 'type': 'employee', - 'attributes': { - 'shortname': 'CFO', - 'firstname': 'Charlie', - 'lastname': 'Ford', - 'email': 'charlie@example.com', - 'marital_status': 'married', - 'nationalities': [ - 'GB' - ], - 'graduation': 'MSc in networking', - 'department_shortname': 'SYS', - 'employment_roles': [] - } - }, - ] - }.to_json - it 'should be able to fetch employee data' do - stub_request(:get, "#{ptime_base_test_url}/api/v1/employees"). - to_return(body: employees_json, headers: { 'content-type': "application/vnd.api+json; charset=utf-8" }, status: 200) - .with(basic_auth: [ptime_api_test_username, ptime_api_test_password]) - - fetched_employees = Ptime::Client.new.get("employees") - expect(fetched_employees).to eq(JSON.parse(employees_json)) + fetched_employees = Ptime::Client.new.request(:get, "employees", { per_page: 1000 }) + expect(fetched_employees).to eq(ptime_employees_data) end - it 'should throw error message when host is not reachable' do - ENV["PTIME_BASE_URL"] = "www.unreachablehost.example.com" + it 'should not raise PTimeClientError if LAST_PTIME_ERROR is more than 5 minutes ago' do + stub_env_var("LAST_PTIME_ERROR", 6.minutes.ago.to_s) + fetched_employees = Ptime::Client.new.request(:get, "employees", { per_page: 1000 }) + expect(fetched_employees).to eq(ptime_employees_data) + end - stub_request(:get, "www.unreachablehost.example.com/api/v1/"). - to_return(body: employees_json, status: 404) - .with(basic_auth: [ptime_api_test_username, ptime_api_test_password]) + it 'should raise PTimeClientError page is unreachable' do + stub_env_var("PTIME_BASE_URL", "irgend.oepp.is") + stub_ptime_request(ptime_employees.to_json, "employees?per_page=1000", 404) + expect { + Ptime::Client.new.request(:get, "employees", { per_page: 1000 }) + }.to raise_error(CustomExceptions::PTimeClientError) + end - expect{ Ptime::Client.new.get("") }.to raise_error(RestClient::NotFound) + it 'should raise PTimeClientError if LAST_PTIME_ERROR is less than 5 minutes ago' do + stub_env_var("LAST_PTIME_ERROR", 4.minutes.ago.to_s) + expect { + Ptime::Client.new.request(:get, "employees", { per_page: 1000 }) + }.to raise_error(CustomExceptions::PTimeClientError) end end diff --git a/spec/domain/ptime/update_people_data_spec.rb b/spec/domain/ptime/update_people_data_spec.rb index 45d6fd365..dd38186df 100644 --- a/spec/domain/ptime/update_people_data_spec.rb +++ b/spec/domain/ptime/update_people_data_spec.rb @@ -1,93 +1,10 @@ require 'rails_helper' -ptime_base_test_url = "www.ptime.example.com" -ptime_api_test_username = "test username" -ptime_api_test_password = "test password" -ENV["PTIME_API_USERNAME"] = ptime_api_test_username -ENV["PTIME_API_PASSWORD"] = ptime_api_test_password - describe Ptime::UpdatePeopleData do - before(:each) do - ENV["PTIME_BASE_URL"] = ptime_base_test_url - end - it 'should update the data of existing people after mapping' do - employees = { - 'data': [ - { - 'id': 33, - 'type': 'employee', - 'attributes': { - 'shortname': 'LSM', - 'firstname': 'Longmax', - 'lastname': 'Smith', - 'email': 'longmax@example.com', - 'marital_status': 'single', - 'nationalities': [ - 'ZW' - ], - 'graduation': 'BSc in Architecture', - 'department_shortname': 'SYS', - 'employment_roles': [] - } - }, - { - 'id': 21, - 'type': 'employee', - 'attributes': { - 'shortname': 'AMA', - 'firstname': 'Alice', - 'lastname': 'Mante', - 'email': 'alice@example.com', - 'marital_status': 'single', - 'nationalities': [ - 'AU' - ], - 'graduation': 'MSc in writing', - 'department_shortname': 'SYS', - 'employment_roles': [] - } - }, - { - 'id': 45, - 'type': 'employee', - 'attributes': { - 'shortname': 'CFO', - 'firstname': 'Charlie', - 'lastname': 'Ford', - 'email': 'charlie@example.com', - 'marital_status': 'married', - 'nationalities': [ - 'GB' - ], - 'graduation': 'MSc in networking', - 'department_shortname': 'SYS', - 'employment_roles': [] - } - }, - { - 'id': 50, - 'type': 'employee', - 'attributes': { - 'shortname': 'WAL', - 'firstname': 'Wally', - 'lastname': 'Allround', - 'email': 'wally@example.com', - 'marital_status': 'married', - 'nationalities': [ - 'US' - ], - 'graduation': 'Full-Stack Developer', - 'department_shortname': 'SYS', - 'employment_roles': [] - } - }, - ] - } + employees = fixture_data "updating_ptime_employees" - stub_request(:get, "#{ptime_base_test_url}/api/v1/employees?per_page=1000"). - to_return(body: employees.to_json, headers: { 'content-type': "application/vnd.api+json; charset=utf-8" }, status: 200) - .with(basic_auth: [ptime_api_test_username, ptime_api_test_password]) + stub_ptime_request(employees.to_json) person_longmax = people(:longmax) person_alice = people(:alice) @@ -95,15 +12,13 @@ person_wally = people(:wally) Ptime::AssignEmployeeIds.new.run(should_map: true) - employees[:data].first[:attributes][:email] = "changedmax@example.com" employees[:data].second[:attributes][:graduation] = "MSc in some other field" employees[:data].third[:attributes][:firstname] = "Claudius" employees[:data].fourth[:attributes][:marital_status] = "single" - stub_request(:get, "#{ptime_base_test_url}/api/v1/employees?per_page=1000"). - to_return(body: employees.to_json, headers: { 'content-type': "application/vnd.api+json; charset=utf-8" }, status: 200) - .with(basic_auth: [ptime_api_test_username, ptime_api_test_password]) + + stub_ptime_request(employees.to_json) Ptime::UpdatePeopleData.new.run @@ -114,40 +29,18 @@ end it 'should create new person when person does not exist' do - new_employee = { - 'data': [ - { - 'id': 33, - 'type': 'employee', - 'attributes': { - 'shortname': 'PFI', - 'firstname': 'Peterson', - 'lastname': 'Findus', - 'email': 'peterson@example.com', - 'marital_status': 'single', - 'nationalities': [ - 'ZW' - ], - 'graduation': 'Cat caretaker', - 'department_shortname': 'CAT', - 'employment_roles': [] - } - } - ] - } - - stub_request(:get, "#{ptime_base_test_url}/api/v1/employees?per_page=1000"). - to_return(body: new_employee.to_json, headers: { 'content-type': "application/vnd.api+json; charset=utf-8" }, status: 200) - .with(basic_auth: [ptime_api_test_username, ptime_api_test_password]) + new_employee = fixture_data "new_ptime_employee" + new_employee_data = new_employee[:data] + stub_ptime_request(new_employee.to_json) Ptime::AssignEmployeeIds.new.run(should_map: true) Ptime::UpdatePeopleData.new.run - new_employee_attributes = new_employee[:data].first[:attributes] + new_employee_attributes = new_employee_data.first[:attributes] new_employee_name = "#{new_employee_attributes[:firstname]} #{new_employee_attributes[:lastname]}" created_person = Person.find_by(name: new_employee_name) expect(created_person).not_to be_nil - expect(created_person.ptime_employee_id).to eq(new_employee[:data].first[:id]) + expect(created_person.ptime_employee_id).to eq(new_employee_data.first[:id]) expect(created_person.shortname).to eq(new_employee_attributes[:shortname]) expect(created_person.name).to eq(new_employee_name) expect(created_person.email).to eq(new_employee_attributes[:email]) @@ -158,4 +51,4 @@ expect(created_person.location).to eq('Bern') expect(created_person.nationality).to eq('CH') end -end \ No newline at end of file +end diff --git a/spec/features/edit_people_skills_spec.rb b/spec/features/edit_people_skills_spec.rb index 13a30b28d..da178ab78 100644 --- a/spec/features/edit_people_skills_spec.rb +++ b/spec/features/edit_people_skills_spec.rb @@ -32,6 +32,7 @@ within("#default-skill-#{ember.skill.id}") do select_star_rating(2) select_level(3, "person[people_skills_attributes][0][level]") + find('#person_people_skills_attributes_0_certificate').hover end # Check if changes were saved diff --git a/spec/features/people_spec.rb b/spec/features/people_spec.rb index 32942bfbc..77f7e4c6d 100644 --- a/spec/features/people_spec.rb +++ b/spec/features/people_spec.rb @@ -4,6 +4,7 @@ describe 'People Search', type: :feature, js: true do before(:each) do + use_skills_db sign_in auth_users(:user), scope: :auth_user end @@ -49,6 +50,16 @@ expect(page).to have_no_text(name) end end + + it 'should redirect to correct person ' do + alice = people(:alice) + alice.ptime_employee_id = 21 + alice.save! + visit people_path + select_from_slim_select("#person_id_person", alice.name) + expect(page).to have_current_path(person_path(alice)) + expect(page).to have_css('.ss-single', text: 'Alice Mante') + end end def common_languages_translated diff --git a/spec/features/profile_scroll_to_spec.rb b/spec/features/profile_scroll_to_spec.rb index ca99864d6..f227eb7c4 100644 --- a/spec/features/profile_scroll_to_spec.rb +++ b/spec/features/profile_scroll_to_spec.rb @@ -6,7 +6,6 @@ before(:each) do sign_in auth_users(:user), scope: :auth_user visit root_path - Capybara.page.driver.browser.manage.window.maximize end it 'Should change background of selected section' do diff --git a/spec/fixtures/files/json/all_ptime_employees.json b/spec/fixtures/files/json/all_ptime_employees.json new file mode 100644 index 000000000..685e11445 --- /dev/null +++ b/spec/fixtures/files/json/all_ptime_employees.json @@ -0,0 +1,53 @@ +{ + "data": [ + { + "id" : 33, + "type" : "employee", + "attributes" : { + "shortname" : "LSM", + "firstname" : "Longmax", + "lastname" : "Smith", + "email" : "longmax@example.com", + "marital_status" : "single", + "nationalities" : ["ZW"], + "graduation" : "BSc in Architecture", + "department_shortname" : "SYS", + "employment_roles" : [] + } + }, + { + "id" : 21, + "type" : "employee", + "attributes" : { + "shortname" : "AMA", + "firstname" : "Alice", + "lastname" : "Mante", + "full_name" : "Alice Mante", + "email" : "alice@example.com", + "marital_status" : "single", + "nationalities" : ["AU"], + "graduation" : "MSc in writing", + "department_shortname" : "SYS", + "employment_roles" : [], + "is_employed" : false, + "birthdate" : "01.04.2001", + "location" : "Bern" + } + }, + { + "id" : 45, + "type" : "employee", + "attributes" : { + "shortname" : "CFO", + "firstname" : "Charlie", + "lastname" : "Ford", + "email" : "charlie@example.com", + "marital_status" : "married", + "nationalities" : ["GB"], + "graduation" : "MSc in networking", + "department_shortname" : "SYS", + "employment_roles" : [] + } + } + ] +} \ No newline at end of file diff --git a/spec/fixtures/files/json/new_ptime_employee.json b/spec/fixtures/files/json/new_ptime_employee.json new file mode 100644 index 000000000..308d4fcb4 --- /dev/null +++ b/spec/fixtures/files/json/new_ptime_employee.json @@ -0,0 +1,21 @@ +{ + "data": [ + { + "id": 33, + "type": "employee", + "attributes": { + "shortname": "PFI", + "firstname": "Peterson", + "lastname": "Findus", + "email": "peterson@example.com", + "marital_status": "single", + "nationalities": [ + "ZW" + ], + "graduation": "Cat caretaker", + "department_shortname": "CAT", + "employment_roles": [] + } + } + ] + } \ No newline at end of file diff --git a/spec/fixtures/files/json/updating_ptime_employees.json b/spec/fixtures/files/json/updating_ptime_employees.json new file mode 100644 index 000000000..14be70aba --- /dev/null +++ b/spec/fixtures/files/json/updating_ptime_employees.json @@ -0,0 +1,72 @@ +{ + "data": [ + { + "id": 33, + "type": "employee", + "attributes": { + "shortname": "LSM", + "firstname": "Longmax", + "lastname": "Smith", + "email": "longmax@example.com", + "marital_status": "single", + "nationalities": [ + "ZW" + ], + "graduation": "BSc in Architecture", + "department_shortname": "SYS", + "employment_roles": [] + } + }, + { + "id": 21, + "type": "employee", + "attributes": { + "shortname": "AMA", + "firstname": "Alice", + "lastname": "Mante", + "email": "alice@example.com", + "marital_status": "single", + "nationalities": [ + "AU" + ], + "graduation": "MSc in writing", + "department_shortname": "SYS", + "employment_roles": [] + } + }, + { + "id": 45, + "type": "employee", + "attributes": { + "shortname": "CFO", + "firstname": "Charlie", + "lastname": "Ford", + "email": "charlie@example.com", + "marital_status": "married", + "nationalities": [ + "GB" + ], + "graduation": "MSc in networking", + "department_shortname": "SYS", + "employment_roles": [] + } + }, + { + "id": 50, + "type": "employee", + "attributes": { + "shortname": "WAL", + "firstname": "Wally", + "lastname": "Allround", + "email": "wally@example.com", + "marital_status": "married", + "nationalities": [ + "US" + ], + "graduation": "Full-Stack Developer", + "department_shortname": "SYS", + "employment_roles": [] + } + } + ] + } \ No newline at end of file diff --git a/spec/helpers/person_helper_spec.rb b/spec/helpers/person_helper_spec.rb new file mode 100644 index 000000000..d832ed96b --- /dev/null +++ b/spec/helpers/person_helper_spec.rb @@ -0,0 +1,52 @@ +require 'rails_helper' +RSpec.describe PersonHelper, type: :helper do + + describe '#fetch_ptime_or_skills_data' do + + it 'should send request to ptime api' do + allow(helper).to receive(:ptime_available?).and_return(true) + skills_people = helper.fetch_ptime_or_skills_data + expected = [ + ["Longmax Smith", "/people/new?ptime_employee_id=33"], + ["Alice Mante", "/people/new?ptime_employee_id=21"], + ["Charlie Ford", "/people/new?ptime_employee_id=45"] + ] + expect(skills_people).to eq(expected) + end + + it 'should return people from skills database if last request was right now' do + allow(helper).to receive(:ptime_available?).and_return(false) + + skills_people = helper.fetch_ptime_or_skills_data + expected = [ + ["Bob Anderson", "/people/902541635"], + ["Alice Mante", "/people/663665735"], + ["ken", "/people/155397742"], + ["Charlie Ford", "/people/786122151"], + ["Wally Allround", "/people/790004949"], + ["Hope Sunday", "/people/247095502"], + ["Longmax Smith", "/people/169654640"] + ] + expect(skills_people).to eq(expected) + end + end + + describe '#build_dropdown_data' do + it 'should build correct dropdown data' do + longmax = people(:longmax) + alice = people(:alice) + people(:charlie) + + longmax.update!(ptime_employee_id: 33) + alice.update!(ptime_employee_id: 21) + + dropdown_data = build_dropdown_data(ptime_employees_data) + expected = [ + ["Longmax Smith", "/people/169654640"], + ["Alice Mante", "/people/663665735"], + ["Charlie Ford", "/people/new?ptime_employee_id=45"]] + expect(dropdown_data).to eq(expected) + + end + end +end diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index b2531af7d..d57d07b37 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -45,6 +45,10 @@ self.class.fixtures :all end + config.before(:each, js: true) do + Capybara.page.current_window.resize_to(1920, 1080) + end + if Bullet.enable? config.before(:each) do Bullet.start_request @@ -58,6 +62,13 @@ config.infer_spec_type_from_file_location! + config.before(:each) do + stub_env_variables_and_request + end + + show_logs = ENV.fetch('SHOW_LOGS', false) + config.before { allow($stdout).to receive(:puts) } unless show_logs + # Controller helper config.include(JsonMacros, type: :controller) config.include(JsonAssertion, type: :controller) @@ -71,6 +82,11 @@ config.include(ActionView::RecordIdentifier, type: :feature) # Custom helpers + ## Global helpers + config.include(PtimeHelpers) + config.include(JsonHelpers) + + ## Feature helpers config.include(PersonRelationsHelpers, type: :feature) config.include(SlimselectHelpers, type: :feature) config.include(PeopleSkillsHelpers, type: :feature) diff --git a/spec/support/json_helpers.rb b/spec/support/json_helpers.rb new file mode 100644 index 000000000..66c213946 --- /dev/null +++ b/spec/support/json_helpers.rb @@ -0,0 +1,7 @@ +module JsonHelpers + + def fixture_data(filename, symbolize_names= true) + file_content= file_fixture("json/#{filename}.json").read + JSON.parse(file_content, symbolize_names: symbolize_names) + end +end diff --git a/spec/support/ptime_helpers.rb b/spec/support/ptime_helpers.rb new file mode 100644 index 000000000..72da9c69b --- /dev/null +++ b/spec/support/ptime_helpers.rb @@ -0,0 +1,37 @@ +module PtimeHelpers + def stub_env_variables_and_request + stub_env_var("PTIME_BASE_URL", "www.ptime.example.com") + stub_env_var("PTIME_API_USERNAME", "test username") + stub_env_var("PTIME_API_PASSWORD", "test password") + stub_ptime_request(ptime_employees.to_json) + end + + def ptime_employees + fixture_data("all_ptime_employees") + end + + def ptime_employees_data + fixture_data("all_ptime_employees")[:data] + end + + def stub_ptime_request(return_body, path =nil, status = 200) + path ||= "employees?per_page=1000" + url = "http://#{ENV["PTIME_BASE_URL"]}/api/v1/#{path}" + content_type = "application/vnd.api+json; charset=utf-8" + + stub_request(:get, url) + .to_return(body: return_body, headers: { 'content-type': content_type }, status: status) + .with(basic_auth: [ENV["PTIME_API_USERNAME"], ENV["PTIME_API_PASSWORD"]]) + end + + def use_skills_db + allow_any_instance_of(RestClient::Request) + .to receive(:execute) + .and_raise(RestClient::ExceptionWithResponse) + end + + def stub_env_var(name, value) + allow(ENV).to receive(:fetch).with(name).and_return(value) + stub_const('ENV', ENV.to_hash.merge(name => value)) + end +end diff --git a/yarn.lock b/yarn.lock index 00836deb0..1c57f5884 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,120 +2,125 @@ # yarn lockfile v1 -"@esbuild/aix-ppc64@0.20.1": - version "0.20.1" - resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.20.1.tgz#eafa8775019b3650a77e8310ba4dbd17ca7af6d5" - integrity sha512-m55cpeupQ2DbuRGQMMZDzbv9J9PgVelPjlcmM5kxHnrBdBx6REaEd7LamYV7Dm8N7rCyR/XwU6rVP8ploKtIkA== - -"@esbuild/android-arm64@0.20.1": - version "0.20.1" - resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.20.1.tgz#68791afa389550736f682c15b963a4f37ec2f5f6" - integrity sha512-hCnXNF0HM6AjowP+Zou0ZJMWWa1VkD77BXe959zERgGJBBxB+sV+J9f/rcjeg2c5bsukD/n17RKWXGFCO5dD5A== - -"@esbuild/android-arm@0.20.1": - version "0.20.1" - resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.20.1.tgz#38c91d8ee8d5196f7fbbdf4f0061415dde3a473a" - integrity sha512-4j0+G27/2ZXGWR5okcJi7pQYhmkVgb4D7UKwxcqrjhvp5TKWx3cUjgB1CGj1mfdmJBQ9VnUGgUhign+FPF2Zgw== - -"@esbuild/android-x64@0.20.1": - version "0.20.1" - resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.20.1.tgz#93f6190ce997b313669c20edbf3645fc6c8d8f22" - integrity sha512-MSfZMBoAsnhpS+2yMFYIQUPs8Z19ajwfuaSZx+tSl09xrHZCjbeXXMsUF/0oq7ojxYEpsSo4c0SfjxOYXRbpaA== - -"@esbuild/darwin-arm64@0.20.1": - version "0.20.1" - resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.20.1.tgz#0d391f2e81fda833fe609182cc2fbb65e03a3c46" - integrity sha512-Ylk6rzgMD8klUklGPzS414UQLa5NPXZD5tf8JmQU8GQrj6BrFA/Ic9tb2zRe1kOZyCbGl+e8VMbDRazCEBqPvA== - -"@esbuild/darwin-x64@0.20.1": - version "0.20.1" - resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.20.1.tgz#92504077424584684862f483a2242cfde4055ba2" - integrity sha512-pFIfj7U2w5sMp52wTY1XVOdoxw+GDwy9FsK3OFz4BpMAjvZVs0dT1VXs8aQm22nhwoIWUmIRaE+4xow8xfIDZA== - -"@esbuild/freebsd-arm64@0.20.1": - version "0.20.1" - resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.20.1.tgz#a1646fa6ba87029c67ac8a102bb34384b9290774" - integrity sha512-UyW1WZvHDuM4xDz0jWun4qtQFauNdXjXOtIy7SYdf7pbxSWWVlqhnR/T2TpX6LX5NI62spt0a3ldIIEkPM6RHw== - -"@esbuild/freebsd-x64@0.20.1": - version "0.20.1" - resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.20.1.tgz#41c9243ab2b3254ea7fb512f71ffdb341562e951" - integrity sha512-itPwCw5C+Jh/c624vcDd9kRCCZVpzpQn8dtwoYIt2TJF3S9xJLiRohnnNrKwREvcZYx0n8sCSbvGH349XkcQeg== - -"@esbuild/linux-arm64@0.20.1": - version "0.20.1" - resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.20.1.tgz#f3c1e1269fbc9eedd9591a5bdd32bf707a883156" - integrity sha512-cX8WdlF6Cnvw/DO9/X7XLH2J6CkBnz7Twjpk56cshk9sjYVcuh4sXQBy5bmTwzBjNVZze2yaV1vtcJS04LbN8w== - -"@esbuild/linux-arm@0.20.1": - version "0.20.1" - resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.20.1.tgz#4503ca7001a8ee99589c072801ce9d7540717a21" - integrity sha512-LojC28v3+IhIbfQ+Vu4Ut5n3wKcgTu6POKIHN9Wpt0HnfgUGlBuyDDQR4jWZUZFyYLiz4RBBBmfU6sNfn6RhLw== - -"@esbuild/linux-ia32@0.20.1": - version "0.20.1" - resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.20.1.tgz#98c474e3e0cbb5bcbdd8561a6e65d18f5767ce48" - integrity sha512-4H/sQCy1mnnGkUt/xszaLlYJVTz3W9ep52xEefGtd6yXDQbz/5fZE5dFLUgsPdbUOQANcVUa5iO6g3nyy5BJiw== - -"@esbuild/linux-loong64@0.20.1": - version "0.20.1" - resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.20.1.tgz#a8097d28d14b9165c725fe58fc438f80decd2f33" - integrity sha512-c0jgtB+sRHCciVXlyjDcWb2FUuzlGVRwGXgI+3WqKOIuoo8AmZAddzeOHeYLtD+dmtHw3B4Xo9wAUdjlfW5yYA== - -"@esbuild/linux-mips64el@0.20.1": - version "0.20.1" - resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.20.1.tgz#c44f6f0d7d017c41ad3bb15bfdb69b690656b5ea" - integrity sha512-TgFyCfIxSujyuqdZKDZ3yTwWiGv+KnlOeXXitCQ+trDODJ+ZtGOzLkSWngynP0HZnTsDyBbPy7GWVXWaEl6lhA== - -"@esbuild/linux-ppc64@0.20.1": - version "0.20.1" - resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.20.1.tgz#0765a55389a99237b3c84227948c6e47eba96f0d" - integrity sha512-b+yuD1IUeL+Y93PmFZDZFIElwbmFfIKLKlYI8M6tRyzE6u7oEP7onGk0vZRh8wfVGC2dZoy0EqX1V8qok4qHaw== - -"@esbuild/linux-riscv64@0.20.1": - version "0.20.1" - resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.20.1.tgz#e4153b032288e3095ddf4c8be07893781b309a7e" - integrity sha512-wpDlpE0oRKZwX+GfomcALcouqjjV8MIX8DyTrxfyCfXxoKQSDm45CZr9fanJ4F6ckD4yDEPT98SrjvLwIqUCgg== - -"@esbuild/linux-s390x@0.20.1": - version "0.20.1" - resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.20.1.tgz#b9ab8af6e4b73b26d63c1c426d7669a5d53eb5a7" - integrity sha512-5BepC2Au80EohQ2dBpyTquqGCES7++p7G+7lXe1bAIvMdXm4YYcEfZtQrP4gaoZ96Wv1Ute61CEHFU7h4FMueQ== - -"@esbuild/linux-x64@0.20.1": - version "0.20.1" - resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.20.1.tgz#0b25da17ac38c3e11cdd06ca3691d4d6bef2755f" - integrity sha512-5gRPk7pKuaIB+tmH+yKd2aQTRpqlf1E4f/mC+tawIm/CGJemZcHZpp2ic8oD83nKgUPMEd0fNanrnFljiruuyA== - -"@esbuild/netbsd-x64@0.20.1": - version "0.20.1" - resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.20.1.tgz#3148e48406cd0d4f7ba1e0bf3f4d77d548c98407" - integrity sha512-4fL68JdrLV2nVW2AaWZBv3XEm3Ae3NZn/7qy2KGAt3dexAgSVT+Hc97JKSZnqezgMlv9x6KV0ZkZY7UO5cNLCg== - -"@esbuild/openbsd-x64@0.20.1": - version "0.20.1" - resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.20.1.tgz#7b73e852986a9750192626d377ac96ac2b749b76" - integrity sha512-GhRuXlvRE+twf2ES+8REbeCb/zeikNqwD3+6S5y5/x+DYbAQUNl0HNBs4RQJqrechS4v4MruEr8ZtAin/hK5iw== - -"@esbuild/sunos-x64@0.20.1": - version "0.20.1" - resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.20.1.tgz#402a441cdac2eee98d8be378c7bc23e00c1861c5" - integrity sha512-ZnWEyCM0G1Ex6JtsygvC3KUUrlDXqOihw8RicRuQAzw+c4f1D66YlPNNV3rkjVW90zXVsHwZYWbJh3v+oQFM9Q== - -"@esbuild/win32-arm64@0.20.1": - version "0.20.1" - resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.20.1.tgz#36c4e311085806a6a0c5fc54d1ac4d7b27e94d7b" - integrity sha512-QZ6gXue0vVQY2Oon9WyLFCdSuYbXSoxaZrPuJ4c20j6ICedfsDilNPYfHLlMH7vGfU5DQR0czHLmJvH4Nzis/A== - -"@esbuild/win32-ia32@0.20.1": - version "0.20.1" - resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.20.1.tgz#0cf933be3fb9dc58b45d149559fe03e9e22b54fe" - integrity sha512-HzcJa1NcSWTAU0MJIxOho8JftNp9YALui3o+Ny7hCh0v5f90nprly1U3Sj1Ldj/CvKKdvvFsCRvDkpsEMp4DNw== - -"@esbuild/win32-x64@0.20.1": - version "0.20.1" - resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.20.1.tgz#77583b6ea54cee7c1410ebbd54051b6a3fcbd8ba" - integrity sha512-0MBh53o6XtI6ctDnRMeQ+xoCN8kD2qI1rY1KgF/xdWQwoFeKou7puvDfV8/Wv4Ctx2rRpET/gGdz3YlNtNACSA== +"@esbuild/aix-ppc64@0.23.0": + version "0.23.0" + resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.23.0.tgz#145b74d5e4a5223489cabdc238d8dad902df5259" + integrity sha512-3sG8Zwa5fMcA9bgqB8AfWPQ+HFke6uD3h1s3RIwUNK8EG7a4buxvuFTs3j1IMs2NXAk9F30C/FF4vxRgQCcmoQ== + +"@esbuild/android-arm64@0.23.0": + version "0.23.0" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.23.0.tgz#453bbe079fc8d364d4c5545069e8260228559832" + integrity sha512-EuHFUYkAVfU4qBdyivULuu03FhJO4IJN9PGuABGrFy4vUuzk91P2d+npxHcFdpUnfYKy0PuV+n6bKIpHOB3prQ== + +"@esbuild/android-arm@0.23.0": + version "0.23.0" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.23.0.tgz#26c806853aa4a4f7e683e519cd9d68e201ebcf99" + integrity sha512-+KuOHTKKyIKgEEqKbGTK8W7mPp+hKinbMBeEnNzjJGyFcWsfrXjSTNluJHCY1RqhxFurdD8uNXQDei7qDlR6+g== + +"@esbuild/android-x64@0.23.0": + version "0.23.0" + resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.23.0.tgz#1e51af9a6ac1f7143769f7ee58df5b274ed202e6" + integrity sha512-WRrmKidLoKDl56LsbBMhzTTBxrsVwTKdNbKDalbEZr0tcsBgCLbEtoNthOW6PX942YiYq8HzEnb4yWQMLQuipQ== + +"@esbuild/darwin-arm64@0.23.0": + version "0.23.0" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.23.0.tgz#d996187a606c9534173ebd78c58098a44dd7ef9e" + integrity sha512-YLntie/IdS31H54Ogdn+v50NuoWF5BDkEUFpiOChVa9UnKpftgwzZRrI4J132ETIi+D8n6xh9IviFV3eXdxfow== + +"@esbuild/darwin-x64@0.23.0": + version "0.23.0" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.23.0.tgz#30c8f28a7ef4e32fe46501434ebe6b0912e9e86c" + integrity sha512-IMQ6eme4AfznElesHUPDZ+teuGwoRmVuuixu7sv92ZkdQcPbsNHzutd+rAfaBKo8YK3IrBEi9SLLKWJdEvJniQ== + +"@esbuild/freebsd-arm64@0.23.0": + version "0.23.0" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.23.0.tgz#30f4fcec8167c08a6e8af9fc14b66152232e7fb4" + integrity sha512-0muYWCng5vqaxobq6LB3YNtevDFSAZGlgtLoAc81PjUfiFz36n4KMpwhtAd4he8ToSI3TGyuhyx5xmiWNYZFyw== + +"@esbuild/freebsd-x64@0.23.0": + version "0.23.0" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.23.0.tgz#1003a6668fe1f5d4439e6813e5b09a92981bc79d" + integrity sha512-XKDVu8IsD0/q3foBzsXGt/KjD/yTKBCIwOHE1XwiXmrRwrX6Hbnd5Eqn/WvDekddK21tfszBSrE/WMaZh+1buQ== + +"@esbuild/linux-arm64@0.23.0": + version "0.23.0" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.23.0.tgz#3b9a56abfb1410bb6c9138790f062587df3e6e3a" + integrity sha512-j1t5iG8jE7BhonbsEg5d9qOYcVZv/Rv6tghaXM/Ug9xahM0nX/H2gfu6X6z11QRTMT6+aywOMA8TDkhPo8aCGw== + +"@esbuild/linux-arm@0.23.0": + version "0.23.0" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.23.0.tgz#237a8548e3da2c48cd79ae339a588f03d1889aad" + integrity sha512-SEELSTEtOFu5LPykzA395Mc+54RMg1EUgXP+iw2SJ72+ooMwVsgfuwXo5Fn0wXNgWZsTVHwY2cg4Vi/bOD88qw== + +"@esbuild/linux-ia32@0.23.0": + version "0.23.0" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.23.0.tgz#4269cd19cb2de5de03a7ccfc8855dde3d284a238" + integrity sha512-P7O5Tkh2NbgIm2R6x1zGJJsnacDzTFcRWZyTTMgFdVit6E98LTxO+v8LCCLWRvPrjdzXHx9FEOA8oAZPyApWUA== + +"@esbuild/linux-loong64@0.23.0": + version "0.23.0" + resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.23.0.tgz#82b568f5658a52580827cc891cb69d2cb4f86280" + integrity sha512-InQwepswq6urikQiIC/kkx412fqUZudBO4SYKu0N+tGhXRWUqAx+Q+341tFV6QdBifpjYgUndV1hhMq3WeJi7A== + +"@esbuild/linux-mips64el@0.23.0": + version "0.23.0" + resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.23.0.tgz#9a57386c926262ae9861c929a6023ed9d43f73e5" + integrity sha512-J9rflLtqdYrxHv2FqXE2i1ELgNjT+JFURt/uDMoPQLcjWQA5wDKgQA4t/dTqGa88ZVECKaD0TctwsUfHbVoi4w== + +"@esbuild/linux-ppc64@0.23.0": + version "0.23.0" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.23.0.tgz#f3a79fd636ba0c82285d227eb20ed8e31b4444f6" + integrity sha512-cShCXtEOVc5GxU0fM+dsFD10qZ5UpcQ8AM22bYj0u/yaAykWnqXJDpd77ublcX6vdDsWLuweeuSNZk4yUxZwtw== + +"@esbuild/linux-riscv64@0.23.0": + version "0.23.0" + resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.23.0.tgz#f9d2ef8356ce6ce140f76029680558126b74c780" + integrity sha512-HEtaN7Y5UB4tZPeQmgz/UhzoEyYftbMXrBCUjINGjh3uil+rB/QzzpMshz3cNUxqXN7Vr93zzVtpIDL99t9aRw== + +"@esbuild/linux-s390x@0.23.0": + version "0.23.0" + resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.23.0.tgz#45390f12e802201f38a0229e216a6aed4351dfe8" + integrity sha512-WDi3+NVAuyjg/Wxi+o5KPqRbZY0QhI9TjrEEm+8dmpY9Xir8+HE/HNx2JoLckhKbFopW0RdO2D72w8trZOV+Wg== + +"@esbuild/linux-x64@0.23.0": + version "0.23.0" + resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.23.0.tgz#c8409761996e3f6db29abcf9b05bee8d7d80e910" + integrity sha512-a3pMQhUEJkITgAw6e0bWA+F+vFtCciMjW/LPtoj99MhVt+Mfb6bbL9hu2wmTZgNd994qTAEw+U/r6k3qHWWaOQ== + +"@esbuild/netbsd-x64@0.23.0": + version "0.23.0" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.23.0.tgz#ba70db0114380d5f6cfb9003f1d378ce989cd65c" + integrity sha512-cRK+YDem7lFTs2Q5nEv/HHc4LnrfBCbH5+JHu6wm2eP+d8OZNoSMYgPZJq78vqQ9g+9+nMuIsAO7skzphRXHyw== + +"@esbuild/openbsd-arm64@0.23.0": + version "0.23.0" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-arm64/-/openbsd-arm64-0.23.0.tgz#72fc55f0b189f7a882e3cf23f332370d69dfd5db" + integrity sha512-suXjq53gERueVWu0OKxzWqk7NxiUWSUlrxoZK7usiF50C6ipColGR5qie2496iKGYNLhDZkPxBI3erbnYkU0rQ== + +"@esbuild/openbsd-x64@0.23.0": + version "0.23.0" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.23.0.tgz#b6ae7a0911c18fe30da3db1d6d17a497a550e5d8" + integrity sha512-6p3nHpby0DM/v15IFKMjAaayFhqnXV52aEmv1whZHX56pdkK+MEaLoQWj+H42ssFarP1PcomVhbsR4pkz09qBg== + +"@esbuild/sunos-x64@0.23.0": + version "0.23.0" + resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.23.0.tgz#58f0d5e55b9b21a086bfafaa29f62a3eb3470ad8" + integrity sha512-BFelBGfrBwk6LVrmFzCq1u1dZbG4zy/Kp93w2+y83Q5UGYF1d8sCzeLI9NXjKyujjBBniQa8R8PzLFAUrSM9OA== + +"@esbuild/win32-arm64@0.23.0": + version "0.23.0" + resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.23.0.tgz#b858b2432edfad62e945d5c7c9e5ddd0f528ca6d" + integrity sha512-lY6AC8p4Cnb7xYHuIxQ6iYPe6MfO2CC43XXKo9nBXDb35krYt7KGhQnOkRGar5psxYkircpCqfbNDB4uJbS2jQ== + +"@esbuild/win32-ia32@0.23.0": + version "0.23.0" + resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.23.0.tgz#167ef6ca22a476c6c0c014a58b4f43ae4b80dec7" + integrity sha512-7L1bHlOTcO4ByvI7OXVI5pNN6HSu6pUQq9yodga8izeuB1KcT2UkHaH6118QJwopExPn0rMHIseCTx1CRo/uNA== + +"@esbuild/win32-x64@0.23.0": + version "0.23.0" + resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.23.0.tgz#db44a6a08520b5f25bbe409f34a59f2d4bcc7ced" + integrity sha512-Arm+WgUFLUATuoxCJcahGuk6Yj9Pzxd6l11Zb/2aAuv5kWWvvfhLFo2fni4uSK5vzlUdCGZ/BdV5tH8klj8p8g== "@hotwired/stimulus@^3.2.2": version "3.2.2" @@ -321,34 +326,35 @@ emoji-regex@^8.0.0: resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== -esbuild@^0.20.0: - version "0.20.1" - resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.20.1.tgz#1e4cbb380ad1959db7609cb9573ee77257724a3e" - integrity sha512-OJwEgrpWm/PCMsLVWXKqvcjme3bHNpOgN7Tb6cQnR5n0TPbQx1/Xrn7rqM+wn17bYeT6MGB5sn1Bh5YiGi70nA== +esbuild@^0.23.0: + version "0.23.0" + resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.23.0.tgz#de06002d48424d9fdb7eb52dbe8e95927f852599" + integrity sha512-1lvV17H2bMYda/WaFb2jLPeHU3zml2k4/yagNMG8Q/YtfMjCwEUZa2eXXMgZTVSL5q1n4H7sQ0X6CdJDqqeCFA== optionalDependencies: - "@esbuild/aix-ppc64" "0.20.1" - "@esbuild/android-arm" "0.20.1" - "@esbuild/android-arm64" "0.20.1" - "@esbuild/android-x64" "0.20.1" - "@esbuild/darwin-arm64" "0.20.1" - "@esbuild/darwin-x64" "0.20.1" - "@esbuild/freebsd-arm64" "0.20.1" - "@esbuild/freebsd-x64" "0.20.1" - "@esbuild/linux-arm" "0.20.1" - "@esbuild/linux-arm64" "0.20.1" - "@esbuild/linux-ia32" "0.20.1" - "@esbuild/linux-loong64" "0.20.1" - "@esbuild/linux-mips64el" "0.20.1" - "@esbuild/linux-ppc64" "0.20.1" - "@esbuild/linux-riscv64" "0.20.1" - "@esbuild/linux-s390x" "0.20.1" - "@esbuild/linux-x64" "0.20.1" - "@esbuild/netbsd-x64" "0.20.1" - "@esbuild/openbsd-x64" "0.20.1" - "@esbuild/sunos-x64" "0.20.1" - "@esbuild/win32-arm64" "0.20.1" - "@esbuild/win32-ia32" "0.20.1" - "@esbuild/win32-x64" "0.20.1" + "@esbuild/aix-ppc64" "0.23.0" + "@esbuild/android-arm" "0.23.0" + "@esbuild/android-arm64" "0.23.0" + "@esbuild/android-x64" "0.23.0" + "@esbuild/darwin-arm64" "0.23.0" + "@esbuild/darwin-x64" "0.23.0" + "@esbuild/freebsd-arm64" "0.23.0" + "@esbuild/freebsd-x64" "0.23.0" + "@esbuild/linux-arm" "0.23.0" + "@esbuild/linux-arm64" "0.23.0" + "@esbuild/linux-ia32" "0.23.0" + "@esbuild/linux-loong64" "0.23.0" + "@esbuild/linux-mips64el" "0.23.0" + "@esbuild/linux-ppc64" "0.23.0" + "@esbuild/linux-riscv64" "0.23.0" + "@esbuild/linux-s390x" "0.23.0" + "@esbuild/linux-x64" "0.23.0" + "@esbuild/netbsd-x64" "0.23.0" + "@esbuild/openbsd-arm64" "0.23.0" + "@esbuild/openbsd-x64" "0.23.0" + "@esbuild/sunos-x64" "0.23.0" + "@esbuild/win32-arm64" "0.23.0" + "@esbuild/win32-ia32" "0.23.0" + "@esbuild/win32-x64" "0.23.0" escalade@^3.1.1: version "3.1.2" @@ -543,10 +549,10 @@ node-releases@^2.0.14: resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.14.tgz#2ffb053bceb8b2be8495ece1ab6ce600c4461b0b" integrity sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw== -nodemon@^3.0.3: - version "3.0.3" - resolved "https://registry.yarnpkg.com/nodemon/-/nodemon-3.0.3.tgz#244a62d1c690eece3f6165c6cdb0db03ebd80b76" - integrity sha512-7jH/NXbFPxVaMwmBCC2B9F/V6X1VkEdNgx3iu9jji8WxWcvhMWkmhNWhI5077zknOnZnBzba9hZP6bCPJLSReQ== +nodemon@^3.1.4: + version "3.1.4" + resolved "https://registry.yarnpkg.com/nodemon/-/nodemon-3.1.4.tgz#c34dcd8eb46a05723ccde60cbdd25addcc8725e4" + integrity sha512-wjPBbFhtpJwmIeY2yP7QF+UKzPfltVGtfce1g/bB15/8vCGZj8uxD62b/b9M9/WVgme0NZudpownKN+c0plXlQ== dependencies: chokidar "^3.5.2" debug "^4"