-
Notifications
You must be signed in to change notification settings - Fork 25
Migrate guides from FCP Development guide #139
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
Open
johnwatson484
wants to merge
8
commits into
main
Choose a base branch
from
migrate-from-fcp
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
b4f4dc2
Migrate guides from FCP Development guide
johnwatson484 7fa24bd
Update caching guide title to reflect server-side caching with Hapi.js
johnwatson484 22fefdd
Iterate docker guidance
johnwatson484 4cc9a4f
Update docker
johnwatson484 03b9e63
Revert docker guidance changes (covered by #140)
johnwatson484 651ffb2
Merge remote-tracking branch 'origin/main' into migrate-from-fcp
johnwatson484 c0f7e5a
Update Sonar
johnwatson484 6b69760
Address review comments
johnwatson484 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,169 @@ | ||
| # Server-Side Caching with Hapi.js | ||
|
|
||
| ## Redis | ||
|
|
||
| When an external cache is needed, Redis is the recommended choice. For local development, a [Docker image](https://hub.docker.com/_/redis) can be used. | ||
|
|
||
| ## Hapi.js server-side caching | ||
|
|
||
| Hapi server-side caching uses the [catbox](https://hapi.dev/module/catbox/) interface to abstract the underlying caching technology (e.g. memory, Redis). | ||
|
|
||
| There are three main concepts: | ||
|
|
||
| * The cache **strategy** (or provider): the underlying caching technology. [`@hapi/catbox-redis`](https://github.com/hapijs/catbox-redis) is the Redis adapter for catbox. | ||
| * The cache **client**: a low-level cache abstraction, initialised using a strategy. Hapi initialises an in-memory cache client by default, and you can create additional cache clients using the same or different strategies. | ||
| * The cache **policy**: a higher-level cache abstraction that sets a policy on storage within the cache (e.g. expiry times). The cache policy provides additional segmentation within the cache client. Typically the cache policy is how you interact with cache values via the `get` and `set` methods. | ||
|
|
||
| ### Configuring the default cache client | ||
|
|
||
| Hapi initialises an in-memory cache client by default. You can make the default cache client use a different strategy. For example, the following uses Redis or memory depending on configuration: | ||
|
|
||
| ```javascript | ||
| import Hapi from '@hapi/hapi' | ||
| import { Engine as CatboxRedis } from '@hapi/catbox-redis' | ||
| import { Engine as CatboxMemory } from '@hapi/catbox-memory' | ||
| import config from './config.js' | ||
|
|
||
| const CacheEngine = config.useRedis ? CatboxRedis : CatboxMemory | ||
| const cacheOptions = config.useRedis | ||
| ? { | ||
| host: config.cache.host, | ||
| port: config.cache.port, | ||
| password: config.cache.password, | ||
| partition: config.cache.partition, | ||
| tls: config.isProd ? {} : undefined | ||
| } | ||
| : {} | ||
|
|
||
| const server = Hapi.server({ | ||
| port: config.port, | ||
| cache: [{ | ||
| provider: { | ||
| constructor: CacheEngine, | ||
| options: cacheOptions | ||
| } | ||
| }] | ||
| }) | ||
| ``` | ||
|
|
||
| ### Configuring additional cache clients | ||
|
|
||
| Additional cache clients can be created when initialising the Hapi server by adding new definitions to the `cache` array. Additional caches must be given a `name`. For example, the following creates a new Redis cache client called `session`: | ||
|
|
||
| ```javascript | ||
| import Hapi from '@hapi/hapi' | ||
| import { Engine as CatboxRedis } from '@hapi/catbox-redis' | ||
| import config from './config.js' | ||
|
|
||
| const server = Hapi.server({ | ||
| port: config.port, | ||
| cache: [{ | ||
| name: 'session', | ||
| provider: { | ||
| constructor: CatboxRedis, | ||
| options: { | ||
| host: config.cache.host, | ||
| port: config.cache.port, | ||
| password: config.cache.password, | ||
| partition: config.cache.partition, | ||
| tls: config.isProd ? {} : undefined | ||
| } | ||
| } | ||
| }] | ||
| }) | ||
| ``` | ||
|
|
||
| This example creates two cache clients: the default in-memory cache client and a new cache client called `session` that uses Redis. | ||
|
|
||
| Hapi will always use the default in-memory cache client unless you specify the `name` when using it (either directly or via the cache policy). | ||
|
|
||
| ### Creating and using a cache policy | ||
|
|
||
| The cache policy is typically how you interact with the cache. See the [catbox policy documentation](https://hapi.dev/module/catbox/api/) for details on how to set and get data. | ||
|
|
||
| To create a cache policy using a segment within the default cache client: | ||
|
|
||
| ```javascript | ||
| const myCache = server.cache({ | ||
| expiresIn: config.cache.ttl, | ||
| segment: 'mySegment' | ||
| }) | ||
| ``` | ||
|
|
||
| To create a cache policy using a segment within a named cache client: | ||
|
|
||
| ```javascript | ||
| const myCache = server.cache({ | ||
| cache: 'session', | ||
| expiresIn: config.cache.ttl, | ||
| segment: 'mySegment' | ||
| }) | ||
| ``` | ||
|
|
||
| ## Integration with yar session cookies | ||
|
|
||
| [`@hapi/yar`](https://hapi.dev/module/yar/) is a plugin that adds unauthenticated session support (state across multiple browser requests) to Hapi. By default it tries to fit session data into a session cookie, but will use server-side storage via the Hapi cache interface if the session data exceeds the maximum cookie size. | ||
|
|
||
| Combining yar with Redis caching allows multiple replicas of a web server to share server-side user session data. | ||
|
|
||
| Example configuration using the default cache client: | ||
|
|
||
| ```javascript | ||
| import Yar from '@hapi/yar' | ||
|
|
||
| await server.register({ | ||
| plugin: Yar, | ||
| options: { | ||
| maxCookieSize: 0, | ||
| cache: { | ||
| expiresIn: config.cache.ttl | ||
| }, | ||
| cookieOptions: { | ||
| password: config.cookie.password, | ||
| isSecure: config.isProd | ||
| } | ||
| } | ||
| }) | ||
| ``` | ||
|
|
||
| Setting `maxCookieSize` to `0` forces all session data to be stored server-side. | ||
|
|
||
| Example configuration using a named cache client: | ||
|
|
||
| ```javascript | ||
| import Yar from '@hapi/yar' | ||
|
|
||
| await server.register({ | ||
| plugin: Yar, | ||
| options: { | ||
| maxCookieSize: 0, | ||
| cache: { | ||
| cache: 'session', | ||
| expiresIn: config.cache.ttl | ||
| }, | ||
| cookieOptions: { | ||
| password: config.cookie.password, | ||
| isSecure: config.isProd | ||
| } | ||
| } | ||
| }) | ||
| ``` | ||
|
|
||
| ## catbox-redis connection options | ||
|
|
||
| `@hapi/catbox-redis` v7 uses [ioredis](https://github.com/redis/ioredis) under the hood. The following options are supported: | ||
|
|
||
| | Option | Description | | ||
| | --- | --- | | ||
| | `host` | Redis server hostname (default: `127.0.0.1`) | | ||
| | `port` | Redis server port (default: `6379`) | | ||
| | `password` | Authentication password | | ||
| | `db` | Database number | | ||
| | `partition` | Key prefix for cache segmentation | | ||
| | `tls` | TLS configuration object (pass `{}` for default TLS settings) | | ||
| | `url` | Redis connection URL (alternative to `host`/`port`) | | ||
| | `client` | Pre-configured ioredis instance (must expose a `status` property set to `'ready'`) | | ||
| | `sentinels` | Array of `{ host, port }` sentinel addresses | | ||
| | `sentinelName` | Sentinel master name (required with `sentinels`) | | ||
|
|
||
| Only one connection method should be used: `host`/`port`, `url`, or `client`. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| # SonarQube for IDE | ||
|
|
||
| [SonarQube for IDE](https://www.sonarsource.com/products/sonarlint/) is an IDE extension that identifies code quality issues as you code, providing immediate feedback before committing. | ||
|
|
||
| Running analysis locally, rather than waiting for a CI pipeline or a reviewer to flag issues, means you can: | ||
|
|
||
| - fix bugs, code smells and security vulnerabilities as you introduce them, when the context is still fresh in your mind | ||
| - avoid pull requests being blocked or sent back for issues that a quick local check would have caught | ||
| - spend review time on design and logic, rather than on issues a linter could have found | ||
| - learn the reasoning behind each rule from inline explanations, rather than just being told something is wrong | ||
|
|
||
| All Defra projects are required to set up their repositories within the [SonarQube Cloud Defra organisation](https://sonarcloud.io/organizations/defra/projects). Using the IDE extension in connected mode ensures you get the same rules and quality gates locally as in CI, so there are no surprises when your code reaches the pipeline. | ||
|
|
||
| ## Dependencies | ||
|
|
||
| - Java Runtime Environment v21+ | ||
|
|
||
| With Ubuntu (including WSL), install the open source JRE: | ||
|
|
||
| ```bash | ||
| sudo apt-get install openjdk-21-jre | ||
| ``` | ||
|
|
||
| ## VS Code setup | ||
|
|
||
| 1. Install the [SonarQube for IDE](https://marketplace.visualstudio.com/items?itemName=SonarSource.sonarlint-vscode) extension. | ||
|
|
||
| 2. Set the JRE location in VS Code settings: | ||
|
|
||
| ```json | ||
| { | ||
| "sonarlint.ls.javaHome": "/usr/lib/jvm/java-21-openjdk-amd64" | ||
| } | ||
| ``` | ||
|
|
||
| This gives you Sonar code analysis using default quality gates for supported languages. | ||
|
|
||
| ## Connected mode | ||
|
|
||
| Connected mode binds the extension to your actual project in SonarQube Cloud. This ensures the IDE uses the same rules, quality gates, and exclusions as the SonarQube project configured for your repository. | ||
|
|
||
| Follow the [connected mode setup documentation](https://docs.sonarsource.com/sonarqube-for-ide/vs-code/team-features/connected-mode-setup/#connection-setup) to connect your SonarQube for IDE extension to the SonarQube Cloud project. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's useful to have the above bit of text about having at least one review merged into the "Get it looked at" section. But the rest of the sections about how and when to review could go stay at the end in a "Tips for reviewing PRs" section.
My only concern is that if it all went into the "Get it looked at" section, it's a bit long and the "Completing the PR" will get lost. So having all the below text at the end as tips might work well? (People can ready if they feel they need the additional guidance)?