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

feature: Query builder #38

Merged
merged 10 commits into from
Nov 11, 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
105 changes: 105 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
name: Test

on:
push:
branches:
- rewrite
pull_request:

jobs:
analyze:
name: dart analyze
runs-on: ubuntu-latest
steps:
- name: Setup Dart Action
uses: dart-lang/setup-dart@v1

- name: Checkout
uses: actions/checkout@v4

- name: Cache
uses: actions/cache@v4
with:
path: ~/.pub-cache
key: ${{ runner.os }}-pubspec-${{ hashFiles('**/pubspec.lock') }}
restore-keys: |
${{ runner.os }}-pubspec-

- name: Install dependencies
run: dart pub get

- name: Analyze project source
run: dart analyze

fix:
name: dart fix
runs-on: ubuntu-latest
steps:
- name: Setup Dart Action
uses: dart-lang/setup-dart@v1

- name: Checkout
uses: actions/checkout@v4

- name: Cache
uses: actions/cache@v4
with:
path: ~/.pub-cache
key: ${{ runner.os }}-pubspec-${{ hashFiles('**/pubspec.lock') }}
restore-keys: |
${{ runner.os }}-pubspec-

- name: Install dependencies
run: dart pub get

- name: Analyze project source
run: dart fix --dry-run

format:
name: dart format
runs-on: ubuntu-latest
steps:
- name: Setup Dart Action
uses: dart-lang/setup-dart@v1

- name: Checkout
uses: actions/checkout@v4

- name: Cache
uses: actions/cache@v4
with:
path: ~/.pub-cache
key: ${{ runner.os }}-pubspec-${{ hashFiles('**/pubspec.lock') }}
restore-keys: |
${{ runner.os }}-pubspec-

- name: Install dependencies
run: dart pub get

- name: Format
run: dart format --set-exit-if-changed -l120 ./lib

tests:
needs: [ format, analyze, fix ]
name: Unit tests
runs-on: ubuntu-latest
steps:
- name: Setup Dart Action
uses: dart-lang/setup-dart@v1

- name: Checkout
uses: actions/checkout@v4

- name: Cache
uses: actions/cache@v4
with:
path: ~/.pub-cache
key: ${{ runner.os }}-pubspec-${{ hashFiles('**/pubspec.lock') }}
restore-keys: |
${{ runner.os }}-pubspec-

- name: Install dependencies
run: dart pub get

- name: Unit tests
run: dart run test
12 changes: 10 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,15 @@ format: ## Run dart format
fix: ## Run dart fix
dart fix --apply

fix-project: fix format ## Fix whole project
analyze: ## Run dart analyze
dart analyze

tests: ## Run unit tests
dart run test

run: ## Run dev project
docker compose up --build
docker compose up --build

fix-project: analyze fix format ## Fix whole project

check-project: fix-project tests ## Run all checks
95 changes: 43 additions & 52 deletions lib/src/repository/tag.dart
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import 'package:injector/injector.dart';
import 'package:logging/logging.dart';
import 'package:postgres/postgres.dart';
import 'package:running_on_dart/running_on_dart.dart';
import 'package:running_on_dart/src/models/tag.dart';
import 'package:running_on_dart/src/util/query_builder.dart';

class TagRepository {
final _database = Injector.appInstance.get<DatabaseService>();
Expand All @@ -11,17 +11,19 @@ class TagRepository {

/// Fetch all existing tags from the database.
Future<Iterable<Tag>> fetchAllActiveTags() async {
final result = await _database.getConnection().execute(Sql.named('''
SELECT * FROM tags WHERE enabled = TRUE;
'''));
final query = SelectQuery.selectAll('tags')..andWhere('enabled = TRUE');

final result = await _database.executeQuery(query);

return result.map((row) => row.toColumnMap()).map(Tag.fromRow);
}

Future<Iterable<Tag>> fetchActiveTagsByName(String nameQuery) async {
final result = await _database.getConnection().execute(Sql.named('''
SELECT * FROM tags WHERE enabled = TRUE AND name LIKE @nameQuery;
'''), parameters: {'nameQuery': '%$nameQuery%'});
final query = SelectQuery.selectAll("tags")
..andWhere("enabled = TRUE")
..andWhere("name LIKE @nameQuery");

final result = await _database.executeQuery(query, parameters: {'nameQuery': '%$nameQuery%'});

return result.map((row) => row.toColumnMap()).map(Tag.fromRow);
}
Expand All @@ -34,11 +36,11 @@ class TagRepository {
return;
}

await _database.getConnection().execute(Sql.named('''
UPDATE tags SET enabled = FALSE WHERE id = @id;
'''), parameters: {
'id': id,
});
final query = UpdateQuery("tags")
..addSet("enabled", "FALSE")
..andWhere("id = @id");

await _database.executeQuery(query, parameters: {'id': id});
}

/// Add a tag to the database.
Expand All @@ -48,21 +50,15 @@ class TagRepository {
return;
}

final result = await _database.getConnection().execute(Sql.named('''
INSERT INTO tags (
name,
content,
enabled,
guild_id,
author_id
) VALUES (
@name,
@content,
@enabled,
@guild_id,
@author_id
) RETURNING id;
'''), parameters: {
final query = InsertQuery("tags")
..addNamedInsert("name")
..addNamedInsert("content")
..addNamedInsert("enabled")
..addNamedInsert("guild_id")
..addNamedInsert("author_id")
..addReturning("id");

final result = await _database.executeQuery(query, parameters: {
'name': tag.name,
'content': tag.content,
'enabled': tag.enabled,
Expand All @@ -79,16 +75,15 @@ class TagRepository {
return addTag(tag);
}

await _database.getConnection().execute(Sql.named('''
UPDATE tags SET
name = @name,
content = @content,
enabled = @enabled,
guild_id = @guild_id,
author_id = @author_id
WHERE
id = @id
'''), parameters: {
final query = UpdateQuery("tags")
..addNamedSet('name')
..addNamedSet('content')
..addNamedSet('enabled')
..addNamedSet('guild_id')
..addNamedSet('author_id')
..andWhere("id = @id");

await _database.executeQuery(query, parameters: {
'id': tag.id,
'name': tag.name,
'content': tag.content,
Expand All @@ -99,26 +94,22 @@ class TagRepository {
}

Future<Iterable<TagUsedEvent>> fetchTagUsage() async {
final result = await _database.getConnection().execute(Sql.named('''
SELECT tu.* FROM tag_usage tu JOIN tags t ON t.id = tu.command_id AND t.enabled = TRUE;
'''));
final query = SelectQuery.selectAll("tag_usage", alias: "tu")
..addJoin('tags', 't', ['t.id = tu.command_id', 't.enabled = TRUE']);

final result = await _database.executeQuery(query);

return result.map((row) => row.toColumnMap()).map(TagUsedEvent.fromRow);
}

Future<void> registerTagUsedEvent(TagUsedEvent event) async {
await _database.getConnection().execute(Sql.named('''
INSERT INTO tag_usage (
command_id,
use_date,
hidden
) VALUES (
@tag_id,
@use_date,
@hidden
)
'''), parameters: {
'tag_id': event.tagId,
final query = InsertQuery("tag_usage")
..addNamedInsert("command_id")
..addNamedInsert("use_date")
..addNamedInsert("hidden");

await _database.executeQuery(query, parameters: {
'command_id': event.tagId,
'use_date': event.usedAt,
'hidden': event.hidden,
});
Expand Down
7 changes: 6 additions & 1 deletion lib/src/services/db.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import 'package:migent/migent.dart';
import 'package:postgres/postgres.dart';

import 'package:running_on_dart/src/settings.dart';
import 'package:running_on_dart/src/util/query_builder.dart';
import 'package:running_on_dart/src/util/util.dart';

/// The user to use when connecting to the database.
Expand Down Expand Up @@ -163,7 +164,7 @@ class DatabaseService implements RequiresInitialization {
token VARCHAR NOT NULL,
jellyfin_config_id INT NOT NULL,
CONSTRAINT fk_jellyfin_configs
FOREIGN KEY(jellyfin_config_id)
FOREIGN KEY(jellyfin_config_id)
REFERENCES jellyfin_configs(id)
);
''')
Expand All @@ -178,4 +179,8 @@ class DatabaseService implements RequiresInitialization {
}

Connection getConnection() => _connection;

Future<Result> executeQuery(Query query, {Map<String, dynamic>? parameters}) {
return getConnection().execute(query.build(), parameters: parameters);
}
}
Loading