Skip to content
This repository has been archived by the owner on Nov 14, 2024. It is now read-only.

Commit

Permalink
refactor(api): on fait passer les paramètres dans un objet plutôt qu'…
Browse files Browse the repository at this point in the history
…à plat
  • Loading branch information
MichaelBitard committed Sep 16, 2024
1 parent d139c9b commit 74b7b8b
Show file tree
Hide file tree
Showing 6 changed files with 89 additions and 99 deletions.
93 changes: 48 additions & 45 deletions packages/api/src/api/rest/administrations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,67 +2,70 @@ import { Request as JWTRequest } from 'express-jwt'
import { HTTP_STATUS } from 'camino-common/src/http'

import { CustomResponse } from './express-type'
import { AdminUserNotNull, User, UserNotNull } from 'camino-common/src/roles'
import { AdminUserNotNull, User } from 'camino-common/src/roles'
import { Pool } from 'pg'
import { AdministrationId, administrationIdValidator } from 'camino-common/src/static/administrations'
import { administrationIdValidator } from 'camino-common/src/static/administrations'
import { canReadAdministrations } from 'camino-common/src/permissions/administrations'
import { deleteAdministrationActiviteTypeEmail, getActiviteTypeEmailsByAdministrationId, getUtilisateursByAdministrationId, insertAdministrationActiviteTypeEmail } from './administrations.queries'
import { AdministrationActiviteTypeEmail } from 'camino-common/src/administrations'
import { CaminoApiError } from '../../types'
import { ZodUnparseable } from '../../tools/fp-tools'
import { DeepReadonly } from 'camino-common/src/typescript-tools'
import { DbQueryAccessError } from '../../pg-database'
import { Effect, Match } from 'effect'
import { RestNewPostCall } from '../../server/rest'

export const getAdministrationUtilisateurs = (pool: Pool) => async (req: JWTRequest<User>, res: CustomResponse<AdminUserNotNull[]>) => {
const user = req.auth
export const getAdministrationUtilisateurs =
(pool: Pool) =>
async (req: JWTRequest<User>, res: CustomResponse<AdminUserNotNull[]>): Promise<void> => {
const user = req.auth

const parsed = administrationIdValidator.safeParse(req.params.administrationId)
const parsed = administrationIdValidator.safeParse(req.params.administrationId)

if (!parsed.success) {
console.warn(`l'administrationId est obligatoire`)
res.sendStatus(HTTP_STATUS.FORBIDDEN)
} else if (!canReadAdministrations(user)) {
res.sendStatus(HTTP_STATUS.FORBIDDEN)
} else {
try {
res.json(await getUtilisateursByAdministrationId(pool, parsed.data))
} catch (e) {
console.error(e)
if (!parsed.success) {
console.warn(`l'administrationId est obligatoire`)
res.sendStatus(HTTP_STATUS.FORBIDDEN)
} else if (!canReadAdministrations(user)) {
res.sendStatus(HTTP_STATUS.FORBIDDEN)
} else {
try {
res.json(await getUtilisateursByAdministrationId(pool, parsed.data))
} catch (e) {
console.error(e)

res.sendStatus(HTTP_STATUS.INTERNAL_SERVER_ERROR)
res.sendStatus(HTTP_STATUS.INTERNAL_SERVER_ERROR)
}
}
}
}

export const getAdministrationActiviteTypeEmails = (pool: Pool) => async (req: JWTRequest<User>, res: CustomResponse<AdministrationActiviteTypeEmail[]>) => {
const user = req.auth
export const getAdministrationActiviteTypeEmails =
(pool: Pool) =>
async (req: JWTRequest<User>, res: CustomResponse<AdministrationActiviteTypeEmail[]>): Promise<void> => {
const user = req.auth

const parsed = administrationIdValidator.safeParse(req.params.administrationId)
const parsed = administrationIdValidator.safeParse(req.params.administrationId)

if (!parsed.success) {
console.warn(`l'administrationId est obligatoire`)
res.sendStatus(HTTP_STATUS.FORBIDDEN)
} else if (!canReadAdministrations(user)) {
res.sendStatus(HTTP_STATUS.FORBIDDEN)
} else {
try {
res.json(await getActiviteTypeEmailsByAdministrationId(pool, parsed.data))
} catch (e) {
console.error(e)
if (!parsed.success) {
console.warn(`l'administrationId est obligatoire`)
res.sendStatus(HTTP_STATUS.FORBIDDEN)
} else if (!canReadAdministrations(user)) {
res.sendStatus(HTTP_STATUS.FORBIDDEN)
} else {
try {
res.json(await getActiviteTypeEmailsByAdministrationId(pool, parsed.data))
} catch (e) {
console.error(e)

res.sendStatus(HTTP_STATUS.INTERNAL_SERVER_ERROR)
res.sendStatus(HTTP_STATUS.INTERNAL_SERVER_ERROR)
}
}
}
}

export const addAdministrationActiviteTypeEmails: RestNewPostCall<'/rest/administrations/:administrationId/activiteTypeEmails'> = (
pool: Pool,
user: DeepReadonly<UserNotNull>,
body: DeepReadonly<AdministrationActiviteTypeEmail>,
params: { administrationId: AdministrationId }
): Effect.Effect<boolean, CaminoApiError<'Accès interdit' | ZodUnparseable | DbQueryAccessError>> => {
export const addAdministrationActiviteTypeEmails: RestNewPostCall<'/rest/administrations/:administrationId/activiteTypeEmails'> = ({
pool,
user,
body,
params,
}): Effect.Effect<boolean, CaminoApiError<'Accès interdit' | ZodUnparseable | DbQueryAccessError>> => {
return Effect.Do.pipe(
Effect.filterOrFail(
() => canReadAdministrations(user),
Expand All @@ -80,12 +83,12 @@ export const addAdministrationActiviteTypeEmails: RestNewPostCall<'/rest/adminis
)
}

export const deleteAdministrationActiviteTypeEmails: RestNewPostCall<'/rest/administrations/:administrationId/activiteTypeEmails/delete'> = (
pool: Pool,
user: DeepReadonly<UserNotNull>,
body: DeepReadonly<AdministrationActiviteTypeEmail>,
params: { administrationId: AdministrationId }
): Effect.Effect<boolean, CaminoApiError<'Accès interdit' | ZodUnparseable | DbQueryAccessError>> => {
export const deleteAdministrationActiviteTypeEmails: RestNewPostCall<'/rest/administrations/:administrationId/activiteTypeEmails/delete'> = ({
pool,
user,
body,
params,
}): Effect.Effect<boolean, CaminoApiError<'Accès interdit' | ZodUnparseable | DbQueryAccessError>> => {
return Effect.Do.pipe(
Effect.filterOrFail(
() => canReadAdministrations(user),
Expand Down
2 changes: 1 addition & 1 deletion packages/api/src/api/rest/demarches.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ export const demarcheSupprimer =
}
}

export const demarcheCreer: RestNewPostCall<'/rest/demarches'> = (pool, user, demarche): Effect.Effect<DemarcheCreationOutput, CaminoApiError<string>> => {
export const demarcheCreer: RestNewPostCall<'/rest/demarches'> = ({ pool, user, body: demarche }): Effect.Effect<DemarcheCreationOutput, CaminoApiError<string>> => {
return pipe(
Effect.tryPromise({
try: async () => {
Expand Down
42 changes: 19 additions & 23 deletions packages/api/src/api/rest/perimetre.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { DemarcheId, demarcheIdOrSlugValidator } from 'camino-common/src/demarch
import { CaminoRequest, CustomResponse } from './express-type'
import { Pool } from 'pg'
import { pipe, Effect, Match } from 'effect'
import { GeoSystemeId, GeoSystemes } from 'camino-common/src/static/geoSystemes'
import { GeoSystemes } from 'camino-common/src/static/geoSystemes'
import { HTTP_STATUS } from 'camino-common/src/http'
import {
ConvertPointsErrors,
Expand All @@ -16,7 +16,7 @@ import {
} from './perimetre.queries'
import { TitreTypeId } from 'camino-common/src/static/titresTypes'
import { getMostRecentEtapeFondamentaleValide } from './titre-heritage'
import { isAdministrationAdmin, isAdministrationEditeur, isDefault, isSuper, User, UserNotNull } from 'camino-common/src/roles'
import { isAdministrationAdmin, isAdministrationEditeur, isDefault, isSuper, User } from 'camino-common/src/roles'
import { getDemarcheByIdOrSlug, getEtapesByDemarcheId } from './demarches.queries'
import { getAdministrationsLocalesByTitreId, getTitreByIdOrSlug, getTitulairesAmodiatairesByTitreId } from './titres.queries'
import { etapeIdOrSlugValidator } from 'camino-common/src/etape'
Expand All @@ -37,9 +37,6 @@ import {
featureCollectionForagesValidator,
FeatureCollectionForages,
featureForagePropertiesValidator,
GeojsonImportPointsBody,
GeojsonImportBody,
GeojsonImportForagesBody,
} from 'camino-common/src/perimetre'
import { join } from 'node:path'
import { readFileSync } from 'node:fs'
Expand Down Expand Up @@ -196,12 +193,12 @@ type GeojsonImportErrorMessages =
| GetGeojsonByGeoSystemeIdErrorMessages
| GetGeojsonInformationErrorMessages
| ConvertPointsErrors
export const geojsonImport: RestNewPostCall<'/rest/geojson/import/:geoSystemeId'> = (
pool: Pool,
user: DeepReadonly<UserNotNull>,
body: DeepReadonly<GeojsonImportBody>,
params: { geoSystemeId: GeoSystemeId }
): Effect.Effect<DeepReadonly<GeojsonInformations>, CaminoApiError<GeojsonImportErrorMessages>> => {
export const geojsonImport: RestNewPostCall<'/rest/geojson/import/:geoSystemeId'> = ({
pool,
user,
body,
params,
}): Effect.Effect<DeepReadonly<GeojsonInformations>, CaminoApiError<GeojsonImportErrorMessages>> => {
const pathFrom = join(process.cwd(), `/files/tmp/${body.tempDocumentName}`)

return pipe(
Expand Down Expand Up @@ -452,12 +449,12 @@ const fileNameToCsv = (pathFrom: string): Effect.Effect<unknown[], CaminoError<t
const accesInterditError = 'Accès interdit' as const
type GeosjsonImportPointsErrorMessages = ZodUnparseable | DbQueryAccessError | typeof accesInterditError | 'Fichier incorrect' | ConvertPointsErrors

export const geojsonImportPoints: RestNewPostCall<'/rest/geojson_points/import/:geoSystemeId'> = (
pool: Pool,
user: DeepReadonly<UserNotNull>,
geojsonImportInput: DeepReadonly<GeojsonImportPointsBody>,
params: { geoSystemeId: GeoSystemeId }
): Effect.Effect<GeojsonImportPointsResponse, CaminoApiError<GeosjsonImportPointsErrorMessages>> => {
export const geojsonImportPoints: RestNewPostCall<'/rest/geojson_points/import/:geoSystemeId'> = ({
pool,
user,
body: geojsonImportInput,
params,
}): Effect.Effect<GeojsonImportPointsResponse, CaminoApiError<GeosjsonImportPointsErrorMessages>> => {
return Effect.Do.pipe(
Effect.filterOrFail(
() => !isDefault(user),
Expand Down Expand Up @@ -500,12 +497,11 @@ export const geojsonImportPoints: RestNewPostCall<'/rest/geojson_points/import/:
}

type GeosjsonImportForagesErrorMessages = ZodUnparseable | DbQueryAccessError | typeof ouvertureCsvError | typeof ouvertureShapeError | typeof ouvertureGeoJSONError | ConvertPointsErrors
export const geojsonImportForages: RestNewPostCall<'/rest/geojson_forages/import/:geoSystemeId'> = (
pool: Pool,
_user: DeepReadonly<UserNotNull>,
body: DeepReadonly<GeojsonImportForagesBody>,
params: { geoSystemeId: GeoSystemeId }
): Effect.Effect<GeojsonImportForagesResponse, CaminoApiError<GeosjsonImportForagesErrorMessages>> => {
export const geojsonImportForages: RestNewPostCall<'/rest/geojson_forages/import/:geoSystemeId'> = ({
pool,
body,
params,
}): Effect.Effect<GeojsonImportForagesResponse, CaminoApiError<GeosjsonImportForagesErrorMessages>> => {
const filename = body.tempDocumentName

const pathFrom = join(process.cwd(), `/files/tmp/${filename}`)
Expand Down
14 changes: 4 additions & 10 deletions packages/api/src/api/rest/titre-demande.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
import { titresGet } from '../../database/queries/titres'

import { titreUpdateTask } from '../../business/titre-update'
import { UserNotNull, isEntrepriseOrBureauDEtude } from 'camino-common/src/roles'
import { isEntrepriseOrBureauDEtude } from 'camino-common/src/roles'
import { linkTitres } from '../../database/queries/titres-titres.queries'
import { canCreateTitre } from 'camino-common/src/permissions/titres'
import { checkTitreLinks } from '../../business/validations/titre-links-validate'
import { DeepReadonly, isNotNullNorUndefinedNorEmpty, isNullOrUndefined, isNullOrUndefinedOrEmpty } from 'camino-common/src/typescript-tools'
import { createAutomaticallyEtapeWhenCreatingTitre, TitreDemande, TitreDemandeOutput } from 'camino-common/src/titres'
import { isNotNullNorUndefinedNorEmpty, isNullOrUndefined, isNullOrUndefinedOrEmpty } from 'camino-common/src/typescript-tools'
import { createAutomaticallyEtapeWhenCreatingTitre, TitreDemandeOutput } from 'camino-common/src/titres'
import { HTTP_STATUS } from 'camino-common/src/http'
import { Pool } from 'pg'
import { ETAPE_IS_BROUILLON } from 'camino-common/src/etape'
import { Effect, pipe, Match } from 'effect'
import { capitalize } from 'effect/String'
Expand Down Expand Up @@ -36,12 +35,7 @@ type TitreDemandeCreerErrors =
| "Problème lors de l'abonnement de l'utilisateur au titre"
| "L'entreprise est obligatoire"
| "L'entreprise ne doit pas être présente"
export const titreDemandeCreer: RestNewPostCall<'/rest/titres'> = (
pool: Pool,
user: DeepReadonly<UserNotNull>,
titreDemande: DeepReadonly<TitreDemande>,
_params: {}
): Effect.Effect<TitreDemandeOutput, CaminoApiError<TitreDemandeCreerErrors>> => {
export const titreDemandeCreer: RestNewPostCall<'/rest/titres'> = ({ pool, user, body: titreDemande }): Effect.Effect<TitreDemandeOutput, CaminoApiError<TitreDemandeCreerErrors>> => {
return Effect.Do.pipe(
Effect.filterOrFail(
() => canCreateTitre(user, titreDemande.titreTypeId),
Expand Down
13 changes: 5 additions & 8 deletions packages/api/src/api/rest/utilisateurs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -223,12 +223,9 @@ export const generateQgisToken =
}
}

export const registerToNewsletter: RestNewGetCall<'/rest/utilisateurs/registerToNewsletter'> = (
_pool: Pool,
_user: DeepReadonly<User>,
_params: Record<string, never>,
searchParams: { email: string }
): Effect.Effect<boolean, CaminoApiError<"Impossible de s'enregistrer à la newsletter">> => {
export const registerToNewsletter: RestNewGetCall<'/rest/utilisateurs/registerToNewsletter'> = ({
searchParams,
}): Effect.Effect<boolean, CaminoApiError<"Impossible de s'enregistrer à la newsletter">> => {
return pipe(
Effect.tryPromise({
try: async () => {
Expand Down Expand Up @@ -286,7 +283,7 @@ export const utilisateurs =

type GetUtilisateursError = DbQueryAccessError | ZodUnparseable | "Impossible d'accéder à la liste des utilisateurs" | 'droits insuffisants'

export const getUtilisateurs: RestNewGetCall<'/rest/utilisateurs'> = (pool, user, _params, searchParams): Effect.Effect<DeepReadonly<UtilisateursTable>, CaminoApiError<GetUtilisateursError>> => {
export const getUtilisateurs: RestNewGetCall<'/rest/utilisateurs'> = ({ pool, user, searchParams }): Effect.Effect<DeepReadonly<UtilisateursTable>, CaminoApiError<GetUtilisateursError>> => {
return Effect.Do.pipe(
Effect.flatMap(() => getUtilisateursFilteredAndSorted(pool, user, searchParams)),
Effect.map(utilisateurs => {
Expand All @@ -307,7 +304,7 @@ export const getUtilisateurs: RestNewGetCall<'/rest/utilisateurs'> = (pool, user
}

type GetUtilisateurError = DbQueryAccessError | ZodUnparseable | 'droits insuffisants'
export const getUtilisateur: RestNewGetCall<'/rest/utilisateurs/:id'> = (pool, user, params, _searchParams): Effect.Effect<DeepReadonly<UserNotNull>, CaminoApiError<GetUtilisateurError>> => {
export const getUtilisateur: RestNewGetCall<'/rest/utilisateurs/:id'> = ({ pool, user, params }): Effect.Effect<DeepReadonly<UserNotNull>, CaminoApiError<GetUtilisateurError>> => {
return Effect.Do.pipe(
Effect.flatMap(() => newGetUtilisateurById(pool, params.id, user)),
Effect.mapError(caminoError =>
Expand Down
24 changes: 12 additions & 12 deletions packages/api/src/server/rest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,23 +89,23 @@ type IRestResolver = (

type CaminoRestRoutesType = typeof CaminoRestRoutes
type RestGetCall<Route extends GetRestRoutes> = (pool: Pool) => (req: CaminoRequest, res: CustomResponse<DeepReadonly<z.infer<CaminoRestRoutesType[Route]['get']['output']>>>) => Promise<void>
export type RestNewPostCall<Route extends NewPostRestRoutes> = (
pool: Pool,
user: DeepReadonly<UserNotNull>,
body: DeepReadonly<z.infer<CaminoRestRoutesType[Route]['newPost']['input']>>,
export type RestNewPostCall<Route extends NewPostRestRoutes> = (data: {
pool: Pool
user: DeepReadonly<UserNotNull>
body: DeepReadonly<z.infer<CaminoRestRoutesType[Route]['newPost']['input']>>
params: DeepReadonly<z.infer<CaminoRestRoutesType[Route]['params']>>
) => Effect.Effect<DeepReadonly<z.infer<CaminoRestRoutesType[Route]['newPost']['output']>>, CaminoApiError<string>>
}) => Effect.Effect<DeepReadonly<z.infer<CaminoRestRoutesType[Route]['newPost']['output']>>, CaminoApiError<string>>

type SearchParams<Route extends NewGetRestRoutes> = CaminoRestRoutesType[Route]['newGet'] extends { searchParams: ZodType }
? z.infer<CaminoRestRoutesType[Route]['newGet']['searchParams']>
: Record<never, string>

export type RestNewGetCall<Route extends NewGetRestRoutes> = (
pool: Pool,
user: DeepReadonly<User>,
params: DeepReadonly<z.infer<CaminoRestRoutesType[Route]['params']>>,
export type RestNewGetCall<Route extends NewGetRestRoutes> = (data: {
pool: Pool
user: DeepReadonly<User>
params: DeepReadonly<z.infer<CaminoRestRoutesType[Route]['params']>>
searchParams: SearchParams<Route>
) => Effect.Effect<DeepReadonly<z.infer<CaminoRestRoutesType[Route]['newGet']['output']>>, CaminoApiError<string>>
}) => Effect.Effect<DeepReadonly<z.infer<CaminoRestRoutesType[Route]['newGet']['output']>>, CaminoApiError<string>>

type RestPostCall<Route extends PostRestRoutes> = (pool: Pool) => (req: CaminoRequest, res: CustomResponse<z.infer<CaminoRestRoutesType[Route]['post']['output']>>) => Promise<void>
type RestPutCall<Route extends PutRestRoutes> = (pool: Pool) => (req: CaminoRequest, res: CustomResponse<z.infer<CaminoRestRoutesType[Route]['put']['output']>>) => Promise<void>
Expand Down Expand Up @@ -235,7 +235,7 @@ export const restWithPool = (dbPool: Pool): Router => {
Effect.bind<'result', { searchParams: any; params: any }, DeepReadonly<z.infer<(typeof maRoute)['newGet']['output']>>, CaminoApiError<string>, never>(
'result',
({ searchParams, params }) => {
return maRoute.newGetCall(dbPool, req.auth, params, searchParams)
return maRoute.newGetCall({ pool: dbPool, user: req.auth, params, searchParams })
}
),
Effect.bind('parsedResult', ({ result }) =>
Expand Down Expand Up @@ -298,7 +298,7 @@ export const restWithPool = (dbPool: Pool): Router => {
// TODO 2024-06-26 ici, si on ne met pas le body et params à any, on se retrouve avec une typescript union hell qui fait tout planter
Effect.bind<'result', { body: any; user: UserNotNull; params: any }, DeepReadonly<z.infer<(typeof maRoute)['newPost']['output']>>, CaminoApiError<string>, never>(
'result',
({ user, body, params }) => maRoute.newPostCall(dbPool, user, body, params)
({ user, body, params }) => maRoute.newPostCall({ pool: dbPool, user, body, params })
),
Effect.bind('parsedResult', ({ result }) =>
pipe(
Expand Down

0 comments on commit 74b7b8b

Please sign in to comment.