Skip to content
Open
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
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added

- Page to overview released and pending releases of a questionnaire
- Form to create a release of a questionnaire

### Changed

- change end-point to get units in legacy client (according to the API)
Expand Down
63 changes: 63 additions & 0 deletions next/src/api/models/releaseDTO.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import type { TargetMode } from '@/models/questionnaires'

/** Model of the registry release returned by the Pogues API's releases endpoint. */
export type RegistryReleaseDTO = {
collectionInstrumentId: string
version: number
author: string
releaseDate: number
poguesVersionId: string
releaseDescription: string
mode: TargetMode
context: ReleaseContextDTO
overrideGenerationParameters: OverrideGenerationParametersDTO | null
visualizeUrl: string
}

/** Model of the release request returned by the Pogues API's release-requests endpoint. */
export type ReleaseRequestDTO = {
trackerId: number
author: string
requestDate: number
currentStep: CurrentStepDTO
status: ReleaseRequestStatusDTO
statusDescription: string
poguesVersionId: string
poguesId: string
releaseDescription: string
mode: TargetMode
context: ReleaseContextDTO
overrideGenerationParameters: OverrideGenerationParametersDTO
}

export type CreateReleaseDTO = {
poguesId: string
releaseDescription: string
mode: TargetMode
context: ReleaseContextDTO
overrideGenerationParameters: OverrideGenerationParametersDTO
}

export type OverrideGenerationParametersDTO = {
questionNumberingMode: QuestionNumberingModeDTO
responseTimeQuestion: boolean
}

export type ReleaseContextDTO = 'HOUSEHOLD' | 'BUSINESS'

export type CurrentStepDTO =
| 'BUILD_PARAMETERS'
| 'PUBLISH_PRERELEASE'
| 'GENERATE_DDI'
| 'GENERATE_LUNATIC'
| 'PUBLISH_DDI'
| 'PUBLISH_LUNATIC'
| 'FINISHED'

export type ReleaseRequestStatusDTO =
| 'CREATED'
| 'RUNNING'
| 'COMPLETED'
| 'FAILED'

export type QuestionNumberingModeDTO = 'NONE' | 'SEQUENCE' | 'ALL'
69 changes: 69 additions & 0 deletions next/src/api/releases.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import nock from 'nock'

import type { CreateReleaseDTO } from './models/releaseDTO'
import {
MOCK_PUBLICATIONS,
MOCK_REQUESTS,
deleteReleaseRequest,
getPendingReleases,
getReleases,
postRelease,
} from './releases'

vi.mock('@/lib/auth/oidc')

it('Get releases works', async () => {
// const releases: RegistryReleaseDTO[] = [registryReleaseDTO]

// nock('https://mock-api')
// .get('/persistence/questionnaire/my-questionnaire/releases')
// .reply(200, releases)

// const res = await getReleases('my-questionnaire')
// expect(res).toEqual([registryRelease])

const res = await getReleases('my-questionnaire')
expect(res).toEqual(MOCK_PUBLICATIONS)
})

it('Get pending releases works', async () => {
// const requests: ReleaseRequestDTO[] = [releaseRequestDTO]

// nock('https://mock-api')
// .get('/persistence/questionnaire/my-questionnaire/release-requests')
// .reply(200, requests)

// const res = await getPendingReleases('my-questionnaire')
// expect(res).toEqual([releaseRequest])
const res = await getPendingReleases('my-questionnaire')
expect(res).toEqual(MOCK_REQUESTS)
})

it('Post release works', async () => {
const createRelease: CreateReleaseDTO = {
poguesId: 'my-questionnaire',
releaseDescription: 'New release',
mode: 'CAWI',
context: 'HOUSEHOLD',
overrideGenerationParameters: {
questionNumberingMode: 'SEQUENCE',
responseTimeQuestion: true,
},
}

nock('https://mock-api')
.post('/persistence/questionnaire/my-questionnaire/releases')
.reply(201)

const res = await postRelease('my-questionnaire', createRelease)
expect(res.status).toEqual(201)
})

