From ab7362436682536a31419fd59c8ebaa55c2fc700 Mon Sep 17 00:00:00 2001 From: steve-chavez Date: Mon, 8 Apr 2024 11:30:29 -0500 Subject: [PATCH] docs: add architecture diagram --- ARCHITECTURE.md | 67 +--------------------------------------- docs/_diagrams/arch.uml | 56 +++++++++++++++++++++++++++++++++ docs/_static/arch.png | Bin 0 -> 48660 bytes nix/tools/docs.nix | 3 ++ 4 files changed, 60 insertions(+), 66 deletions(-) create mode 100644 docs/_diagrams/arch.uml create mode 100644 docs/_static/arch.png diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index e0933c8dd..9f01eda61 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -1,68 +1,3 @@ # Architecture -This document describes the high-level architecture of PostgREST. - -## Bird's Eye View - -```haskell -postgrest :: Request -> Either Error SQLStatement -> Response -``` - -On the highest level, PostgREST processes an HTTP request, if it's accepted it builds a SQL statement for it, executes it, and produces a response. - -## Code Map - -This section talks briefly about various important modules. - -The starting point of the program is `main/Main.hs`, which calls `src/PostgREST/CLI.hs` which then calls `src/PostgREST/App.hs`. - -`App.hs` is then in charge of composing the different modules. - -### ApiRequest.hs - -PostgREST operates over two types of resources: database relations(tables or views) and database functions; providing different representations(depending on the media type) -for them. - -This module is in charge of representing the operation over an `ApiRequest` type. It parses the URL querystring following PostgREST syntax, the request headers, and the request body -(if possible it avoids parsing the body and sends it directly to the db). - -A request might be rejected at this level if it's invalid, e.g. providing an unknown media type to PostgREST or using an unknown HTTP method. - -### Plan.hs - -Using the Schema Cache, this module enables more complex functionality(like resource embedding) by enriching the ApiRequest. It generates Plan types(`ReadPlan`, `MutatePlan`) -that then will be used to generate a SQL statement. - -A request might be rejected at this level if it's invalid, e.g. by doing resource embedding on a nonexistent resource. - -An OPTIONS request doesn't require a plan to be generated. - -### Query.hs - -This module constructs single SQL statements that can be parametrized and prepared. Only at this stage a PostgreSQL connection from the pool is used. - -A query might fail(and be rollbacked) at this level if it doesn't comply to certain conditions, e.g. by not returning a single row when a ``Accept: application/vnd.pgrst.object`` header is specified. - -An OPTIONS request doesn't require a query to be executed. - -### Response.hs - -This module constructs the HTTP response body with the right headers. - -It builds the OpenAPI response using the schema cache. - -### Auth.hs - -This module provides functions to deal with JWT authorization. - -### SchemaCache.hs - -This queries the PostgreSQL system catalogs and caches the metadata into a SchemaCache type, - -### AppState.hs - -The state of the App which is kept across requests. - -This spawns threads which are used to execute concurrent jobs. - -Jobs include connection recover and a listener for the PostgreSQL LISTEN command. +![PostgREST Architecture](docs/_static/arch.png) diff --git a/docs/_diagrams/arch.uml b/docs/_diagrams/arch.uml new file mode 100644 index 000000000..0a2d7d729 --- /dev/null +++ b/docs/_diagrams/arch.uml @@ -0,0 +1,56 @@ +@startuml + +:user: + +package "PostgREST" { + () HTTP as HTTPAPI + HTTPAPI - [Auth] + [Auth] -r.> [ApiRequest] + [ApiRequest] -r.> [Plan] + [Plan] -r.> [Query] + [Query] - () "Connection Pool" : "\t" + [Plan] -u-> [Schema Cache]:uses + [Query] -u-> [Schema Cache]:uses + [Schema Cache] <- () Listener : reloads "\t" + () HTTP as HTTPADMIN + [Admin] - () HTTPADMIN + + /'HTTPADMIN <-r[hidden]- [Schema Cache] : "\t\t\t"'/ +} + + +database "PostgreSQL" { + node Authorization { + rectangle "Roles, GRANT, RLS" + } + node API { + rectangle "Functions, Views" + } + rectangle "Tables, extensions" as texts + API -d- texts +} + +HTTPAPI <.l- user : "\t" +operator .d-> HTTPADMIN + +Listener -r.> "PostgreSQL" +"Connection Pool" -r.> "PostgreSQL" : "\t\t" + + +note bottom of Auth + Authenticates the user request +end note + +note bottom of ApiRequest + Parses the URL syntax +end note + +note bottom of Query + Generates the SQL +end note + +note top of Listener + LISTEN session +end note + +@enduml diff --git a/docs/_static/arch.png b/docs/_static/arch.png new file mode 100644 index 0000000000000000000000000000000000000000..f06ea0b813654a8735e494792c2ceedcccb64c73 GIT binary patch literal 48660 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 XAwelubP0l+XkKry(MO literal 0 HcmV?d00001 diff --git a/nix/tools/docs.nix b/nix/tools/docs.nix index b015dfbb0..62e031da9 100644 --- a/nix/tools/docs.nix +++ b/nix/tools/docs.nix @@ -7,6 +7,7 @@ , python3Packages , texlive , writers +, plantuml }: let selectPythonPackages = ps: [ @@ -87,6 +88,8 @@ let '' ${pdflatex}/bin/pdflatex -halt-on-error -output-directory="$tmpdir" db.tex ${imagemagick}/bin/convert -density 300 "$tmpdir/db.pdf" ../_static/db.png + + ${plantuml}/bin/plantuml arch.uml -o ../_static ''; server =