From 8c1d8a14705c7278d2b0b042159409b6c78adf07 Mon Sep 17 00:00:00 2001 From: Eugene Burmakin Date: Sat, 25 May 2024 12:47:25 +0200 Subject: [PATCH 01/10] Accept API key in query string for api/v1/overland/batches --- .../api/v1/overland/batches_controller.rb | 3 ++- app/controllers/application_controller.rb | 14 ++++++++++ app/views/devise/registrations/edit.html.erb | 2 +- spec/requests/api/v1/overland/batches_spec.rb | 26 ++++++++++++++----- .../v1/overland/batches_controller_spec.rb | 14 ++++++++++ swagger/v1/swagger.yaml | 12 +++++++-- 6 files changed, 60 insertions(+), 11 deletions(-) diff --git a/app/controllers/api/v1/overland/batches_controller.rb b/app/controllers/api/v1/overland/batches_controller.rb index 2b0aacc8..014b43f5 100644 --- a/app/controllers/api/v1/overland/batches_controller.rb +++ b/app/controllers/api/v1/overland/batches_controller.rb @@ -2,6 +2,7 @@ class Api::V1::Overland::BatchesController < ApplicationController skip_forgery_protection + before_action :authenticate_api_key def create Overland::BatchCreatingJob.perform_later(batch_params) @@ -12,6 +13,6 @@ class Api::V1::Overland::BatchesController < ApplicationController private def batch_params - params.permit(locations: [:type, geometry: {}, properties: {}], batch: {}) + params.permit(locations: [:type, { geometry: {}, properties: {} }], batch: {}) end end diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index cfb0a0a5..71021818 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -1,3 +1,17 @@ +# frozen_string_literal: true + class ApplicationController < ActionController::Base include Pundit::Authorization + + protected + + def authenticate_api_key + return head :unauthorized unless current_api_user + + true + end + + def current_api_user + @current_api_user ||= User.find_by(api_key: params[:api_key]) + end end diff --git a/app/views/devise/registrations/edit.html.erb b/app/views/devise/registrations/edit.html.erb index 8e0968fb..73cab649 100644 --- a/app/views/devise/registrations/edit.html.erb +++ b/app/views/devise/registrations/edit.html.erb @@ -2,7 +2,7 @@

Edit your account!

