Skip to content
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

fix(oianalytics): send oibus info to oianalytics on engine name or version update #3454

Merged
merged 1 commit into from
Jun 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions backend/src/db/entity-migrations/v3.3.7-add-oia-message.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { Knex } from 'knex';
import { OIANALYTICS_MESSAGE_TABLE } from '../../repository/oianalytics-message.repository';
import { ENGINES_TABLE } from '../../repository/engine.repository';
import { version } from '../../../package.json';
import CreateTableBuilder = Knex.CreateTableBuilder;
import { OIANALYTICS_MESSAGE_STATUS } from '../../../../shared/model/oianalytics-message.model';

function createDefaultEntityFields(table: CreateTableBuilder): void {
table.uuid('id').primary();
table.timestamps(false, true);
}

export async function up(knex: Knex): Promise<void> {
await createOIAMessageTable(knex);
await addVersionInEngineSettings(knex);
}

async function createOIAMessageTable(knex: Knex): Promise<void> {
await knex.schema.createTable(OIANALYTICS_MESSAGE_TABLE, table => {
createDefaultEntityFields(table);
table.string('type').notNullable();
table.json('content').notNullable();
table.datetime('completed_date');
table.string('error');
table.enum('status', OIANALYTICS_MESSAGE_STATUS).notNullable().defaultTo('PENDING');
});
}

async function addVersionInEngineSettings(knex: Knex) {
await knex.schema.raw(`ALTER TABLE ${ENGINES_TABLE} ADD oibus_version NOT NULL DEFAULT "${version}"`);
}

export async function down(knex: Knex): Promise<void> {
await knex.schema.dropTable(OIANALYTICS_MESSAGE_TABLE);
}
14 changes: 13 additions & 1 deletion backend/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import HomeMetricsService from './service/home-metrics.service';
import CommandService from './service/oia/command.service';
import RegistrationService from './service/oia/registration.service';
import ProxyServer from './web-server/proxy-server';
import OIAnalyticsMessageService from './service/oia/message.service';

const CONFIG_DATABASE = 'oibus.db';
const CRYPTO_DATABASE = 'crypto.db';
Expand Down Expand Up @@ -107,7 +108,15 @@ const LOG_DB_NAME = 'logs.db';
loggerService.logger!
);

const commandService = new CommandService(repositoryService, encryptionService, loggerService.logger!, binaryFolder);
const oianalyticsMessageService = new OIAnalyticsMessageService(repositoryService, encryptionService, loggerService.logger!);
oianalyticsMessageService.start();
const commandService = new CommandService(
repositoryService,
encryptionService,
oianalyticsMessageService,
loggerService.logger!,
binaryFolder
);
commandService.start();

const oibusService = new OIBusService(engine, historyQueryEngine);
Expand Down Expand Up @@ -138,13 +147,15 @@ const LOG_DB_NAME = 'logs.db';
engine,
historyQueryEngine,
oibusService,
oianalyticsMessageService,
proxyServer
);

