Checkpoint: Les connecteurs web acceptent désormais les noms de domaine saisis sans protocole : HTTPS est ajouté automatiquement dans l’interface et à l’API. Les URL HTTP(S) valides restent acceptées et les adresses incorrectes affichent un message métier clair. Sept tests dédiés, la suite complète (71 tests), TypeScript et build ont été validés.
This commit is contained in:
@@ -2,6 +2,7 @@ import { z } from "zod";
|
||||
import { COOKIE_NAME } from "@shared/const";
|
||||
import { BAP_MIN_QUALITY_SCORE, meetsBapQualityThreshold } from "@shared/bapEligibility";
|
||||
import { calculateInvoiceQualityScore } from "@shared/invoiceQuality";
|
||||
import { webPortalUrlSchema } from "./webPortalUrl";
|
||||
|
||||
interface Condition {
|
||||
field: string;
|
||||
@@ -2743,7 +2744,7 @@ export const appRouter = router({
|
||||
.input(z.object({
|
||||
name: z.string().min(1).max(100),
|
||||
connectorType: z.string().min(1).max(50),
|
||||
portalUrl: z.string().url(),
|
||||
portalUrl: webPortalUrlSchema,
|
||||
loginEmail: z.string().min(1),
|
||||
loginPassword: z.string().min(1),
|
||||
frequency: z.enum(['manual', 'daily', 'weekly', 'monthly']).default('monthly'),
|
||||
@@ -2758,7 +2759,7 @@ export const appRouter = router({
|
||||
id: z.number(),
|
||||
name: z.string().min(1).max(100).optional(),
|
||||
connectorType: z.string().min(1).max(50).optional(),
|
||||
portalUrl: z.string().url().optional(),
|
||||
portalUrl: webPortalUrlSchema.optional(),
|
||||
loginEmail: z.string().min(1).optional(),
|
||||
loginPassword: z.string().optional(),
|
||||
frequency: z.enum(['manual', 'daily', 'weekly', 'monthly']).optional(),
|
||||
|
||||
49
server/webPortalUrl.test.ts
Normal file
49
server/webPortalUrl.test.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { isValidWebPortalUrl, normalizeWebPortalUrl } from "@shared/webPortalUrl";
|
||||
import { webPortalUrlSchema } from "./webPortalUrl";
|
||||
|
||||
describe("normalizeWebPortalUrl", () => {
|
||||
it("ajoute HTTPS aux adresses de portail saisies sans protocole", () => {
|
||||
expect(normalizeWebPortalUrl("www.sfr.fr/mon-espace-client")).toBe(
|
||||
"https://www.sfr.fr/mon-espace-client",
|
||||
);
|
||||
});
|
||||
|
||||
it("retire les espaces sans modifier une URL HTTPS déjà valide", () => {
|
||||
expect(normalizeWebPortalUrl(" https://pro.free.fr/ ")).toBe("https://pro.free.fr/");
|
||||
});
|
||||
|
||||
it("ne transforme pas un protocole non web en URL HTTPS", () => {
|
||||
expect(normalizeWebPortalUrl("ftp://example.org/factures")).toBe("ftp://example.org/factures");
|
||||
});
|
||||
});
|
||||
|
||||
describe("isValidWebPortalUrl", () => {
|
||||
it("accepte exclusivement les URLs HTTP ou HTTPS avec un hôte", () => {
|
||||
expect(isValidWebPortalUrl("https://espaceclient.orange.fr/")).toBe(true);
|
||||
expect(isValidWebPortalUrl("http://intranet.local/factures")).toBe(true);
|
||||
});
|
||||
|
||||
it("refuse les valeurs vides, incomplètes ou avec un protocole non web", () => {
|
||||
expect(isValidWebPortalUrl("")).toBe(false);
|
||||
expect(isValidWebPortalUrl("https://")).toBe(false);
|
||||
expect(isValidWebPortalUrl("ftp://example.org/factures")).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("webPortalUrlSchema", () => {
|
||||
it("normalise l’URL reçue par l’API avant de la persister", () => {
|
||||
expect(webPortalUrlSchema.parse("espaceclient.orange.fr")).toBe(
|
||||
"https://espaceclient.orange.fr",
|
||||
);
|
||||
});
|
||||
|
||||
it("retourne un message métier pour une URL de portail non HTTP(S)", () => {
|
||||
const result = webPortalUrlSchema.safeParse("ftp://example.org/factures");
|
||||
|
||||
expect(result.success).toBe(false);
|
||||
if (!result.success) {
|
||||
expect(result.error.issues[0]?.message).toContain("URL HTTP(S) valide");
|
||||
}
|
||||
});
|
||||
});
|
||||
13
server/webPortalUrl.ts
Normal file
13
server/webPortalUrl.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import { z } from "zod";
|
||||
import { isValidWebPortalUrl, normalizeWebPortalUrl } from "@shared/webPortalUrl";
|
||||
|
||||
/**
|
||||
* Normalise les domaines saisis sans protocole puis impose une URL HTTP(S).
|
||||
* Le même schéma est utilisé à la création et à la mise à jour d’un connecteur.
|
||||
*/
|
||||
export const webPortalUrlSchema = z
|
||||
.string()
|
||||
.transform(normalizeWebPortalUrl)
|
||||
.refine(isValidWebPortalUrl, {
|
||||
message: "Saisissez une URL HTTP(S) valide, par exemple https://espaceclient.exemple.fr.",
|
||||
});
|
||||
Reference in New Issue
Block a user