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

WIP: Add seaography documentation #41

Merged
merged 14 commits into from
Sep 12, 2022
Merged
8 changes: 8 additions & 0 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,14 @@ jobs:
- name: Build SeaORM Docs
run: sh build.sh

- name: Build Seaography Docs
run: |
cd Seaography
npm i
npm run build
[[ -d ../docs/Seaography ]] && rm -r ../docs/Seaography
mv build ../docs/Seaography

- name: Deploy GitHub Pages
uses: JamesIves/github-pages-deploy-action@v4
with:
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@
- [SeaQL Blog](https://www.sea-ql.org/blog/)
- [SeaORM Documentation](https://www.sea-ql.org/SeaORM/)
- [StarfishQL Documentation](https://www.sea-ql.org/StarfishQL/)
- [Seaography Documentation](https://www.sea-ql.org/Seaography/)
20 changes: 20 additions & 0 deletions Seaography/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Dependencies
/node_modules

# Production
/build

# Generated files
.docusaurus
.cache-loader

# Misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local

npm-debug.log*
yarn-debug.log*
yarn-error.log*
41 changes: 41 additions & 0 deletions Seaography/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Website

This website is built using [Docusaurus 2](https://docusaurus.io/), a modern static website generator.

### Installation

```
$ yarn
```

### Local Development

```
$ yarn start
```

This command starts a local development server and opens up a browser window. Most changes are reflected live without having to restart the server.

### Build

```
$ yarn build
```

This command generates static content into the `build` directory and can be served using any static contents hosting service.

### Deployment

Using SSH:

```
$ USE_SSH=true yarn deploy
```

Not using SSH:

```
$ GIT_USER=<Your GitHub username> yarn deploy
```

If you are using GitHub pages for hosting, this command is a convenient way to build the website and push to the `gh-pages` branch.
3 changes: 3 additions & 0 deletions Seaography/babel.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module.exports = {
presets: [require.resolve('@docusaurus/core/lib/babel/preset')],
};
Empty file added Seaography/blog/.gitkeep
Empty file.
44 changes: 44 additions & 0 deletions Seaography/docs/01-getting-started.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Getting started


## Installing CLI
```bash
cargo install seaography
```

## CLI parameters

```bash
seaography <database_url> <crate_name> <output_folder>
```

* **database_url:** a valid URL pointing to the database.
examples: `mysql://user:pass@127.0.0.1:1235/database_name`, `sqlite://my_db.db`, `pgsql://user:pass@127.0.0.1/base_name`
* **crate_name:** the cargo crate name of the generated project
* **output_folder:** path pointing to the output folder, it will create it if does not exist

## Prerequisites

In order to understand how the generated code works its recommended to study the following resources:

1. [async-graphql](https://docs.rs/async-graphql/latest/async_graphql/)

Is a server side GraphQL library for Rust.

2. [sea-orm](https://docs.rs/sea-orm/latest/sea_orm/)

SeaORM is a relational ORM to help you build web services in Rust with the familiarity of dynamic languages.

3. [poem](https://docs.rs/crate/poem/latest)

A full-featured and easy-to-use web framework with the Rust programming language.

4. [tokio*](https://docs.rs/tokio/latest/tokio/)

A runtime for writing reliable, asynchronous, and slim applications with the Rust programming language.

5. [itertools*](https://docs.rs/itertools/latest/itertools/)

Extra iterator adaptors, functions and macros.

* Recommended to study, but not required
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The * makes this line being interpreted as a list. Consider escaping it :)

Suggested change
* Recommended to study, but not required
\* Recommended to study, but not required

164 changes: 164 additions & 0 deletions Seaography/docs/02-tool-internals.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
# Tool internals

Here we will describe the internals of the CLI tool. Firstly, we will study the tool itself, and how its structured. Then, we will focus on the generator sub-crate and study the different generator modes.

## Internal crates

The project depends on 3 sub-crates (`types`, `discovery`, `generator`). This helps with separation of logic into simpler modules which are easier to maintain, extend and debug.

### `types`

The `types` sub-crate contains all required types and utilities that are shared between the other sub-crates.

* `schema_meta`: holds database metadata (type, version, url), table metadata, and enumeration metadata
* `table_meta`: holds table metadata (name, columns, relations)
* `relationship_meta`: holds tables relationship metadata (source/destination table names, source/destination columns)
* `column_meta`: holds table column metadata (name, type, nullable, is_primary)
* `enum_meta`: holds enumeration metadata (name, values)
* `column_type`: column type enumeration

### `discoverer`

The `discoverer` sub-crate contains all required functions to read any database (sqlite, mysql, pgsql) and convert it to `schema_meta`.

* `lib`: contains the main function that receives database url, check that is correct and exports `schema_meta`
* `utils`: contains all function to convert table crate statements into `schema_meta`
* `mysql`: contains a function that connects to mysql db and returns table create statements
* `sqlite`: contains a function that connects to sqlite db and returns table create statements
* `pgsql`: contains a function that connects to pgsql db and returns table create statements

### `generator`

The `generator` sub-crate contains functions to generate GraphQL compatible entities.

* `toml`: generate rust crate toml specification
* `lib`: generate for main.rs file
* `root_node`: generate GraphQL root node with queries for all entities
* `type_filter`: generate TypeFilter GraphQL type
* `orm_dataloader`: generate OrmDataloader utility type
* `enumeration`: generator for GraphQL enumerations
* `entity`: generator for GraphQL entities (field getters, relationship getters, Filter data type, dataloader load functions)

### CLI tool

The core of the tool, coordinates all sub-crates.

* `main`: the main CLI program
* `lib`: contains functions used from main and are responsible for generating ORM & GraphQL entities

## Generators

The generators are responsible for generating the Rust code. It is the main point where the core features we provide reside. Currently the project only implements the expanded code generator. A procedural macro based generator is WIP and will depend on the expanded format generator to generate code with fewer lines of code but same functionality.

All examples are based on https://dev.mysql.com/doc/sakila/en/ database.

### Expanded mode

The expanded generator produce all Rust code. Its easier to modify, but harder to maintain long term. In case a new version of the generator exists you have to regenerate the whole project and apply the your custom modifications again.

#### Example entity

```rust
use crate::graphql::*;
pub use crate::orm::category::*;
use sea_orm::prelude::*;
#[async_graphql::Object(name = "Category")]
impl Model {
pub async fn category_id(&self) -> &u8 {
&self.category_id
}
pub async fn name(&self) -> &String {
&self.name
}
pub async fn last_update(&self) -> &DateTimeUtc {
&self.last_update
}
pub async fn category_film_category<'a>(
&self,
ctx: &async_graphql::Context<'a>,
) -> Vec<crate::orm::film_category::Model> {
let data_loader = ctx
.data::<async_graphql::dataloader::DataLoader<OrmDataloader>>()
.unwrap();
let key = CategoryFilmCategoryFK(self.category_id.clone());
let data: Option<_> = data_loader.load_one(key).await.unwrap();
data.unwrap_or(vec![])
}
}
#[derive(async_graphql :: InputObject, Debug)]
#[graphql(name = "CategoryFilter")]
pub struct Filter {
pub or: Option<Vec<Box<Filter>>>,
pub and: Option<Vec<Box<Filter>>>,
pub category_id: Option<TypeFilter<u8>>,
pub name: Option<TypeFilter<String>>,
pub last_update: Option<TypeFilter<DateTimeUtc>>,
}
#[derive(Clone, Eq, PartialEq, Hash, Debug)]
pub struct CategoryFilmCategoryFK(u8);
#[async_trait::async_trait]
impl async_graphql::dataloader::Loader<CategoryFilmCategoryFK> for OrmDataloader {
type Value = Vec<crate::orm::film_category::Model>;
type Error = std::sync::Arc<sea_orm::error::DbErr>;
async fn load(
&self,
keys: &[CategoryFilmCategoryFK],
) -> Result<std::collections::HashMap<CategoryFilmCategoryFK, Self::Value>, Self::Error> {
let filter = sea_orm::Condition::all().add(sea_orm::sea_query::SimpleExpr::Binary(
Box::new(sea_orm::sea_query::SimpleExpr::Tuple(vec![
sea_orm::sea_query::Expr::col(
crate::orm::film_category::Column::CategoryId.as_column_ref(),
)
.into_simple_expr(),
])),
sea_orm::sea_query::BinOper::In,
Box::new(sea_orm::sea_query::SimpleExpr::Tuple(
keys.iter()
.map(|tuple| {
sea_orm::sea_query::SimpleExpr::Values(vec![tuple.0.clone().into()])
})
.collect(),
)),
));
use itertools::Itertools;
Ok(crate::orm::film_category::Entity::find()
.filter(filter)
.all(&self.db)
.await?
.into_iter()
.map(|model| {
let key = CategoryFilmCategoryFK(model.category_id.clone());
(key, model)
})
.into_group_map())
}
}
```

#### Example enum

```rust
use crate::orm::sea_orm_active_enums;
use async_graphql::*;
use sea_orm::entity::prelude::*;
#[derive(Debug, Copy, Clone, Eq, PartialEq, EnumIter, DeriveActiveEnum, Enum)]
#[graphql(remote = "sea_orm_active_enums::Rating")]
#[sea_orm(rs_type = "String", db_type = "Enum", enum_name = "Rating")]
pub enum Rating {
#[sea_orm(string_value = "G")]
G,
#[sea_orm(string_value = "PG")]
Pg,
#[sea_orm(string_value = "PG-13")]
Pg13,
#[sea_orm(string_value = "R")]
R,
#[sea_orm(string_value = "NC-17")]
Nc17,
}
```

### Procedural macro mode
TODO: WIP

Based on https://www.sea-ql.org/SeaORM/docs/generate-entity/entity-structure
Loading