diff --git a/CHANGELOG.md b/CHANGELOG.md index f9fee6b..8c336fe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,11 +10,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added * Database migrations -* Improved GitRepo & BuildLog API - * Pagination using `limit` & `offset` query params - * GitRepo: filter by repo - * BuildLog: filter by start & end date, repo, exit code & arch -* CLI flags to take advantage of above API improvements ## [0.3.0-alpha.2](https://git.rustybever.be/vieter/vieter/src/tag/0.3.0-alpha.2) diff --git a/src/client/logs.v b/src/client/logs.v index d4d373f..3484f03 100644 --- a/src/client/logs.v +++ b/src/client/logs.v @@ -1,14 +1,13 @@ module client -import models { BuildLog, BuildLogFilter } +import models { BuildLog } import net.http { Method } import response { Response } import time // get_build_logs returns all build logs. -pub fn (c &Client) get_build_logs(filter BuildLogFilter) ?Response<[]BuildLog> { - params := models.params_from(filter) - data := c.send_request<[]BuildLog>(Method.get, '/api/logs', params)? +pub fn (c &Client) get_build_logs() ?Response<[]BuildLog> { + data := c.send_request<[]BuildLog>(Method.get, '/api/logs', {})? return data } diff --git a/src/console/logs/logs.v b/src/console/logs/logs.v index 3b1c756..1d0bb55 100644 --- a/src/console/logs/logs.v +++ b/src/console/logs/logs.v @@ -4,8 +4,7 @@ import cli import env import client import console -import time -import models { BuildLog, BuildLogFilter } +import models { BuildLog } struct Config { address string [required] @@ -20,114 +19,21 @@ pub fn cmd() cli.Command { commands: [ cli.Command{ name: 'list' - description: 'List build logs.' + description: 'List the build logs. If a repo ID is provided, only list the build logs for that repo.' flags: [ - cli.Flag{ - name: 'limit' - description: 'How many results to return.' - flag: cli.FlagType.int - }, - cli.Flag{ - name: 'offset' - description: 'Minimum index to return.' - flag: cli.FlagType.int - }, cli.Flag{ name: 'repo' - description: 'Only return logs for this repo id.' + description: 'ID of the Git repo to restrict list to.' flag: cli.FlagType.int }, - cli.Flag{ - name: 'today' - description: 'Only list logs started today (UTC time).' - flag: cli.FlagType.bool - }, - cli.Flag{ - name: 'failed' - description: 'Only list logs with non-zero exit codes.' - flag: cli.FlagType.bool - }, - cli.Flag{ - name: 'day' - description: 'Only list logs started on this day. Format is YYYY-MM-DD.' - flag: cli.FlagType.string - }, - cli.Flag{ - name: 'before' - description: 'Only list logs started before this timestamp. Accepts any RFC 3339 date.' - flag: cli.FlagType.string - }, - cli.Flag{ - name: 'after' - description: 'Only list logs started after this timestamp. Accepts any RFC 3339 date.' - flag: cli.FlagType.string - }, ] execute: fn (cmd cli.Command) ? { config_file := cmd.flags.get_string('config-file')? conf := env.load(config_file)? - mut filter := BuildLogFilter{} - - limit := cmd.flags.get_int('limit')? - if limit != 0 { - filter.limit = u64(limit) - } - - offset := cmd.flags.get_int('offset')? - if offset != 0 { - filter.offset = u64(offset) - } - repo_id := cmd.flags.get_int('repo')? - if repo_id != 0 { - filter.repo = repo_id - } - if cmd.flags.get_bool('today')? { - today := time.now() - - filter.after = time.new_time(time.Time{ - year: today.year - month: today.month - day: today.day - }) - filter.before = filter.after.add_days(1) - } - // The -today flag overwrites any of the other date flags. - else { - day_str := cmd.flags.get_string('day')? - before_str := cmd.flags.get_string('before')? - after_str := cmd.flags.get_string('after')? - - if day_str != '' { - day := time.parse_rfc3339(day_str)? - - filter.after = time.new_time(time.Time{ - year: day.year - month: day.month - day: day.day - }) - - filter.before = filter.after.add_days(1) - } else { - if before_str != '' { - filter.before = time.parse_rfc3339(before_str)? - } - - if after_str != '' { - filter.after = time.parse_rfc3339(after_str)? - } - } - } - - if cmd.flags.get_bool('failed')? { - filter.exit_codes = [ - '!0', - ] - } - - list(conf, filter)? + if repo_id == 0 { list(conf)? } else { list_for_repo(conf, repo_id)? } } }, cli.Command{ @@ -169,9 +75,9 @@ fn print_log_list(logs []BuildLog) ? { } // list prints a list of all build logs. -fn list(conf Config, filter BuildLogFilter) ? { +fn list(conf Config) ? { c := client.new(conf.address, conf.api_key) - logs := c.get_build_logs(filter)?.data + logs := c.get_build_logs()?.data print_log_list(logs)? } diff --git a/src/db/db.v b/src/db/db.v index fac1458..9a06a03 100644 --- a/src/db/db.v +++ b/src/db/db.v @@ -1,7 +1,6 @@ module db import sqlite -import time struct VieterDb { conn sqlite.DB @@ -67,23 +66,3 @@ pub fn init(db_path string) ?VieterDb { conn: conn } } - -// row_into converts an sqlite.Row into a given type T by parsing each field -// from a string according to its type. -pub fn row_into(row sqlite.Row) T { - mut i := 0 - mut out := T{} - - $for field in T.fields { - $if field.typ is string { - out.$(field.name) = row.vals[i] - } $else $if field.typ is int { - out.$(field.name) = row.vals[i].int() - } $else $if field.typ is time.Time { - out.$(field.name) = time.unix(row.vals[i].int()) - } - - i += 1 - } - return out -} diff --git a/src/db/logs.v b/src/db/logs.v index cac08e7..129ec4e 100644 --- a/src/db/logs.v +++ b/src/db/logs.v @@ -1,57 +1,13 @@ module db -import models { BuildLog, BuildLogFilter } -import time +import models { BuildLog } // get_build_logs returns all BuildLog's in the database. -pub fn (db &VieterDb) get_build_logs(filter BuildLogFilter) []BuildLog { - mut where_parts := []string{} - - if filter.repo != 0 { - where_parts << 'repo_id == $filter.repo' +pub fn (db &VieterDb) get_build_logs() []BuildLog { + res := sql db.conn { + select from BuildLog order by id } - if filter.before != time.Time{} { - where_parts << 'start_time < $filter.before.unix_time()' - } - - if filter.after != time.Time{} { - where_parts << 'start_time > $filter.after.unix_time()' - } - - // NOTE: possible SQL injection - if filter.arch != '' { - where_parts << "arch == '$filter.arch'" - } - - mut parts := []string{} - - for exp in filter.exit_codes { - if exp[0] == `!` { - code := exp[1..].int() - - parts << 'exit_code != $code' - } else { - code := exp.int() - - parts << 'exit_code == $code' - } - } - - if parts.len > 0 { - where_parts << parts.map('($it)').join(' or ') - } - - mut where_str := '' - - if where_parts.len > 0 { - where_str = 'where ' + where_parts.map('($it)').join(' and ') - } - - query := 'select * from BuildLog $where_str limit $filter.limit offset $filter.offset' - rows, _ := db.conn.exec(query) - res := rows.map(row_into(it)) - return res } diff --git a/src/models/logs.v b/src/models/logs.v index c92dc07..173336f 100644 --- a/src/models/logs.v +++ b/src/models/logs.v @@ -3,7 +3,7 @@ module models import time pub struct BuildLog { -pub mut: +pub: id int [primary; sql: serial] repo_id int [nonull] start_time time.Time [nonull] @@ -26,15 +26,3 @@ pub fn (bl &BuildLog) str() string { return str } - -[params] -pub struct BuildLogFilter { -pub mut: - limit u64 = 25 - offset u64 - repo int - before time.Time - after time.Time - arch string - exit_codes []string -} diff --git a/src/models/models.v b/src/models/models.v index 3a127bc..924f45f 100644 --- a/src/models/models.v +++ b/src/models/models.v @@ -1,7 +1,5 @@ module models -import time - // from_params creates a new instance of T from the given map by parsing all // of its fields from the map. pub fn from_params(params map[string]string) ?T { @@ -25,10 +23,6 @@ pub fn patch_from_params(mut o T, params map[string]string) ? { o.$(field.name) = params[field.name].u64() } $else $if field.typ is []GitRepoArch { o.$(field.name) = params[field.name].split(',').map(GitRepoArch{ value: it }) - } $else $if field.typ is time.Time { - o.$(field.name) = time.unix(params[field.name].int()) - } $else $if field.typ is []string { - o.$(field.name) = params[field.name].split(',') } } else if field.attrs.contains('nonull') { return error('Missing parameter: ${field.name}.') @@ -41,13 +35,7 @@ pub fn params_from(o &T) map[string]string { mut out := map[string]string{} $for field in T.fields { - $if field.typ is time.Time { - out[field.name] = o.$(field.name).unix_time().str() - } $else $if field.typ is []string { - out[field.name] = o.$(field.name).join(',') - } $else { - out[field.name] = o.$(field.name).str() - } + out[field.name] = o.$(field.name).str() } return out } diff --git a/src/server/logs.v b/src/server/logs.v index 51b364f..af0b081 100644 --- a/src/server/logs.v +++ b/src/server/logs.v @@ -8,7 +8,7 @@ import db import time import os import util -import models { BuildLog, BuildLogFilter } +import models { BuildLog } // get_logs returns all build logs in the database. A 'repo' query param can // optionally be added to limit the list of build logs to that repository. @@ -18,10 +18,11 @@ fn (mut app App) get_logs() web.Result { return app.json(http.Status.unauthorized, new_response('Unauthorized.')) } - filter := models.from_params(app.query) or { - return app.json(http.Status.bad_request, new_response('Invalid query parameters.')) + logs := if 'repo' in app.query { + app.db.get_build_logs_for_repo(app.query['repo'].int()) + } else { + app.db.get_build_logs() } - logs := app.db.get_build_logs(filter) return app.json(http.Status.ok, new_data_response(logs)) }