An application to allow internal staff users to answer correspondence.
Work should be based off of, and PRed to, the main branch. We use the GitHub PR approval process so once your PR is ready you'll need to have one person approve it, and the CI tests passing, before it can be merged. Feel free to use the issue tags on your PR to indicate if it is a WIP or if it is ready for reviewing.
Please consider using the provided Docker environment to develop this app over your core linux environment. There are huge benefits using Docker in development including standardisation, increased productivity and CI efficiencies.
- Docker
- Dory Proxy - provides named hosts via reverse proxy, allowing multiple apps to use localhost at one time.
- Docker Sync - provides high-performance 2-way synchronisation of files between host and app containers.
Setup is simple; local-dev is configured to manage the implementation of both Dory and Docker Sync.
Install Dory
brew install dory
Install Docker Sync
gem install docker-sync
Clone this repository then cd
into the new directory
$ git clone [email protected]:ministryofjustice/correspondence_tool_staff.git
$ cd correspondence_tool_staff
Environment settings for Docker reside in .env.example
. When starting Docker the environment will be created for you.
When the service is up and running, an array of pseudo accounts will have been created. The password that is defined in the variable
DEV_PASSWORD
will be needed to access all pseudo accounts.
The easiest way to get the app running is to execute Makefile commands.
The
make
utility is commonly used as a compiler however we use it as a stage to combine, execute and compress more cumbersome commands.
Running the following will get the application started. Please be patient, this process may take a good few minutes to
complete and dory up
will require root access to write to the host resolver - this is expected.
dory up
make build
Once the installation process has completed, a Puma server will be running in your terminal.
The application will be available at the following addresses:
Application:
http://track-a-query.docker/users/sign_in
DB Admin (login details in docker-compose.yml
):
http://pgadmin.track-a-query.docker:5050/
Selenium Grid UI (feature tests):
http://chrome.track-a-query.docker/ui
BrowserSync:
http://track-a-query.docker:3001/
BrowserSync UI:
http://track-a-query.docker:3002/
During usual operation it is normal to
make down
andmake launch
to stop and start the application, respectively.
Run the following in a separate terminal window.
make shell
From this prompt, You can run irb
, rails c
and a host of other commands.
IMPORTANT; the following removes all data and volumes... to nuke the entire installation and rebuild the app, run:
make rebuild
The docker compose
environment comes packed with a dedicated testing environment that requires an initial setup.
In a separate terminal window, execute:
make specs
Once the interface has initialised, execute:
make spec-setup
Once set up has completed you won't have to run that again unless the volumes are removed.
There are several make
commands configured in the Makefile
. These are mostly just convenience wrappers for longer or more complicated commands.
Nb. with exception to make spec-setup
, all other make
commands are run from the host machine, i.e. outside the containers.
Command | Description |
---|---|
make docker-shell |
Generate an .env file (if not exist), run the app in the background and open an interactive shell. Nb. does not launch servers for browser viewing, run make build instead. This command is for accessing a detached app container in order to execute administrative operations. You may like to run make shell to open a prompt in the container launched by docker compose up . From within you can run commands such as irb and rails c |
make |
Alias of make docker-shell . |
make build |
Build and run the application in the background, launch Sidekiq, BrowserSync and Puma. |
make launch |
Run the application in the background, launch Sidekiq, BrowserSync and Puma. |
make rebuild |
Runs make dc-clean and then rebuilds the application from the ground up and brings it online. |
Whilst these can be used independently, they are generally used in the commands above to help overcome complex installation.
Command | Description |
---|---|
make setup |
Jump into the app container and execute the install.sh script |
make sidekiq |
Launch Sidekiq in the background. |
make browser-sync |
Launch BrowserSync in the background. |
make server |
Launch Puma at the command prompt. |
make servers |
A helper to ensure app setup and asynchronously start all servers in this order; Sidekiq, BrowserSync and Puma. |
make dc-clean |
Stop all CTS docker containers, delete all CTS images, volumes and network. Clean the application directory ready for a fresh installation. Nukes databases and Gems. |
make dc-reset |
Clean and rebuild the app, displays stdout doesn't restart the servers |
make down |
Alias of docker compose down |
make up |
Alias of docker compose up |
make up-daemon |
Alias of docker compose up -d app - run docker compose in the background |
make restart |
Stop docker compose (down ), relaunch (up ) and display an interactive shell on the app container |
make shell-app |
Open an interactive command prompt on the app container |
make test |
Run tests on the application. |
make docker-sync |
Starts a docker-sync container used to speed up development on the front end. |
Below is the normal setup outside of Docker. Please consider using Docker as the environment can more closely match production, rather than your machines environment.
This project can produce code coverage data (w/o JS or views) using the simplecov
gem
set COVERAGE=1 (or any value) to generate a coverage report.
Parallel tests are supposed to be supported - however the coverage output from simplecov is a little
strange (the total lines in the project are different for each coverage run)
This project includes the parallel_tests
gem which enables multiple CPUs to be used during testing
in order to speed up execution. Otherwise running the tests takes an unacceptably long amount of time.
The default parallelism is 8 (override by setting PARALLEL_TEST_PROCESSORS) which seems to be about right for a typical Macbook Pro (10,1 single processor with 4 cores)
Create the required number of extra test databases:
rails parallel:create
Load the schema into all of the extra test databases:
rails parallel:load_structure
rails parallel:spec
rails parallel:spec:features
rails parallel:spec:non_features
We use chromedriver for Capybara tests, which require JavaScript. This is managed by selenium-webdriver.
If you have an existing old version on your PATH this may cause an issue so you will need to remove it from your PATH or uninstall it.
Where we don't require JavaScript to test a feature we use Capybara's default driver RackTest which is ruby based and much faster as it does not require a server to be started.
Debugging:
To debug a spec that requires JavaScript, you need to set a environment variable called CHROME_DEBUG. It can be set to any value you like.
Examples:
$ CHROME_DEBUG=1 bundle exec rspec
When you have set CHROME_DEBUG
, you should notice chrome start up and appear on your
taskbar/Docker. You can now click on chrome and watch it run through your tests.
If you have a debugger
in your tests the browser will stop at that point.
Libreoffice is used to convert documents to PDF's so that they can be viewed in a browser. In production environments, the installation of libreoffice is taken care of during the build of the docker container (see the Dockerfile).
In localhost dev testing environments, libreoffice needs to be installed using homebrew, and then
the following shell script needs to created with the name /usr/local/bin/soffice
:
cd /Applications/LibreOffice.app/Contents/MacOS && ./soffice $1 $2 $3 $4 $5 $6
The above script is needed by the libreconv gem to do the conversion.
BrowserSync is setup and configured for local development using the BrowserSync Rails gem. BrowserSync helps us test across different browsers and devices and sync the various actions that take place.
Node.js:
Install using brew install node
and then check its installed using node -v
and npm -v
Bundle install as normal then After bundle install:
bundle exec rails generate browser_sync_rails:install
This will use Node.js npm (Node Package Manager(i.e similar to Bundle or Pythons PIP)) to install BrowserSync and this command is only required once. If you run into problems with your setup visit the Gems README.
To run BrowserSync start your rails server as normal then in a separate terminal window run the following rake task:
bundle exec rails browser_sync:start
You should see the following output:
browser-sync start --proxy localhost:3000 --files 'app/assets, app/views'
[Browsersync] Proxying: http://localhost:3000
[Browsersync] Access URLs:
------------------------------------
Local: http://localhost:3001
External: http://xxx.xxx.xxx.x:3001
------------------------------------
UI: http://localhost:3002
UI External: http://xxx.xxx.xxx.x:3002
------------------------------------
[Browsersync] Watching files...
Open any number of browsers and use either the local or external address and your browser windows should be sync. If you make any changes to assets or views then all the browsers should automatically update and sync.
The UI URL are there if you would like to tweak the BrowserSync server and configure it further
Emails are sent using
the GOVUK Notify service.
Configuration relies on an API key which is not stored with the project, as even
the test API key can be used to access account information. To do local testing
you need to have an account that is attached to the "Track a query" service, and
a "Team and whitelist" API key generated from the GOVUK Notify service website.
See the instructions in the .env.example
file for how to setup the correct
environment variable to override the govuk_notify_api_key
setting.
The urls generated in the mail use the cts_email_url
and cts_email_port
configuration variables from the settings.yml
. These can be overridden by
setting the appropriate environment variables, e.g.
$ export SETTINGS__CTS_EMAIL_URL=localhost
$ export SETTINGS__CTS_EMAIL_PORT=5000
In addition to sign in with email and password, there is an integration with Azure Active Directory through Devise OmniAuth.
For this to work in your local machine, you will need to set 3 ENV variables.
See the instructions in the .env.example
file.
A colleague can provide this to you. Usually, the tenant and client will be the same for all local/dev environments, but the secret should be unique to your machine, as this makes it easier to revoke it in case of a leak.
This feature can be enabled/disabled through the enabled_features
mechanism
configured in config/settings.yml.
Responses and other case attachments are uploaded directly to S3 before being submitted to the application to be added to the case. Each deployed environment has the permissions is needs to access the uploads bucket for that environment.
In local development, uploads are placed in https:///
You'll need to provide access credentials to the aws-sdk gems to access it, there are two ways of doing this:
In order to perform certain actions, you need to have valid S3 credentials active
You can configure the aws-sdk with your access and secret key by placing them in
the [default]
section in .aws/credentials
:
Retrieve details from the secret created in Kubernetes in the s3.tf terraform resource
kubectl -n track-a-query-production get secret track-a-query-ecr-credentials-output -o yaml
Decode the base64 encoded values for access_key_id and secret_access_key from the output returned e.g.
$ echo QUtHQTI3SEpTERJV1RBTFc= | base64 --decode; echo
Place them in ~/.aws/credentals
as the default block:
[default]
aws_access_key_id = AKIA27HHJDDH3GHI
aws_secret_access_key = lSlkajsd9asdlaksd73hLKSFAk
We have functionality to create an anonymised copy of the production or staging database. This feature is to be used as a very last resort. If the copy of the database is needed for debugging please consider the following options first:
- seeing if the issue is covered in the feature tests
- trying to track the issue through Kibana
- recreating the issue locally
If the options above do not solve the issue you by create an anonymised dump of the database by connecting to a pod and running:
rake db:dump:delete_s3_dumps[latest,false] && rake db:dump:local
This will upload the file to an S3 bucket by default which can be loaded to an environment by connecting and running:
rake db:dump:copy_s3_dumps && db:restore:local[,false,]
For more help with the data dump tasks run:
rake db:dump:help
The papertrail gem is used as an auditing tool, keeping the old copies of records every time they are changed. There are a couple of complexities in using this tool which are described below:
The default serializer does not de-serialize the properties column correctly because internally it is
held as JSON, and papertrail serializes the object in YAML. The custom serializer CTSPapertrailSerializer
takes care of this and reconstitutes the JSON fields correctly. See /spec/lib/papertrail_spec.rb
for
examples of how to reify a previous version, or get a hash of field values for the previous version.
Continuous integration is carried out by SemaphoreCI.
The app uses the rails-data-migrations
gem https://github.com/anjlab/rails-data-migrations
Data migrations work like regular migrations but for data; they're found in db/data_migrations
.
To create a data migration you need to run:
rails generate data_migration migration_name
and this will create a migration_name.rb
file in db/data_migrations
folder with the following content:
class MigrationName < DataMigration
def up
# put your code here
end
end
Finally, at release time, you need to run:
rake data:migrate
This will run all pending data migrations and store migration history in data_migrations table.
The app has templated correspondence for generating case-related letters for the Offender SAR case type.
The template body for each letter is maintained in the letter_templates
table in the database, and populated from information in the /db/seeders/letter_template_seeder.rb script.
Whenever any changes to the letter templates are required DO NOT EDIT THE DATABASE, but amend the seeder and then on each environment, run rails db:seed:dev:letter_templates
to delete and re-populate the table.
This is required whenever any new template is added; should someone have edited the versions in the database directly, those changes will be overwritten the next time the seeder is run.
The smoke test runs through the process of signing into the service using a dedicated user account setup as Disclosure BMT team member. It checks that sign in was successful and then randomly views one case in the case list view.
To run the smoke test, set the following environment variables:
SETTINGS__SMOKE_TESTS__USERNAME # the email address to use for smoke tests
SETTINGS__SMOKE_TESTS__PASSWORD # The password for the smoketest email account
and then run
bundle exec rails smoke
The tests use the Site Prism gem to manage page objects which behave as an abstract description of the pages in the application; they're used in feature tests for finding elements, describing the URL path for a given page and for defining useful methods e.g. for completing particular form fields on the page in question.
If you add new Site Prism page objects, it's easy to follow the existing structure - however, there is one gotcha which is that in order to refer to them in your tests, you also need to add the new objects to a manifest file here so that it maps an instantiated object to the new Page object class you've defined.
See spec/site_prism/page_objects/pages/application.rb
As part of the test suite, we check to see if any tr
keys are missing from the localised YAML files
There is a command line tool provided to check for these manually as well - i18n-tasks missing
- you can see the output from it below.
$ i18n-tasks missing
Missing translations (1) | i18n-tasks v0.9.29
+--------+------------------------------------+--------------------------------------------------+
| Locale | Key | Value in other locales or source |
+--------+------------------------------------+--------------------------------------------------+
| all | offender_sars.case_details.heading | app/views/offender_sars/case_details.html.slim:5 |
+--------+------------------------------------+--------------------------------------------------+
...fixing happens...
$ i18n-tasks missing
✓ Good job! No translations are missing.
$
There's also a similar task called i18n-tasks unused
$ i18n-tasks unused
Unused keys (1) | i18n-tasks v0.9.29
+--------+-----------------------+---------------+
| Locale | Key | Value |
+--------+-----------------------+---------------+
| en | steps.new.sub_heading | Create a case |
+--------+-----------------------+---------------+
$ i18n-tasks unused
✓ Well done! Every translation is in use.
$
Docker images are built from a single Dockerfile
which uses build arguments to
control aspects of the build. The available build arguments are:
-
development_mode enable by setting to a non-nil value/empty string to install gems form the
test
anddevelopment
groups in theGemfile
. Used when building withdocker-compose
to build development versions of the images for local development. -
additional_packages set to the list of additional packages to install with
apt-get
. Used by the build system to add packages to theuploads
container:clamav clamav-daemon clamav-freshclam libreoffice
These are required to scan the uploaded files for viruses (clamav & Co.) and to generate a PDF preview (libreoffice).
nodejs
Required to run Puma with ExecJS
zip
Required to run closed case reports
postgresql-client-12.6-r0
Required for debugging database by developers within the running container - app will work without this.
If you are creating a local image for deploying to an environment, you will need to change the target platform by running:
export DOCKER_DEFAULT_PLATFORM=linux/amd64
You can generate documentation for the project with:
bundle exec yardoc
If you need to you can edit settings for Yard in Rakefile
. The documentation
is generated in the doc
folder, to view it on OSX run:
open doc/index.html
For our deploy process please see the our confluence page
There should be absolutely no secure credentials committed in this repo. Information about secret management can be found in the related confluence pages.
-
unassigned A new case entered by a DACU user is created in this state. It is in this state very briefly before it the user assigns it to a team on the next screen.
-
awaiting_responder The new case has been assigned to a business unit for response.
-
drafting A kilo in the responding business unit has accepted the case.
-
pending_dacu_clearance For cases that have an approver assignment with DACU Disclosure, as soon as a response file is uploaded, the case will transition to pending_dacu disclosure. The DACU disclosure team can either clear the case, in which case it goes forward to awaiting dispatch, or request changes, in which case it goes back to drafting.
-
awaiting_dispatch The Kilo has uploaded at least one response document.
-
responded The kilo has marked the response as sent.
-
closed The kilo has marked the case as closed.
- Install Ruby 3.x
$ rbenv install
it should pick up the version defined in .ruby-version
If you get error somehow telling you not being able to find available stable release 3.x, you could try the following commands
$ brew unlink ruby-build
$ brew install --HEAD ruby-build
then run following command to check whether you can see 3.x in the list
$ rbenv install --list-all
once you confirm, you can re-run rbenv install
comand to continue the process.
- Update the gem system
$ gem update --system
- Install bundle 2.4.19 and install those gems
$ gem install bundler -v 2.4.19
$ bundler install
- run
rails s
check the app
Dependabot creates PRs to help us keep track of our dependency updates. This is great but can lead to a little bit of work if you integrate these changes one by one (for instance, having to run the test suite over and over again).
You can manually combine the changes into one PR and then push this and wait for the tests to run, but this is admin that can be automated so why bother?
The app has a github action "Combine PRs" which automatically combines dependabot PRs that have passed the test suite into one PR which you can then merge.
To use this: "Actions" > "All workflows" > on the left "Combine PRs" > "Run workflows"
See here for the original developers README
Please note: the file upload functionality will not work locally without an AWS S3 bucket setup as a file store.