Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 4 additions & 7 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,18 @@

# [2025.7.0](https://github.com/proconnect-gouv/hyyypertool/compare/2025.6.2...2025.7.0) (2025-07-24)


### Bug Fixes

* **typo:** add accent to vérifier ([#947](https://github.com/proconnect-gouv/hyyypertool/issues/947)) ([e2db914](https://github.com/proconnect-gouv/hyyypertool/commit/e2db914f3d6a081a0fcd6e2a703daca4414fc1a2))

- **typo:** add accent to vérifier ([#947](https://github.com/proconnect-gouv/hyyypertool/issues/947)) ([e2db914](https://github.com/proconnect-gouv/hyyypertool/commit/e2db914f3d6a081a0fcd6e2a703daca4414fc1a2))

### Features

* migrate from MonComptePro to Identité ProConnect ([#971](https://github.com/proconnect-gouv/hyyypertool/issues/971)) ([48bce06](https://github.com/proconnect-gouv/hyyypertool/commit/48bce0683e771d7aea71f46a0de19871afbe21b8))
* **moderation:** update agent_comcom_comaglo ([#961](https://github.com/proconnect-gouv/hyyypertool/issues/961)) ([734c5f5](https://github.com/proconnect-gouv/hyyypertool/commit/734c5f5cab9e45a3d3763c11425d5921805b4048))

- migrate from MonComptePro to Identité ProConnect ([#971](https://github.com/proconnect-gouv/hyyypertool/issues/971)) ([48bce06](https://github.com/proconnect-gouv/hyyypertool/commit/48bce0683e771d7aea71f46a0de19871afbe21b8))
- **moderation:** update agent_comcom_comaglo ([#961](https://github.com/proconnect-gouv/hyyypertool/issues/961)) ([734c5f5](https://github.com/proconnect-gouv/hyyypertool/commit/734c5f5cab9e45a3d3763c11425d5921805b4048))

### Reverts

* Revert "🔧 improve dependabot config for bun monorepo support (#975)" (#990) ([8df2db2](https://github.com/proconnect-gouv/hyyypertool/commit/8df2db2121173fa8aa87085fa92ee1d4781c6922)), closes [#975](https://github.com/proconnect-gouv/hyyypertool/issues/975) [#990](https://github.com/proconnect-gouv/hyyypertool/issues/990)
- Revert "🔧 improve dependabot config for bun monorepo support (#975)" (#990) ([8df2db2](https://github.com/proconnect-gouv/hyyypertool/commit/8df2db2121173fa8aa87085fa92ee1d4781c6922)), closes [#975](https://github.com/proconnect-gouv/hyyypertool/issues/975) [#990](https://github.com/proconnect-gouv/hyyypertool/issues/990)

## [2025.6.2](https://github.com/proconnect-gouv/hyyypertool/compare/2025.6.1...2025.6.2) (2025-06-17)

Expand Down
295 changes: 269 additions & 26 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,66 +8,309 @@ Thank you for your interest in contributing! We welcome improvements, bug fixes,

### Main branch

* The default development branch is `main`.
- The default development branch is `main`.

### Branch naming

* Use a descriptive name that recalls the purpose, for example:
- Use a descriptive name that recalls the purpose, for example:
- `feature-add-authentication`
- `fix-update-dependencies`

* `feature-add-authentication`
* `fix-update-dependencies`
* Avoid generic names like `patch-1` or `branch123`.
* Feel free to choose any clear, concise format.
- Avoid generic names like `patch-1` or `branch123`.
- Feel free to choose any clear, concise format.

---

## Commits

### Scope

* Make small, focused commits (micro commits).
* Avoid touching too many files in a single commit.
- Make small, focused commits (micro commits).
- Avoid touching too many files in a single commit.

### Message format

* Use [Gitmoji](https://gitmoji.dev/) for an emoji prefix (e.g., `✨`, `🐛`, `💄`).
* Write a short subject that clearly describes the change; it will appear in the changelog.

* Example: `💄 change duplicate icon`
- Use [Gitmoji](https://gitmoji.dev/) for an emoji prefix (e.g., `✨`, `🐛`, `💄`).
- Write a short subject that clearly describes the change; it will appear in the changelog.
- Example: `💄 change duplicate icon`

### Commit body

* Include detailed context or rationale here.
* Do **not** post PR details or discussion in external chat channels; document them in the commit instead.
- Include detailed context or rationale here.
- Do **not** post PR details or discussion in external chat channels; document them in the commit instead.

---

## Pull Requests

### Labels

* GitHub will apply labels automatically.
- GitHub will apply labels automatically.

### Content

* Keep PRs small and focused; ideally one commit per PR.
* PRs are merged into `main` using a **merge commit**.
* Clean up or squash commits before pushing (on macOS, you can use [GitUp](https://github.com/git-up/GitUp)).
- Keep PRs small and focused; ideally one commit per PR.
- PRs are merged into `main` using a **merge commit**.
- Clean up or squash commits before pushing (on macOS, you can use [GitUp](https://github.com/git-up/GitUp)).

### Title

* **Single-commit PR**: use the default commit message as the PR title.
* **Multi-commit PR**: craft a title following the commit message guidelines above.
- **Single-commit PR**: use the default commit message as the PR title.
- **Multi-commit PR**: craft a title following the commit message guidelines above.

### Description

* **Single-commit PR**: the default description is usually sufficient.
* **When needed**, add context, for example:
- **Single-commit PR**: the default description is usually sufficient.
- **When needed**, add context, for example:
- Database migrations to run: `npm run migrate`
- This PR reverts #123.

- Link related Trello cards using the Trello Power-Up.
- Feel free to include illustrative GIFs to demonstrate changes.

---

## UseCase Convention

UseCases follow a consistent factory pattern for dependency injection and type safety.

### Structure

```typescript
// Factory function that takes dependencies
export function UseCaseName({ dependency1, dependency2 }: DependencyCradle) {
// Return the actual usecase function
return async function use_case_name(params: InputType) {
// Implementation
return result;
};
}

// Export handler type for dependency injection
export type UseCaseNameHandler = ReturnType<typeof UseCaseName>;

// Optional: Export output type if needed elsewhere
export type UseCaseNameOutput = Awaited<ReturnType<UseCaseNameHandler>>;
```

### Conventions

1. **Factory Pattern**: Each usecase is a factory function that accepts dependencies and returns the actual usecase function
2. **Naming**:
- Factory function: `PascalCase` (e.g., `GetUserInfo`)
- Returned function: `snake_case` (e.g., `get_user_info`)
3. **Dependencies**: Use typed cradles (e.g., `IdentiteProconnectDatabaseCradle`) for dependency injection
4. **Types**: Export `Handler` type for the returned function, optionally export `Output` type
5. **Comments**: Use `//` comment blocks to separate sections
6. **Error Handling**:
- Throw appropriate errors (e.g., `NotFoundError`) with descriptive messages
- Prefer `import { to } from "await-to-js"` over try/catch blocks for cleaner error handling
- Always try to track error cause when instantiating new errors using the `cause` option
7. **Async**: All usecase functions should be async

### Example

```typescript
//

import { NotFoundError } from "@~/app.core/error";
import type { IdentiteProconnectDatabaseCradle } from "@~/identite-proconnect.database";
import { to } from "await-to-js";

//

export function GetUserInfo({ pg }: IdentiteProconnectDatabaseCradle) {
return async function get_user_info(id: number) {
const user = await pg.query.users.findFirst({
where: (table, { eq }) => eq(table.id, id),
});

if (!user) throw new NotFoundError(`User ${id} not found.`);
return user;
};
}

export type GetUserInfoHandler = ReturnType<typeof GetUserInfo>;
export type GetUserInfoOutput = Awaited<ReturnType<GetUserInfoHandler>>;
```

**Error Handling with await-to-js:**

```typescript
export function ProcessUserData({ external_service }: Dependencies) {
return async function process_user_data(id: number) {
// Prefer this pattern over try/catch
const [error, result] = await to(external_service.fetchData(id));

if (error) {
// Handle specific error types and track the original cause
if (error instanceof NetworkError) {
throw new ServiceUnavailableError("External service unavailable", {
cause: error,
});
}

// Always preserve the original error as cause when wrapping
throw new ProcessingError("Failed to process user data", {
cause: error,
});
}

return result;
};
}
```

---

## Testing Convention

Write comprehensive tests following these guidelines:

### Repository Layer Tests

1. **Use Real Database**: Import `pg` from `@~/identite-proconnect.database/testing` instead of mocking
2. **Database Setup**: Always include `beforeAll(migrate)` and `beforeEach(empty_database)`
3. **Seed Data**: Use unicorn seed functions (e.g., `create_adora_pony_user`, `create_pink_diamond_user`, `create_red_diamond_user`)
4. **Time Control**: Use `setSystemTime()` for deterministic timestamps in tests
5. **Snapshots Over Multiple Expects**: Prefer `toMatchInlineSnapshot()` for complex object assertions over multiple individual expects
6. **Auto-generated Snapshots**: When using `toMatchInlineSnapshot()`, let Bun test write the string value automatically by running the test first with an empty string or no parameter
7. **Comprehensive Coverage**: Test all major functionality including:
- Basic operations (CRUD)
- Edge cases (empty results, not found)
- Filtering and search functionality
- Pagination behavior
- Data ordering

### Example

```typescript
//

import { schema } from "@~/identite-proconnect.database";
import {
create_adora_pony_user,
create_pink_diamond_user,
} from "@~/identite-proconnect.database/seed/unicorn";
import {
empty_database,
migrate,
pg,
} from "@~/identite-proconnect.database/testing";
import { beforeAll, beforeEach, expect, setSystemTime, test } from "bun:test";
import { GetUsersList } from "./GetUsersList";

//

beforeAll(migrate);
beforeEach(empty_database);

beforeAll(() => {
setSystemTime(new Date("2222-01-01T00:00:00.000Z"));
});

test("returns paginated users list with default pagination", async () => {
await create_adora_pony_user(pg);

const get_users_list = GetUsersList(pg);
const result = await get_users_list({});

// Let Bun auto-generate the snapshot - comprehensive validation
expect(result).toMatchInlineSnapshot(`
{
"count": 1,
"users": [
{
"created_at": "2222-01-01 00:00:00+00",
"email": "adora.pony@unicorn.xyz",
"email_verified_at": null,
"family_name": "Pony",
"given_name": "Adora",
"id": 1,
"last_sign_in_at": null,
},
],
}
`);
});

test("filters users by search term", async () => {
await create_adora_pony_user(pg);
await create_pink_diamond_user(pg);

const get_users_list = GetUsersList(pg);
const result = await get_users_list({ search: "pony" });

expect(result).toMatchInlineSnapshot(`
{
"count": 1,
"users": [
{
"created_at": "2222-01-01 00:00:00+00",
"email": "adora.pony@unicorn.xyz",
"email_verified_at": null,
"family_name": "Pony",
"given_name": "Adora",
"id": 1,
"last_sign_in_at": null,
},
],
}
`);
});
```

---

## Context Usage Convention

### Server vs Client Context

- **Pages/Routes**: Use `useRequestContext` for server data access
- **UI Components**: Use `createContext` for client state management

This separation ensures clear architectural boundaries between server-side data concerns and client-side UI state.

### Page Variables Pattern

Use `loadPageVariables` functions for consistent data loading:

```typescript
export async function loadDomainPageVariables(
pg: IdentiteProconnect_PgDatabase,
{ id }: { id: number },
) {
// Data loading logic
return { data1, data2, ... };
}

export interface ContextVariablesType extends Env {
Variables: Awaited<ReturnType<typeof loadDomainPageVariables>>;
}
```

#### Helper Function

Use `set_variables` helper for bulk context variable assignment:

```typescript
import { set_variables } from "@~/app.middleware/context/set_variables";

async function set_variables_middleware(
{ req, set, var: { identite_pg } },
next,
) {
const { id } = req.valid("param");
const variables = await loadPageVariables(identite_pg, { id });
set_variables(set, variables);
return next();
}
```

* Database migrations to run: `npm run migrate`
* This PR reverts #123.
* Link related Trello cards using the Trello Power-Up.
* Feel free to include illustrative GIFs to demonstrate changes.
- **Naming**: `loadDomainPageVariables` (e.g., `loadUserPageVariables`)
- **Type inference**: Use `Awaited<ReturnType<...>>` for automatic typing
- **Single source**: Consolidate all page data loading in one function
- **Helper usage**: Use `set_variables(set, variables)` for bulk assignment

---

Expand Down
10 changes: 7 additions & 3 deletions e2e/features/moderations/manage_internal_domain.feature
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,21 @@ Fonctionnalité: Gérer un domaine interne lors de la modération
| Domain | Status |
| yopmail.com | ❓ |

Quand je vais à l'intérieur de la rangée nommée "Domaine yopmail.com (null)"
Quand je clique sur "Menu"
Et je clique sur "✅ Domaine autorisé"
Et je réinitialise le contexte
Alors je dois voir un tableau nommé "🌐 1 domaine connu dans l’organisation" et contenant
| Domain | Status |
| yopmail.com | ✅ |

Quand je vais à l'intérieur de la rangée nommée "Domaine yopmail.com (verified)"
Quand je clique sur "Menu"
Et je clique sur le bouton "🚫 Domaine refusé"
Quand je vais à l'intérieur du tableau nommé "🌐 1 domaine connu dans l’organisation"
Alors je vois "🚫"
Et je vois "refused"
Et je réinitialise le contexte
Alors je dois voir un tableau nommé "🌐 1 domaine connu dans l’organisation" et contenant
| Domain | Status | Type |
| yopmail.com | 🚫 | refused |
Et je réinitialise le contexte

Quand je clique sur "Ajouter un domain"
Expand Down
2 changes: 1 addition & 1 deletion hyyypertool.code-workspace
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@
"cSpell.language": "en,fr",
"cucumber.features": ["features/**/*.feature"],
"cucumber.glue": ["cypress/support/step_definitions/**/*.ts"],
"cSpell.words": ["identite", "moderations", "proconnect"],
"cSpell.words": ["identite", "moderations", "proconnect", "zammad"],
},
"tasks": {
"version": "2.0.0",
Expand Down
3 changes: 3 additions & 0 deletions sources/app/middleware/src/context/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
//

export * from "./set_variables";
Loading