const registrationService = new RegistrationService(
repositoryService,
encryptionService,
commandService,
oianalyticsMessageService,
reloadService,
loggerService.logger!
);
Expand Down Expand Up @@ -172,6 +183,7 @@ const LOG_DB_NAME = 'logs.db';
stopping = true;
await oibusService.stopOIBus();
await commandService.stop();
await oianalyticsMessageService.stop();
await proxyServer.stop();
await server.stop();
registrationService.stop();
Expand Down
11 changes: 10 additions & 1 deletion backend/src/repository/engine.repository.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ describe('Non-empty Engine repository', () => {
id: 'id1',
name: 'OIBus',
port: 2223,
version: '3.3.3',
proxyEnabled: false,
proxyPort: 9000,
consoleLogLevel: 'silent',
Expand Down Expand Up @@ -188,6 +189,7 @@ describe('Non-empty Engine repository', () => {
id: 'id1',
name: 'OIBus',
port: 2223,
version: '3.3.3',
proxyEnabled: false,
proxyPort: 9000,
logParameters: {
Expand Down Expand Up @@ -218,7 +220,7 @@ describe('Non-empty Engine repository', () => {
};
const engineSettings = repository.getEngineSettings();
expect(database.prepare).toHaveBeenCalledWith(
'SELECT id, name, port, proxy_enabled AS proxyEnabled, proxy_port AS proxyPort, log_console_level AS consoleLogLevel, log_file_level AS fileLogLevel, ' +
'SELECT id, name, port, oibus_version AS version, proxy_enabled AS proxyEnabled, proxy_port AS proxyPort, log_console_level AS consoleLogLevel, log_file_level AS fileLogLevel, ' +
'log_file_max_file_size AS fileLogMaxFileSize, log_file_number_of_files AS fileLogNumberOfFiles, ' +
'log_database_level AS databaseLogLevel, log_database_max_number_of_logs AS databaseLogMaxNumberOfLogs, ' +
'log_loki_level AS lokiLogLevel, log_loki_interval AS lokiLogInterval, log_loki_address AS lokiLogAddress, ' +
Expand Down Expand Up @@ -264,4 +266,11 @@ describe('Non-empty Engine repository', () => {
repository.createEngineSettings(command);
expect(generateRandomId).not.toHaveBeenCalled();
});

it('should update version', () => {
repository.updateVersion('3.4.0');

expect(database.prepare).toHaveBeenCalledWith('UPDATE engines SET oibus_version = ? WHERE rowid=(SELECT MIN(rowid) FROM engines);');
expect(run).toHaveBeenCalledWith('3.4.0');
});
});
12 changes: 11 additions & 1 deletion backend/src/repository/engine.repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ export default class EngineRepository {
*/
getEngineSettings(): EngineSettingsDTO | null {
const query =
'SELECT id, name, port, proxy_enabled AS proxyEnabled, proxy_port AS proxyPort, ' +
'SELECT id, name, port, oibus_version AS version, proxy_enabled AS proxyEnabled, proxy_port AS proxyPort, ' +
'log_console_level AS consoleLogLevel, ' +
'log_file_level AS fileLogLevel, ' +
'log_file_max_file_size AS fileLogMaxFileSize, ' +
Expand All @@ -75,6 +75,7 @@ export default class EngineRepository {
id: results[0].id,
name: results[0].name,
port: results[0].port,
version: results[0].version,
proxyEnabled: Boolean(results[0].proxyEnabled),
proxyPort: results[0].proxyPort,
logParameters: {
Expand Down Expand Up @@ -152,6 +153,15 @@ export default class EngineRepository {
);
}

/**
* Update OIBus version
*/
updateVersion(version: string): void {
const query = `UPDATE ${ENGINES_TABLE} SET oibus_version = ? WHERE rowid=(SELECT MIN(rowid) FROM ${ENGINES_TABLE});`;

this.database.prepare(query).run(version);
}

/**
* Create engine settings in the database.
*/
Expand Down
176 changes: 176 additions & 0 deletions backend/src/repository/oianalytics-message.repository.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
import SqliteDatabaseMock, { all, get, run } from '../tests/__mocks__/database.mock';
import { Database } from 'better-sqlite3';
import OianalyticsMessageRepository from './oianalytics-message.repository';
import { Page } from '../../../shared/model/types';
import { InfoMessageContent, OIAnalyticsMessageCommand, OIAnalyticsMessageDTO } from '../../../shared/model/oianalytics-message.model';

jest.mock('../tests/__mocks__/database.mock');
jest.mock('../service/utils', () => ({
generateRandomId: jest.fn(() => '123456')
}));
const nowDateString = '2020-02-02T02:02:02.222Z';

const existingMessage: OIAnalyticsMessageDTO = {
id: '1234',
status: 'ERRORED',
type: 'INFO',
content: {} as InfoMessageContent
};

let database: Database;
let repository: OianalyticsMessageRepository;
describe('OIAnalytics Message repository', () => {
beforeEach(() => {
jest.clearAllMocks();
database = new SqliteDatabaseMock();
all.mockReturnValue([existingMessage]);
database.prepare = jest.fn().mockReturnValue({
run,
get,
all
});
repository = new OianalyticsMessageRepository(database);
});

it('should create message', () => {
run.mockReturnValueOnce({ lastInsertRowid: 1 });
get.mockReturnValueOnce({ ...existingMessage, content: '{}' });
const command: OIAnalyticsMessageCommand = {
type: 'INFO',
content: {} as InfoMessageContent
};
repository.createOIAnalyticsMessages(command);
expect(database.prepare).toHaveBeenCalledWith('INSERT INTO oianalytics_messages (id, type, status, content) VALUES (?, ?, ?, ?);');
expect(run).toHaveBeenCalledWith('123456', command.type, 'PENDING', JSON.stringify(command.content));
});

it('should update message', () => {
const command: OIAnalyticsMessageCommand = {
type: 'INFO',
content: {} as InfoMessageContent
};
repository.updateOIAnalyticsMessages('id', command);
expect(database.prepare).toHaveBeenCalledWith('UPDATE oianalytics_messages SET content = ? WHERE id = ?;');
expect(run).toHaveBeenCalledWith(JSON.stringify(command.content), 'id');
});

it('should properly get messages page by search criteria', () => {
const expectedValue: Page<OIAnalyticsMessageDTO> = {
content: [
{
id: '1234',
creationDate: '2023-01-01T00:00:00.000Z',
type: 'INFO',
status: 'PENDING',
content: {} as InfoMessageContent
},
{
id: '1234',
creationDate: '2024-01-01T00:00:00.000Z',
type: 'INFO',
status: 'ERRORED',
content: {} as InfoMessageContent
}
],
size: 50,
number: 0,
totalElements: 2,
totalPages: 1
};
all.mockReturnValueOnce([
{
id: '1234',
creationDate: '2023-01-01T00:00:00.000Z',
type: 'INFO',
status: 'PENDING',
content: '{}'
},
{
id: '1234',
creationDate: '2024-01-01T00:00:00.000Z',
type: 'INFO',
status: 'ERRORED',
content: '{}'
}
]);
get.mockReturnValueOnce({ count: 2 });
const logs = repository.searchMessagesPage(
{
types: ['INFO'],
status: ['PENDING', 'ERRORED'],
start: '2023-01-01T00:00:00.000Z',
end: '2023-01-02T00:00:00.000Z'
},
0
);
expect(database.prepare).toHaveBeenCalledWith(
'SELECT id, created_at as creationDate, completed_date as compeltedDate, type, status, error, content FROM oianalytics_messages WHERE id IS NOT NULL ' +
'AND type IN (?) AND status IN (?,?) AND created_at >= ? AND created_at <= ? ORDER BY created_at DESC LIMIT 50 OFFSET ?;'
);
expect(logs).toEqual(expectedValue);

expect(database.prepare).toHaveBeenCalledWith(
'SELECT COUNT(*) as count FROM oianalytics_messages WHERE id IS NOT NULL AND type IN (?) AND status IN (?,?) AND created_at >= ? AND created_at <= ?;'
);
});

it('should properly get messages list by search criteria', () => {
const expectedValue: Array<OIAnalyticsMessageDTO> = [
{
id: '1234',
creationDate: '2023-01-01T00:00:00.000Z',
type: 'INFO',
status: 'PENDING',
content: {} as InfoMessageContent
},
{
id: '1234',
creationDate: '2024-01-01T00:00:00.000Z',
type: 'INFO',
status: 'ERRORED',
content: {} as InfoMessageContent
}
];
all.mockReturnValueOnce([
{
id: '1234',
creationDate: '2023-01-01T00:00:00.000Z',
type: 'INFO',
status: 'PENDING',
content: '{}'
},
{
id: '1234',
creationDate: '2024-01-01T00:00:00.000Z',
type: 'INFO',
status: 'ERRORED',
content: '{}'
}
]);
const messages = repository.searchMessagesList({
types: ['INFO'],
status: ['PENDING', 'ERRORED'],
start: '2023-01-01T00:00:00.000Z',
end: '2023-01-02T00:00:00.000Z'
});
expect(database.prepare).toHaveBeenCalledWith(
'SELECT id, created_at as creationDate, completed_date as compeltedDate, type, status, error, content FROM oianalytics_messages WHERE id IS NOT NULL ' +
'AND type IN (?) AND status IN (?,?) AND created_at >= ? AND created_at <= ? ORDER BY created_at DESC;'
);
expect(messages).toEqual(expectedValue);
});

it('should mark a command as COMPLETED', () => {
repository.markAsCompleted('id1', nowDateString);
const query = `UPDATE oianalytics_messages SET status = 'COMPLETED', completed_date = ? WHERE id = ?;`;
expect(database.prepare).toHaveBeenCalledWith(query);
expect(run).toHaveBeenCalledWith(nowDateString, 'id1');
});

it('should mark a command as ERRORED', () => {
repository.markAsErrored('id1', nowDateString, 'not ok');
const query = `UPDATE oianalytics_messages SET status = 'ERRORED', completed_date = ?, error = ? WHERE id = ?;`;
expect(database.prepare).toHaveBeenCalledWith(query);
expect(run).toHaveBeenCalledWith(nowDateString, 'not ok', 'id1');
});
});
Loading