This app currently consists of a vessel submission API and a content management system. The API is intended to support on the Gov.UK hosted website as a means for the owner of a (singley owned) small ship to register their vessel under Part III of the UK Ships Register. The content management system is intended to be a means of managing the register and workload of officers of the Maritime and Coastguard Agency.
This is a standard Rails 5.2 web app with a PostgreSQL database.
Bootstrap has been used to theme the internal dashboard.
To set up a new development environment, follow the steps below:
bundle install
cp .env.example .env
cp config/database.yml.example config/database.yml
Then complete the .env file with your environment settings, and complete the
config/database.yml file with your database settings.
Setup your databases with:
bundle exec rake db:setup
bundle exec rake db:test:prepare
If capybara-webkit fails when you run bundle install, refer to the capybara-webkit help page
To run the app locally:
bundle exec rails server
The app will then be available at http://localhost:3000/
We use RSpec for tests. To run the test suite, run:
bundle exec rspec
If you get the error DATABASE_URL environment variable is set you will need to remove the DATABASE_URL from the local .env file.
We are using the rack-cors gem.
CORS is configured in config/initalisers/cors.rb and the origins are defined by ENV['CORS_ORIGINS'].
Emails are created in the notifications table and delivered with delayed_job.
Until we have tested a full data set, it seemed wise to disable the sending of any broadcast emails.
To disable an email notification, override Notification#deliverable? e.g.
def deliverable?
false
end
Automated reminder emails (system-generated) are generated daily. To defend against erroneous processes or bad data, they are created with an inital state of pending_approval. Before these emails can be sent, they need to marked as approved by a user. See the link Automated Email Queue in the top right navigation.
Emails can be previewed at: /rails/mailers in development.
Files are uploaded with paperclip and store on Microsoft Azure. Ensure that the following credentials are set for each environment (except in :test, when the file storage is :filesystem).
ENV['AZURE_STORAGE_ACCOUNT']
ENV['AZURE_ACCESS_KEY']
ENV['AZURE_CONTAINER_NAME']
Refer to config/initializers/paperclip.rb for implementation.
Note that Private URLs are enabled and the file url should be generated with the helper method: azure_private_asset_url, found in app/helpers/asset_helper.rb.
SMS Notifications are delivered by Gov.UK Notify.
This is implemented in app/services/sms_provider.rb.
Ensure that the API key is set as ENV['NOTIFY_API_KEY'],
And the Template ID as ENV['NOTIFY_TEMPLATE_ID'].
Postcode lookups are performed by a Javascript API call to https://ideal-postcodes.co.uk. The account is managed by the MCA and the code uses Ideal Postcode's JQuery API.
Ensure that the API key is set as POSTCODE_LOOKUP_API_KEY
Cron jobs are managed with the whenever gem.
To ensure the crontab is kept up to date, ensure that whenever --update-crontab is called on deployment.
Schedule: Daily
If the submission#referred_until date has been reached, applications should be restored to the unclaimed tasks queue. To run this application_type:
rake waves:expire_referrals
The services that can be performed are stored in the services table. Each service has:
- A name
- Service standard (standard_days or premium_days)
- Pricing for the part / service standard
- UI rules (e.g. validations, form display helpers)
- Activities (e.g. generate_new_5_year_registration, update_registry_details)
- Print templates (e.g. registration_certificate, cover_letter)
Submissions are requests to change something in the Registry of Ships. In the UI, we call them 'Applications' but in the Rails world, 'application' is a reserved word. A submission can be for a variety of different application types. The application type will determine whether a submission is for a registered vessel or a (flavour of) new registration.
The full list of application type can be retrieved from ApplicationType.all_. Note that the ApplicationType class references the WavesUtilities gem. It is also viewable in the admin section at: http://localhost:3000/admin/application_types.
Submissions have a state of active (aka open) or closed. A submission can be closed when all tasks have been completed or cancelled.
A registered vessel can have one open submission at a time.
There are three points of entry for a submission:
- Via a customer entry on the Online portal (submission#source
:online) - Via Document Entry by a Reg Officer (submission#source
:manual_entry) - Finance Team create Payments which are linked or converted to submissions by a Registration Officer.
When a submission enters the default :incomplete state, it fires an event build_default, invoking the SubmissionBuilder and setting up all the defaults. If you want to initialize a submission object to build a form or any other instance when you don't want a record to be created, you need to forecfully set the state to :initializing. For example: Submission.new(state: :initializing).
Submissions have many tasks (to be performed by a Reg Officer), and they travel through a state machine.
:initialising - Just added via the Task Manager, pending confirmation
:unclaimed - Confirmed by a Reg Officer. Target Dates have been assigned
:claimed - Add to a Reg Officer's Task Queue. Can be actionned.
:referred - Pending information from the customer.
:completed - Task is done. Activities have been performed. Print Jobs have been added to the Print Queue
:cancelled - Irreversible.
A task's behaviour depends on the Service, viewable within each part at: http://localhost:3000/admin/services/processes
- Rules - define the page templates to display and validations to be applied.
- Activities - describe what happens when the task is processed (
application_processor.rb) - Print Templates - determine the printable outputs of a completed task
RSS require monitoring of staff performance, so actions such as completing or referring tasks are logged. There is a helper method available globally: log_work!.
Work logs can be viewed at: http://localhost:3000/work_logs
The changes that the submission will perform are stored as JSON objects in submission#changeset. If the submission is associated with a registered vessel, the 'current information' will be stored as JSON objects in submission#changeset.
These JSON objects are mounted with the VirtualModel class and exposed by the submission as: submission.vessel, etc.
Payments come in two flavours and have a polymorphic association with the Payment model.
- World Pay Payments (online)
- Finance Payments (manual_entry)
In the submission context, we store owner details in the declarations table. The changes requested are stored as JSON objects in declaration#changeset. One owner maps to one declaration, and a submission can have many declarations.
Note that, while a changeset may contain owners, these are ignored once the Builder::SubmissionBuilder has run and the declarations have been built.
The Registry consists of:
- Registrations. The record of the current and previous registration details for a vessel.
- Vessels. The current vessel record.
- Owners. The current record of vessel owners.
The app follows the standard Rails MVC pattern, additionally:
- Builders are used to perform actions that create or update database records.
- Decorators are used to add UI behaviour to the models.
- Policies are user to answer questions: 'Can the submission be approved now?'
- Services are used to encapsulate helper methods that don't make any database changes.
And mixins:
- Concerns. Tried to limit the use of Active Record Concerns to functionality that was open to all models.
- Submission mixins. The Submission class was getting too big for rubocop, so
app/model/submission/associations.rbandapp/model/submission/state_machine.rbwere extracted to modules. No reason other than code organization. Would welcome smart refactoring.
Searching is helped along with pg_search gem.
To rebuild the (e.g. Submission) indexes after making changes to the configuration:
PgSearch::Multisearch.rebuild(Submission)
Global search configuration can be added to:
config/initializers/pg_search.rb
Adding attributes to the data model can be a fairly complex process as there are a number of moving parts to consider. In the example below, we want to record the number of shares_held by an owner. If adding additional tables (e.g. groups of shareholders), be mindful that the table(s) will need to exist in both the submission domain and the registry.
- Add a column
shares_heldto thedeclarationstable. Note thatdeclarationsare the owners associated with a submission. - Build out the model, controller and views in Waves so that a reg officer can record the
shares_heldby an owner. - Add a column
shares_heldto thecustomerstable. Note that all entities associated with a registered vessel are sub-classed from this table. - Extend
Builder::RegistryBuilderto add theshares_heldby an owner to thecustomerstable. - Extend
Register::Vessel#registry_infoto ensure that theshares_heldis captured.#registry_infois used to store a snapshot of the registered vessel in theregistrationstable. For ourshares_heldexample we don't need to make any changes toRegister::Vessel#registry_infobecause all owner attributes are assigned by default:owners: owners.map(&:attributes) - If the new data attribute will not be stored and edited in the
Submission#changeset(i.e. it's not vessel_info), then extendBuilder::SubmissionBuilderorBuilder::DeclarationBuilderto enable the new attribute. For ourshares_heldexample, we add the lineshares_held: owner[:shares_held].to_itoBuilder::DeclarationBuilder#build_declarations. - The
shares_heldname / value should also be added somewhere on the registered vessel page (app/views/vessels).