it('Delete release request works', async () => {
nock('https://mock-api')
.delete('/persistence/questionnaire/my-questionnaire/release-requests/42')
.reply(204)

const res = await deleteReleaseRequest('my-questionnaire', 42)
expect(res.status).toEqual(204)
})
185 changes: 185 additions & 0 deletions next/src/api/releases.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
import { queryOptions } from '@tanstack/react-query'

import { TargetModes } from '@/models/questionnaires'
import type { RegistryRelease, ReleaseRequest } from '@/models/releases'

import { instance } from './instance'
import type { CreateReleaseDTO } from './models/releaseDTO'

export const MOCK_REQUESTS: ReleaseRequest[] = [
{
trackerId: 1,
author: 'xbeltv',
requestDate: new Date('2026-07-06T10:30:00').getTime(),
currentStep: 'BUILD_PARAMETERS',
status: 'RUNNING',
statusDescription: '',
poguesVersionId: '550e8400-e29b-41d4-a716-446655440000',
poguesId: 'SRCV_REINTERRO',
releaseDescription: 'Recette intégrée oct 2025 pour SRCV_REINTERRO 2026',
mode: TargetModes.CAPI,
context: 'HOUSEHOLD',
overrideGenerationParameters: {
questionNumberingMode: 'SEQUENCE',
responseTimeQuestion: true,
},
},
{
trackerId: 2,
author: 'nazdsn',
requestDate: new Date('2026-07-05T14:15:00').getTime(),
currentStep: 'GENERATE_DDI',
status: 'FAILED',
statusDescription: 'Erreur lors de la génération Eno',
poguesVersionId: '550e8400-e29b-41d4-a716-446655440001',
poguesId: 'SRCV_REINTERRO',
releaseDescription: 'Autre demande de publication pour SRCV_REINTERRO 2026',
mode: TargetModes.CAWI,
context: 'BUSINESS',
overrideGenerationParameters: {
questionNumberingMode: 'NONE',
responseTimeQuestion: false,
},
},
{
trackerId: 3,
author: 'bcbab8',
requestDate: new Date('2026-07-04T09:00:00').getTime(),
currentStep: 'PUBLISH_DDI',
status: 'FAILED',
statusDescription: 'Erreur 409 - Conflit de version',
poguesVersionId: '550e8400-e29b-41d4-a716-446655440002',
poguesId: 'SRCV_REINTERRO',
releaseDescription: 'Recette intégrée oct 2025 pour SRCV_REINTERRO 2026',
mode: TargetModes.CAWI,
context: 'HOUSEHOLD',
overrideGenerationParameters: {
questionNumberingMode: 'ALL',
responseTimeQuestion: true,
},
},
]

export const MOCK_PUBLICATIONS: RegistryRelease[] = [
{
collectionInstrumentId: '550e8400-e29b-41d4-a716-446655440001',
version: 3,
author: 'xbeltv',
releaseDate: new Date('2026-07-04T08:00:00').getTime(),
poguesVersionId: '93d1e85c-327d-4153-a5fa-e04f54ca0e3e',
releaseDescription: 'ESA 2026 PROD',
mode: TargetModes.CAWI,
context: 'HOUSEHOLD',
overrideGenerationParameters: {
questionNumberingMode: 'NONE',
responseTimeQuestion: false,
},
visualizeUrl: 'https://visu.example.com/esa-2026-prod',
},
{
collectionInstrumentId: '550e8400-e29b-41d4-a716-446655440002',
version: 2,
author: 'nazdsn',
releaseDate: new Date('2026-06-28T10:00:00').getTime(),
poguesVersionId: '5ddf61df-00c8-4018-a763-fa7bc91b0162',
releaseDescription: 'ESA 2026 TEST TERRAIN',
mode: TargetModes.CAPI,
context: 'BUSINESS',
overrideGenerationParameters: {
questionNumberingMode: 'NONE',
responseTimeQuestion: false,
},
visualizeUrl: 'https://visu.example.com/esa-2026-test',
},
{
collectionInstrumentId: '550e8400-e29b-41d4-a716-446655440003',
version: 1,
author: 'bcbab8',
releaseDate: new Date('2026-06-15T11:30:00').getTime(),
poguesVersionId: 'b77e7cad-475d-4d83-b036-fa7a98a84a8a',
releaseDescription: "Publication la plus ancienne d'ESA",
mode: TargetModes.CAWI,
context: 'HOUSEHOLD',
overrideGenerationParameters: null,
visualizeUrl: 'https://visu.example.com/esa-2026-old',
},
]

export const releasesKeys = {
released: (questionnaireId: string) => ['released', questionnaireId] as const,
pending: (questionnaireId: string) => ['pending', questionnaireId] as const,
}

/**
* Used to retrieve questionnaire registry releases associated to its id.
*/
export const releasesQueryOptions = (questionnaireId: string) =>
queryOptions({
queryKey: releasesKeys.released(questionnaireId),
queryFn: () => getReleases(questionnaireId),
})

/**
* Used to retrieve pending questionnaire registry releases associated to its id.
*/
export const pendingReleasesQueryOptions = (questionnaireId: string) =>
queryOptions({
queryKey: releasesKeys.pending(questionnaireId),
queryFn: () => getPendingReleases(questionnaireId),
})

/** Retrieve questionnaire releases by the questionnaire id. */
export async function getReleases(
questionnaireId: string,
): Promise<RegistryRelease[]> {
console.log('questionnaireId', questionnaireId)

// return instance
// .get(`/persistence/questionnaire/${questionnaireId}/releases`, {
// headers: { Accept: 'application/json' },
// })
// .then(({ data }: { data: RegistryReleaseDTO[] }) => {
// return computeRegistryReleases(data)
// })
return Promise.resolve(MOCK_PUBLICATIONS)

Check failure on line 144 in next/src/api/releases.ts

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Prefer `return value` over `return Promise.resolve(value)`.

See more on https://sonarcloud.io/project/issues?id=inseefr_pogues-next&issues=AZ-D2adi7MbkhXJVJNei&open=AZ-D2adi7MbkhXJVJNei&pullRequest=1177
}

/** Retrieve questionnaire pending releases by the questionnaire id. */
export async function getPendingReleases(
questionnaireId: string,
): Promise<ReleaseRequest[]> {
console.log('questionnaireId', questionnaireId)

// return instance
// .get(`/persistence/questionnaire/${questionnaireId}/release-requests`, {
// headers: { Accept: 'application/json' },
// })
// .then(({ data }: { data: ReleaseRequestDTO[] }) => {
// return computeReleaseRequests(data)
// })
return Promise.resolve(MOCK_REQUESTS)

Check failure on line 160 in next/src/api/releases.ts

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Prefer `return value` over `return Promise.resolve(value)`.

See more on https://sonarcloud.io/project/issues?id=inseefr_pogues-next&issues=AZ-D2adi7MbkhXJVJNej&open=AZ-D2adi7MbkhXJVJNej&pullRequest=1177
}

/** Create a new release. */
export async function postRelease(
questionnaireId: string,
release: CreateReleaseDTO,
): Promise<Response> {
return instance.post(
`/persistence/questionnaire/${questionnaireId}/releases`,
release,
{
headers: { 'Content-Type': 'application/json' },
},
)
}

/** Delete a release request by tracker id. */
export async function deleteReleaseRequest(
questionnaireId: string,
trackerId: number,
): Promise<Response> {
return instance.delete(
`/persistence/questionnaire/${questionnaireId}/release-requests/${trackerId}`,
)
}
Loading
Loading