Files
Laufzettel/src/lib/platzhalter.ts
T
florian.wilde eec5de404a
Prüfung / pruefung (push) Canceled after 0s
Prüfung / e2e (push) Canceled after 0s
feat(platzhalter): Aufloesung, Namensregeln und Shell-Maskierung (#26)
2026-09-25 13:30:31 +00:00

147 lines
6.0 KiB
TypeScript

/**
* Die eingebauten Platzhalter (#010, aufgeloest in #012).
*
* Ein Platzhalter `{{schluessel}}` in Titel, Hilfetext oder Befehl wird beim
* Abarbeiten eines Laufs ersetzt. Woraus, steht hier: entweder aus einer
* Variablen der Vorlage oder aus einer der eingebauten Quellen. Ein Schluessel,
* der zu keiner von beiden passt, bleibt im Lauf als `{{schluessel}}` stehen —
* sichtbar, aber nutzlos. Deshalb warnt der Editor davor.
*
* Die Liste ist bewusst Daten und kein Code: der Editor zeigt sie als Hilfe an,
* die Warnung vergleicht dagegen, und #012 baut die Aufloesung darauf auf. Eine
* zweite Aufzaehlung an anderer Stelle wuerde unweigerlich auseinanderlaufen.
*/
export type PlatzhalterQuelle = "GERAET_LAUF" | "MITARBEITER" | "ABTEILUNG" | "BESONDERS";
export type EingebauterPlatzhalter = {
readonly schluessel: string;
readonly quelle: PlatzhalterQuelle;
readonly erklaerung: string;
/** Nur im Modus DIENSTLEISTUNG gefuellt; im Modus INTERN bleibt der Wert leer. */
readonly nurDienstleistung?: true;
};
export const EINGEBAUTE_PLATZHALTER: readonly EingebauterPlatzhalter[] = [
// Aus Geraet und Lauf
{ schluessel: "hostname", quelle: "GERAET_LAUF", erklaerung: "Hostname des Geräts im Lauf" },
{ schluessel: "seriennummer", quelle: "GERAET_LAUF", erklaerung: "Seriennummer des Geräts" },
{ schluessel: "inventarnummer", quelle: "GERAET_LAUF", erklaerung: "Inventarnummer des Geräts" },
{ schluessel: "ticket", quelle: "GERAET_LAUF", erklaerung: "Ticketnummer am Lauf" },
{ schluessel: "datum", quelle: "GERAET_LAUF", erklaerung: "heutiges Datum" },
{ schluessel: "techniker", quelle: "GERAET_LAUF", erklaerung: "Name des Bearbeiters" },
{
schluessel: "kunde",
quelle: "GERAET_LAUF",
erklaerung: "Name des Kunden",
nurDienstleistung: true,
},
{
schluessel: "kundenkuerzel",
quelle: "GERAET_LAUF",
erklaerung: "Kürzel des Kunden",
nurDienstleistung: true,
},
// Aus dem Mitarbeiter
{ schluessel: "mitarbeiter", quelle: "MITARBEITER", erklaerung: "vollständiger Name" },
{ schluessel: "benutzer", quelle: "MITARBEITER", erklaerung: "gleichbedeutend mit mitarbeiter" },
{ schluessel: "vorname", quelle: "MITARBEITER", erklaerung: "Vorname" },
{ schluessel: "nachname", quelle: "MITARBEITER", erklaerung: "Nachname" },
{ schluessel: "upn", quelle: "MITARBEITER", erklaerung: "User Principal Name" },
{ schluessel: "sam", quelle: "MITARBEITER", erklaerung: "sAMAccountName, höchstens 20 Zeichen" },
{ schluessel: "mail", quelle: "MITARBEITER", erklaerung: "Mail-Adresse" },
{ schluessel: "abteilung", quelle: "MITARBEITER", erklaerung: "Name der Abteilung" },
{ schluessel: "titel", quelle: "MITARBEITER", erklaerung: "Funktion oder Titel" },
{ schluessel: "vorgesetzter", quelle: "MITARBEITER", erklaerung: "Name des Vorgesetzten" },
{ schluessel: "vorgesetzter_sam", quelle: "MITARBEITER", erklaerung: "sAMAccountName des Vorgesetzten" },
{ schluessel: "dn", quelle: "MITARBEITER", erklaerung: "Distinguished Name im Verzeichnis" },
// Aus der Abteilung
{
schluessel: "ou",
quelle: "ABTEILUNG",
erklaerung: "Ziel-Container: Eltern-Container des dn, sonst die Ziel-OU der Abteilung",
},
{ schluessel: "gruppen", quelle: "ABTEILUNG", erklaerung: "Standardgruppen, kommagetrennt" },
// Besonders
{
schluessel: "generiertes_passwort",
quelle: "BESONDERS",
erklaerung:
"20 Zeichen, bei jeder Anzeige neu erzeugt und nirgends gespeichert — auch nicht im Protokoll",
},
];
export const QUELLEN_TEXT: Record<PlatzhalterQuelle, string> = {
GERAET_LAUF: "Gerät und Lauf",
MITARBEITER: "Mitarbeiter",
ABTEILUNG: "Abteilung",
BESONDERS: "Besonders",
};
const EINGEBAUTE_SCHLUESSEL: ReadonlySet<string> = new Set(
EINGEBAUTE_PLATZHALTER.map((platzhalter) => platzhalter.schluessel),
);
export function istEingebaut(schluessel: string): boolean {
return EINGEBAUTE_SCHLUESSEL.has(schluessel);
}
/**
* Eingebaute Platzhalter, die eine Vorlagenvariable **nicht** belegen darf.
*
* Es sind die fuenf, die eine Aussage ueber **wer, wann und fuer wen** machen:
*
* - `techniker` und `datum` unterschreiben einen Befehl. Eine Variable dafuer
* hiesse, dass in einem fertigen Befehl ein fremder Name und ein falsches
* Datum stehen koennen, ohne dass es auffaellt.
* - `kunde` und `kundenkuerzel` sind im Modus INTERN ausdruecklich leer (#008).
* Eine Variable haette die Kundenebene in den Befehl zurueckgeholt, die es
* dort nicht gibt.
* - `generiertes_passwort` wird ohnehin vor jeder Zuordnung abgefangen; hier
* steht es, damit niemand eine Variable anlegt, die nie wirkt.
*
* Alles andere bleibt erlaubt, und das ist wichtig: beim Onboarding gibt es die
* Person im Verzeichnis noch nicht, die Vorlage fragt `upn`, `sam` und `mail`
* also selbst ab. Genau dafuer sind Variablen da.
*/
export const GESPERRTE_VARIABLENNAMEN: ReadonlySet<string> = new Set([
"techniker",
"datum",
"kunde",
"kundenkuerzel",
"generiertes_passwort",
]);
/** Darf eine Vorlagenvariable so heissen? */
export function istAlsVariableErlaubt(schluessel: string): boolean {
return !GESPERRTE_VARIABLENNAMEN.has(schluessel);
}
/**
* Die Platzhalter, die weder eingebaut noch als Variable definiert sind.
*
* Das ist eine **Warnung**, keine Ablehnung: ein Tippfehler faellt so im Editor
* auf, statt erst dem Techniker im Lauf. Speichern und Veroeffentlichen bleiben
* erlaubt — es kann gute Gruende geben, einen Platzhalter stehen zu lassen, etwa
* wenn die Variable noch kommt.
*/
export function unbekanntePlatzhalter(
verwendete: readonly string[],
variablenSchluessel: readonly string[],
): readonly string[] {
const bekannt = new Set(variablenSchluessel);
return verwendete.filter((schluessel) => !bekannt.has(schluessel) && !istEingebaut(schluessel));
}
/** Variablen, die definiert sind, aber nirgends verwendet werden. */
export function unbenutzteVariablen(
verwendete: readonly string[],
variablenSchluessel: readonly string[],
): readonly string[] {
const benutzt = new Set(verwendete);
return variablenSchluessel.filter((schluessel) => !benutzt.has(schluessel));
}