Skip to content

GitLab

  • Menu
Projects Groups Snippets
  • Help
    • Help
    • Support
    • Community forum
    • Submit feedback
    • Contribute to GitLab
  • Sign in
  • P Perun Core
  • Project information
    • Project information
    • Activity
    • Labels
    • Members
  • Repository
    • Repository
    • Files
    • Commits
    • Branches
    • Tags
    • Contributors
    • Graph
    • Compare
  • Issues 5
    • Issues 5
    • List
    • Boards
    • Service Desk
    • Milestones
  • Merge requests 0
    • Merge requests 0
  • CI/CD
    • CI/CD
    • Pipelines
    • Jobs
    • Schedules
  • Deployments
    • Deployments
    • Environments
    • Releases
  • Monitor
    • Monitor
    • Incidents
  • Packages & Registries
    • Packages & Registries
    • Package Registry
    • Infrastructure Registry
  • Analytics
    • Analytics
    • Value stream
    • CI/CD
    • Repository
  • Wiki
    • Wiki
  • Snippets
    • Snippets
  • Activity
  • Graph
  • Create a new issue
  • Jobs
  • Commits
  • Issue Boards
Collapse sidebar
  • Svarog4
  • Perun Core
  • Wiki
  • Home

Home · Changes

Page history
Update home authored May 18, 2026 by Tomi Karikj's avatar Tomi Karikj
Show whitespace changes
Inline Side-by-side
home.md 0 → 100644
View page @ 6567f137
# Front End Architecture
> **Version:** 1.0 | **Last updated:** 2026-05-18 | **Author:** Tomi Karikj
---
## Table of Contents
1. [Project Overview](#1-project-overview)
2. [Tech Stack & Architecture](#2-tech-stack--architecture)
3. [Getting Started](#3-getting-started)
4. [Key Conventions](#4-key-conventions)
5. [Data Flow](#5-data-flow)
6. [Testing Strategy](#6-testing-strategy)
7. [CI/CD & Deployment](#7-cicd--deployment)
8. [Appendix](#8-appendix)
---
## 1. Project Overview
### 1.1 What is this product?
Perun Core is the shared front-end module of the **Svarog framework**. It provides the core application shell — login, navigation, admin console, plugin management, and a library of reusable React components (grids, forms, modals, dropdowns) — to every other Svarog module.
Other modules consume `perun-core` as an **npm dependency**. The compiled `www/perun-core.js` bundle is the primary artifact. Dependent projects import React components, Redux store utilities, and helper functions directly from it at runtime; they do not re-bundle this code.
### 1.2 Business context
- Perun Core is a **library first, application second**. Changes here can break all dependent Svarog modules, so stability matters more than speed.
- The compiled bundle (`www/perun-core.js`) is committed to the repository so dependent projects can consume it via `npm install` without running a build.
- On every push to `dev` or `main`, CI can auto-commit a freshly built bundle back to the branch (manual trigger — see §7).
### 1.3 Key links
| Resource | Link / Location |
|---|---|
| Repository | Internal GitLab |
| Issue tracker | Internal GitLab project board |
| CI/CD pipeline | `.gitlab-ci.yml` in repo root |
---
## 2. Tech Stack & Architecture
### 2.1 Core technologies
| Technology | Version & Notes |
|---|---|
| Framework | React 16.14 — kept at 16 for compatibility with older dependent modules |
| Language | JavaScript (ES2020+), transpiled with Babel |
| Styling | Plain CSS + CSS Modules for scoped component styles |
| State management | Redux 4 — `redux-updeep`, `redux-magic-async-middleware`, `redux-promise-middleware`, `redux-thunk` |
| HTTP | axios 1.x with global request/response interceptors |
| Routing | React Router DOM 5 (hash-based routing via `createHashHistory`) |
| i18n | react-intl v2 + react-intl-redux; labels fetched from the backend at startup |
| Forms | RJSF (`@rjsf/core` v5) with AJV8 validator |
| Grids | react-data-grid v5 (`ExportableGrid`, `GenericGrid`) |
| Icons | `@tabler/icons-react` 3.x |
| Build | Webpack 5, Babel loader, thread-loader (parallelised), filesystem cache |
| Linting | ESLint 9 with `eslint-plugin-react` and `eslint-plugin-react-hooks` |
| Analytics | react-ga (Google Analytics, optional) |
### 2.2 Folder structure
```
perun-core/
frontend/
assets/ — global CSS and theme files
client.js — Webpack entry point; exports the public API; bootstraps the app
components/ — Feature components (AdminConsole, Logon, Navbar, Modal, etc.)
config/ — svConfig (REST base URL) and labelBasePath
containers/ — Legacy Redux-connected container components
elements/ — Reusable UI primitives exported to dependent modules
base/ — Button, Dropdown, DependencyDropdown, InputElement
form/ — FormManager, GenericForm, CustomOnchangeFunction
grid/ — ExportableGrid, GenericGrid, GridManager, toolbar, row renderers
util/ — Icon, alertUser, alertUserV2
functions/ — Pure utility functions (utils.js, cookies.js)
loadConfiguration/ — Configurator HOC; loads DB-driven component config at mount
model/ — Redux store, reducers, actions
actions/ — Action creators (logon, grid, form, notifications, …)
reducers/ — One file per Redux slice
store.js — Store factory; injectAsyncReducer / removeAsyncReducer helpers
createReducers.js — Combines all static and async reducers
routes/ — Router setup, PluginManager, route-level page components
backend/ — Java OSGi bundle (Activator, PerunPluginInfo) — minimal, rarely touched
www/ — Webpack output; perun-core.js is committed here for npm consumers
docs/ — index.html.template for consuming projects
```
### 2.3 Architecture decisions
- **Hash-based routing** — `createHashHistory` is used because consuming projects are typically served from Java OSGi containers that don't support HTML5 push-state.
- **Redux-persist whitelist** — only specific slices are persisted to `sessionStorage` (see `whitelistRoot` in `client.js`). Dependent modules register their own slices via `persistBundleReducers`.
- **Async reducer injection** — plugins can call `injectAsyncReducer` / `removeAsyncReducer` to mount their own Redux slices at runtime without rebuilding the core bundle.
- **`window.server`** — the backend URL is injected by the consuming project's `config.js` file as `window.server` before the bundle loads. There is no `.env` equivalent for the API URL.
---
## 3. Getting Started
### 3.1 Prerequisites
| Tool | Required version |
|---|---|
| Node.js | >= 18.12.0 |
| npm | >= 8 |
| Java JDK | 11 (only needed for the Maven/OSGi bundle build) |
### 3.2 Local setup
```bash
# 1. Clone the repo
git clone <internal-gitlab-url>/perun-core.git
cd perun-core
# 2. Install dependencies
npm install
# 3. Create www/config.js pointing at your backend
echo "window.server = 'http://<host>:<port>/services'" > www/config.js
# 4. Create www/index.html (copy from docs/index.html.template, customize as needed)
# 5. Start the dev server
npm run dev
# Open http://localhost:8080 (or the port printed in terminal)
```
### 3.3 Environment variables
Set in a `.env` file at the project root (loaded by Webpack via `dotenv/config`):
| Variable | Purpose | Required |
|---|---|---|
| `GA_TRACKING_ID` | Google Analytics tracking ID | No |
For CI/CD set variables under **GitLab > Settings > CI/CD > Variables**.
Note: the backend URL is **not** an env variable — it is set as `window.server` in `www/config.js` at runtime.
### 3.4 Common scripts
| Command | What it does |
|---|---|
| `npm run dev` | Dev server with hot reload, source maps, debug on |
| `npm run build` | Production build to `www/` — minified, no source maps |
| `npm run build-dev` | Production build to `www/` — with source maps and debug |
| `npm run test` | ESLint with auto-fix (`eslint --fix frontend/`) |
### 3.5 Using your local build in a dependent project
After `npm run build` (or `build-dev`), copy `www/perun-core.js` into:
```
<dependent-project>/node_modules/perun-core/www/perun-core.js
```
This is the standard workflow because the bundle is not published to a registry.
---
## 4. Key Conventions
### 4.1 Naming
| Thing | Convention |
|---|---|
| Component files | PascalCase — `ExportableGrid.js` |
| Utility / hook files | camelCase — `utils.js`, `cookies.js` |
| CSS Modules files | `ComponentName.module.css` |
| Redux action name constants | camelCase keys in `actionNames.json` |
| Redux slice files | camelCase — `logonReducer.js` |
### 4.2 Component patterns
- Prefer **function components with hooks**. Class components exist in older code (e.g. `Configurator`) but must not be added.
- Keep data-fetching out of presentational components — use Redux actions or custom hooks in the parent.
- `react/prop-types` validation is disabled by ESLint config. Don't add prop-types to new components.
- Prefix intentionally unused variables with `_` to satisfy the `no-unused-vars` rule.
### 4.3 State management
| When to use | Mechanism |
|---|---|
| Transient UI state (open/closed, loading flag) | `useState` in the component |
| Data shared across components | Redux slice |
| Plugin-specific data | Async reducer injected via `injectAsyncReducer` |
### 4.4 Styling
- Global styles live in a **dedicated assets project**, organised by environment/application. They are loaded by the consuming project's `index.html` at runtime.
- `frontend/assets/css/style.css` exists for local development convenience — it lets you iterate on styles without deploying the assets project. It is **never committed with changes**.
- Component-scoped styles use CSS Modules (`*.module.css`). Webpack maps class names to `[name]-[local]`.
- No styled-components, Tailwind, or inline styles.
### 4.5 API calls
All HTTP calls go through **axios**. Two global interceptors are configured in `client.js`:
1. **Request** — injects the `sessionId` header from `store.getState().security.svSession`.
2. **Response error** — handles 401 (redirect to login + logout dispatch), 302, 502/503 globally.
The base URL comes from `window.server`. URLs are now constructed and passed directly by each caller — the `svConfig.triglavRestVerbs` map in `frontend/config/config.js` is legacy; do not add new entries to it.
The session token can also be embedded directly in the URL path (many backend endpoints expect it as a path segment). Retrieve it either via `mapStateToProps`:
```js
const mapStateToProps = state => ({ svSession: state.security.svSession })
```
or directly from the store outside of a component:
```js
const svSession = store.getState().security.svSession
```
---
## 5. Data Flow
### 5.1 Authentication
1. User submits credentials on the Logon screen.
2. `loginUser` action (axios POST) hits the backend login endpoint.
3. On success the response contains a `token` (the `svSession`). The `loginFulfilled` reducer stores it in `store.security.svSession`.
4. The axios request interceptor automatically attaches this token as `sessionId` on every subsequent request. It can also be embedded directly in the URL path when the endpoint expects it as a path segment — retrieve it via `mapStateToProps` or `store.getState().security.svSession`.
5. On 401, the response interceptor clears the session, dispatches `LOGOUT_FULFILLED`, and redirects to `/home/login`.
6. Session is persisted across page reloads via `redux-persist` (whitelisted slices include `security`).
There is no OAuth or JWT — the session token is an opaque server-side session ID.
### 5.2 API integration
- Base URL: `window.server` (set by the consuming project's `config.js`).
- URLs are constructed and passed directly by each caller.
- `dataToRedux` (`frontend/model/dataToRedux.js`) is a utility that fetches data and dispatches it directly to the Redux store — used mainly for i18n labels at startup.
- Errors are surfaced either via `alertUserV2` (toast/SweetAlert2 dialog) or by setting `title`/`message`/`status` in the relevant Redux slice.
### 5.3 Global state overview
| Redux slice | What it manages |
|---|---|
| `security` | Session token (`svSession`), login/logout state, user data |
| `configurator` | DB-driven component configurations loaded by the `Configurator` HOC |
| `intl` | Active locale and translated label messages |
| `gridConfig` | Grid column/row configuration per grid name |
| `selectedGridRows` | Currently selected rows per grid |
| `modal` | Active modal stack |
| `userInfo` | Logged-in user profile data |
| `businessLogicReducer` | Custom business logic flags shared across components |
| `moduleLinks` | Navigation links from the server |
| `clickedMenuReducer` | Currently active menu item |
| `routes` | Dynamically registered plugin routes |
| `globalRequestProcessor` | Global loading / in-flight request state |
| `notificationReducer` | Notification messages |
| `admConsoleFormData` | Admin Console form state |
| `checkForInvalidSession` | Flag to detect and handle expired sessions |
Dependent modules inject additional slices at runtime using `persistBundleReducers`.
### 5.4 Startup sequence
1. `client.js` is evaluated; axios interceptors and Redux store are created.
2. `persistBundleReducers(whitelistRoot)` is called → triggers `changeLanguageAndLocale`.
3. `changeLanguageAndLocale` calls `dataToRedux` to fetch i18n labels from the backend.
4. Once labels are loaded, the `Router` waits for plugin bootstrap (`waitForPlugins`).
5. When plugins are ready, `ReactDOM.render(<App />, app)` mounts the application.
---
## 6. Testing Strategy
### 6.1 What exists
There is currently **one automated check**: ESLint.
```bash
npm run test # runs: eslint --fix frontend/
```
There are no unit tests, integration tests, or E2E tests.
### 6.2 What to do before merging
1. `npm run test` — must pass with no errors.
2. `npm run build-dev` — must compile cleanly.
3. Manual smoke test in the browser against a running backend.
---
## 7. CI/CD & Deployment
### 7.1 Pipeline overview (`.gitlab-ci.yml`)
| Stage | Job | Trigger |
|---|---|---|
| `1_linter` | `npm ci` + ESLint + `npm run build` | Automatic on push |
| `2_build_and_install` | `mvn clean test` | Automatic on push |
| `3_mvn_install` | `mvn clean deploy` (publishes OSGi bundle to Maven repo) | Automatic — `dev` or `main` only |
| `3_mvn_install` | JS bundle auto-commit (`*-generate-js`) | **Manual** — maintainer triggers after review |
| `4_sonarqube` | SonarQube analysis | **Manual** |
### 7.2 Branches
| Branch | Purpose |
|---|---|
| `dev` | Active development; all feature branches merge here |
| `main` | Stable / release branch; only merge from `dev` when releasing |
### 7.3 Releasing a new bundle
1. Document all changes in `CHANGELOG.md` under a new version entry (e.g. `[v4.5.5] - 2026-05-18`).
2. Merge `dev` → `main`.
3. Push a version tag matching the release (e.g. `v4.5.5`).
4. CI builds and deploys the Maven artifact automatically.
5. A maintainer manually triggers the `*-generate-js` job, which:
- Runs `npm run build`
- Commits the updated `www/perun-core.js` back to the branch
6. Dependent projects run `npm install` to pick up the new commit.
### 7.4 Local build distribution
When you need a dependent project to use an unreleased build:
```bash
npm run build-dev
cp www/perun-core.js ../other-project/node_modules/perun-core/www/
```
---
## 8. Appendix
### 8.1 Glossary
| Term | Definition |
|---|---|
| `svSession` | The opaque session token returned by the backend on login; sent as `sessionId` on every request |
| `svConfig` | Global config object in `frontend/config/config.js` — holds `restSvcBaseUrl` and the legacy `triglavRestVerbs` map |
| `triglavRestVerbs` | Legacy map of named REST endpoint path templates in `svConfig` — deprecated, URLs are now passed directly by callers |
| `Configurator` | HOC that loads a DB-driven component configuration at mount and passes it as a `configuration` prop |
| `dataToRedux` | Utility that fetches data from the backend and dispatches it to the Redux store |
| `persistBundleReducers` | Function exported from `client.js`; dependent modules call it to register their Redux slices for persistence and trigger the app bootstrap |
| `injectAsyncReducer` | Adds a Redux reducer to the live store at runtime (used by plugins) |
| `PluginManager` | Manages dynamic route and reducer registration for Svarog plugins |
| `ExportableGrid` | Grid component that supports search, selection, and CSV/XLSX export |
| `GenericForm` | RJSF-based form component driven by a JSON Schema fetched from the backend |
| Svarog | The broader Java + React framework that `perun-core` belongs to |
| OSGi bundle | The Java packaging format used to deploy modules to the Svarog backend container |
### 8.2 Document history
| Version | Date | Author | Changes |
|---|---|---|---|
| 1.0 | 2026-05-18 | Tomi Karikj | Initial draft |
---
\ No newline at end of file
Clone repository
  • Home