Repository navigation
fix: persist keyword changes reliably in backend #72
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -2,18 +2,57 @@ import cors from "cors"; | |||||
| import express from "express"; | ||||||
| import { existsSync, mkdirSync, readdirSync, readFileSync, statSync, writeFileSync } from "fs"; | ||||||
| import path from "path"; | ||||||
| import { fileURLToPath } from "url"; | ||||||
| import XLSX from "xlsx"; | ||||||
| import { run as runScraper } from "./app.js"; | ||||||
| import { getConfig } from "./config.js"; | ||||||
| import { searchJobsWithCache } from "./pipeline/searchJobsWithCache.js"; | ||||||
| import { sources } from "./sources/index.js"; | ||||||
|
|
||||||
| const MODULE_DIR = path.dirname(fileURLToPath(import.meta.url)); | ||||||
|
|
||||||
| const DEFAULT_ALLOWED_ORIGINS = [ | ||||||
| "https://painel-vagas-lake.vercel.app", | ||||||
| "http://localhost:5173", | ||||||
| "http://localhost:5174", | ||||||
| ]; | ||||||
|
|
||||||
| function getKeywordsFilePath() { | ||||||
| const configuredPath = process.env.KEYWORDS_FILE_PATH?.trim(); | ||||||
| return configuredPath | ||||||
| ? path.resolve(configuredPath) | ||||||
| : path.resolve(MODULE_DIR, "db", "environment.json"); | ||||||
| } | ||||||
|
|
||||||
| function normalizeKeywords(keywords) { | ||||||
| if (!Array.isArray(keywords)) { | ||||||
| return null; | ||||||
| } | ||||||
|
|
||||||
| return [...new Set(keywords.map((item) => String(item ?? "").trim()).filter(Boolean))]; | ||||||
| } | ||||||
|
Comment on lines
+20
to
+33
|
||||||
|
|
||||||
| function readEnvironmentData() { | ||||||
| const envPath = getKeywordsFilePath(); | ||||||
|
|
||||||
| if (!existsSync(envPath)) { | ||||||
| return { KEYWORDS: [] }; | ||||||
| } | ||||||
|
|
||||||
| try { | ||||||
| const data = JSON.parse(readFileSync(envPath, "utf-8")); | ||||||
| return data && typeof data === "object" ? data : { KEYWORDS: [] }; | ||||||
|
||||||
| return data && typeof data === "object" ? data : { KEYWORDS: [] }; | |
| return data && typeof data === "object" && !Array.isArray(data) ? data : { KEYWORDS: [] }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -21,9 +21,11 @@ describe("jobs API", () => { | |
| beforeEach(() => { | ||
| vi.clearAllMocks(); | ||
| mocks.run.mockResolvedValue(undefined); | ||
| delete process.env.KEYWORDS_FILE_PATH; | ||
| }); | ||
|
|
||
| afterEach(() => { | ||
| delete process.env.KEYWORDS_FILE_PATH; | ||
| tmpDir = undefined; | ||
| }); | ||
|
|
||
|
|
@@ -179,6 +181,23 @@ describe("jobs API", () => { | |
| }); | ||
| }); | ||
|
|
||
| it("POST /api/keywords cria o arquivo configurado e aceita lista vazia", async () => { | ||
| tmpDir = mkdtempSync(join(tmpdir(), "jobs-api-")); | ||
| process.env.KEYWORDS_FILE_PATH = join(tmpDir, "nested", "environment.json"); | ||
|
|
||
| const app = createJobsApiApp({ outputDir: tmpDir }); | ||
| const res = await request(app) | ||
| .post("/api/keywords") | ||
| .send({ keywords: [" ", ""] }) | ||
| .expect(200); | ||
|
|
||
| expect(res.body).toEqual({ | ||
| ok: true, | ||
| message: "Keywords atualizadas com sucesso.", | ||
| keywords: [], | ||
| }); | ||
| }); | ||
|
Comment on lines
+184
to
+199
|
||
|
|
||
| it("GET /api/keywords retorna 200 com as keywords", async () => { | ||
| const app = createJobsApiApp({ outputDir: tmpDir }); | ||
| const res = await request(app) | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,6 @@ | ||
| import { mkdtempSync, writeFileSync } from "fs"; | ||
| import { tmpdir } from "os"; | ||
| import path from "path"; | ||
| import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; | ||
| import { getConfig } from "../../../src/config.js"; | ||
|
|
||
|
|
@@ -17,6 +20,7 @@ const CONFIG_ENV_KEYS = [ | |
| "JOB_TYPES", | ||
| "TIME_FILTER", | ||
| "SEARCH_KEYWORDS", | ||
| "KEYWORDS_FILE_PATH", | ||
| ]; | ||
|
|
||
| describe("getConfig", () => { | ||
|
|
@@ -45,9 +49,12 @@ describe("getConfig", () => { | |
| }); | ||
|
|
||
| it("parseia SEARCH_KEYWORDS em lista", () => { | ||
| vi.stubEnv("SEARCH_KEYWORDS", "Java","Spring","RabbitMQ","Docker"); | ||
| const tempDir = mkdtempSync(path.join(tmpdir(), "jobs-config-")); | ||
| vi.stubEnv("KEYWORDS_FILE_PATH", path.join(tempDir, "missing-environment.json")); | ||
| vi.stubEnv("SEARCH_KEYWORDS", "Java,Spring,RabbitMQ,Docker"); | ||
|
|
||
|
Comment on lines
51
to
+55
|
||
| const config = getConfig(); | ||
| expect(config.keywords).toEqual(["Java","Spring","RabbitMQ","Docker"]); | ||
| expect(config.keywords).toEqual(["Java", "Spring", "RabbitMQ", "Docker"]); | ||
| }); | ||
|
|
||
| it("rejeita TIME_FILTER invalido e usa fallback", () => { | ||
|
|
@@ -98,9 +105,24 @@ describe("getConfig", () => { | |
| }); | ||
|
|
||
| it("retorna keywords padrao quando lista informada e vazia", () => { | ||
| const tempDir = mkdtempSync(path.join(tmpdir(), "jobs-config-")); | ||
| vi.stubEnv("KEYWORDS_FILE_PATH", path.join(tempDir, "missing-environment.json")); | ||
| vi.stubEnv("SEARCH_KEYWORDS", " , , "); | ||
|
|
||
| const config = getConfig(); | ||
| expect(config.keywords.length).toBeGreaterThan(0); | ||
| expect(config.keywords).toContain("Java"); | ||
| }); | ||
|
|
||
| it("preserva lista vazia quando o arquivo de keywords ja existe", () => { | ||
| const tempDir = mkdtempSync(path.join(tmpdir(), "jobs-config-")); | ||
| const keywordsFile = path.join(tempDir, "environment.json"); | ||
| writeFileSync(keywordsFile, JSON.stringify({ KEYWORDS: [] }), "utf-8"); | ||
|
|
||
| vi.stubEnv("KEYWORDS_FILE_PATH", keywordsFile); | ||
| vi.stubEnv("SEARCH_KEYWORDS", "Java,Node"); | ||
|
|
||
| const config = getConfig(); | ||
| expect(config.keywords).toEqual([]); | ||
| }); | ||
| }); | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
getKeywordsFilePath()/normalizeKeywords() are duplicated here and in src/jobsApiApp.js. Since the API writes the keywords file and getConfig() reads it, duplicating the path/normalization logic risks divergence and hard-to-debug persistence issues. Consider extracting a shared helper module to keep these rules in one place.