From 5723dbf31bc5d1c2a07a53373e4df13972bb8c5e Mon Sep 17 00:00:00 2001 From: Faruk AYDIN Date: Mon, 7 Apr 2025 14:22:27 +0200 Subject: [PATCH] feat: Implement API Token model --- .../20250407121304_create_api_tokens.js | 12 +++++++++++ .../__snapshots__/api-token.test.js.snap | 20 +++++++++++++++++++ packages/backend/src/models/api-token.js | 17 ++++++++++++++++ packages/backend/src/models/api-token.test.js | 12 +++++++++++ packages/backend/test/factories/api-token.js | 10 ++++++++++ 5 files changed, 71 insertions(+) create mode 100644 packages/backend/src/db/migrations/20250407121304_create_api_tokens.js create mode 100644 packages/backend/src/models/__snapshots__/api-token.test.js.snap create mode 100644 packages/backend/src/models/api-token.js create mode 100644 packages/backend/src/models/api-token.test.js create mode 100644 packages/backend/test/factories/api-token.js diff --git a/packages/backend/src/db/migrations/20250407121304_create_api_tokens.js b/packages/backend/src/db/migrations/20250407121304_create_api_tokens.js new file mode 100644 index 00000000..80cb484a --- /dev/null +++ b/packages/backend/src/db/migrations/20250407121304_create_api_tokens.js @@ -0,0 +1,12 @@ +export async function up(knex) { + return knex.schema.createTable('api_tokens', (table) => { + table.uuid('id').primary().defaultTo(knex.raw('gen_random_uuid()')); + table.string('token').notNullable().index(); + + table.timestamps(true, true); + }); +} + +export async function down(knex) { + return knex.schema.dropTable('api_tokens'); +} diff --git a/packages/backend/src/models/__snapshots__/api-token.test.js.snap b/packages/backend/src/models/__snapshots__/api-token.test.js.snap new file mode 100644 index 00000000..6f52bfc8 --- /dev/null +++ b/packages/backend/src/models/__snapshots__/api-token.test.js.snap @@ -0,0 +1,20 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`ApiToken model > jsonSchema should have correct validations 1`] = ` +{ + "properties": { + "id": { + "format": "uuid", + "type": "string", + }, + "token": { + "minLength": 32, + "type": "string", + }, + }, + "required": [ + "token", + ], + "type": "object", +} +`; diff --git a/packages/backend/src/models/api-token.js b/packages/backend/src/models/api-token.js new file mode 100644 index 00000000..f5a520d1 --- /dev/null +++ b/packages/backend/src/models/api-token.js @@ -0,0 +1,17 @@ +import Base from './base.js'; + +class ApiToken extends Base { + static tableName = 'api_tokens'; + + static jsonSchema = { + type: 'object', + required: ['token'], + + properties: { + id: { type: 'string', format: 'uuid' }, + token: { type: 'string', minLength: 32 }, + }, + }; +} + +export default ApiToken; diff --git a/packages/backend/src/models/api-token.test.js b/packages/backend/src/models/api-token.test.js new file mode 100644 index 00000000..0fcbb806 --- /dev/null +++ b/packages/backend/src/models/api-token.test.js @@ -0,0 +1,12 @@ +import { describe, it, expect } from 'vitest'; +import ApiToken from './api-token.js'; + +describe('ApiToken model', () => { + it('tableName should return correct name', () => { + expect(ApiToken.tableName).toBe('api_tokens'); + }); + + it('jsonSchema should have correct validations', () => { + expect(ApiToken.jsonSchema).toMatchSnapshot(); + }); +}); diff --git a/packages/backend/test/factories/api-token.js b/packages/backend/test/factories/api-token.js new file mode 100644 index 00000000..7eaac70c --- /dev/null +++ b/packages/backend/test/factories/api-token.js @@ -0,0 +1,10 @@ +import crypto from 'crypto'; +import ApiToken from '../../src/models/api-token.js'; + +export const createApiToken = async (params = {}) => { + params.token = params.token || crypto.randomBytes(48).toString('hex'); + + const apiToken = await ApiToken.query().insertAndFetch(params); + + return apiToken; +};