- <%#= render 'devise/registrations/api_key' %> + <%= render 'devise/registrations/api_key' %>
<%= form_for(resource, as: resource_name, url: registration_path(resource_name), class: 'form-body', html: { method: :put }) do |f| %> diff --git a/spec/requests/api/v1/overland/batches_spec.rb b/spec/requests/api/v1/overland/batches_spec.rb index 22d82818..912aa280 100644 --- a/spec/requests/api/v1/overland/batches_spec.rb +++ b/spec/requests/api/v1/overland/batches_spec.rb @@ -9,16 +9,28 @@ RSpec.describe 'Api::V1::Overland::Batches', type: :request do let(:json) { JSON.parse(file.read) } let(:params) { json } - it 'returns http success' do - post '/api/v1/overland/batches', params: params + context 'with invalid api key' do + it 'returns http unauthorized' do + post '/api/v1/overland/batches', params: params - expect(response).to have_http_status(:created) + expect(response).to have_http_status(:unauthorized) + end end - it 'enqueues a job' do - expect do - post '/api/v1/overland/batches', params: params - end.to have_enqueued_job(Overland::BatchCreatingJob) + context 'with valid api key' do + let(:user) { create(:user) } + + it 'returns http success' do + post "/api/v1/overland/batches?api_key=#{user.api_key}", params: params + + expect(response).to have_http_status(:created) + end + + it 'enqueues a job' do + expect do + post "/api/v1/overland/batches?api_key=#{user.api_key}", params: params + end.to have_enqueued_job(Overland::BatchCreatingJob) + end end end end diff --git a/spec/swagger/api/v1/overland/batches_controller_spec.rb b/spec/swagger/api/v1/overland/batches_controller_spec.rb index 038acb30..d0d0a2b5 100644 --- a/spec/swagger/api/v1/overland/batches_controller_spec.rb +++ b/spec/swagger/api/v1/overland/batches_controller_spec.rb @@ -72,12 +72,26 @@ describe 'Batches API', type: :request do } } + parameter name: :api_key, in: :query, type: :string, required: true, description: 'API Key' + response '201', 'Batch of points created' do let(:file_path) { 'spec/fixtures/files/overland/geodata.json' } let(:file) { File.open(file_path) } let(:json) { JSON.parse(file.read) } let(:params) { json } let(:locations) { params['locations'] } + let(:api_key) { create(:user).api_key } + + run_test! + end + + response '401', 'Unauthorized' do + let(:file_path) { 'spec/fixtures/files/overland/geodata.json' } + let(:file) { File.open(file_path) } + let(:json) { JSON.parse(file.read) } + let(:params) { json } + let(:locations) { params['locations'] } + let(:api_key) { nil } run_test! end diff --git a/swagger/v1/swagger.yaml b/swagger/v1/swagger.yaml index e710236d..32ddedfc 100644 --- a/swagger/v1/swagger.yaml +++ b/swagger/v1/swagger.yaml @@ -9,10 +9,18 @@ paths: summary: Creates a batch of points tags: - Batches - parameters: [] + parameters: + - name: api_key + in: query + required: true + description: API Key + schema: + type: string responses: '201': description: Batch of points created + '401': + description: Unauthorized requestBody: content: application/json: @@ -172,7 +180,7 @@ paths: lat: 52.502397 lon: 13.356718 tid: Swagger - tst: 1716488929 + tst: 1716633953 servers: - url: http://{defaultHost} variables: From df687db91f4cc2e258ea23dc7c762c62b9f4d951 Mon Sep 17 00:00:00 2001 From: Eugene Burmakin Date: Sat, 25 May 2024 12:57:39 +0200 Subject: [PATCH 02/10] Add Owntracks API endpoint with api_key authentication --- .../api/v1/owntracks/points_controller.rb | 18 ++++++++++ app/controllers/api/v1/points_controller.rb | 10 ++---- config/routes.rb | 7 +++- .../api/v1/owntracks/points_controller.rb | 36 +++++++++++++++++++ swagger/v1/swagger.yaml | 2 +- 5 files changed, 64 insertions(+), 9 deletions(-) create mode 100644 app/controllers/api/v1/owntracks/points_controller.rb create mode 100644 spec/requests/api/v1/owntracks/points_controller.rb diff --git a/app/controllers/api/v1/owntracks/points_controller.rb b/app/controllers/api/v1/owntracks/points_controller.rb new file mode 100644 index 00000000..e610a07a --- /dev/null +++ b/app/controllers/api/v1/owntracks/points_controller.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +class Api::V1::PointsController < ApplicationController + skip_forgery_protection + before_action :authenticate_api_key + + def create + Owntracks::PointCreatingJob.perform_later(point_params) + + render json: {}, status: :ok + end + + private + + def point_params + params.permit! + end +end diff --git a/app/controllers/api/v1/points_controller.rb b/app/controllers/api/v1/points_controller.rb index 675cca1a..b7501b59 100644 --- a/app/controllers/api/v1/points_controller.rb +++ b/app/controllers/api/v1/points_controller.rb @@ -1,21 +1,17 @@ # frozen_string_literal: true +# TODO: Deprecate in 1.0 + class Api::V1::PointsController < ApplicationController skip_forgery_protection def create + Rails.logger.info 'This endpoint will be deprecated in 1.0. Use /api/v1/owntracks/points instead' Owntracks::PointCreatingJob.perform_later(point_params) render json: {}, status: :ok end - def destroy - @point = Point.find(params[:id]) - @point.destroy - - head :no_content - end - private def point_params diff --git a/config/routes.rb b/config/routes.rb index fba7dabf..ffcdcc97 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -6,6 +6,7 @@ Rails.application.routes.draw do mount Rswag::Api::Engine => '/api-docs' mount Rswag::Ui::Engine => '/api-docs' mount Sidekiq::Web => '/sidekiq' + get 'settings/theme', to: 'settings#theme' get 'export', to: 'export#index' get 'export/download', to: 'export#download' @@ -32,11 +33,15 @@ Rails.application.routes.draw do namespace :api do namespace :v1 do - resources :points + resources :points, only: :create # TODO: Deprecate in 1.0 namespace :overland do resources :batches, only: :create end + + namespace :owntracks do + resources :points, only: :create + end end end end diff --git a/spec/requests/api/v1/owntracks/points_controller.rb b/spec/requests/api/v1/owntracks/points_controller.rb new file mode 100644 index 00000000..73e669d4 --- /dev/null +++ b/spec/requests/api/v1/owntracks/points_controller.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe 'Api::V1::Owntracks::Points', type: :request do + describe 'POST /api/v1/owntracks/points' do + context 'with valid params' do + let(:params) do + { lat: 1.0, lon: 1.0, tid: 'test', tst: Time.current.to_i, topic: 'iPhone 12 pro' } + end + let(:user) { create(:user) } + + context 'with invalid api key' do + it 'returns http unauthorized' do + post api_v1_points_path, params: params + + expect(response).to have_http_status(:unauthorized) + end + end + + context 'with valid api key' do + it 'returns http success' do + post api_v1_points_path(api_key: user.api_key), params: params + + expect(response).to have_http_status(:success) + end + + it 'enqueues a job' do + expect do + post api_v1_points_path(api_key: user.api_key), params: params + end.to have_enqueued_job(Owntracks::PointCreatingJob) + end + end + end + end +end diff --git a/swagger/v1/swagger.yaml b/swagger/v1/swagger.yaml index 32ddedfc..530ad1c2 100644 --- a/swagger/v1/swagger.yaml +++ b/swagger/v1/swagger.yaml @@ -180,7 +180,7 @@ paths: lat: 52.502397 lon: 13.356718 tid: Swagger - tst: 1716633953 + tst: 1716634644 servers: - url: http://{defaultHost} variables: From 082cbf159d7b42de5e5e75b83376689ae79fd8f0 Mon Sep 17 00:00:00 2001 From: Eugene Burmakin Date: Sat, 25 May 2024 13:26:56 +0200 Subject: [PATCH 03/10] Scope points to a user --- Gemfile | 1 + Gemfile.lock | 4 +++ .../api/v1/overland/batches_controller.rb | 2 +- .../api/v1/owntracks/points_controller.rb | 2 +- app/controllers/export_controller.rb | 4 +-- app/controllers/map_controller.rb | 2 +- app/controllers/points_controller.rb | 7 ++--- app/helpers/application_helper.rb | 2 +- app/jobs/overland/batch_creating_job.rb | 4 +-- app/jobs/owntracks/point_creating_job.rb | 5 ++-- app/models/point.rb | 1 + app/models/stat.rb | 6 ++--- app/models/user.rb | 1 + app/views/stats/index.html.erb | 2 +- ...0530_bind_existing_points_to_first_user.rb | 17 ++++++++++++ db/data_schema.rb | 1 + .../20240525110244_add_user_id_to_points.rb | 7 +++++ db/schema.rb | 5 +++- dev-docker-entrypoint.sh | 4 +++ spec/jobs/overland/batch_creating_job_spec.rb | 9 ++++++- .../jobs/owntracks/point_creating_job_spec.rb | 9 ++++++- spec/models/point_spec.rb | 3 +++ spec/models/stat_spec.rb | 27 ++++++++++--------- spec/models/user_spec.rb | 1 + spec/requests/points_spec.rb | 7 ++--- swagger/v1/swagger.yaml | 2 +- 26 files changed, 98 insertions(+), 37 deletions(-) create mode 100644 db/data/20240525110530_bind_existing_points_to_first_user.rb create mode 100644 db/data_schema.rb create mode 100644 db/migrate/20240525110244_add_user_id_to_points.rb diff --git a/Gemfile b/Gemfile index eb432aed..2b3f7406 100644 --- a/Gemfile +++ b/Gemfile @@ -6,6 +6,7 @@ git_source(:github) { |repo| "https://github.com/#{repo}.git" } ruby '3.2.3' gem 'bootsnap', require: false gem 'chartkick' +gem 'data_migrate' gem 'devise' gem 'geocoder' gem 'importmap-rails' diff --git a/Gemfile.lock b/Gemfile.lock index b2c9bfb7..fc2ea24e 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -96,6 +96,9 @@ GEM rexml crass (1.0.6) csv (3.3.0) + data_migrate (9.4.0) + activerecord (>= 6.1) + railties (>= 6.1) date (3.3.4) debug (1.9.2) irb (~> 1.10) @@ -391,6 +394,7 @@ PLATFORMS DEPENDENCIES bootsnap chartkick + data_migrate debug devise dotenv-rails diff --git a/app/controllers/api/v1/overland/batches_controller.rb b/app/controllers/api/v1/overland/batches_controller.rb index 014b43f5..3aa3cd39 100644 --- a/app/controllers/api/v1/overland/batches_controller.rb +++ b/app/controllers/api/v1/overland/batches_controller.rb @@ -5,7 +5,7 @@ class Api::V1::Overland::BatchesController < ApplicationController before_action :authenticate_api_key def create - Overland::BatchCreatingJob.perform_later(batch_params) + Overland::BatchCreatingJob.perform_later(batch_params, current_api_user.id) render json: { result: 'ok' }, status: :created end diff --git a/app/controllers/api/v1/owntracks/points_controller.rb b/app/controllers/api/v1/owntracks/points_controller.rb index e610a07a..c28d6df7 100644 --- a/app/controllers/api/v1/owntracks/points_controller.rb +++ b/app/controllers/api/v1/owntracks/points_controller.rb @@ -5,7 +5,7 @@ class Api::V1::PointsController < ApplicationController before_action :authenticate_api_key def create - Owntracks::PointCreatingJob.perform_later(point_params) + Owntracks::PointCreatingJob.perform_later(point_params, current_user.id) render json: {}, status: :ok end diff --git a/app/controllers/export_controller.rb b/app/controllers/export_controller.rb index cbd3e712..833d88ef 100644 --- a/app/controllers/export_controller.rb +++ b/app/controllers/export_controller.rb @@ -24,7 +24,7 @@ class ExportController < ApplicationController end def start_at - first_point_timestamp = Point.order(timestamp: :asc)&.first&.timestamp + first_point_timestamp = current_user.tracked_points.order(timestamp: :asc)&.first&.timestamp @start_at ||= if params[:start_at].nil? && first_point_timestamp.present? @@ -37,7 +37,7 @@ class ExportController < ApplicationController end def end_at - last_point_timestamp = Point.order(timestamp: :desc)&.last&.timestamp + last_point_timestamp = current_user.tracked_points.order(timestamp: :desc)&.last&.timestamp @end_at ||= if params[:end_at].nil? && last_point_timestamp.present? diff --git a/app/controllers/map_controller.rb b/app/controllers/map_controller.rb index 01cee763..794b0dbd 100644 --- a/app/controllers/map_controller.rb +++ b/app/controllers/map_controller.rb @@ -3,7 +3,7 @@ class MapController < ApplicationController before_action :authenticate_user! def index - @points = Point.without_raw_data.where('timestamp >= ? AND timestamp <= ?', start_at, end_at).order(timestamp: :asc) + @points = current_user.tracked_points.without_raw_data.where('timestamp >= ? AND timestamp <= ?', start_at, end_at).order(timestamp: :asc) @countries_and_cities = CountriesAndCities.new(@points).call @coordinates = diff --git a/app/controllers/points_controller.rb b/app/controllers/points_controller.rb index 2c08c11d..179daf7a 100644 --- a/app/controllers/points_controller.rb +++ b/app/controllers/points_controller.rb @@ -5,7 +5,8 @@ class PointsController < ApplicationController def index @points = - Point + current_user + .tracked_points .without_raw_data .where('timestamp >= ? AND timestamp <= ?', start_at, end_at) .order(timestamp: :asc) @@ -16,9 +17,9 @@ class PointsController < ApplicationController end def bulk_destroy - Point.where(id: params[:point_ids].compact).destroy_all + current_user.tracked_points.where(id: params[:point_ids].compact).destroy_all - redirect_to points_url, notice: "Points were successfully destroyed.", status: :see_other + redirect_to points_url, notice: 'Points were successfully destroyed.', status: :see_other end private diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 0b1f8c90..52256b5f 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -38,7 +38,7 @@ module ApplicationHelper end def countries_and_cities_stat(year) - data = Stat.year_cities_and_countries(year) + data = Stat.year_cities_and_countries(year, user) countries = data[:countries] cities = data[:cities] diff --git a/app/jobs/overland/batch_creating_job.rb b/app/jobs/overland/batch_creating_job.rb index 6172e4d5..a3d6da1d 100644 --- a/app/jobs/overland/batch_creating_job.rb +++ b/app/jobs/overland/batch_creating_job.rb @@ -3,11 +3,11 @@ class Overland::BatchCreatingJob < ApplicationJob queue_as :default - def perform(params) + def perform(params, user_id) data = Overland::Params.new(params).call data.each do |location| - Point.create!(location) + Point.create!(location.merge(user_id:)) end end end diff --git a/app/jobs/owntracks/point_creating_job.rb b/app/jobs/owntracks/point_creating_job.rb index 928d75a0..d4df3971 100644 --- a/app/jobs/owntracks/point_creating_job.rb +++ b/app/jobs/owntracks/point_creating_job.rb @@ -3,9 +3,10 @@ class Owntracks::PointCreatingJob < ApplicationJob queue_as :default - def perform(point_params) + # TODO: after deprecation of old endpoint, make user_id required + def perform(point_params, user_id = nil) parsed_params = OwnTracks::Params.new(point_params).call - Point.create(parsed_params) + Point.create!(parsed_params.merge(user_id:)) end end diff --git a/app/models/point.rb b/app/models/point.rb index c15984e5..df627fc9 100644 --- a/app/models/point.rb +++ b/app/models/point.rb @@ -4,6 +4,7 @@ class Point < ApplicationRecord # self.ignored_columns = %w[raw_data] belongs_to :import, optional: true + belongs_to :user, optional: true validates :latitude, :longitude, :timestamp, presence: true diff --git a/app/models/stat.rb b/app/models/stat.rb index 3caa33c5..da10fece 100644 --- a/app/models/stat.rb +++ b/app/models/stat.rb @@ -11,7 +11,7 @@ class Stat < ApplicationRecord end_of_day = day.end_of_day.to_i # We have to filter by user as well - points = Point.without_raw_data.where(timestamp: beginning_of_day..end_of_day) + points = user.tracked_points.without_raw_data.where(timestamp: beginning_of_day..end_of_day) data = { day: index, distance: 0 } @@ -40,8 +40,8 @@ class Stat < ApplicationRecord end end - def self.year_cities_and_countries(year) - points = Point.where(timestamp: DateTime.new(year).beginning_of_year..DateTime.new(year).end_of_year) + def self.year_cities_and_countries(year, user) + points = user.tracked_points.where(timestamp: DateTime.new(year).beginning_of_year..DateTime.new(year).end_of_year) data = CountriesAndCities.new(points).call diff --git a/app/models/user.rb b/app/models/user.rb index de70dd3e..2201ef14 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -9,6 +9,7 @@ class User < ApplicationRecord has_many :imports, dependent: :destroy has_many :points, through: :imports has_many :stats, dependent: :destroy + has_many :tracked_points, class_name: 'Point', dependent: :destroy after_create :create_api_key diff --git a/app/views/stats/index.html.erb b/app/views/stats/index.html.erb index 92951adb..33508af3 100644 --- a/app/views/stats/index.html.erb +++ b/app/views/stats/index.html.erb @@ -83,7 +83,7 @@ <% if REVERSE_GEOCODING_ENABLED %>
<% cache [current_user, 'countries_and_cities_stat', year], skip_digest: true do %> - <%= countries_and_cities_stat(year) %> + <%= countries_and_cities_stat(year, current_user) %> <% end %>
<% end %> diff --git a/db/data/20240525110530_bind_existing_points_to_first_user.rb b/db/data/20240525110530_bind_existing_points_to_first_user.rb new file mode 100644 index 00000000..21fab9a4 --- /dev/null +++ b/db/data/20240525110530_bind_existing_points_to_first_user.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +class BindExistingPointsToFirstUser < ActiveRecord::Migration[7.1] + def up + user = User.first + + points = Point.where(user_id: nil) + + points.update_all(user_id: user.id) + + Rails.logger.info "Bound #{points.count} points to user #{user.email}" + end + + def down + raise ActiveRecord::IrreversibleMigration + end +end diff --git a/db/data_schema.rb b/db/data_schema.rb new file mode 100644 index 00000000..394d4c85 --- /dev/null +++ b/db/data_schema.rb @@ -0,0 +1 @@ +DataMigrate::Data.define(version: 20240525110530) diff --git a/db/migrate/20240525110244_add_user_id_to_points.rb b/db/migrate/20240525110244_add_user_id_to_points.rb new file mode 100644 index 00000000..9715621d --- /dev/null +++ b/db/migrate/20240525110244_add_user_id_to_points.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +class AddUserIdToPoints < ActiveRecord::Migration[7.1] + def change + add_reference :points, :user, foreign_key: true + end +end diff --git a/db/schema.rb b/db/schema.rb index 867f1b87..4130dfc1 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.1].define(version: 2024_05_18_095848) do +ActiveRecord::Schema[7.1].define(version: 2024_05_25_110244) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -82,6 +82,7 @@ ActiveRecord::Schema[7.1].define(version: 2024_05_18_095848) do t.string "country" t.datetime "created_at", null: false t.datetime "updated_at", null: false + t.bigint "user_id" t.index ["altitude"], name: "index_points_on_altitude" t.index ["battery"], name: "index_points_on_battery" t.index ["battery_status"], name: "index_points_on_battery_status" @@ -92,6 +93,7 @@ ActiveRecord::Schema[7.1].define(version: 2024_05_18_095848) do t.index ["latitude", "longitude"], name: "index_points_on_latitude_and_longitude" t.index ["timestamp"], name: "index_points_on_timestamp" t.index ["trigger"], name: "index_points_on_trigger" + t.index ["user_id"], name: "index_points_on_user_id" end create_table "stats", force: :cascade do |t| @@ -125,5 +127,6 @@ ActiveRecord::Schema[7.1].define(version: 2024_05_18_095848) do add_foreign_key "active_storage_attachments", "active_storage_blobs", column: "blob_id" add_foreign_key "active_storage_variant_records", "active_storage_blobs", column: "blob_id" + add_foreign_key "points", "users" add_foreign_key "stats", "users" end diff --git a/dev-docker-entrypoint.sh b/dev-docker-entrypoint.sh index f50f6906..a21b8738 100644 --- a/dev-docker-entrypoint.sh +++ b/dev-docker-entrypoint.sh @@ -28,5 +28,9 @@ bundle exec rails db:create echo "PostgreSQL is ready. Running database migrations..." bundle exec rails db:prepare +# Run data migrations +echo "Running DATA migrations..." +bundle exec rake data:migrate + # run passed commands bundle exec ${@} diff --git a/spec/jobs/overland/batch_creating_job_spec.rb b/spec/jobs/overland/batch_creating_job_spec.rb index 63f5e2c8..d1d842c8 100644 --- a/spec/jobs/overland/batch_creating_job_spec.rb +++ b/spec/jobs/overland/batch_creating_job_spec.rb @@ -2,14 +2,21 @@ require 'rails_helper' RSpec.describe Overland::BatchCreatingJob, type: :job do describe '#perform' do - subject(:perform) { described_class.new.perform(json) } + subject(:perform) { described_class.new.perform(json, user.id) } let(:file_path) { 'spec/fixtures/files/overland/geodata.json' } let(:file) { File.open(file_path) } let(:json) { JSON.parse(file.read) } + let(:user) { create(:user) } it 'creates a location' do expect { perform }.to change { Point.count }.by(1) end + + it 'creates a point with the correct user_id' do + perform + + expect(Point.last.user_id).to eq(user.id) + end end end diff --git a/spec/jobs/owntracks/point_creating_job_spec.rb b/spec/jobs/owntracks/point_creating_job_spec.rb index 625fbce6..7c15b5d3 100644 --- a/spec/jobs/owntracks/point_creating_job_spec.rb +++ b/spec/jobs/owntracks/point_creating_job_spec.rb @@ -2,14 +2,21 @@ require 'rails_helper' RSpec.describe Owntracks::PointCreatingJob, type: :job do describe '#perform' do - subject(:perform) { described_class.new.perform(point_params) } + subject(:perform) { described_class.new.perform(point_params, user.id) } let(:point_params) do { lat: 1.0, lon: 1.0, tid: 'test', tst: Time.now.to_i, topic: 'iPhone 12 pro' } end + let(:user) { create(:user) } it 'creates a point' do expect { perform }.to change { Point.count }.by(1) end + + it 'creates a point with the correct user_id' do + perform + + expect(Point.last.user_id).to eq(user.id) + end end end diff --git a/spec/models/point_spec.rb b/spec/models/point_spec.rb index 5ff7afdb..4548a9f6 100644 --- a/spec/models/point_spec.rb +++ b/spec/models/point_spec.rb @@ -1,8 +1,11 @@ +# frozen_string_literal: true + require 'rails_helper' RSpec.describe Point, type: :model do describe 'associations' do it { is_expected.to belong_to(:import).optional } + it { is_expected.to belong_to(:user).optional } end describe 'validations' do diff --git a/spec/models/stat_spec.rb b/spec/models/stat_spec.rb index 535d64cc..72999b5b 100644 --- a/spec/models/stat_spec.rb +++ b/spec/models/stat_spec.rb @@ -11,9 +11,10 @@ RSpec.describe Stat, type: :model do describe 'methods' do let(:year) { 2021 } + let(:user) { create(:user) } describe '.year_cities_and_countries' do - subject { described_class.year_cities_and_countries(year) } + subject { described_class.year_cities_and_countries(year, user) } let(:timestamp) { DateTime.new(year, 1, 1, 0, 0, 0) } @@ -24,16 +25,16 @@ RSpec.describe Stat, type: :model do context 'when there are points' do let!(:points) do [ - create(:point, city: 'Berlin', country: 'Germany', timestamp:), - create(:point, city: 'Berlin', country: 'Germany', timestamp: timestamp + 10.minutes), - create(:point, city: 'Berlin', country: 'Germany', timestamp: timestamp + 20.minutes), - create(:point, city: 'Berlin', country: 'Germany', timestamp: timestamp + 30.minutes), - create(:point, city: 'Berlin', country: 'Germany', timestamp: timestamp + 40.minutes), - create(:point, city: 'Berlin', country: 'Germany', timestamp: timestamp + 50.minutes), - create(:point, city: 'Berlin', country: 'Germany', timestamp: timestamp + 60.minutes), - create(:point, city: 'Berlin', country: 'Germany', timestamp: timestamp + 70.minutes), - create(:point, city: 'Brugges', country: 'Belgium', timestamp: timestamp + 80.minutes), - create(:point, city: 'Brugges', country: 'Belgium', timestamp: timestamp + 90.minutes) + create(:point, user:, city: 'Berlin', country: 'Germany', timestamp:), + create(:point, user:, city: 'Berlin', country: 'Germany', timestamp: timestamp + 10.minutes), + create(:point, user:, city: 'Berlin', country: 'Germany', timestamp: timestamp + 20.minutes), + create(:point, user:, city: 'Berlin', country: 'Germany', timestamp: timestamp + 30.minutes), + create(:point, user:, city: 'Berlin', country: 'Germany', timestamp: timestamp + 40.minutes), + create(:point, user:, city: 'Berlin', country: 'Germany', timestamp: timestamp + 50.minutes), + create(:point, user:, city: 'Berlin', country: 'Germany', timestamp: timestamp + 60.minutes), + create(:point, user:, city: 'Berlin', country: 'Germany', timestamp: timestamp + 70.minutes), + create(:point, user:, city: 'Brugges', country: 'Belgium', timestamp: timestamp + 80.minutes), + create(:point, user:, city: 'Brugges', country: 'Belgium', timestamp: timestamp + 90.minutes) ] end @@ -86,8 +87,8 @@ RSpec.describe Stat, type: :model do context 'when there are points' do let!(:points) do - create(:point, latitude: 1, longitude: 1, timestamp: DateTime.new(year, 1, 1, 1)) - create(:point, latitude: 2, longitude: 2, timestamp: DateTime.new(year, 1, 1, 2)) + create(:point, user:, latitude: 1, longitude: 1, timestamp: DateTime.new(year, 1, 1, 1)) + create(:point, user:, latitude: 2, longitude: 2, timestamp: DateTime.new(year, 1, 1, 2)) end before { expected_distance[0][1] = 157.23 } diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 3407d6e0..4098ad6c 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -7,6 +7,7 @@ RSpec.describe User, type: :model do it { is_expected.to have_many(:imports).dependent(:destroy) } it { is_expected.to have_many(:points).through(:imports) } it { is_expected.to have_many(:stats) } + it { is_expected.to have_many(:tracked_points).class_name('Point').dependent(:destroy) } end describe 'callbacks' do diff --git a/spec/requests/points_spec.rb b/spec/requests/points_spec.rb index 1e583c56..b65f66c5 100644 --- a/spec/requests/points_spec.rb +++ b/spec/requests/points_spec.rb @@ -30,11 +30,12 @@ RSpec.describe '/points', type: :request do end describe 'DELETE /bulk_destroy' do - let(:point1) { create(:point) } - let(:point2) { create(:point) } + let(:user) { create(:user) } + let(:point1) { create(:point, user:) } + let(:point2) { create(:point, user:) } before do - sign_in create(:user) + sign_in user end it 'destroys the selected points' do diff --git a/swagger/v1/swagger.yaml b/swagger/v1/swagger.yaml index 530ad1c2..718e55fe 100644 --- a/swagger/v1/swagger.yaml +++ b/swagger/v1/swagger.yaml @@ -180,7 +180,7 @@ paths: lat: 52.502397 lon: 13.356718 tid: Swagger - tst: 1716634644 + tst: 1716636414 servers: - url: http://{defaultHost} variables: From 5f18a3051d3d9c257049eedb4fba4474ffc5c39d Mon Sep 17 00:00:00 2001 From: Eugene Burmakin Date: Sat, 25 May 2024 13:36:15 +0200 Subject: [PATCH 04/10] Fix interface inconsistencies --- app/helpers/application_helper.rb | 4 ++-- app/jobs/import_job.rb | 2 +- app/services/create_stats.rb | 7 ++++--- app/services/google_maps/semantic_history_parser.rb | 8 +++++--- app/services/own_tracks/export_parser.rb | 8 +++++--- app/views/shared/_right_sidebar.html.erb | 4 ++-- spec/services/create_stats_spec.rb | 13 +++++++------ spec/services/own_tracks/export_parser_spec.rb | 2 +- swagger/v1/swagger.yaml | 2 +- 9 files changed, 28 insertions(+), 22 deletions(-) diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 52256b5f..a32fb095 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -53,8 +53,8 @@ module ApplicationHelper DateTime.new(year, month).past? end - def points_exist?(year, month) - Point.where( + def points_exist?(year, month, user) + user.tracked_points.where( timestamp: DateTime.new(year, month).beginning_of_month..DateTime.new(year, month).end_of_month ).exists? end diff --git a/app/jobs/import_job.rb b/app/jobs/import_job.rb index 49e7492a..7c1a9282 100644 --- a/app/jobs/import_job.rb +++ b/app/jobs/import_job.rb @@ -7,7 +7,7 @@ class ImportJob < ApplicationJob user = User.find(user_id) import = user.imports.find(import_id) - result = parser(import.source).new(import).call + result = parser(import.source).new(import, user_id).call import.update( raw_points: result[:raw_points], doubles: result[:doubles], processed: result[:processed] diff --git a/app/services/create_stats.rb b/app/services/create_stats.rb index 4d9c2e8a..24ecb3f2 100644 --- a/app/services/create_stats.rb +++ b/app/services/create_stats.rb @@ -16,7 +16,7 @@ class CreateStats beginning_of_month_timestamp = DateTime.new(year, month).beginning_of_month.to_i end_of_month_timestamp = DateTime.new(year, month).end_of_month.to_i - points = points(beginning_of_month_timestamp, end_of_month_timestamp) + points = points(user, beginning_of_month_timestamp, end_of_month_timestamp) next if points.empty? stat = Stat.find_or_initialize_by(year:, month:, user:) @@ -31,8 +31,9 @@ class CreateStats private - def points(beginning_of_month_timestamp, end_of_month_timestamp) - Point + def points(user, beginning_of_month_timestamp, end_of_month_timestamp) + user + .tracked_points .without_raw_data .where(timestamp: beginning_of_month_timestamp..end_of_month_timestamp) .order(:timestamp) diff --git a/app/services/google_maps/semantic_history_parser.rb b/app/services/google_maps/semantic_history_parser.rb index eb4ecb58..6129cd4a 100644 --- a/app/services/google_maps/semantic_history_parser.rb +++ b/app/services/google_maps/semantic_history_parser.rb @@ -1,10 +1,11 @@ # frozen_string_literal: true class GoogleMaps::SemanticHistoryParser - attr_reader :import + attr_reader :import, :user_id - def initialize(import) + def initialize(import, user_id) @import = import + @user_id = user_id end def call @@ -22,7 +23,8 @@ class GoogleMaps::SemanticHistoryParser raw_data: point_data[:raw_data], topic: 'Google Maps Timeline Export', tracker_id: 'google-maps-timeline-export', - import_id: import.id + import_id: import.id, + user_id: ) points += 1 diff --git a/app/services/own_tracks/export_parser.rb b/app/services/own_tracks/export_parser.rb index 9e03bb74..c9b64029 100644 --- a/app/services/own_tracks/export_parser.rb +++ b/app/services/own_tracks/export_parser.rb @@ -1,11 +1,12 @@ # frozen_string_literal: true class OwnTracks::ExportParser - attr_reader :import, :json + attr_reader :import, :json, :user_id - def initialize(import) + def initialize(import, user_id) @import = import @json = import.raw_data + @user_id = user_id end def call @@ -23,7 +24,8 @@ class OwnTracks::ExportParser raw_data: point_data[:raw_data], topic: point_data[:topic], tracker_id: point_data[:tracker_id], - import_id: import.id + import_id: import.id, + user_id: ) points += 1 diff --git a/app/views/shared/_right_sidebar.html.erb b/app/views/shared/_right_sidebar.html.erb index 9c599d00..e5d27f07 100644 --- a/app/views/shared/_right_sidebar.html.erb +++ b/app/views/shared/_right_sidebar.html.erb @@ -4,7 +4,7 @@ <% end %> + +
- <%= number_with_delimiter current_user.points.without_raw_data.count(:id) %> + <%= number_with_delimiter current_user.tracked_points.without_raw_data.count(:id) %>
Geopoints tracked
diff --git a/docker-compose.yml b/docker-compose.yml index 2f54d1bc..1d1ef563 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -44,6 +44,7 @@ services: DATABASE_NAME: dawarich_development MIN_MINUTES_SPENT_IN_CITY: 60 APPLICATION_HOST: localhost + TIME_ZONE: UTC depends_on: - dawarich_db - dawarich_redis