Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add initial seeding support using users #285

Merged
merged 16 commits into from
Jul 6, 2023
Merged

Conversation

Cruikshanks
Copy link
Member

https://eaflood.atlassian.net/browse/WATER-3981

The water-abstraction-service has a mechanism for seeding data for testing that relies on loading in YAML fixture files, sort of. Some content comes from these but other stuff is hard-coded into the 'loaders'.

Also, because you often need to add the ID of a record into something else your seeding it supports a system of 'named references' so the fixture-loader knows how to link things. Again though, it depends if the specific fixture-loader has been written to support this.

What we've been left with is an extremely complex, brittle seeding mechanism that is also very slow. If only there was something simpler that existed to do this!?

There is, and we're already using it. Knex supports seeding using the same syntax we already use elsewhere in the app. It's something we've used previously on the charging-module-api.

We think this is a better way to go and means we can start to move away from the hand-cranked version in water-abstraction-service.

This change gets the ball rolling by creating a seed for users, and hooking it into the project so it can be triggered as a package.json script or from an endpoint (needed to support water-abstraction-acceptance-tests).

https://eaflood.atlassian.net/browse/WATER-3981

The [water-abstraction-service](https://gitub.com/DEFRA/water-abstraction-service) has a mechanism for seeding data for testing that relies on reading in YAML fixture files, sort of. Some content comes from these but other stuff is hard coded into the readers.

Also, because you often need to add the ID of a record into something else your seeding it supports a system of 'named references' so the fixture-loader knows how to link things. Again though, it depends if the specific fixture-loader has been written to support this.

What we've been left with is an extremely complex, brittle seeding mechanism that is also very slow. If only there was something simpler that existed to do this!?

There is, and we're already using it. [Knex](https://knexjs.org/guide/migrations.html#seed-files) supports seeding using the same syntax we already use elsewhere in the app. It's something we've used previously on the [charging-module-api](https://github.com/DEFRA/sroc-charging-module-api).

We think this is a better way to go and means we can start to move away from the hand-cranked version in **water-abstraction-service**.

This change gets the ball rolling by creating a seed for users, and hooking it into the project so it can be triggered as a package.json script or from an endpoint (needed to support [water-abstraction-acceptance-tests](https://github.com/DEFRA/water-abstraction-acceptance-tests)).
This allows you to kick off the seed process from the command line.
We know we want to seed the dev/test users as our first 'test' of seeding. We want to take the opportunity to move away from hard coding the password we use in the source.

So, in preparation for this we add a new env var, something we do on all our previous services.
First thing to get your head around is that unlike migrations Knex always runs _all_ seed files. So, it's important to write them in the knowledge the data they are seeding might already exist.

For users, our initial seed example, we intend to only ever seed them once. So, we have implemented a method that first checks if a user already exists. If it doesn't we create the new user.

This then applies to the user groups, which determines the permissions a user has in the service.

To be able to log in with these users the password we generate must be recognised by the legacy code. So, we're forced to use the same mechanism currently used in [water-abstraction-tactical-idm](https://github.com/DEFRA/water-abstraction-tactical-idm) even if the package now appears to be no longer maintained.
Normally you would run the Knex seed process from the command line as part of your deployment. But we intend to use this mechanism within our acceptance tests. This means we need a method to programmatically trigger the process.

This adds a new endpoint, which with some help from Stackoverflow allows us to do that!
We admit they are pretty 'noddy'. But all the work is done by Knex and you don't typically write unit tests for the seed files themselves.
@Cruikshanks Cruikshanks added the enhancement New feature or request label Jun 27, 2023
@Cruikshanks Cruikshanks marked this pull request as ready for review June 27, 2023 13:41
Copy link
Contributor

@StuAA78 StuAA78 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good, just a couple of suggested code changes and some tiny tweaks 😁

db/seeds/01-users.js Outdated Show resolved Hide resolved
db/seeds/01-users.js Outdated Show resolved Hide resolved
db/seeds/01-users.js Outdated Show resolved Hide resolved
app/services/data/seed/seed.service.js Outdated Show resolved Hide resolved
db/seeds/01-users.js Outdated Show resolved Hide resolved
Cruikshanks and others added 5 commits June 27, 2023 16:28
@StuAA78 had a great suggestion in the comments to make the pattern of checking if something exists before inserting easier to follow.
I swear this didn't work when I tried it!

Co-authored-by: Stuart Adair <[email protected]>
Co-authored-by: Stuart Adair <[email protected]>
@Cruikshanks Cruikshanks force-pushed the add-initial-seeding-users branch from fd7828b to 5183c45 Compare June 27, 2023 15:40
@Cruikshanks Cruikshanks requested a review from StuAA78 June 27, 2023 21:47
Jozzey
Jozzey previously approved these changes Jun 28, 2023
Copy link
Contributor

@Jozzey Jozzey left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor

@StuAA78 StuAA78 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-adding a comment from previous review that I think was overlooked; and one small tweak we could make to destructure one of our requires if we wanted.

db/seeds/01-users.js Outdated Show resolved Hide resolved
const bcrypt = require('bcryptjs')
const { randomUUID } = require('crypto')

const DatabaseConfig = require('../../config/database.config.js')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could destructure defaultUserPassword if we wanted seeing as it's the only bit of DatabaseConfig we use here? Not going to insist on it though 😁

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think (as in you have forced me to think why I did this!) when we are talking about a value as opposed to a function it's best to leave the object so you have some context when it appears in the code, for example, I'm using it in

function _generateHashedPassword () {
  const salt = bcrypt.genSaltSync(10)

  return bcrypt.hashSync(DatabaseConfig.defaultUserPassword, salt)
}

Remove DatabaseConfig it starts looking a bit like it appears from nowhere.

function _generateHashedPassword () {
  const salt = bcrypt.genSaltSync(10)

  return bcrypt.hashSync(defaultUserPassword, salt)
}

I am conscious they are both just objects declared at the top of the file.

Again, I'm not precious about it either 🤷 . But it feels like a 'convention-in-the-making'. So, one to think about and discuss on the Dev call in my absence!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As we are having a bit of a discussion about this 😁 I was wondering why we are generating the salt separately? I think we get the same result if we just do this return bcrypt.hashSync(defaultUserPassword, 10).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor

@Jozzey Jozzey Jul 5, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have a look at Technique 2 in that readme 😉

I would guess that the technique to generate the salt separately would be more efficient if you were performing multiple hashes as you could reuse the salt to save a bit of CPU? Not fussed either way but thought I'd mention it since we were having a chat about that chunk of code.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a valid point. I didn't dig that far because I was just trying to replicate what they had done so we generate passwords the rest of the service can recognise. But I think technique 2 makes the code just a bit simpler so leave this with me and I'll make the change.

Co-authored-by: Stuart Adair <[email protected]>
@Cruikshanks Cruikshanks requested review from Jozzey and StuAA78 July 6, 2023 10:39
Copy link
Contributor

@Jozzey Jozzey left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍🏼

@Cruikshanks Cruikshanks merged commit 0002535 into main Jul 6, 2023
@Cruikshanks Cruikshanks deleted the add-initial-seeding-users branch July 6, 2023 15:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants