Skip to content

Commit

Permalink
Permissions annotations privées (#82)
Browse files Browse the repository at this point in the history
  • Loading branch information
DavidBruant committed Sep 16, 2024
1 parent da5bb33 commit 6762bc1
Show file tree
Hide file tree
Showing 17 changed files with 480 additions and 86 deletions.
29 changes: 29 additions & 0 deletions migrations/20240912110240_ajouter-capability-instructeur-id.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/**
* @param { import("knex").Knex } knex
* @returns { Promise<void> }
*/
export async function up(knex) {
await knex.schema.createTable('cap_écriture_annotation', function (table) {
table.uuid('cap').primary().defaultTo(knex.fn.uuid())
table.string('instructeur_id').notNullable().unique()
});

await knex.schema.createTable('arête_personne__cap_écriture_annotation', function (table) {
table.string('personne_cap').unique()
table.foreign('personne_cap')
.references('code_accès').inTable('personne').onDelete('CASCADE')

table.uuid('écriture_annotation_cap')
table.foreign('écriture_annotation_cap')
.references('cap').inTable('cap_écriture_annotation').onDelete('CASCADE')
});
};

/**
* @param { import("knex").Knex } knex
* @returns { Promise<void> }
*/
export async function down(knex) {
await knex.schema.dropTable('arête_personne__cap_écriture_annotation');
await knex.schema.dropTable('cap_écriture_annotation');
};
91 changes: 91 additions & 0 deletions scripts/front-end/actions/créerObjetCapDepuisURLs.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
//@ts-check

import {json} from 'd3-fetch'

//@ts-expect-error TS ne comprend pas que c'est utilisé
/** @import {StringValues} from '../../types.js' */
//@ts-expect-error TS ne comprend pas que c'est utilisé
/** @import {PitchouInstructeurCapabilities} from '../../types/capabilities.ts' */
//@ts-expect-error TS ne comprend pas que c'est utilisé
/** @import {default as Dossier} from '../../types/database/public/Dossier.ts' */

/**
*
* @param {string | undefined} url
* @returns {(() => Promise<any>) | undefined}
*/
function wrapGETUrl(url){
if(!url)
return undefined

return () => json(url)
}

/**
*
* @param {string | undefined} url
* @returns {((body: any) => Promise<any>) | undefined}
*/
function wrapPOSTUrl(url){
if(!url)
return undefined

return (/** @type {any} */ args) => json(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(args)
})
}

const dossierIdURLParam = ':dossierId'

/**
*
* @param {string | undefined} url
* @returns {((dossierId: Dossier['id'], body: any) => Promise<any>) | undefined}
*/
function wrapModifierDossier(url){
if(!url)
return undefined

if(!url.includes(dossierIdURLParam)){
throw new Error(`La capability modifierDossier ne contient pas '${dossierIdURLParam}'`)
}

/**
*
* @param {Dossier['id']} dossierId
* @param {any} args
* @returns
*/
function modifierDossier(dossierId, args){
console.log('modifierDossier cap', args)

return json(
// @ts-ignore
url.replace(dossierIdURLParam, dossierId),
{
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(args)
}
)
}

return modifierDossier
}

/**
*
* @param {StringValues<PitchouInstructeurCapabilities>} capURLs
* @returns {PitchouInstructeurCapabilities}
*/
export default function(capURLs){

return {
listerDossiers: wrapGETUrl(capURLs.listerDossiers),
modifierDossier: wrapModifierDossier(capURLs.modifierDossier),
remplirAnnotations: wrapPOSTUrl(capURLs.remplirAnnotations),
}

}
18 changes: 4 additions & 14 deletions scripts/front-end/actions/dossier.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
//@ts-check

import {json} from 'd3-fetch'

import store from "../store"

//@ts-expect-error TS ne comprends pas que le type est utilisé dans le jsdoc
Expand All @@ -15,22 +13,14 @@ import store from "../store"
* @returns {Promise<Dossier>}
*/
export function modifierDossier(id, dossierParams) {
if(store.state.secret){
return json(
`/dossier/${id}?cap=${store.state.secret}`,
{
method: "PUT",
body: JSON.stringify({ dossierParams }),
}
)
if(!store.state.capabilities?.modifierDossier)
throw new TypeError(`Capability modifierDossier manquante`)

return store.state.capabilities?.modifierDossier(id, dossierParams)
.then(databaseResponse => {
//@ts-ignore
const dossierAJour = databaseResponse[0]
store.mutations.setDossier(dossierAJour)
return dossierAJour
})
}
else{
return Promise.reject(new TypeError('Impossible de modifier le dossier, secret manquant'))
}
}
67 changes: 45 additions & 22 deletions scripts/front-end/actions/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,18 @@ import store from '../store.js';
import { getURL } from '../getLinkURL.js';

import { isDossierArray } from '../../types/typeguards.js';
import créerObjetCapDepuisURLs from './créerObjetCapDepuisURLs.js';

/** @typedef {import('../../types/database/public/Dossier.js').default} Dossier */

const PITCHOU_SECRET_STORAGE_KEY = 'secret-pitchou'

export function chargerDossiers(){
console.log('store.state.secret', store.state.secret)
if(store.state.secret){
return json(`/dossiers?secret=${store.state.secret}`)

if(store.state.capabilities?.listerDossiers){
return store.state.capabilities?.listerDossiers()
.then(dossiers => {
if (!isDossierArray(dossiers)) {
throw new Error("On attendait un tableau de dossiers ici !")
throw new TypeError("On attendait un tableau de dossiers ici !")
}

const dossiersById = dossiers.reduce((objetFinal, dossier) => {
Expand All @@ -28,11 +28,11 @@ export function chargerDossiers(){
console.log('dossiersById', dossiersById)
store.mutations.setDossiers(dossiersById)

return dossiers
return dossiersById
})
}
else{
return Promise.reject(new TypeError('Impossible de charger les dossiers, secret manquant'))
return Promise.reject(new TypeError('Impossible de charger les dossiers, capability manquante'))
}
}

Expand All @@ -44,18 +44,6 @@ export function chargerSchemaDS88444() {
})
}

export function init(){
return remember(PITCHOU_SECRET_STORAGE_KEY)
.then(secret => {
if(secret){
// @ts-ignore
store.mutations.setSecret(secret)
return chargerDossiers()
}
})
.then(chargerSchemaDS88444)
.catch(() => logout())
}

export async function secretFromURL(){
const secret = new URLSearchParams(location.search).get("secret")
Expand All @@ -64,15 +52,50 @@ export async function secretFromURL(){
const newURL = new URL(location.href)
newURL.searchParams.delete("secret")

// nettoyer l'url pour que le secret n'y apparaisse plus
history.replaceState(null, "", newURL)
store.mutations.setSecret(secret)

return remember(PITCHOU_SECRET_STORAGE_KEY, secret)
return Promise.all([
remember(PITCHOU_SECRET_STORAGE_KEY, secret),
initCapabilities(secret)
])
}
}

export async function logout(){
store.mutations.setSecret(undefined)
store.mutations.setCapabilities(undefined)
store.mutations.setDossiers(undefined)
return forget(PITCHOU_SECRET_STORAGE_KEY)
}

/**
*
* @param {string} secret
* @returns
*/
function initCapabilities(secret){
return json(`/caps?secret=${secret}`)
.then(capsURLs => {
if(capsURLs && typeof capsURLs === 'object'){
store.mutations.setCapabilities(
créerObjetCapDepuisURLs(capsURLs)
)
}
else{
throw new TypeError(`capsURLs non-reconnu (${typeof capsURLs} - ${capsURLs})`)
}
})
}


export function init(){

return Promise.all([
remember(PITCHOU_SECRET_STORAGE_KEY)
//@ts-ignore
.then(secret => secret ? initCapabilities(secret) : undefined)
.catch(logout),
chargerSchemaDS88444()
])

}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
/** @import {GeoAPICommune, GeoAPIDépartement} from "../../../types/GeoAPI.ts" */
/** @import { DossierTableauSuiviNouvelleAquitaine2023 } from '../../../import-dossiers-historiques/nouvelle-aquitaine/types.js' */
/** @import {DossierComplet} from '../../../types.js' */
//@ts-ignore
/** @import {default as Dossier} from '../../../types/database/public/Dossier.ts' */
export let email
Expand All @@ -30,8 +31,8 @@
/** @type { Map<DossierTableauSuiviNouvelleAquitaine2023['Type de projet'], string> } */
export let typeVersObjet
/** @type {string} */
export let remplirAnnotationsURL
/** @type { (_: {dossierId: string, annotations: any}) => Promise<void> } */
export let remplirAnnotations
/** @type {FileList | undefined} */
let fichiersImportRaw;
Expand Down Expand Up @@ -260,19 +261,10 @@
function ajouterAnnotations(dossierPitchou, annotations) {
dossierPitchouToRemplissageAnnotation.set(
dossierPitchou,
text(
remplirAnnotationsURL,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
dossierId: dossierPitchou.id_demarches_simplifiées,
annotations
})
}
)
remplirAnnotations({
dossierId: dossierPitchou.id_demarches_simplifiées,
annotations
})
)
dossierPitchouToRemplissageAnnotation = dossierPitchouToRemplissageAnnotation // re-render
Expand Down
2 changes: 1 addition & 1 deletion scripts/front-end/mapStateToComponentProps.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,6 @@
*/
export const mapStateToSqueletteProps = (state) => {
return {
email: state.secret ? '@' : undefined
email: state.capabilities ? '@' : undefined
}
}
16 changes: 10 additions & 6 deletions scripts/front-end/routes/Dossier.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
//@ts-check

import page from 'page'

import { replaceComponent } from '../routeComponentLifeCycle.js'
import store from '../store.js'
import { svelteTarget } from '../config.js'
import { mapStateToSqueletteProps } from '../mapStateToComponentProps.js';

import Dossier from '../components/screens/Dossier.svelte';
import { chargerDossiers } from '../actions/main.js';

/** @import {PitchouState} from '../store.js' */
/** @import {DossierId} from '../../types/database/public/Dossier.ts' */
Expand All @@ -17,15 +18,18 @@ import Dossier from '../components/screens/Dossier.svelte';
* @param {Object} ctx.params
* @param {string} ctx.params.dossierId
*/
export default ({params: {dossierId}}) => {
export default async ({params: {dossierId}}) => {
/** @type {DossierId} */
// @ts-ignore
const id = Number(dossierId)
const { state } = store
const { dossiers } = state
let { dossiers } = state

// TODO: expliquer que le dossier n'existe pas ?
if (!dossiers) return page("/")
if (!dossiers){
dossiers = await chargerDossiers()
}

const dossier = dossiers.get(/** @type {DossierId} */(id))
const dossier = dossiers.get(id)

// TODO: expliquer que le dossier n'existe pas ?
if (!dossier) return page('/')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,19 +81,17 @@ export default async () => {
}
}

const remplirAnnotationsURL = store.state.secret ? `/remplir-annotations?cap=${store.state.secret}` : undefined


/**
*
* @param {import('../../store.js').PitchouState} _
* @returns
*/
function mapStateToProps({dossiers}){
function mapStateToProps({dossiers, capabilities}){
return {
...mapStateToSqueletteProps(store.state),
dossiers, nomToCommune, stringToDépartement, typeVersObjet,
remplirAnnotationsURL
remplirAnnotationsURL: capabilities?.remplirAnnotations
}
}

Expand Down
Loading

0 comments on commit 6762bc1

Please sign in to comment.