Update docs
This commit is contained in:
parent
b2c87dd207
commit
bb509dd6f7
1 changed files with 50 additions and 40 deletions
|
@ -1,7 +1,9 @@
|
||||||
# Overview
|
# Overview
|
||||||
|
|
||||||
## Packages
|
## Packages
|
||||||
- `main`: primary package, has application-specific functionality
|
- `main`: executable package, includes base server and controllers
|
||||||
|
* TODO: Consider moving controllers to own package
|
||||||
|
- `api`: primary package, implements server logic
|
||||||
- `util`: utility packages
|
- `util`: utility packages
|
||||||
- `sql`: SQL library
|
- `sql`: SQL library
|
||||||
- `http`: HTTP Server
|
- `http`: HTTP Server
|
||||||
|
@ -28,9 +30,8 @@
|
||||||
- Also prefer to return by value (take advantage of [Result Location Semantics](https://github.com/ziglang/zig/issues/287!)).
|
- Also prefer to return by value (take advantage of [Result Location Semantics](https://github.com/ziglang/zig/issues/287!)).
|
||||||
|
|
||||||
- **RUN `zig fmt` BEFORE SUBMITTING A PR, IDEALLY ON EVERY SAVE**
|
- **RUN `zig fmt` BEFORE SUBMITTING A PR, IDEALLY ON EVERY SAVE**
|
||||||
- Unit tests should go in the same directory, if not the same file, as the
|
- Unit tests should go in the same directory, as the code they are testing.
|
||||||
code they are testing.
|
- Integration tests should go in `tests/`
|
||||||
- Integration tests should go in `integration_tests/`
|
|
||||||
- When making changes to DB queries, please test them against both SQLite and Postgres
|
- When making changes to DB queries, please test them against both SQLite and Postgres
|
||||||
* Queries should, whenever possible, run on *both* database engines.
|
* Queries should, whenever possible, run on *both* database engines.
|
||||||
* If you absolutely need to do something different, switch on `db.engineType()`
|
* If you absolutely need to do something different, switch on `db.engineType()`
|
||||||
|
@ -55,31 +56,39 @@ avoiding tangled dependency graphs.
|
||||||
* `controllers/**.zig`:
|
* `controllers/**.zig`:
|
||||||
- Transforms HTTP to/from API calls
|
- Transforms HTTP to/from API calls
|
||||||
- Turns error codes into HTTP statuses
|
- Turns error codes into HTTP statuses
|
||||||
* `api.zig`:
|
|
||||||
- Makes sure API call is allowed with the given user/host context
|
|
||||||
- Transforms API models into display models
|
|
||||||
- `api/**.zig`: Performs action associated with API call
|
|
||||||
* Transforms DB models into API models
|
|
||||||
* Data validation
|
|
||||||
- TODO: the distinction between what goes in `api.zig` and in its submodules is gross. Refactor?
|
|
||||||
* `migrations.zig`:
|
* `migrations.zig`:
|
||||||
- Defines database migrations to apply
|
- Defines database migrations to apply
|
||||||
- Should be ran on startup
|
- Should be ran on startup
|
||||||
|
|
||||||
|
### `api` package
|
||||||
|
* `lib.zig`:
|
||||||
|
- Makes sure API call is allowed with the given user/host context
|
||||||
|
* `services/**.zig`: Performs action associated with API call
|
||||||
|
- Transforms DB models into API models
|
||||||
|
- Most data validation
|
||||||
|
|
||||||
### `util` package
|
### `util` package
|
||||||
#### Components:
|
#### Components:
|
||||||
- `Uuid`: UUID utils (random uuid generation, equality, parsing, printing)
|
- `Uuid`: UUID utils (random uuid generation, equality, parsing, printing)
|
||||||
* `Uuid.eql`
|
* `Uuid.eql`
|
||||||
* `Uuid.randV4`
|
* `Uuid.randV4`
|
||||||
* UUID's are serialized to their string representation for JSON, db
|
* UUID's are serialized to their string representation for JSON, db
|
||||||
- `PathIter`: Path segment iterator
|
- `iters.zig`
|
||||||
|
* `PathIter`: Path segment iterator
|
||||||
|
* `QueryIter`: Query parameter iterator
|
||||||
|
* `SqlStmtIter`
|
||||||
- `Url`: URL utils (parsing)
|
- `Url`: URL utils (parsing)
|
||||||
- `ciutf8`: case-insensitive UTF-8 (TODO: Scrap this, replace with ICU library)
|
* TODO: This isn't used anywhere yet and kinda sucks. Scrap?
|
||||||
|
- `ciutf8`: case-insensitive UTF-8
|
||||||
|
* TODO: Scrap this, replace with ICU library
|
||||||
- `DateTime`: Time utils
|
- `DateTime`: Time utils
|
||||||
* TODO add a TimeSpan or Duration type
|
|
||||||
- `deepClone(alloc, orig)`/`deepFree(alloc, to_free)`
|
- `deepClone(alloc, orig)`/`deepFree(alloc, to_free)`
|
||||||
* Utils for cloning and freeing basic data structs
|
* Utils for cloning and freeing basic data structs
|
||||||
* Clones/frees any strings/sub structs within the value
|
* Clones/frees any strings/sub structs within the value
|
||||||
|
- `getThreadPrng`/`seedThreadPrng`
|
||||||
|
- `comptimeJoinWithPrefix`: metaprogramming helper
|
||||||
|
- `jsonSerializeEnumAsString`: json helper
|
||||||
|
* add `pub const jsonSerialize = util.jsonSerializeEnumAsString` to your enum
|
||||||
|
|
||||||
### `sql` package
|
### `sql` package
|
||||||
|
|
||||||
|
@ -95,30 +104,32 @@ of returning proper errors.
|
||||||
|
|
||||||
#### Usage Example
|
#### Usage Example
|
||||||
```c
|
```c
|
||||||
// open transaction
|
// open transaction context
|
||||||
// only necessary if you need transactional sematics, otherwise
|
// only necessary if you need transactional sematics, otherwise
|
||||||
// just call query methods directly on the database and use the
|
// just call query methods directly on the database and use the
|
||||||
// implied transaction
|
// implied transaction
|
||||||
var tx = try db.begin();
|
var tx = try db.beginOrSavepoint();
|
||||||
errdefer tx.rollback();
|
errdefer tx.rollback();
|
||||||
var results = try tx.query(
|
{
|
||||||
Tuple(&.{[]const u8}),
|
var results = try tx.query(
|
||||||
"SELECT username FROM account WHERE community_id = $1",
|
Tuple(&.{[]const u8}),
|
||||||
.{community_id},
|
"SELECT username FROM account WHERE community_id = $1",
|
||||||
allocator,
|
.{community_id},
|
||||||
);
|
allocator,
|
||||||
defer results.finish();
|
);
|
||||||
|
defer results.finish();
|
||||||
|
|
||||||
std.log.info("Listing users", .{});
|
std.log.info("Listing users", .{});
|
||||||
for (try results.row(allocator)) |row| {
|
for (try results.row(allocator)) |row| {
|
||||||
// Don't forget to free values if applicable!
|
// Don't forget to free values if applicable!
|
||||||
defer allocator.free(row[0]);
|
defer allocator.free(row[0]);
|
||||||
// do some stuff with the username idk
|
// do some stuff with the username idk
|
||||||
std.log.info("- {s}", .{row[0]};
|
std.log.info("- {s}", .{row[0]};
|
||||||
|
}
|
||||||
|
std.log.info("Done", .{});
|
||||||
}
|
}
|
||||||
std.log.info("Done", .{});
|
|
||||||
// commit transaction
|
// commit transaction
|
||||||
try tx.commit();
|
try tx.commitOrRelease();
|
||||||
```
|
```
|
||||||
|
|
||||||
#### `queryRow`
|
#### `queryRow`
|
||||||
|
@ -153,8 +164,6 @@ defer allocator.free(row[0]);
|
||||||
std.log.info("Username: {s}", .{row[0]});
|
std.log.info("Username: {s}", .{row[0]});
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### Avoiding SQL Injection
|
#### Avoiding SQL Injection
|
||||||
|
|
||||||
<b style="font-size: 32px">
|
<b style="font-size: 32px">
|
||||||
|
@ -270,13 +279,10 @@ Note that this may require you to rename some of your result columns using `AS`.
|
||||||
- `insert("accounts", Account{ ... }, allocator)`
|
- `insert("accounts", Account{ ... }, allocator)`
|
||||||
* Equivalent to exec("INSERT INTO accounts(.....) VALUES (......)", .{...}, allocator);
|
* Equivalent to exec("INSERT INTO accounts(.....) VALUES (......)", .{...}, allocator);
|
||||||
- `queryWithOptions(RowType, sql, args, .{ advanced options and stuff... });`
|
- `queryWithOptions(RowType, sql, args, .{ advanced options and stuff... });`
|
||||||
|
- `const tx = try db.beginOrSavepoint()`
|
||||||
##### `db` only
|
* Creates a transaction or savepoint (like a nested transaction) context
|
||||||
- `const tx = try db.begin();`
|
- `tx.commitOrRelease()`
|
||||||
* Will return an error if a transaction is open on this database connection
|
* Commits this transaction or releases the savepoint as necessary
|
||||||
|
|
||||||
#### `tx` only
|
|
||||||
- `try tx.commit()`
|
|
||||||
- `tx.rollback()`
|
- `tx.rollback()`
|
||||||
* Note that this *does not* return an error union, and *does not* need
|
* Note that this *does not* return an error union, and *does not* need
|
||||||
to be prefixed with `try`. This is because rollback is *supposed*
|
to be prefixed with `try`. This is because rollback is *supposed*
|
||||||
|
@ -284,6 +290,10 @@ Note that this may require you to rename some of your result columns using `AS`.
|
||||||
losses, in which the DB should have done a `rollback` for us anyway.
|
losses, in which the DB should have done a `rollback` for us anyway.
|
||||||
If connection errors do occur, then `tx.rollback()` will log them
|
If connection errors do occur, then `tx.rollback()` will log them
|
||||||
to the console and swallow the error value.
|
to the console and swallow the error value.
|
||||||
|
* If you want to check that the rollback completed successfully, you need
|
||||||
|
to know whether it is a savepoint or a transaction being rolled back.
|
||||||
|
Then call either `try tx.rollbackTx()` or `try tx.rollbackSavepoint()`
|
||||||
|
as appropriate.
|
||||||
|
|
||||||
In all cases, you can supply a null allocator if you know for certain that
|
In all cases, you can supply a null allocator if you know for certain that
|
||||||
the query engine does not need to allocate a buffer to bind the arguments
|
the query engine does not need to allocate a buffer to bind the arguments
|
||||||
|
|
Loading…
Reference in a new issue