From b7ffefde51bc72394709e3de0df811d2186f51fe Mon Sep 17 00:00:00 2001 From: burgerni10 Date: Fri, 20 Jan 2023 16:44:52 +0100 Subject: [PATCH] feat(history-query): add backend endpoint and repository for history queries --- backend/model/history-query.model.ts | 39 ++ backend/model/south-connector.model.ts | 7 + .../history-query.repository.spec.ts | 362 ++++++++++++++++++ .../repository/history-query.repository.ts | 177 +++++++++ backend/service/repository.service.spec.ts | 4 + backend/service/repository.service.ts | 7 + .../controllers/history-query.controller.js | 108 ------ .../controllers/history-query.controller.ts | 52 +++ .../controllers/north-connector.controller.ts | 1 + .../controllers/south-connector.controller.ts | 1 + backend/web-server/routes/index.js | 18 +- 11 files changed, 658 insertions(+), 118 deletions(-) create mode 100644 backend/model/history-query.model.ts create mode 100644 backend/repository/history-query.repository.spec.ts create mode 100644 backend/repository/history-query.repository.ts delete mode 100644 backend/web-server/controllers/history-query.controller.js create mode 100644 backend/web-server/controllers/history-query.controller.ts diff --git a/backend/model/history-query.model.ts b/backend/model/history-query.model.ts new file mode 100644 index 0000000000..9b8224de78 --- /dev/null +++ b/backend/model/history-query.model.ts @@ -0,0 +1,39 @@ +import { + NorthArchiveSettings, + NorthCacheSettingsDTO, +} from "./north-connector.model"; + +/** + * DTO for history queries + */ +export interface HistoryQueryDTO { + id: string; + name: string; + description: string; + enabled: boolean; + startTime: string; + endTime: string; + southType: string; + northType: string; + southSettings: object; + northSettings: object; + caching: NorthCacheSettingsDTO; + archive: NorthArchiveSettings; +} + +/** + * Command DTO for history queries + */ +export interface HistoryQueryCommandDTO { + name: string; + description: string; + enabled: boolean; + startTime: string; + endTime: string; + southType: string; + northType: string; + southSettings: object; + northSettings: object; + caching: NorthCacheSettingsDTO; + archive: NorthArchiveSettings; +} diff --git a/backend/model/south-connector.model.ts b/backend/model/south-connector.model.ts index 9300132ede..a3e26f2b87 100644 --- a/backend/model/south-connector.model.ts +++ b/backend/model/south-connector.model.ts @@ -2,6 +2,13 @@ export interface SouthType { category: string; type: string; description: string; + modes: { + subscription: boolean; + lastPoint: boolean; + lastFile: boolean; + historyPoint: boolean; + historyFile: boolean; + }; } /** diff --git a/backend/repository/history-query.repository.spec.ts b/backend/repository/history-query.repository.spec.ts new file mode 100644 index 0000000000..da248f3d51 --- /dev/null +++ b/backend/repository/history-query.repository.spec.ts @@ -0,0 +1,362 @@ +import SqliteDatabaseMock from "../tests/__mocks__/database.mock"; +import { generateRandomId } from "./utils"; +import HistoryQueryRepository from "./history-query.repository"; +import { + HistoryQueryCommandDTO, + HistoryQueryDTO, +} from "../model/history-query.model"; + +jest.mock("../tests/__mocks__/database.mock"); +jest.mock("./utils", () => ({ + generateRandomId: jest.fn(() => "123456"), +})); + +let database; +let repository: HistoryQueryRepository; +describe("History Query repository", () => { + beforeEach(() => { + jest.clearAllMocks(); + database = new SqliteDatabaseMock(); + repository = new HistoryQueryRepository(database); + }); + + it("should properly init north connector table", () => { + expect(database.prepare).toHaveBeenCalledWith( + `CREATE TABLE IF NOT EXISTS history_queries (id TEXT PRIMARY KEY, name TEXT, description TEXT, ` + + `enabled INTEGER, start_time TEXT, end_time TEXT, south_type TEXT, north_type TEXT, ` + + `south_settings TEXT, north_settings TEXT, caching_scan_mode_id TEXT, caching_group_count INTEGER, caching_retry_interval INTEGER, ` + + `caching_retry_count INTEGER, caching_max_send_count INTEGER, caching_timeout INTEGER, archive_enabled INTEGER, ` + + `archive_retention_duration INTEGER, FOREIGN KEY(caching_scan_mode_id) REFERENCES scan_mode(id));` + ); + expect(database.run).toHaveBeenCalledTimes(1); + }); + + it("should properly get north connectors", () => { + const expectedValue: Array = [ + { + id: "id1", + name: "historyQuery1", + description: "My history query", + enabled: true, + startTime: "2023-01-01T00:00:00.000Z", + endTime: "2023-02-01T00:00:00.000Z", + southType: "SQL", + northType: "OIConnect", + southSettings: {}, + northSettings: {}, + caching: { + scanModeId: "scanId1", + retryInterval: 5000, + retryCount: 3, + groupCount: 1000, + maxSendCount: 10000, + timeout: 10000, + }, + archive: { + enabled: true, + retentionDuration: 1000, + }, + }, + { + id: "id2", + name: "historyQuery2", + description: "My second history query", + enabled: true, + startTime: "2023-01-01T00:00:00.000Z", + endTime: "2023-02-01T00:00:00.000Z", + southType: "SQL", + northType: "OIConnect", + southSettings: {}, + northSettings: {}, + caching: { + scanModeId: "scanId1", + retryInterval: 5000, + retryCount: 3, + groupCount: 1000, + maxSendCount: 10000, + timeout: 10000, + }, + archive: { + enabled: true, + retentionDuration: 1000, + }, + }, + ]; + database.all.mockReturnValueOnce([ + { + id: "id1", + name: "historyQuery1", + description: "My history query", + enabled: true, + startTime: "2023-01-01T00:00:00.000Z", + endTime: "2023-02-01T00:00:00.000Z", + southType: "SQL", + northType: "OIConnect", + southSettings: JSON.stringify({}), + northSettings: JSON.stringify({}), + cachingScanModeId: "scanId1", + cachingRetryInterval: 5000, + cachingRetryCount: 3, + cachingGroupCount: 1000, + cachingMaxSendCount: 10000, + cachingTimeout: 10000, + archiveEnabled: true, + archiveRetentionDuration: 1000, + }, + { + id: "id2", + name: "historyQuery2", + description: "My second history query", + enabled: true, + startTime: "2023-01-01T00:00:00.000Z", + endTime: "2023-02-01T00:00:00.000Z", + southType: "SQL", + northType: "OIConnect", + southSettings: JSON.stringify({}), + northSettings: JSON.stringify({}), + cachingScanModeId: "scanId1", + cachingRetryInterval: 5000, + cachingRetryCount: 3, + cachingGroupCount: 1000, + cachingMaxSendCount: 10000, + cachingTimeout: 10000, + archiveEnabled: true, + archiveRetentionDuration: 1000, + }, + ]); + const southConnectors = repository.getHistoryQueries(); + expect(database.prepare).toHaveBeenCalledWith( + `SELECT id, name, description, enabled, start_time as startTime, end_time as endTime, south_type AS southType, ` + + `north_type AS northType, south_settings AS southSettings, north_settings AS northSettings, ` + + `caching_scan_mode_id AS cachingScanModeId, caching_group_count AS cachingGroupCount, caching_retry_interval AS ` + + `cachingRetryInterval, caching_retry_count AS cachingRetryCount, caching_max_send_count AS cachingMaxSendCount, ` + + `caching_timeout AS cachingTimeout, archive_enabled AS archiveEnabled, ` + + `archive_retention_duration AS archiveRetentionDuration FROM history_queries;` + ); + expect(southConnectors).toEqual(expectedValue); + }); + + it("should properly get a history query", () => { + const expectedValue: HistoryQueryDTO = { + id: "id1", + name: "historyQuery1", + description: "My history query", + enabled: true, + startTime: "2023-01-01T00:00:00.000Z", + endTime: "2023-02-01T00:00:00.000Z", + southType: "SQL", + northType: "OIConnect", + southSettings: {}, + northSettings: {}, + caching: { + scanModeId: "scanId1", + retryInterval: 5000, + retryCount: 3, + groupCount: 1000, + maxSendCount: 10000, + timeout: 10000, + }, + archive: { + enabled: true, + retentionDuration: 1000, + }, + }; + database.get.mockReturnValueOnce({ + id: "id1", + name: "historyQuery1", + description: "My history query", + enabled: true, + startTime: "2023-01-01T00:00:00.000Z", + endTime: "2023-02-01T00:00:00.000Z", + southType: "SQL", + northType: "OIConnect", + southSettings: JSON.stringify({}), + northSettings: JSON.stringify({}), + cachingScanModeId: "scanId1", + cachingRetryInterval: 5000, + cachingRetryCount: 3, + cachingGroupCount: 1000, + cachingMaxSendCount: 10000, + cachingTimeout: 10000, + archiveEnabled: true, + archiveRetentionDuration: 1000, + }); + const historyQuery = repository.getHistoryQuery("id1"); + expect(database.prepare).toHaveBeenCalledWith( + `SELECT id, name, description, enabled, start_time as startTime, end_time as endTime, south_type AS southType, ` + + `north_type AS northType, south_settings AS southSettings, north_settings AS northSettings, ` + + `caching_scan_mode_id AS cachingScanModeId, caching_group_count AS cachingGroupCount, caching_retry_interval AS ` + + `cachingRetryInterval, caching_retry_count AS cachingRetryCount, caching_max_send_count AS cachingMaxSendCount, ` + + `caching_timeout AS cachingTimeout, archive_enabled AS archiveEnabled, ` + + `archive_retention_duration AS archiveRetentionDuration FROM history_queries WHERE id = ?;` + ); + expect(database.get).toHaveBeenCalledWith("id1"); + expect(historyQuery).toEqual(expectedValue); + }); + + it("should return null if not found", () => { + database.get.mockReturnValueOnce(null); + const historyQuery = repository.getHistoryQuery("id1"); + expect(database.prepare).toHaveBeenCalledWith( + `SELECT id, name, description, enabled, start_time as startTime, end_time as endTime, south_type AS southType, ` + + `north_type AS northType, south_settings AS southSettings, north_settings AS northSettings, ` + + `caching_scan_mode_id AS cachingScanModeId, caching_group_count AS cachingGroupCount, caching_retry_interval AS ` + + `cachingRetryInterval, caching_retry_count AS cachingRetryCount, caching_max_send_count AS cachingMaxSendCount, ` + + `caching_timeout AS cachingTimeout, archive_enabled AS archiveEnabled, ` + + `archive_retention_duration AS archiveRetentionDuration FROM history_queries WHERE id = ?;` + ); + expect(database.get).toHaveBeenCalledWith("id1"); + expect(historyQuery).toEqual(null); + }); + + it("should create a history query", () => { + const command: HistoryQueryCommandDTO = { + name: "historyQuery1", + description: "My history query", + enabled: true, + startTime: "2023-01-01T00:00:00.000Z", + endTime: "2023-02-01T00:00:00.000Z", + southType: "SQL", + northType: "OIConnect", + southSettings: {}, + northSettings: {}, + caching: { + scanModeId: "scanId1", + retryInterval: 5000, + retryCount: 3, + groupCount: 1000, + maxSendCount: 10000, + timeout: 10000, + }, + archive: { + enabled: true, + retentionDuration: 1000, + }, + }; + + const result = { + id: "id1", + name: "historyQuery1", + description: "My history query", + enabled: true, + startTime: "2023-01-01T00:00:00.000Z", + endTime: "2023-02-01T00:00:00.000Z", + southType: "SQL", + northType: "OIConnect", + southSettings: JSON.stringify({}), + northSettings: JSON.stringify({}), + cachingScanModeId: "scanId1", + cachingRetryInterval: 5000, + cachingRetryCount: 3, + cachingGroupCount: 1000, + cachingMaxSendCount: 10000, + cachingTimeout: 10000, + archiveEnabled: true, + archiveRetentionDuration: 1000, + }; + const runFn = jest.fn(() => ({ lastInsertRowId: 1 })); + const getFn = jest.fn(() => result); + database.prepare + .mockReturnValueOnce({ run: runFn }) + .mockReturnValueOnce({ get: getFn }); + repository.createHistoryQuery(command); + expect(generateRandomId).toHaveBeenCalledWith(6); + expect(database.prepare).toHaveBeenCalledWith( + `INSERT INTO history_queries (id, name, description, enabled, start_time, end_time, ` + + `south_type, north_type, south_settings, north_settings, caching_scan_mode_id, caching_group_count, ` + + `caching_retry_interval, caching_retry_count, caching_max_send_count, caching_timeout, archive_enabled, ` + + `archive_retention_duration) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)` + ); + expect(runFn).toHaveBeenCalledWith( + "123456", + command.name, + command.description, + +command.enabled, + command.startTime, + command.endTime, + command.southType, + command.northType, + JSON.stringify(command.southSettings), + JSON.stringify(command.northSettings), + command.caching.scanModeId, + command.caching.groupCount, + command.caching.retryInterval, + command.caching.retryCount, + command.caching.maxSendCount, + command.caching.timeout, + +command.archive.enabled, + command.archive.retentionDuration + ); + + expect(database.prepare).toHaveBeenCalledWith( + `SELECT id, name, description, enabled, start_time as startTime, end_time as endTime, south_type AS southType, ` + + `north_type AS northType, south_settings AS southSettings, north_settings AS northSettings, ` + + `caching_scan_mode_id AS cachingScanModeId, caching_group_count AS cachingGroupCount, caching_retry_interval AS ` + + `cachingRetryInterval, caching_retry_count AS cachingRetryCount, caching_max_send_count AS cachingMaxSendCount, ` + + `caching_timeout AS cachingTimeout, archive_enabled AS archiveEnabled, ` + + `archive_retention_duration AS archiveRetentionDuration FROM history_queries WHERE ROWID = ?;` + ); + }); + + it("should update a history query", () => { + const command: HistoryQueryCommandDTO = { + name: "historyQuery1", + description: "My history query", + enabled: true, + startTime: "2023-01-01T00:00:00.000Z", + endTime: "2023-02-01T00:00:00.000Z", + southType: "SQL", + northType: "OIConnect", + southSettings: {}, + northSettings: {}, + caching: { + scanModeId: "scanId1", + retryInterval: 5000, + retryCount: 3, + groupCount: 1000, + maxSendCount: 10000, + timeout: 10000, + }, + archive: { + enabled: true, + retentionDuration: 1000, + }, + }; + repository.updateHistoryQuery("id1", command); + expect(database.prepare).toHaveBeenCalledWith( + `UPDATE history_queries SET name = ?, description = ?, enabled = ?, start_time = ?, ` + + `end_time = ?, south_type = ?, north_type = ?, south_settings = ?, north_settings = ?,` + + `caching_scan_mode_id = ?, caching_group_count = ?, caching_retry_interval = ?, caching_retry_count = ?, ` + + `caching_max_send_count = ?, caching_timeout = ?, archive_enabled = ?, archive_retention_duration = ? ` + + `WHERE id = ?;` + ); + expect(database.run).toHaveBeenCalledWith( + command.name, + command.description, + +command.enabled, + command.startTime, + command.endTime, + command.southType, + command.northType, + JSON.stringify(command.southSettings), + JSON.stringify(command.northSettings), + command.caching.scanModeId, + command.caching.groupCount, + command.caching.retryInterval, + command.caching.retryCount, + command.caching.maxSendCount, + command.caching.timeout, + +command.archive.enabled, + command.archive.retentionDuration, + "id1" + ); + }); + + it("should delete a history query", () => { + repository.deleteHistoryQuery("id1"); + expect(database.prepare).toHaveBeenCalledWith( + "DELETE FROM history_queries WHERE id = ?;" + ); + expect(database.run).toHaveBeenCalledWith("id1"); + }); +}); diff --git a/backend/repository/history-query.repository.ts b/backend/repository/history-query.repository.ts new file mode 100644 index 0000000000..2263f56dea --- /dev/null +++ b/backend/repository/history-query.repository.ts @@ -0,0 +1,177 @@ +import { SCAN_MODE_TABLE } from "./scan-mode.repository"; +import { + HistoryQueryCommandDTO, + HistoryQueryDTO, +} from "../model/history-query.model"; +import { generateRandomId } from "./utils"; +import { NORTH_CONNECTOR_TABLE } from "./north-connector.repository"; + +const HISTORY_QUERIES_TABLE = "history_queries"; + +export default class HistoryQueryRepository { + private readonly database; + + constructor(database) { + this.database = database; + const query = + `CREATE TABLE IF NOT EXISTS ${HISTORY_QUERIES_TABLE} (id TEXT PRIMARY KEY, name TEXT, description TEXT, ` + + `enabled INTEGER, start_time TEXT, end_time TEXT, south_type TEXT, north_type TEXT, ` + + `south_settings TEXT, north_settings TEXT, caching_scan_mode_id TEXT, caching_group_count INTEGER, caching_retry_interval INTEGER, ` + + `caching_retry_count INTEGER, caching_max_send_count INTEGER, caching_timeout INTEGER, archive_enabled INTEGER, ` + + `archive_retention_duration INTEGER, FOREIGN KEY(caching_scan_mode_id) REFERENCES ${SCAN_MODE_TABLE}(id));`; + this.database.prepare(query).run(); + } + + /** + * Get all HistoryQueries. + * @return {Object[]} - The HistoryQueries + */ + getHistoryQueries(): Array { + const query = + `SELECT id, name, description, enabled, start_time as startTime, end_time as endTime, south_type AS southType, ` + + `north_type AS northType, south_settings AS southSettings, north_settings AS northSettings, ` + + `caching_scan_mode_id AS cachingScanModeId, caching_group_count AS cachingGroupCount, caching_retry_interval AS ` + + `cachingRetryInterval, caching_retry_count AS cachingRetryCount, caching_max_send_count AS cachingMaxSendCount, ` + + `caching_timeout AS cachingTimeout, archive_enabled AS archiveEnabled, ` + + `archive_retention_duration AS archiveRetentionDuration FROM ${HISTORY_QUERIES_TABLE};`; + const results = this.database.prepare(query).all(); + return results.map((result) => this.toHistoryQueryDTO(result)); + } + + /** + * Get a HistoryQuery + */ + getHistoryQuery(id: string): HistoryQueryDTO { + const query = + `SELECT id, name, description, enabled, start_time as startTime, end_time as endTime, south_type AS southType, ` + + `north_type AS northType, south_settings AS southSettings, north_settings AS northSettings, ` + + `caching_scan_mode_id AS cachingScanModeId, caching_group_count AS cachingGroupCount, caching_retry_interval AS ` + + `cachingRetryInterval, caching_retry_count AS cachingRetryCount, caching_max_send_count AS cachingMaxSendCount, ` + + `caching_timeout AS cachingTimeout, archive_enabled AS archiveEnabled, ` + + `archive_retention_duration AS archiveRetentionDuration FROM ${HISTORY_QUERIES_TABLE} WHERE id = ?;`; + const result = this.database.prepare(query).get(id); + + if (!result) { + return null; + } + + return this.toHistoryQueryDTO(result); + } + + /** + * Create a new HistoryQuery + */ + createHistoryQuery(command: HistoryQueryCommandDTO): HistoryQueryDTO { + const id = generateRandomId(6); + + const insertQuery = + `INSERT INTO ${HISTORY_QUERIES_TABLE} (id, name, description, enabled, start_time, end_time, ` + + `south_type, north_type, south_settings, north_settings, caching_scan_mode_id, caching_group_count, ` + + `caching_retry_interval, caching_retry_count, caching_max_send_count, caching_timeout, archive_enabled, ` + + `archive_retention_duration) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`; + const insertResult = this.database + .prepare(insertQuery) + .run( + id, + command.name, + command.description, + +command.enabled, + command.startTime, + command.endTime, + command.southType, + command.northType, + JSON.stringify(command.southSettings), + JSON.stringify(command.northSettings), + command.caching.scanModeId, + command.caching.groupCount, + command.caching.retryInterval, + command.caching.retryCount, + command.caching.maxSendCount, + command.caching.timeout, + +command.archive.enabled, + command.archive.retentionDuration + ); + + const query = + `SELECT id, name, description, enabled, start_time as startTime, end_time as endTime, south_type AS southType, ` + + `north_type AS northType, south_settings AS southSettings, north_settings AS northSettings, ` + + `caching_scan_mode_id AS cachingScanModeId, caching_group_count AS cachingGroupCount, caching_retry_interval AS ` + + `cachingRetryInterval, caching_retry_count AS cachingRetryCount, caching_max_send_count AS cachingMaxSendCount, ` + + `caching_timeout AS cachingTimeout, archive_enabled AS archiveEnabled, ` + + `archive_retention_duration AS archiveRetentionDuration FROM ${HISTORY_QUERIES_TABLE} WHERE ROWID = ?;`; + const result = this.database + .prepare(query) + .get(insertResult.lastInsertRowid); + + return this.toHistoryQueryDTO(result); + } + + /** + * Update a History query by its ID + */ + updateHistoryQuery(id: string, command: HistoryQueryCommandDTO): void { + const query = + `UPDATE ${HISTORY_QUERIES_TABLE} SET name = ?, description = ?, enabled = ?, start_time = ?, ` + + `end_time = ?, south_type = ?, north_type = ?, south_settings = ?, north_settings = ?,` + + `caching_scan_mode_id = ?, caching_group_count = ?, caching_retry_interval = ?, caching_retry_count = ?, ` + + `caching_max_send_count = ?, caching_timeout = ?, archive_enabled = ?, archive_retention_duration = ? ` + + `WHERE id = ?;`; + return this.database + .prepare(query) + .run( + command.name, + command.description, + +command.enabled, + command.startTime, + command.endTime, + command.southType, + command.northType, + JSON.stringify(command.southSettings), + JSON.stringify(command.northSettings), + command.caching.scanModeId, + command.caching.groupCount, + command.caching.retryInterval, + command.caching.retryCount, + command.caching.maxSendCount, + command.caching.timeout, + +command.archive.enabled, + command.archive.retentionDuration, + id + ); + } + + /** + * Delete a History query by its ID + */ + deleteHistoryQuery(id: string): void { + const query = `DELETE FROM ${HISTORY_QUERIES_TABLE} WHERE id = ?;`; + return this.database.prepare(query).run(id); + } + + private toHistoryQueryDTO(result): HistoryQueryDTO { + return { + id: result.id, + name: result.name, + description: result.description, + enabled: result.enabled, + startTime: result.startTime, + endTime: result.endTime, + southType: result.southType, + northType: result.northType, + southSettings: JSON.parse(result.southSettings), + northSettings: JSON.parse(result.northSettings), + caching: { + scanModeId: result.cachingScanModeId, + groupCount: result.cachingGroupCount, + retryInterval: result.cachingRetryInterval, + retryCount: result.cachingRetryCount, + maxSendCount: result.cachingMaxSendCount, + timeout: result.cachingTimeout, + }, + archive: { + enabled: result.archiveEnabled, + retentionDuration: result.archiveRetentionDuration, + }, + }; + } +} diff --git a/backend/service/repository.service.spec.ts b/backend/service/repository.service.spec.ts index 35c4541605..7a9cd440a6 100644 --- a/backend/service/repository.service.spec.ts +++ b/backend/service/repository.service.spec.ts @@ -11,6 +11,7 @@ import SouthConnectorRepository from "../repository/south-connector.repository"; import SouthItemRepository from "../repository/south-item.repository"; import NorthConnectorRepository from "../repository/north-connector.repository"; import LogRepository from "../repository/log.repository"; +import HistoryQueryRepository from "../repository/history-query.repository"; jest.mock("better-sqlite3", () => jest.fn(() => "sqlite database")); jest.mock("../repository/external-source.repository"); @@ -22,6 +23,7 @@ jest.mock("../repository/north-connector.repository"); jest.mock("../repository/south-connector.repository"); jest.mock("../repository/south-item.repository"); jest.mock("../repository/log.repository"); +jest.mock("../repository/history-query.repository"); describe("Repository service", () => { it("should properly initialize service", () => { @@ -40,6 +42,7 @@ describe("Repository service", () => { expect(SouthConnectorRepository).toHaveBeenCalledWith("sqlite database"); expect(SouthItemRepository).toHaveBeenCalledWith("sqlite database"); expect(LogRepository).toHaveBeenCalledWith("sqlite database"); + expect(HistoryQueryRepository).toHaveBeenCalledWith("sqlite database"); expect(repositoryService.engineRepository).toBeDefined(); expect(repositoryService.externalSourceRepository).toBeDefined(); @@ -50,5 +53,6 @@ describe("Repository service", () => { expect(repositoryService.southConnectorRepository).toBeDefined(); expect(repositoryService.southItemRepository).toBeDefined(); expect(repositoryService.logRepository).toBeDefined(); + expect(repositoryService.historyQueryRepository).toBeDefined(); }); }); diff --git a/backend/service/repository.service.ts b/backend/service/repository.service.ts index 533934c5a6..d3677428a0 100644 --- a/backend/service/repository.service.ts +++ b/backend/service/repository.service.ts @@ -9,6 +9,7 @@ import SouthConnectorRepository from "../repository/south-connector.repository"; import SouthItemRepository from "../repository/south-item.repository"; import NorthConnectorRepository from "../repository/north-connector.repository"; import LogRepository from "../repository/log.repository"; +import HistoryQueryRepository from "../repository/history-query.repository"; export default class RepositoryService { private readonly _engineRepository: EngineRepository; @@ -20,6 +21,7 @@ export default class RepositoryService { private readonly _southConnectorRepository: SouthConnectorRepository; private readonly _southItemRepository: SouthItemRepository; private readonly _logRepository: LogRepository; + private readonly _historyQueryRepository: HistoryQueryRepository; constructor(oibusDatabasePath: string, logsDatabasePath: string) { const oibusDatabase = db(oibusDatabasePath); @@ -39,6 +41,7 @@ export default class RepositoryService { ); this._southItemRepository = new SouthItemRepository(oibusDatabase); this._southItemRepository = new SouthItemRepository(oibusDatabase); + this._historyQueryRepository = new HistoryQueryRepository(oibusDatabase); this._logRepository = new LogRepository(logsDatabase); } @@ -77,4 +80,8 @@ export default class RepositoryService { get southItemRepository(): SouthItemRepository { return this._southItemRepository; } + + get historyQueryRepository(): HistoryQueryRepository { + return this._historyQueryRepository; + } } diff --git a/backend/web-server/controllers/history-query.controller.js b/backend/web-server/controllers/history-query.controller.js deleted file mode 100644 index 4cb0e7a957..0000000000 --- a/backend/web-server/controllers/history-query.controller.js +++ /dev/null @@ -1,108 +0,0 @@ -import { nanoid } from 'nanoid' -/** - * Create a new HistoryQuery entry. - * @param {Object} ctx - The KOA context - * @return {void} - */ -const createHistoryQuery = (ctx) => { - const id = nanoid() - const historyQuery = ctx.app.historyQueryEngine.historyQueryRepository.create({ ...ctx.request.body, id }) - ctx.ok(historyQuery) -} - -/** - * Get HistoryQuery entries. - * @param {Object} ctx - The KOA context - * @return {void} - */ -const getHistoryQueries = (ctx) => { - const historyQueries = ctx.app.historyQueryEngine.historyQueryRepository.getAll() - ctx.ok(historyQueries) -} - -/** - * Get HistoryQuery by id. - * @param {Object} ctx - The KOA context - * @return {void} - */ -const getHistoryQueryById = (ctx) => { - const historyQueries = ctx.app.historyQueryEngine.historyQueryRepository.get(ctx.params.id) - ctx.ok(historyQueries) -} - -/** - * Update a HistoryQuery entry. - * @param {Object} ctx - The KOA context - * @return {void} - */ -const updateHistoryQuery = async (ctx) => { - const updatedHistoryQuery = ctx.app.historyQueryEngine.historyQueryRepository.update(ctx.request.body) - if (ctx.request.body.id === ctx.app.historyQueryEngine.historyQuery?.historyConfiguration.id) { - await ctx.app.historyQueryEngine.historyQuery.stop() - } - ctx.ok(updatedHistoryQuery) -} - -/** - * Enable/Disable a HistoryQuery entry. - * @param {Object} ctx - The KOA context - * @return {void} - */ -const enableHistoryQuery = async (ctx) => { - const historyQuery = ctx.app.historyQueryEngine.historyQueryRepository.get(ctx.params.id) - historyQuery.enabled = ctx.request.body.enabled - const updatedHistoryQuery = ctx.app.historyQueryEngine.historyQueryRepository.update(historyQuery) - if (ctx.params.id === ctx.app.historyQueryEngine.historyQuery?.historyConfiguration.id) { - await ctx.app.historyQueryEngine.historyQuery.stop() - } - ctx.ok(updatedHistoryQuery) -} - -/** - * Re-order a HistoryQuery entry. - * @param {Object} ctx - The KOA context - * @return {void} - */ -const orderHistoryQuery = (ctx) => { - const updatedHistoryQuery = ctx.app.historyQueryEngine.historyQueryRepository.order(ctx.params.id, ctx.request.body.orderColumn) - ctx.ok(updatedHistoryQuery) -} - -/** - * Delete a HistoryQuery entry. - * @param {Object} ctx - The KOA context - * @return {void} - */ -const deleteHistoryQuery = async (ctx) => { - const { position } = ctx.query - ctx.app.historyQueryEngine.historyQueryRepository.delete(ctx.params.id) - if (ctx.params.id === ctx.app.historyQueryEngine.historyQuery?.historyConfiguration.id) { - await ctx.app.historyQueryEngine.historyQuery.stop() - } - - const queries = ctx.app.historyQueryEngine.historyQueryRepository.getAll() - queries.filter((query) => query.orderColumn > position + 1) - .map((query) => ctx.app.historyQueryEngine.historyQueryRepository.order(query.id, query.orderColumn - 1)) - ctx.ok() -} - -/** - * Get status for the given HistoryQuery. - * @param {Object} ctx - The KOA context - * @return {Promise} - The result promise - */ -const getStatus = async (ctx) => { - const status = await ctx.app.historyQueryEngine.getStatusForHistoryQuery(ctx.params.id) - ctx.ok(status) -} - -export default { - createHistoryQuery, - getHistoryQueries, - getHistoryQueryById, - updateHistoryQuery, - enableHistoryQuery, - orderHistoryQuery, - deleteHistoryQuery, - getStatus, -} diff --git a/backend/web-server/controllers/history-query.controller.ts b/backend/web-server/controllers/history-query.controller.ts new file mode 100644 index 0000000000..543a6d3ac8 --- /dev/null +++ b/backend/web-server/controllers/history-query.controller.ts @@ -0,0 +1,52 @@ +import { KoaContext } from "../koa"; +import { + HistoryQueryCommandDTO, + HistoryQueryDTO, +} from "../../model/history-query.model"; + +const getHistoryQueries = (ctx: KoaContext>) => { + const historyQueries = + ctx.app.repositoryService.historyQueryRepository.getHistoryQueries(); + ctx.ok(historyQueries); +}; + +const getHistoryQuery = (ctx: KoaContext) => { + const historyQuery = + ctx.app.repositoryService.historyQueryRepository.getHistoryQuery( + ctx.params.id + ); + ctx.ok(historyQuery); +}; + +const createHistoryQuery = (ctx: KoaContext) => { + const historyQuery = + ctx.app.repositoryService.historyQueryRepository.createHistoryQuery( + ctx.request.body + ); + ctx.ok(historyQuery); +}; + +const updateHistoryQuery = async ( + ctx: KoaContext +) => { + ctx.app.repositoryService.historyQueryRepository.updateHistoryQuery( + ctx.params.id, + ctx.request.body + ); + ctx.noContent(); +}; + +const deleteHistoryQuery = async (ctx: KoaContext) => { + ctx.app.repositoryService.historyQueryRepository.deleteHistoryQuery( + ctx.params.id + ); + ctx.noContent(); +}; + +export default { + getHistoryQueries, + getHistoryQuery, + createHistoryQuery, + updateHistoryQuery, + deleteHistoryQuery, +}; diff --git a/backend/web-server/controllers/north-connector.controller.ts b/backend/web-server/controllers/north-connector.controller.ts index 8ff221a313..2741f09280 100644 --- a/backend/web-server/controllers/north-connector.controller.ts +++ b/backend/web-server/controllers/north-connector.controller.ts @@ -41,6 +41,7 @@ const getNorthConnectorTypes = async ( category: connector.category, type: connector.name, description: connector.description, + modes: connector.modes, })) ); }; diff --git a/backend/web-server/controllers/south-connector.controller.ts b/backend/web-server/controllers/south-connector.controller.ts index f2fc6fdec9..5bec050713 100644 --- a/backend/web-server/controllers/south-connector.controller.ts +++ b/backend/web-server/controllers/south-connector.controller.ts @@ -40,6 +40,7 @@ const getSouthConnectorTypes = async ( category: connector.category, type: connector.name, description: connector.description, + modes: connector.modes, })) ); }; diff --git a/backend/web-server/routes/index.js b/backend/web-server/routes/index.js index 9975349202..9dbff750c3 100644 --- a/backend/web-server/routes/index.js +++ b/backend/web-server/routes/index.js @@ -4,9 +4,9 @@ import multer from '@koa/multer' import configController from '../controllers/config.controller.js' import logController from '../controllers/log.controller.js' import engineController from '../controllers/engine.controller.js' -import historyQueryController from '../controllers/history-query.controller.js' import oibusController from '../controllers/oibus.controller.js' import fileCacheController from '../controllers/file-cache.controller.js' + import apiController from '../controllers/api.controller' import proxyController from '../controllers/proxy.controller' import externalSourceController from '../controllers/external-source.controller' @@ -15,6 +15,7 @@ import scanModeController from '../controllers/scan-mode.controller' import southConnectorController from '../controllers/south-connector.controller' import northConnectorController from '../controllers/north-connector.controller' import logApiController from '../controllers/log-api.controller' +import historyQueryController from '../controllers/history-query.controller' const router = new Router() @@ -42,15 +43,6 @@ router.get('/legacy/installed-south', engineController.getSouthList) router.get('/legacy/north/:id', engineController.getNorth) router.get('/legacy/south/:id', engineController.getSouth) -router.post('/history-queries', historyQueryController.createHistoryQuery) -router.get('/history-queries', historyQueryController.getHistoryQueries) -router.get('/history-queries/:id', historyQueryController.getHistoryQueryById) -router.put('/history-queries/:id', historyQueryController.updateHistoryQuery) -router.put('/history-queries/:id/enable', historyQueryController.enableHistoryQuery) -router.put('/history-queries/:id/order', historyQueryController.orderHistoryQuery) -router.delete('/history-queries/:id', historyQueryController.deleteHistoryQuery) -router.get('/history-queries/:id/status', historyQueryController.getStatus) - router.get('/north/:id/cache/file-errors', fileCacheController.getFileErrors) router.delete('/north/:id/cache/file-errors', fileCacheController.removeFileErrors) router.post('/north/:id/cache/file-errors/retry', fileCacheController.retryFileErrors) @@ -108,6 +100,12 @@ router.post('/api/south/:southId/items', southConnectorController.createSouthIte router.put('/api/south/:southId/items/:id', southConnectorController.updateSouthItem) router.delete('/api/south/:southId/items/:id', southConnectorController.deleteSouthItem) +router.get('/api/history-queries', historyQueryController.getHistoryQueries) +router.get('/api/history-queries/:id', historyQueryController.getHistoryQuery) +router.post('/api/history-queries', historyQueryController.createHistoryQuery) +router.put('/api/history-queries/:id', historyQueryController.updateHistoryQuery) +router.delete('/api/history-queries/:id', historyQueryController.deleteHistoryQuery) + router.get('/api/logs', logApiController.searchLogs) router.post('/api/logs', logApiController.addLogs)