From 0918bb1b8d948d30533d5ab4ed81965921cd9664 Mon Sep 17 00:00:00 2001 From: Eugene Burmakin Date: Sun, 3 Aug 2025 22:19:57 +0200 Subject: [PATCH 1/5] Extract scratch map to a separate file. --- CHANGELOG.md | 1 + .../api/v1/countries/borders_controller.rb | 2 +- app/javascript/controllers/maps_controller.js | 194 +++--------------- app/javascript/maps/scratch_layer.js | 177 ++++++++++++++++ app/services/visits/place_finder.rb | 8 +- spec/services/visits/place_finder_spec.rb | 3 +- 6 files changed, 213 insertions(+), 172 deletions(-) create mode 100644 app/javascript/maps/scratch_layer.js diff --git a/CHANGELOG.md b/CHANGELOG.md index f89a0cf9..2d04cd66 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## Fixed - Fog of war is now working correctly on zoom and map movement. #1603 +- Possibly fixed a bug where visits were no suggested correctly. #984 diff --git a/app/controllers/api/v1/countries/borders_controller.rb b/app/controllers/api/v1/countries/borders_controller.rb index 1c3d13a8..6be8195a 100644 --- a/app/controllers/api/v1/countries/borders_controller.rb +++ b/app/controllers/api/v1/countries/borders_controller.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -class Api::V1::Countries::BordersController < ApplicationController +class Api::V1::Countries::BordersController < ApiController def index countries = Rails.cache.fetch('dawarich/countries_codes', expires_in: 1.day) do Oj.load(File.read(Rails.root.join('lib/assets/countries.geojson'))) diff --git a/app/javascript/controllers/maps_controller.js b/app/javascript/controllers/maps_controller.js index 4453388d..a1c22787 100644 --- a/app/javascript/controllers/maps_controller.js +++ b/app/javascript/controllers/maps_controller.js @@ -35,6 +35,7 @@ import { showFlashMessage } from "../maps/helpers"; import { fetchAndDisplayPhotos } from "../maps/photos"; import { countryCodesMap } from "../maps/country_codes"; import { VisitsManager } from "../maps/visits"; +import { ScratchLayer } from "../maps/scratch_layer"; import "leaflet-draw"; import { initializeFogCanvas, drawFogCanvas, createFogOverlay } from "../maps/fog_of_war"; @@ -49,7 +50,6 @@ export default class extends BaseController { layerControl = null; visitedCitiesCache = new Map(); trackedMonthsCache = null; - currentPopup = null; tracksLayer = null; tracksVisible = false; tracksSubscription = null; @@ -181,7 +181,7 @@ export default class extends BaseController { this.areasLayer = new L.FeatureGroup(); this.photoMarkers = L.layerGroup(); - this.setupScratchLayer(this.countryCodesMap); + this.initializeScratchLayer(); if (!this.settingsButtonAdded) { this.addSettingsButton(); @@ -197,7 +197,7 @@ export default class extends BaseController { Tracks: this.tracksLayer, Heatmap: this.heatmapLayer, "Fog of War": this.fogOverlay, - "Scratch map": this.scratchLayer, + "Scratch map": this.scratchLayerManager?.getLayer() || L.layerGroup(), Areas: this.areasLayer, Photos: this.photoMarkers, "Suggested Visits": this.visitsManager.getVisitCirclesLayer(), @@ -348,127 +348,23 @@ export default class extends BaseController { appendPoint(data) { if (this.liveMapHandler && this.liveMapEnabled) { this.liveMapHandler.appendPoint(data); + // Update scratch layer manager with new markers + if (this.scratchLayerManager) { + this.scratchLayerManager.updateMarkers(this.markers); + } } else { console.warn('LiveMapHandler not initialized or live mode not enabled'); } } - async setupScratchLayer(countryCodesMap) { - this.scratchLayer = L.geoJSON(null, { - style: { - fillColor: '#FFD700', - fillOpacity: 0.3, - color: '#FFA500', - weight: 1 - } - }) - - try { - // Up-to-date version can be found on Github: - // https://raw.githubusercontent.com/datasets/geo-countries/master/data/countries.geojson - const response = await fetch('/api/v1/countries/borders.json', { - headers: { - 'Accept': 'application/geo+json,application/json' - } - }); - - if (!response.ok) { - throw new Error(`HTTP error! status: ${response.status}`); - } - - const worldData = await response.json(); - // Cache the world borders data for future use - this.worldBordersData = worldData; - - const visitedCountries = this.getVisitedCountries(countryCodesMap) - const filteredFeatures = worldData.features.filter(feature => - visitedCountries.includes(feature.properties["ISO3166-1-Alpha-2"]) - ) - - this.scratchLayer.addData({ - type: 'FeatureCollection', - features: filteredFeatures - }) - } catch (error) { - console.error('Error loading GeoJSON:', error); - } + async initializeScratchLayer() { + this.scratchLayerManager = new ScratchLayer(this.map, this.markers, this.countryCodesMap, this.apiKey); + this.scratchLayer = await this.scratchLayerManager.setup(); } - getVisitedCountries(countryCodesMap) { - if (!this.markers) return []; - - return [...new Set( - this.markers - .filter(marker => marker[7]) // Ensure country exists - .map(marker => { - // Convert country name to ISO code, or return the original if not found - return countryCodesMap[marker[7]] || marker[7]; - }) - )]; - } - - // Optional: Add methods to handle user interactions toggleScratchLayer() { - if (this.map.hasLayer(this.scratchLayer)) { - this.map.removeLayer(this.scratchLayer) - } else { - this.scratchLayer.addTo(this.map) - } - } - - async refreshScratchLayer() { - console.log('Refreshing scratch layer with current data'); - - if (!this.scratchLayer) { - console.log('Scratch layer not initialized, setting up'); - await this.setupScratchLayer(this.countryCodesMap); - return; - } - - try { - // Clear existing data - this.scratchLayer.clearLayers(); - - // Get current visited countries based on current markers - const visitedCountries = this.getVisitedCountries(this.countryCodesMap); - console.log('Current visited countries:', visitedCountries); - - if (visitedCountries.length === 0) { - console.log('No visited countries found'); - return; - } - - // Fetch country borders data (reuse if already loaded) - if (!this.worldBordersData) { - console.log('Loading world borders data'); - const response = await fetch('/api/v1/countries/borders.json', { - headers: { - 'Accept': 'application/geo+json,application/json' - } - }); - - if (!response.ok) { - throw new Error(`HTTP error! status: ${response.status}`); - } - - this.worldBordersData = await response.json(); - } - - // Filter for visited countries - const filteredFeatures = this.worldBordersData.features.filter(feature => - visitedCountries.includes(feature.properties["ISO3166-1-Alpha-2"]) - ); - - console.log('Filtered features for visited countries:', filteredFeatures.length); - - // Add the filtered country data to the scratch layer - this.scratchLayer.addData({ - type: 'FeatureCollection', - features: filteredFeatures - }); - - } catch (error) { - console.error('Error refreshing scratch layer:', error); + if (this.scratchLayerManager) { + this.scratchLayerManager.toggle(); } } @@ -591,9 +487,11 @@ export default class extends BaseController { this.visitsManager.fetchAndDisplayVisits(); } } else if (event.name === 'Scratch map') { - // Refresh scratch map with current visited countries + // Add scratch map layer console.log('Scratch map layer enabled via layer control'); - this.refreshScratchLayer(); + if (this.scratchLayerManager) { + this.scratchLayerManager.addToMap(); + } } else if (event.name === 'Fog of War') { // Enable fog of war when layer is added this.fogOverlay = event.layer; @@ -626,6 +524,12 @@ export default class extends BaseController { // Clear the visit circles when layer is disabled this.visitsManager.visitCircles.clearLayers(); } + } else if (event.name === 'Scratch map') { + // Handle scratch map layer removal + console.log('Scratch map layer disabled via layer control'); + if (this.scratchLayerManager) { + this.scratchLayerManager.remove(); + } } else if (event.name === 'Fog of War') { // Fog canvas will be automatically removed by the layer's onRemove method this.fogOverlay = null; @@ -703,7 +607,7 @@ export default class extends BaseController { Routes: this.polylinesLayer || L.layerGroup(), Heatmap: this.heatmapLayer || L.layerGroup(), "Fog of War": this.fogOverlay, - "Scratch map": this.scratchLayer || L.layerGroup(), + "Scratch map": this.scratchLayerManager?.getLayer() || L.layerGroup(), Areas: this.areasLayer || L.layerGroup(), Photos: this.photoMarkers || L.layerGroup() }; @@ -741,18 +645,14 @@ export default class extends BaseController { const markerId = parseInt(marker[6]); return markerId !== numericId; }); + + // Update scratch layer manager with updated markers + if (this.scratchLayerManager) { + this.scratchLayerManager.updateMarkers(this.markers); + } } } - addLastMarker(map, markers) { - if (markers.length > 0) { - const lastMarker = markers[markers.length - 1].slice(0, 2); - const marker = L.marker(lastMarker).addTo(map); - return marker; // Return marker reference for tracking - } - return null; - } - updateFog(markers, clearFogRadius, fogLineThreshold) { // Call the fog overlay's updateFog method if it exists if (this.fogOverlay && typeof this.fogOverlay.updateFog === 'function') { @@ -1104,7 +1004,7 @@ export default class extends BaseController { Tracks: this.tracksLayer ? this.map.hasLayer(this.tracksLayer) : false, Heatmap: this.map.hasLayer(this.heatmapLayer), "Fog of War": this.map.hasLayer(this.fogOverlay), - "Scratch map": this.map.hasLayer(this.scratchLayer), + "Scratch map": this.scratchLayerManager?.isVisible() || false, Areas: this.map.hasLayer(this.areasLayer), Photos: this.map.hasLayer(this.photoMarkers) }; @@ -1646,14 +1546,6 @@ export default class extends BaseController { } } - chunk(array, size) { - const chunked = []; - for (let i = 0; i < array.length; i += size) { - chunked.push(array.slice(i, i + size)); - } - return chunked; - } - getWholeYearLink() { // First try to get year from URL parameters const urlParams = new URLSearchParams(window.location.search); @@ -1918,30 +1810,6 @@ export default class extends BaseController { }); } - updateLayerControl() { - if (!this.layerControl) return; - - // Remove existing layer control - this.map.removeControl(this.layerControl); - - // Create new controls layer object - const controlsLayer = { - Points: this.markersLayer || L.layerGroup(), - Routes: this.polylinesLayer || L.layerGroup(), - Tracks: this.tracksLayer || L.layerGroup(), - Heatmap: this.heatmapLayer || L.heatLayer([]), - "Fog of War": this.fogOverlay, - "Scratch map": this.scratchLayer || L.layerGroup(), - Areas: this.areasLayer || L.layerGroup(), - Photos: this.photoMarkers || L.layerGroup(), - "Suggested Visits": this.visitsManager?.getVisitCirclesLayer() || L.layerGroup(), - "Confirmed Visits": this.visitsManager?.getConfirmedVisitCirclesLayer() || L.layerGroup() - }; - - // Re-add the layer control - this.layerControl = L.control.layers(this.baseMaps(), controlsLayer).addTo(this.map); - } - toggleTracksVisibility(event) { this.tracksVisible = event.target.checked; @@ -1949,8 +1817,4 @@ export default class extends BaseController { toggleTracksVisibility(this.tracksLayer, this.map, this.tracksVisible); } } - - - - } diff --git a/app/javascript/maps/scratch_layer.js b/app/javascript/maps/scratch_layer.js new file mode 100644 index 00000000..ffaa9590 --- /dev/null +++ b/app/javascript/maps/scratch_layer.js @@ -0,0 +1,177 @@ +import L from "leaflet"; + +export class ScratchLayer { + constructor(map, markers, countryCodesMap, apiKey) { + this.map = map; + this.markers = markers; + this.countryCodesMap = countryCodesMap; + this.apiKey = apiKey; + this.scratchLayer = null; + this.worldBordersData = null; + } + + async setup() { + this.scratchLayer = L.geoJSON(null, { + style: { + fillColor: '#FFD700', + fillOpacity: 0.3, + color: '#FFA500', + weight: 1 + } + }); + + try { + // Up-to-date version can be found on Github: + // https://raw.githubusercontent.com/datasets/geo-countries/master/data/countries.geojson + const response = await fetch('/api/v1/countries/borders.json', { + headers: { + 'Accept': 'application/geo+json,application/json', + 'Authorization': `Bearer ${this.apiKey}` + } + }); + + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + + const worldData = await response.json(); + // Cache the world borders data for future use + this.worldBordersData = worldData; + + const visitedCountries = this.getVisitedCountries(); + console.log('Current visited countries:', visitedCountries); + + if (visitedCountries.length === 0) { + console.log('No visited countries found'); + return this.scratchLayer; + } + + const filteredFeatures = worldData.features.filter(feature => + visitedCountries.includes(feature.properties["ISO3166-1-Alpha-2"]) + ); + + console.log('Filtered features for visited countries:', filteredFeatures.length); + + this.scratchLayer.addData({ + type: 'FeatureCollection', + features: filteredFeatures + }); + } catch (error) { + console.error('Error loading GeoJSON:', error); + } + + return this.scratchLayer; + } + + getVisitedCountries() { + if (!this.markers) return []; + + return [...new Set( + this.markers + .filter(marker => marker[7]) // Ensure country exists + .map(marker => { + // Convert country name to ISO code, or return the original if not found + return this.countryCodesMap[marker[7]] || marker[7]; + }) + )]; + } + + toggle() { + if (!this.scratchLayer) { + console.warn('Scratch layer not initialized'); + return; + } + + if (this.map.hasLayer(this.scratchLayer)) { + this.map.removeLayer(this.scratchLayer); + } else { + this.scratchLayer.addTo(this.map); + } + } + + async refresh() { + console.log('Refreshing scratch layer with current data'); + + if (!this.scratchLayer) { + console.log('Scratch layer not initialized, setting up'); + await this.setup(); + return; + } + + try { + // Clear existing data + this.scratchLayer.clearLayers(); + + // Get current visited countries based on current markers + const visitedCountries = this.getVisitedCountries(); + console.log('Current visited countries:', visitedCountries); + + if (visitedCountries.length === 0) { + console.log('No visited countries found'); + return; + } + + // Fetch country borders data (reuse if already loaded) + if (!this.worldBordersData) { + console.log('Loading world borders data'); + const response = await fetch('/api/v1/countries/borders.json', { + headers: { + 'Accept': 'application/geo+json,application/json', + 'Authorization': `Bearer ${this.apiKey}` + } + }); + + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + + this.worldBordersData = await response.json(); + } + + // Filter for visited countries + const filteredFeatures = this.worldBordersData.features.filter(feature => + visitedCountries.includes(feature.properties["ISO3166-1-Alpha-2"]) + ); + + console.log('Filtered features for visited countries:', filteredFeatures.length); + + // Add the filtered country data to the scratch layer + this.scratchLayer.addData({ + type: 'FeatureCollection', + features: filteredFeatures + }); + + } catch (error) { + console.error('Error refreshing scratch layer:', error); + } + } + + // Update markers reference when they change + updateMarkers(markers) { + this.markers = markers; + } + + // Get the Leaflet layer for use in layer controls + getLayer() { + return this.scratchLayer; + } + + // Check if layer is currently visible on map + isVisible() { + return this.scratchLayer && this.map.hasLayer(this.scratchLayer); + } + + // Remove layer from map + remove() { + if (this.scratchLayer && this.map.hasLayer(this.scratchLayer)) { + this.map.removeLayer(this.scratchLayer); + } + } + + // Add layer to map + addToMap() { + if (this.scratchLayer) { + this.scratchLayer.addTo(this.map); + } + } +} diff --git a/app/services/visits/place_finder.rb b/app/services/visits/place_finder.rb index 86f0a547..e2f3a3ab 100644 --- a/app/services/visits/place_finder.rb +++ b/app/services/visits/place_finder.rb @@ -114,7 +114,7 @@ module Visits # Look for existing place with this name existing = Place.where(name: name) - .near([point.latitude, point.longitude], SIMILARITY_RADIUS, :m) + .near([point.lat, point.lon], SIMILARITY_RADIUS, :m) .first return existing if existing @@ -122,9 +122,9 @@ module Visits # Create new place place = Place.new( name: name, - lonlat: "POINT(#{point.longitude} #{point.latitude})", - latitude: point.latitude, - longitude: point.longitude, + lonlat: "POINT(#{point.lon} #{point.lat})", + latitude: point.lat, + longitude: point.lon, city: properties['city'], country: properties['country'], geodata: point.geodata, diff --git a/spec/services/visits/place_finder_spec.rb b/spec/services/visits/place_finder_spec.rb index b924ffae..3da17828 100644 --- a/spec/services/visits/place_finder_spec.rb +++ b/spec/services/visits/place_finder_spec.rb @@ -58,8 +58,7 @@ RSpec.describe Visits::PlaceFinder do context 'with places from points data' do let(:point_with_geodata) do build_stubbed(:point, - latitude: latitude, - longitude: longitude, + lonlat: "POINT(#{longitude} #{latitude})", geodata: { 'properties' => { 'name' => 'POI from Point', From c8fadaa91de62afb4f1dfbd2bd4cb95f401ab7ce Mon Sep 17 00:00:00 2001 From: Eugene Burmakin Date: Sun, 3 Aug 2025 22:20:28 +0200 Subject: [PATCH 2/5] Update changelog. --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2d04cd66..30f554a6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Fog of war is now working correctly on zoom and map movement. #1603 - Possibly fixed a bug where visits were no suggested correctly. #984 +- Scratch map is now working correctly. From dfbe9a9821ca7382f4cea16cbf1fb201a0365737 Mon Sep 17 00:00:00 2001 From: Eugene Burmakin Date: Sun, 3 Aug 2025 23:35:29 +0200 Subject: [PATCH 3/5] Dry out scratch map. --- app/javascript/maps/scratch_layer.js | 54 +++++++++++++--------------- 1 file changed, 24 insertions(+), 30 deletions(-) diff --git a/app/javascript/maps/scratch_layer.js b/app/javascript/maps/scratch_layer.js index ffaa9590..f83844ae 100644 --- a/app/javascript/maps/scratch_layer.js +++ b/app/javascript/maps/scratch_layer.js @@ -23,20 +23,7 @@ export class ScratchLayer { try { // Up-to-date version can be found on Github: // https://raw.githubusercontent.com/datasets/geo-countries/master/data/countries.geojson - const response = await fetch('/api/v1/countries/borders.json', { - headers: { - 'Accept': 'application/geo+json,application/json', - 'Authorization': `Bearer ${this.apiKey}` - } - }); - - if (!response.ok) { - throw new Error(`HTTP error! status: ${response.status}`); - } - - const worldData = await response.json(); - // Cache the world borders data for future use - this.worldBordersData = worldData; + const worldData = await this._fetchWorldBordersData(); const visitedCountries = this.getVisitedCountries(); console.log('Current visited countries:', visitedCountries); @@ -63,6 +50,27 @@ export class ScratchLayer { return this.scratchLayer; } + async _fetchWorldBordersData() { + if (this.worldBordersData) { + return this.worldBordersData; + } + + console.log('Loading world borders data'); + const response = await fetch('/api/v1/countries/borders.json', { + headers: { + 'Accept': 'application/geo+json,application/json', + 'Authorization': `Bearer ${this.apiKey}` + } + }); + + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + + this.worldBordersData = await response.json(); + return this.worldBordersData; + } + getVisitedCountries() { if (!this.markers) return []; @@ -112,24 +120,10 @@ export class ScratchLayer { } // Fetch country borders data (reuse if already loaded) - if (!this.worldBordersData) { - console.log('Loading world borders data'); - const response = await fetch('/api/v1/countries/borders.json', { - headers: { - 'Accept': 'application/geo+json,application/json', - 'Authorization': `Bearer ${this.apiKey}` - } - }); - - if (!response.ok) { - throw new Error(`HTTP error! status: ${response.status}`); - } - - this.worldBordersData = await response.json(); - } + const worldData = await this._fetchWorldBordersData(); // Filter for visited countries - const filteredFeatures = this.worldBordersData.features.filter(feature => + const filteredFeatures = worldData.features.filter(feature => visitedCountries.includes(feature.properties["ISO3166-1-Alpha-2"]) ); From da438d9c93535dc0ebed19f188356d6268173504 Mon Sep 17 00:00:00 2001 From: Eugene Burmakin Date: Sun, 10 Aug 2025 12:05:34 +0200 Subject: [PATCH 4/5] Fix borders specs --- .../requests/api/v1/countries/borders_spec.rb | 22 ++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/spec/requests/api/v1/countries/borders_spec.rb b/spec/requests/api/v1/countries/borders_spec.rb index 1162e198..d0717dcf 100644 --- a/spec/requests/api/v1/countries/borders_spec.rb +++ b/spec/requests/api/v1/countries/borders_spec.rb @@ -4,12 +4,24 @@ require 'rails_helper' RSpec.describe 'Api::V1::Countries::Borders', type: :request do describe 'GET /index' do - it 'returns a list of countries with borders' do - get '/api/v1/countries/borders' + let(:user) { create(:user) } - expect(response).to have_http_status(:success) - expect(response.body).to include('AF') - expect(response.body).to include('ZW') + context 'when user is not authenticated' do + it 'returns http unauthorized' do + get '/api/v1/countries/borders' + + expect(response).to have_http_status(:unauthorized) + end + end + + context 'when user is authenticated' do + it 'returns a list of countries with borders' do + get '/api/v1/countries/borders', headers: { 'Authorization' => "Bearer #{user.api_key}" } + + expect(response).to have_http_status(:success) + expect(response.body).to include('AF') + expect(response.body).to include('ZW') + end end end end From f6b7652a011392c955654a5c873caa08d396f88d Mon Sep 17 00:00:00 2001 From: Eugene Burmakin Date: Mon, 11 Aug 2025 00:21:58 +0200 Subject: [PATCH 5/5] Return dawarich headers on all API responses --- CHANGELOG.md | 6 ++++++ app/controllers/api/v1/health_controller.rb | 8 -------- app/controllers/api_controller.rb | 8 ++++++++ spec/requests/api/v1/countries/borders_spec.rb | 14 ++++++++++++++ 4 files changed, 28 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 30f554a6..62a6aa37 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). +# [0.30.9] - 2025-08-11 + +## Added + +- X-Dawarich-Response and X-Dawarich-Version headers are now returned for all API responses. + # [0.30.8] - 2025-08-01 ## Fixed diff --git a/app/controllers/api/v1/health_controller.rb b/app/controllers/api/v1/health_controller.rb index 8e13d165..1e5ab2f1 100644 --- a/app/controllers/api/v1/health_controller.rb +++ b/app/controllers/api/v1/health_controller.rb @@ -4,14 +4,6 @@ class Api::V1::HealthController < ApiController skip_before_action :authenticate_api_key def index - if current_api_user - response.set_header('X-Dawarich-Response', 'Hey, I\'m alive and authenticated!') - else - response.set_header('X-Dawarich-Response', 'Hey, I\'m alive!') - end - - response.set_header('X-Dawarich-Version', APP_VERSION) - render json: { status: 'ok' } end end diff --git a/app/controllers/api_controller.rb b/app/controllers/api_controller.rb index 4d13bdaf..d53f57ae 100644 --- a/app/controllers/api_controller.rb +++ b/app/controllers/api_controller.rb @@ -2,10 +2,18 @@ class ApiController < ApplicationController skip_before_action :verify_authenticity_token + before_action :set_version_header before_action :authenticate_api_key private + def set_version_header + message = "Hey, I\'m alive#{current_api_user ? ' and authenticated' : ''}!" + + response.set_header('X-Dawarich-Response', message) + response.set_header('X-Dawarich-Version', APP_VERSION) + end + def authenticate_api_key return head :unauthorized unless current_api_user diff --git a/spec/requests/api/v1/countries/borders_spec.rb b/spec/requests/api/v1/countries/borders_spec.rb index d0717dcf..b5922b73 100644 --- a/spec/requests/api/v1/countries/borders_spec.rb +++ b/spec/requests/api/v1/countries/borders_spec.rb @@ -12,6 +12,13 @@ RSpec.describe 'Api::V1::Countries::Borders', type: :request do expect(response).to have_http_status(:unauthorized) end + + it 'returns X-Dawarich-Response header' do + get '/api/v1/countries/borders' + + expect(response.headers['X-Dawarich-Response']).to eq('Hey, I\'m alive!') + expect(response.headers['X-Dawarich-Version']).to eq(APP_VERSION) + end end context 'when user is authenticated' do @@ -22,6 +29,13 @@ RSpec.describe 'Api::V1::Countries::Borders', type: :request do expect(response.body).to include('AF') expect(response.body).to include('ZW') end + + it 'returns X-Dawarich-Response header' do + get '/api/v1/countries/borders', headers: { 'Authorization' => "Bearer #{user.api_key}" } + + expect(response.headers['X-Dawarich-Response']).to eq('Hey, I\'m alive and authenticated!') + expect(response.headers['X-Dawarich-Version']).to eq(APP_VERSION) + end end end end