Rachael Booth
12 months ago
150 changed files with 3923 additions and 806 deletions
@ -0,0 +1,220 @@
|
||||
name: PaaS-only Production CI/CD Pipeline |
||||
|
||||
on: |
||||
workflow_dispatch: |
||||
|
||||
defaults: |
||||
run: |
||||
shell: bash |
||||
|
||||
jobs: |
||||
test: |
||||
name: Tests |
||||
runs-on: ubuntu-latest |
||||
|
||||
services: |
||||
postgres: |
||||
image: postgres:13.5 |
||||
env: |
||||
POSTGRES_PASSWORD: password |
||||
POSTGRES_USER: postgres |
||||
POSTGRES_DB: data_collector |
||||
ports: |
||||
- 5432:5432 |
||||
# Needed because the Postgres container does not provide a health check |
||||
# tmpfs makes database faster by using RAM |
||||
options: >- |
||||
--mount type=tmpfs,destination=/var/lib/postgresql/data |
||||
--health-cmd pg_isready |
||||
--health-interval 10s |
||||
--health-timeout 5s |
||||
--health-retries 5 |
||||
|
||||
env: |
||||
RAILS_ENV: test |
||||
GEMFILE_RUBY_VERSION: 3.1.1 |
||||
DB_HOST: localhost |
||||
DB_DATABASE: data_collector |
||||
DB_USERNAME: postgres |
||||
DB_PASSWORD: password |
||||
RAILS_MASTER_KEY: ${{ secrets.RAILS_MASTER_KEY }} |
||||
PARALLEL_TEST_PROCESSORS: 4 |
||||
|
||||
steps: |
||||
- name: Checkout |
||||
uses: actions/checkout@v3 |
||||
|
||||
- name: Set up Ruby |
||||
uses: ruby/setup-ruby@v1 |
||||
with: |
||||
bundler-cache: true |
||||
|
||||
- name: Set up Node.js |
||||
uses: actions/setup-node@v3 |
||||
with: |
||||
cache: yarn |
||||
node-version: 18 |
||||
|
||||
- name: Create database |
||||
run: | |
||||
bundle exec rake parallel:setup |
||||
|
||||
- name: Compile assets |
||||
run: | |
||||
bundle exec rake assets:precompile |
||||
|
||||
- name: Run tests |
||||
run: | |
||||
bundle exec rake parallel:spec['spec\/(?!features)'] |
||||
|
||||
feature_test: |
||||
name: Feature Tests |
||||
runs-on: ubuntu-latest |
||||
|
||||
services: |
||||
postgres: |
||||
image: postgres:13.5 |
||||
env: |
||||
POSTGRES_PASSWORD: password |
||||
POSTGRES_USER: postgres |
||||
POSTGRES_DB: data_collector |
||||
ports: |
||||
- 5432:5432 |
||||
# Needed because the Postgres container does not provide a health check |
||||
# tmpfs makes database faster by using RAM |
||||
options: >- |
||||
--mount type=tmpfs,destination=/var/lib/postgresql/data |
||||
--health-cmd pg_isready |
||||
--health-interval 10s |
||||
--health-timeout 5s |
||||
--health-retries 5 |
||||
|
||||
env: |
||||
RAILS_ENV: test |
||||
GEMFILE_RUBY_VERSION: 3.1.1 |
||||
DB_HOST: localhost |
||||
DB_DATABASE: data_collector |
||||
DB_USERNAME: postgres |
||||
DB_PASSWORD: password |
||||
RAILS_MASTER_KEY: ${{ secrets.RAILS_MASTER_KEY }} |
||||
|
||||
steps: |
||||
- name: Checkout |
||||
uses: actions/checkout@v3 |
||||
|
||||
- name: Set up Ruby |
||||
uses: ruby/setup-ruby@v1 |
||||
with: |
||||
bundler-cache: true |
||||
|
||||
- name: Set up Node.js |
||||
uses: actions/setup-node@v3 |
||||
with: |
||||
cache: yarn |
||||
node-version: 18 |
||||
|
||||
- name: Create database |
||||
run: | |
||||
bundle exec rake db:prepare |
||||
|
||||
- name: Compile assets |
||||
run: | |
||||
bundle exec rake assets:precompile |
||||
|
||||
- name: Run tests |
||||
run: | |
||||
bundle exec rspec spec/features --fail-fast |
||||
|
||||
lint: |
||||
name: Lint |
||||
runs-on: ubuntu-latest |
||||
|
||||
steps: |
||||
- name: Checkout |
||||
uses: actions/checkout@v3 |
||||
|
||||
- name: Set up Ruby |
||||
uses: ruby/setup-ruby@v1 |
||||
with: |
||||
bundler-cache: true |
||||
|
||||
- name: Set up Node.js |
||||
uses: actions/setup-node@v3 |
||||
with: |
||||
cache: yarn |
||||
node-version: 18 |
||||
|
||||
- name: Install packages and symlink local dependencies |
||||
run: | |
||||
yarn install --immutable --immutable-cache --check-cache |
||||
|
||||
- name: Lint |
||||
run: | |
||||
bundle exec rake lint |
||||
|
||||
audit: |
||||
name: Audit dependencies |
||||
runs-on: ubuntu-latest |
||||
|
||||
steps: |
||||
- name: Checkout |
||||
uses: actions/checkout@v3 |
||||
|
||||
- name: Set up Ruby |
||||
uses: ruby/setup-ruby@v1 |
||||
with: |
||||
bundler-cache: true |
||||
|
||||
- name: Audit |
||||
run: | |
||||
bundle exec bundler-audit |
||||
|
||||
deploy: |
||||
name: Deploy |
||||
concurrency: "production" |
||||
runs-on: ubuntu-latest |
||||
environment: "production" |
||||
needs: [lint, test, feature_test, audit] |
||||
|
||||
steps: |
||||
- name: Checkout code |
||||
uses: actions/checkout@v3 |
||||
|
||||
- name: Install Cloud Foundry CLI |
||||
run: | |
||||
wget --user-agent "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.3 Safari/605.1.15" -q -O - https://packages.cloudfoundry.org/debian/cli.cloudfoundry.org.key | sudo apt-key add - |
||||
echo "deb https://packages.cloudfoundry.org/debian stable main" | sudo tee /etc/apt/sources.list.d/cloudfoundry-cli.list |
||||
sudo apt-get update |
||||
sudo apt-get install cf8-cli |
||||
|
||||
- name: Deploy |
||||
env: |
||||
CF_USERNAME: ${{ secrets.CF_USERNAME }} |
||||
CF_PASSWORD: ${{ secrets.CF_PASSWORD }} |
||||
CF_API_ENDPOINT: ${{ secrets.CF_API_ENDPOINT }} |
||||
CF_SPACE: ${{ secrets.CF_SPACE }} |
||||
CF_ORG: ${{ secrets.CF_ORG }} |
||||
APP_NAME: dluhc-core-production |
||||
GOVUK_NOTIFY_API_KEY: ${{ secrets.GOVUK_NOTIFY_API_KEY }} |
||||
APP_HOST: ${{ secrets.APP_HOST }} |
||||
RAILS_MASTER_KEY: ${{ secrets.RAILS_MASTER_KEY }} |
||||
OS_DATA_KEY: ${{ secrets.OS_DATA_KEY }} |
||||
IMPORT_PAAS_INSTANCE: ${{ secrets.IMPORT_PAAS_INSTANCE }} |
||||
EXPORT_PAAS_INSTANCE: ${{ secrets.EXPORT_PAAS_INSTANCE }} |
||||
S3_CONFIG: ${{ secrets.S3_CONFIG }} |
||||
CSV_DOWNLOAD_PAAS_INSTANCE: ${{ secrets.CSV_DOWNLOAD_PAAS_INSTANCE }} |
||||
SENTRY_DSN: ${{ secrets.SENTRY_DSN }} |
||||
run: | |
||||
cf api $CF_API_ENDPOINT |
||||
cf auth |
||||
cf target -o $CF_ORG -s $CF_SPACE |
||||
cf set-env $APP_NAME GOVUK_NOTIFY_API_KEY $GOVUK_NOTIFY_API_KEY |
||||
cf set-env $APP_NAME APP_HOST $APP_HOST |
||||
cf set-env $APP_NAME RAILS_MASTER_KEY $RAILS_MASTER_KEY |
||||
cf set-env $APP_NAME OS_DATA_KEY $OS_DATA_KEY |
||||
cf set-env $APP_NAME IMPORT_PAAS_INSTANCE $IMPORT_PAAS_INSTANCE |
||||
cf set-env $APP_NAME EXPORT_PAAS_INSTANCE $EXPORT_PAAS_INSTANCE |
||||
cf set-env $APP_NAME S3_CONFIG $S3_CONFIG |
||||
cf set-env $APP_NAME CSV_DOWNLOAD_PAAS_INSTANCE $CSV_DOWNLOAD_PAAS_INSTANCE |
||||
cf set-env $APP_NAME SENTRY_DSN $SENTRY_DSN |
||||
cf push $APP_NAME --strategy rolling |
@ -1,7 +1,11 @@
|
||||
<span class="govuk-!-margin-right-4"> |
||||
<% if searched.present? %> |
||||
<strong><%= count %></strong> <%= item_label %> found matching ‘<%= searched %>’ of <strong><%= total_count %></strong> total <%= item %>. <%= govuk_link_to("Clear search", path) %> |
||||
<% if searched.present? && filters_count&.positive? %> |
||||
<strong><%= count %></strong> <%= item_label.pluralize(count) %> matching search and filters<br> |
||||
<% elsif searched.present? %> |
||||
<strong><%= count %></strong> <%= item_label.pluralize(count) %> matching search<br> |
||||
<% elsif filters_count&.positive? %> |
||||
<strong><%= count %></strong> <%= item_label.pluralize(count) %> matching filters<br> |
||||
<% else %> |
||||
<strong><%= count %></strong> total <%= item %> |
||||
<strong><%= count %></strong> matching <%= item %> |
||||
<% end %> |
||||
</span> |
||||
|
@ -1,13 +1,13 @@
|
||||
class SearchResultCaptionComponent < ViewComponent::Base |
||||
attr_reader :searched, :count, :item_label, :total_count, :item, :path |
||||
attr_reader :searched, :count, :item_label, :total_count, :item, :filters_count |
||||
|
||||
def initialize(searched:, count:, item_label:, total_count:, item:, path:) |
||||
def initialize(searched:, count:, item_label:, total_count:, item:, filters_count:) |
||||
@searched = searched |
||||
@count = count |
||||
@item_label = item_label |
||||
@total_count = total_count |
||||
@item = item |
||||
@path = path |
||||
@filters_count = filters_count |
||||
super |
||||
end |
||||
end |
||||
|
@ -0,0 +1,7 @@
|
||||
class MaintenanceController < ApplicationController |
||||
def service_unavailable |
||||
if current_user |
||||
sign_out |
||||
end |
||||
end |
||||
end |
@ -0,0 +1,11 @@
|
||||
<div class="govuk-grid-row"> |
||||
<div class="govuk-grid-column-two-thirds"> |
||||
<h1 class="govuk-heading-l">There are no more duplicate logs</h1> |
||||
</div> |
||||
</div> |
||||
|
||||
<p class="govuk-body"> |
||||
You have either changed or deleted all the duplicate logs. |
||||
</p> |
||||
|
||||
<%= govuk_button_link_to "Back to all logs", lettings_logs_path %> |
@ -0,0 +1,4 @@
|
||||
<%= govuk_details(summary_text: "What is a location?") do %> |
||||
<p class="govuk-body">A location is a postcode where supported housing is provided under a scheme. A scheme can have multiple locations, and a location can have multiple units at the same postcode.</p> |
||||
<p class="govuk-body"><%= govuk_link_to("Read more about schemes and locations", scheme_changes_path) %></p> |
||||
<% end %> |
@ -0,0 +1,11 @@
|
||||
<h1 class="govuk-heading-l govuk-!-width-two-thirds"> |
||||
Sorry, the service is unavailable |
||||
</h1> |
||||
|
||||
<div class="govuk-grid-row"> |
||||
<div class="govuk-grid-column-two-thirds-from-desktop"> |
||||
<p class="govuk-body">You will be able to use the service from 9am on Thursday 16 November 2023.</p> |
||||
<p class="govuk-body">Changes from the page you were on have not been saved. Changes on pages where you have selected 'save and continue' have been saved.</p> |
||||
<p class="govuk-body"><%= govuk_link_to "Contact the helpdesk", "https://dluhcdigital.atlassian.net/servicedesk/customer/portal/6/group/11" %> if you need help.</p> |
||||
</div> |
||||
</div> |
@ -0,0 +1,34 @@
|
||||
<% if @organisation.recently_absorbed_organisations_grouped_by_merge_date.present? %> |
||||
<%= govuk_details(summary_text: "View all organisations that were merged into #{@organisation.name}") do %> |
||||
<% @organisation.recently_absorbed_organisations_grouped_by_merge_date.each do |merge_date, organisations| %> |
||||
<p><strong>Merge date:</strong> <%= merge_date&.to_formatted_s(:govuk_date) %></p> |
||||
<%= govuk_table do |table| %> |
||||
<%= table.head do |head| %> |
||||
<%= head.row do |row| %> |
||||
<% row.cell(header: true, text: "Organisation name", html_attributes: { scope: "col", class: "govuk-!-width-one-half" }) %> |
||||
<% row.cell(header: true, text: "Organisation ID", html_attributes: { scope: "col", class: "govuk-!-width-one-half" }) %> |
||||
<% end %> |
||||
<% end %> |
||||
<% organisations.each do |absorbed_org| %> |
||||
<%= table.body do |body| %> |
||||
<%= body.row do |row| %> |
||||
<% if current_user.support? %> |
||||
<% row.cell(text: simple_format(govuk_link_to(absorbed_org.name, organisation_path(absorbed_org)), { class: "govuk-!-font-weight-bold scheme-name-cell" }, wrapper_tag: "div")) %> |
||||
<% else %> |
||||
<% row.cell(text: absorbed_org.name) %> |
||||
<% end %> |
||||
<% row.cell(text: "ORG#{absorbed_org.id}") %> |
||||
<% end %> |
||||
<% end %> |
||||
<% end %> |
||||
<% end %> |
||||
<% end %> |
||||
<% end %> |
||||
<% end %> |
||||
<% if @organisation.absorbing_organisation.present? %> |
||||
<% if current_user.support? %> |
||||
<p><%= @organisation.name %> was merged into <%= govuk_link_to(@organisation.absorbing_organisation.name, organisation_path(@organisation.absorbing_organisation)) %><%= @organisation.merge_date ? " on #{@organisation.merge_date.to_formatted_s(:govuk_date)}" : "" %>.</p> |
||||
<% else %> |
||||
<p><%= @organisation.name %> was merged into <%= @organisation.absorbing_organisation.name %><%= @organisation.merge_date ? " on #{@organisation.merge_date.to_formatted_s(:govuk_date)}" : "" %>.</p> |
||||
<% end %> |
||||
<% end %> |
@ -0,0 +1,5 @@
|
||||
class AddAvailableFromToOrg < ActiveRecord::Migration[7.0] |
||||
def change |
||||
add_column :organisations, :available_from, :datetime |
||||
end |
||||
end |
@ -0,0 +1,15 @@
|
||||
--- |
||||
nav_order: 8 |
||||
--- |
||||
|
||||
# Using the App API |
||||
|
||||
In order to use the app as an API, you will need to configure requests to the API as so: |
||||
|
||||
* Configure your request with Basic Auth. Set the username to be the same as `API_USER` and password as the `API_KEY` (`API_USER` and `API_KEY` are environment variables that should be set for the application) |
||||
* Check the endpoint you are calling is an action that is `create`, `show` or `update` |
||||
* Check you are setting the following request headers: |
||||
* `Content-Type = application/json` |
||||
* `Action = application/json` N.B. If you use `*/*` instead, the request won't be recognised as an API request` |
||||
|
||||
Currently only the logs controller is configured to accept and authenticate API requests, when the above API environment variables are set. |
@ -1,12 +1,37 @@
|
||||
namespace :merge do |
||||
desc "Merge organisations into one" |
||||
task :merge_organisations, %i[absorbing_organisation_id merging_organisation_ids] => :environment do |_task, args| |
||||
desc "Merge organisations into an existing organisation" |
||||
task :merge_organisations, %i[absorbing_organisation_id merging_organisation_ids merge_date] => :environment do |_task, args| |
||||
absorbing_organisation_id = args[:absorbing_organisation_id] |
||||
merging_organisation_ids = args[:merging_organisation_ids]&.split(" ")&.map(&:to_i) |
||||
begin |
||||
merge_date = args[:merge_date].present? ? Date.parse(args[:merge_date]) : nil |
||||
rescue StandardError |
||||
raise "Usage: rake merge:merge_organisations[absorbing_organisation_id, merging_organisation_ids, merge_date]. Merge date must be in format YYYY-MM-DD" |
||||
end |
||||
|
||||
if merging_organisation_ids.blank? || absorbing_organisation_id.blank? |
||||
raise "Usage: rake merge:merge_organisations[absorbing_organisation_id, merging_organisation_ids, merge_date]" |
||||
end |
||||
|
||||
service = Merge::MergeOrganisationsService.new(absorbing_organisation_id:, merging_organisation_ids:, merge_date:) |
||||
service.call |
||||
end |
||||
|
||||
raise "Usage: rake merge:merge_organisations[absorbing_organisation_id, merging_organisation_ids]" if merging_organisation_ids.blank? || absorbing_organisation_id.blank? |
||||
desc "Merge organisations into an existing organisation, make the absorbing organisation active from merge date only" |
||||
task :merge_organisations_into_new_organisation, %i[absorbing_organisation_id merging_organisation_ids merge_date] => :environment do |_task, args| |
||||
absorbing_organisation_id = args[:absorbing_organisation_id] |
||||
merging_organisation_ids = args[:merging_organisation_ids]&.split(" ")&.map(&:to_i) |
||||
begin |
||||
merge_date = args[:merge_date].present? ? Date.parse(args[:merge_date]) : nil |
||||
rescue StandardError |
||||
raise "Usage: rake merge:merge_organisations_into_new_organisation[absorbing_organisation_id, merging_organisation_ids, merge_date]. Merge date must be in format YYYY-MM-DD" |
||||
end |
||||
|
||||
if merging_organisation_ids.blank? || absorbing_organisation_id.blank? |
||||
raise "Usage: rake merge:merge_organisations_into_new_organisation[absorbing_organisation_id, merging_organisation_ids, merge_date]" |
||||
end |
||||
|
||||
service = Merge::MergeOrganisationsService.new(absorbing_organisation_id:, merging_organisation_ids:) |
||||
service = Merge::MergeOrganisationsService.new(absorbing_organisation_id:, merging_organisation_ids:, merge_date:, absorbing_organisation_active_from_merge_date: true) |
||||
service.call |
||||
end |
||||
end |
||||
|
@ -0,0 +1,18 @@
|
||||
desc "Forces to recalculate irproduct values" |
||||
task recalculate_irproduct_values: :environment do |
||||
LettingsLog.exportable.where(rent_type: [3, 4, 5]).where(irproduct: nil).each do |log| # irproduct was never set |
||||
Rails.logger.info("Could not update irproduct for LettingsLog #{log.id}") unless log.update(values_updated_at: Time.zone.now) |
||||
end |
||||
LettingsLog.exportable.where(rent_type: 3).where.not(irproduct: 1).each do |log| # irproduct was set wrong |
||||
Rails.logger.info("Could not update irproduct for LettingsLog #{log.id}") unless log.update(values_updated_at: Time.zone.now) |
||||
end |
||||
LettingsLog.exportable.where(rent_type: 4).where.not(irproduct: 2).each do |log| # irproduct was set wrong |
||||
Rails.logger.info("Could not update irproduct for LettingsLog #{log.id}") unless log.update(values_updated_at: Time.zone.now) |
||||
end |
||||
LettingsLog.exportable.where(rent_type: 5).where.not(irproduct: 3).each do |log| # irproduct was set wrong |
||||
Rails.logger.info("Could not update irproduct for LettingsLog #{log.id}") unless log.update(values_updated_at: Time.zone.now) |
||||
end |
||||
LettingsLog.exportable.where.not(rent_type: [3, 4, 5]).where.not(irproduct: nil).each do |log| # irproduct was set when it should have been nil |
||||
Rails.logger.info("Could not update irproduct for LettingsLog #{log.id}") unless log.update(values_updated_at: Time.zone.now) |
||||
end |
||||
end |
@ -0,0 +1,13 @@
|
||||
desc "Forces to recalculate lar values for affordable rent types and clears irrelevant lar values" |
||||
task recalculate_lar_values: :environment do |
||||
LettingsLog.exportable.where(rent_type: [1, 2], lar: nil).each do |log| # lar was never set |
||||
Rails.logger.info("Could not update lar for LettingsLog #{log.id}") unless log.update(values_updated_at: Time.zone.now) |
||||
end |
||||
LettingsLog.exportable.where(rent_type: 1).where.not(lar: 2).each do |log| # lar was set wrong |
||||
Rails.logger.info("Could not update lar for LettingsLog #{log.id}") unless log.update(values_updated_at: Time.zone.now) |
||||
end |
||||
LettingsLog.exportable.where(rent_type: 2).where.not(lar: 1).each do |log| # lar was set wrong |
||||
Rails.logger.info("Could not update lar for LettingsLog #{log.id}") unless log.update(values_updated_at: Time.zone.now) |
||||
end |
||||
LettingsLog.exportable.where.not(rent_type: [1, 2]).where.not(lar: nil).update_all(lar: nil) # lar was set to 2 but should never have been set |
||||
end |
@ -0,0 +1,35 @@
|
||||
desc "Squish names of locations, schemes, users, and organisations" |
||||
task squish_names: :environment do |
||||
Location.where("name LIKE ?", "% %").each do |location| |
||||
location.name&.squish! |
||||
begin |
||||
location.save! |
||||
rescue StandardError => e |
||||
Sentry.capture_exception(e) |
||||
end |
||||
end |
||||
Scheme.where("service_name LIKE ?", "% %").each do |scheme| |
||||
scheme.service_name&.squish! |
||||
begin |
||||
scheme.save! |
||||
rescue StandardError => e |
||||
Sentry.capture_exception(e) |
||||
end |
||||
end |
||||
User.where("name LIKE ?", "% %").each do |user| |
||||
user.name&.squish! |
||||
begin |
||||
user.save! |
||||
rescue StandardError => e |
||||
Sentry.capture_exception(e) |
||||
end |
||||
end |
||||
Organisation.where("name LIKE ?", "% %").each do |organisation| |
||||
organisation.name&.squish! |
||||
begin |
||||
organisation.save! |
||||
rescue StandardError => e |
||||
Sentry.capture_exception(e) |
||||
end |
||||
end |
||||
end |
@ -0,0 +1,27 @@
|
||||
require "rails_helper" |
||||
|
||||
RSpec.describe MaintenanceController do |
||||
let(:user) { FactoryBot.create(:user) } |
||||
|
||||
describe "GET #service_unavailable" do |
||||
context "when maintenance mode is enabled" do |
||||
it "logs the user out" do |
||||
allow(FeatureToggle).to receive(:maintenance_mode_enabled?).and_return(true) |
||||
sign_in user |
||||
expect(controller).to be_user_signed_in |
||||
get :service_unavailable |
||||
expect(controller).not_to be_user_signed_in |
||||
end |
||||
end |
||||
|
||||
context "when maintenance mode is disabled" do |
||||
it "doesn't log the user out" do |
||||
allow(FeatureToggle).to receive(:maintenance_mode_enabled?).and_return(false) |
||||
sign_in user |
||||
expect(controller).to be_user_signed_in |
||||
get :service_unavailable |
||||
expect(controller).to be_user_signed_in |
||||
end |
||||
end |
||||
end |
||||
end |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue