Update docs

This commit is contained in:
jaina heartles 2022-10-11 20:21:44 -07:00
parent b2c87dd207
commit bb509dd6f7

View file

@ -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