👶 Staff interface for Kulttuurin kummilapset / Culture Kids 🎻
The Culture kids service consists of:
- Kukkuu API: The API backend service - The primary source of data.
- Admin UI: (This service). A restricted UI where the events are maintained and published.
- Public UI: The frontend service where the kids can view and enrol in culture events.
- Headless CMS: Content Management Service that provides dynamic pages and dynamic content for the public UI. It also provides content for the header and the footer. A React component library can be found from https://github.com/City-of-Helsinki/react-helsinki-headless-cms.
- Notification Service API: A service used by the Kukkuu API to send SMS messages.
- Mailer: A service used by the Kukkuu API to send emails.
The admin client environments (this service):
- Production environment: https://kummilapset-admin.hel.fi/
- Staging environment: https://kukkuu-admin.stage.hel.ninja/
- Testing environment: https://kukkuu-admin.test.hel.ninja/
The API environments:
- Production environment: https://kukkuu.api.hel.fi/graphql
- Staging environment: https://kukkuu.api.stage.hel.ninja/graphql
- Testing environment: https://kukkuu.api.test.hel.ninja/graphql
The public client environments:
- Production environment: https://kummilapset.hel.fi/
- Staging environment: https://kukkuu-ui.stage.hel.ninja/
- Testing environment: https://kukkuu-ui.test.hel.ninja/
The headless CMS environments:
- Production environment: https://kukkuu.content.api.hel.fi/graphql
- Testing environment: https://kukkuu.app-staging.hkih.hion.dev/graphql
This project is built using the following key frameworks and libraries:
- Vite: A modern frontend build tool that provides a fast and efficient development experience. It offers features like instant server start, hot module replacement, and optimized builds.
- React: A JavaScript library for building user interfaces. It allows for the creation of reusable UI components and efficient management of application state.
- React Admin: A framework for building admin applications on top of React. It provides a set of reusable components and utilities for common admin tasks, such as creating data grids, forms, and navigation menus. React Admin simplifies the development of admin interfaces by providing a structured and opinionated approach.
- Clone the repo.
cp .env.example .env
- Use file
.env.local
to modify environment variables if needed. For more info, check this. - Run either
yarn start
to run the app normally ordocker compose up
to run the app in a Docker container. In the future, when there are changes that need rebuilding the container, rundocker compose up --build
instead.
- Open http://localhost:3001 to view the app in the browser.
You need to authorize the user you are trying to log in with to Kukkuu-Admin. In order to log in and get the staff / admin privileges, an authorization service is needed.
NOTE: The Kukkuu API needs to be configured to use the same authorization service as the Kukkuu Admin UI is using, because only then the authorization can be verified.
Setup authorization service:
- Use public test Keycloak: The primary option. See Using the Helsinki-Profile Keycloak.
- Use a local Tunnistamo: For a full local environment, see Setting up Tunnistamo and Kukkuu API locally with Docker.
You can use the public Kukkuu API from the test environment or set up a local Kukkuu API. It should be noted that in the public test environment, the data is shared with other users. If you want to test with your own data and have an isolated system, you need to set up a local API.
Choose the environment:
- Use a public test environment API: Check that your environment variables are set correctly. The examples are given in .env.example.
- Setup Kukkuu API locally: See Use Kukkuu API locally.
If you're using a local Kukkuu API backend (VITE_API_URI=http://localhost:8081/graphql
), you can easily grant staff privileges to your user account. Here's how:
-
Start the backend: Ensure your local Kukkuu API backend is running.
-
Access the Django admin interface:
- Open the Django admin interface:
http://localhost:8081/admin/
- Log in with the default credentials: username
admin
, passwordadmin
. If you don't have an admin user yet, you can create one withpython manage.py createsuperuser
.
- Open the Django admin interface:
-
Grant superuser privileges:
- Attempt to log in to Kukkuu-admin (
http://localhost:3001/login
) using your desired user account. This will create the user in the backend if it doesn't exist. - In the Django admin interface, locate the user you just created and grant them admin or superuser privileges.
- Attempt to log in to Kukkuu-admin (
You should now be able to log in to Kukkuu-admin with that user.
If you're using a remote Kukkuu backend (e.g., the test environment; VITE_API_URI=https://kukkuu.api.test.hel.ninja/graphql
), you'll need to grant staff privileges to your user account. Here's how:
-
Obtain Django admin credentials:
- Contact the administrator of the remote backend to get the credentials.
- If you have access to the backend pod, you can create a superuser by running
python manage.py createsuperuser
in the pod's terminal.
-
Access the Django admin interface:
- Open the Django admin interface for the remote backend (e.g.,
https://kukkuu.api.test.hel.ninja/admin
). - Log in using the credentials from step 1.
- Open the Django admin interface for the remote backend (e.g.,
-
Grant superuser privileges:
- Attempt to log in to Kukkuu-admin (
http://localhost:3001/login
). This will create a user account in the backend if one doesn't exist. - In the Django admin interface, find the user you just created and grant them superuser privileges.
- Attempt to log in to Kukkuu-admin (
You should now be able to log in to Kukkuu-admin with your user account.
This section describes how JSON Web Tokens (JWT) are issued for browser tests.
In browser tests, we want to bypass the regular authentication flow and directly issue JWTs for testing user roles and permissions. This is achieved by mocking the authentication service and providing pre-generated JWTs with specific claims.
How it works:
clientUtils
: Contains helper functions that run within the Testcafe browser environment. These functions utilize Testcafe'sClientFunction
to interact with the browser and manage JWTs.mocks
: Provides functions to intercept network requests to the authentication service and replace them with mocked responses containing the test JWTs. This prevents actual authentication and allows us to control the user context during tests.config
: Holds configuration settings for the JWT library used in browser tests.jwt
: Contains utilities to create and sign JWTs symmetrically. The API needs to be configured with the same secret key to verify these tokens.oidc
: Adapts the generated JWTs to a format compatible with the OpenID Connect (OIDC) client used in the application.services
: Includes helper functions for managing test data, such as selecting an admin project for the test user. These functions make actual API calls (not mocked) to prepare the test environment.
Key points:
- The API and the Admin UI must share the same secret key (
BROWSER_TESTS_JWT_SIGN_SECRET
) for JWT verification. - The
BROWSER_TESTS_JWT_AD_GROUP
environment variable defines the Active Directory group used for the test user, which should have admin privileges in the API. - Several environment variables are used to configure the JWT mocking and testing environment.
This project uses Husky to manage Git hooks. Husky is configured to run specific scripts before committing changes to ensure code quality and consistency.
The pre-commit hook is configured to run the following commands:
yarn doctoc .
yarn lint-staged
yarn doctoc .
: This command updates the table of contents in your markdown files.yarn lint-staged
: This command runs linting on staged files to ensure they meet the project's coding standards. The lint-staged configuration can be found from .lintstagedrc.json.
NOTE:
doctoc
andhusky
does not work seamlessly together, since thedoctoc
does update the TOCs of the markdown files, but does not reject the pre-commit hook execution, and only leaves the refactored files as unstaged in Git.
The commit-msg hook is configured to run the following command:
npx --no-install commitlint --edit "$1"
npx --no-install commitlint --edit "$1"
: This command uses Commitlint to lint commit messages based on the project's commit message conventions. This repo follows the Conventional Commits.
In the project directory, you can run:
Runs the app in development mode. Open http://localhost:3001 to view it in the browser.
The page will reload if you make edits. You will also see any lint errors in the console.
Builds the app for production to the build
directory.
It correctly bundles React in production mode and optimizes the build for the best performance.
The build is minified and the filenames include the hashes. Your app is ready to be deployed!
See the section about building for production and CLI guide for more information.
Fetches the GraphQL schema from the backend and updates typing information. The configuration is written in codegen.ts. Check that the environment variables are set properly to match with your API.
Launches the vitest
test runner. See the section about Getting started for more information.
Runs browser tests against your local version of the application (assumes port 3001
).
- The
yarn test:browser:ci
variant of this command is meant to run in the CI, and it targets the staging server. It uses headless mode and may therefore behave differently compared to the local test runner. - The deployment pipelines are running the browser tests as automated actions. They are run against PR and staging environments when after they have been built and deployed.
- See also JWT issuance for browser tests
To run browser tests locally, you need to configure the browser testing environment:
- Run a local Kukkuu API instance with the browser testing JWT features set on. This allows the UI client to issue new JWTs for authorization by itself.
- Run a local Kukkuu Admin UI.
- Carefully double-check that the UI instance is configured to use the local API. The browser test JWT token configurations also need to match in order to successfully verify the newly issued tokens. You can navigate through the UI manually to see that everything is working as expected.
- Run the browser test with
yarn test:browser
oryarn test:browser:ci
.
For configuration, check the following environment variables:
BROWSER_TESTS_JWT_SIGN_SECRET
needs to be a valid 256 bits token and it needs to be configured the same in both the API and the Admin UI in order to verify the self-issued JWT for browser testing.BROWSER_TESTS_JWT_AD_GROUP
defines the AD group that should be used while running the browser tests. This value is used while issuing a JWT for an admin user. The AD group should be made in the API so that it gives admin permissions for the newly created user with this AD group for the (year) project that is created for browser testing. These AD groups and user groups can be managed from the API.BROWSER_TESTS_ENV_URL
tells Testcafe where the testable UI is located.VITE_API_URI
defines the Kukkuu API GraphQL endpoint. It's important in browser testing configuration for JWT mocking reasons.VITE_OIDC_KUKKUU_API_CLIENT_ID
OIDC config that is needed in JWT mocking.VITE_OIDC_CLIENT_ID
OIDC config that is needed in JWT mocking.VITE_OIDC_AUTHORITY
OIDC config that is needed in JWT mocking.
NOTE: There is an .env.test.local.example that can be copied to a file named .env.test.local
. If the .env.test.local
is present, it will be used during the local Testcafe runs.
The used environments are listed in Service environments.
The application uses automatic semantic versions and is released using Release Please.
Release Please is a GitHub Action that automates releases for you. It will create a GitHub release and a GitHub Pull Request with a changelog based on conventional commits.
Each time you merge a "normal" pull request, the release-please-action will create or update a "Release PR" with the changelog and the version bump related to the changes (they're named like release-please--branches--master--components--kukkuu-admin
).
To create a new release for an app, this release PR is merged, which creates a new release with release notes and a new tag. This tag will be picked by Azure pipeline and trigger a new deployment to staging. From there, the release needs to be manually released to production.
When merging release PRs, make sure to use the "Rebase and merge" (or "Squash and merge") option, so that Github doesn't create a merge commit. All the commits must follow the conventional commits format. This is important, because the release-please-action does not work correctly with merge commits (there's an open issue you can track: Chronological commit sorting means that merged PRs can be ignored ).
See Release Please Implementation Design for more details.
And all docs are available here: release-please docs.
Use Conventional Commits to ensure that the changelogs are generated correctly.
Release please goes through commits and tries to find "releasable units" using commit messages as guidance - it will then add these units to their respective release PR's and figures out the version number from the types: fix
for patch, feat
for minor, feat!
for major. None of the other types will be included in the changelog. So, you can use for example chore
or refactor
to do work that does not need to be included in the changelog and won't bump the version.
The release-please workflow is located in the release-please.yml file.
The configuration for release-please is located in the release-please-config.json file. See all the options here: release-please docs.
The manifest file is located in the release-please-manifest.json file.
When adding a new app, add it to both the release-please-config.json and release-please-manifest.json file with the current version of the app. After this, release-please will keep track of versions with release-please-manifest.json.
If you were expecting a new release PR to be created or old one to be updated, but nothing happened, there's probably one of the older release PR's in pending state or action didn't run.
- Check if the release action ran for the last merge to main. If it didn't, run the action manually with a label.
- Check if there's any open release PR. If there is, the work is now included on this one (this is the normal scenario).
- If you do not see any open release PR related to the work, check if any of the closed PR's are labeled with
autorelease: pending
- ie. someone might have closed a release PR manually. Change the closed PR's label toautorelease: tagged
. Then go and re-run the last merge workflow to trigger the release action - a new release PR should now appear. - Finally check the output of the release action. Sometimes the bot can't parse the commit message and there is a notification about this in the action log. If this happens, it won't include the work in the commit either. You can fix this by changing the commit message to follow the Conventional Commits format and rerun the action.
Important! If you have closed a release PR manually, you need to change the label of closed release PR to autorelease: tagged
. Otherwise, the release action will not create a new release PR.
Important! Extra label will force release-please to re-generate PR's. This is done when action is run manually with prlabel -option
Sometimes there might be a merge conflict in release PR - this should resolve itself on the next push to main. It is possible run release-please action manually with label, it should recreate the PR's. You can also resolve it manually, by updating the release-please-manifest.json file.
- Open release-please github action
- Click Run workflow
- Check Branch is master
- Leave label field empty. New label is not needed to fix merge issues
- Click Run workflow -button
There's also a CLI for debugging and manually running releases available for release-please: release-please-cli
When a Release-Please pull request is merged and a version tag is created (or a proper tag name for a commit is manually created), this tag will be picked by Azure pipeline, which then triggers a new deployment to staging. From there, the deployment needs to be manually approved to allow it to proceed to the production environment.
The tag name is defined in the azure-pipelines-release.yml.
This project is licensed under the MIT License.