The goal of polity is to create a - theoretically - global real-time decision platform for political parties, individuals and public bodies.
Our Goal & Figma Clickdummy π
Current development state - prototype (daily database reset)
Message tobias.hassebrock@gmail.com to gain access to the Figma Design files.
You are heartly welcome to collaborate on the project. I created a few tasks in the project section. In case of any questions reach out to me. Besides that, I am open to any kind of new feature which might benefit public decision making.
Discord server:
πIssues
πProjects
Contact:
tobias.hassebrock@gmail.com
Drop me a message/pull request here on GitHub or per mail if you are interested in supporting the project.
- Clone the repo
git clone https://github.com/Donnerstagnacht/polity.git
- Install Node-Js
https://nodejs.org/en/
- Install NPM packages
npm install
- Set up local development for supabase
https://supabase.com/docs/guides/cli/local-development Enable webhooks in the supabase dashboard or create a "supabase_functions" schema manually. Update environment variables (supabaseProjectUrl, supabaseAnonKey, supabaseRoleLevelSecurityKeys VAPID_Keys ) in - src/environments/environment.ts - supabase/migrations/<all files with webhook> - the .bat files Update redirect links in the Auth Service. Optional: Set up a remote environment on supabase.com
- Start Angular dev server
ng serve
Polity is based 100% on OpenSource technologies and is self-hostable.
- Backend: Supabase
- Frontend: Angular
- Frontend: State Management is self written: Angular Signals
- UI-library: Taiga UI
- e2e Testing: Cypress
Data is queried mostly by PostgreSQL functions. The functions are executed in a wrapper service that handles loading and UI flags.
Additionally, a Supabase real-time subscription is used to update the store (and therefore the UI) pessimistically whenever subscribed data in the database is changed.
To display data to the user, the data is stored in Angular Signals.
*** Data in ***
User --> PostgreSQL function --> Database --> UI
*** Data out: ***
Database --> Query Function and/or real-time subscription --> Signal Store--> UI --> User
Summary: Most business logic is transferred to powerful PostgreSQL functions.
Angular files are organized in the src/app
. Subfolders mirror app features and mostly correspond to the available routes.
Cypress files are organized in the Cypress/e2e
folder and the file order should be identical to the test execution order.
Supabase files are organized in the supabase/migrations
folder with subdirectories according to features. These files must have a prefix that determines the migration/execution order.
Most important project directories and files:
.
βββ cypress: End-2-End tests organized by features
β βββ e2e: End to end tests ordered by execution
β β βββ Feature: according to front end
β βββ fixtures: specific test variables
β βββ support
β βββ commands: Definition of cypress commonands
β βββ index.d.ts: Signature and types of cypress commands
β
βββ seed_and_test_data
β βββ Data used to seed database and set test states
β
βββ src
β βββ app: Frontend
β β βββ auth: Authentication functionality
β β βββ features: Features organized by routing
β β β βββ feature: One feature module
β β β βββ components: Presentation logic
β β β βββ routes: Routes of the feature
β β β βββ store: Front end store instantiations
β β β βββ actions: Link between database and frontend store
β β β βββ guards: Protecting routes
β β βββ landing: Non-authenticated features (landing website before sign-in)
β β βββ navigation
β β βββ signal-store: Frontend data store
β β βββ ui: Pure UI components
β βββ assets: static files
β βββ environments: Supabase parameter
β βββ styles-global: Global style sheets
β βββ Global component styles
β βββ Polity utility styles
β βββ TUI extensions & overwrites
β
βββ supabase: Supabase / Backend, directories by feature, but file execution by prefix
β βββ Functions: Supabase edge functions
β βββ migrations
β β βββ feature: according to frontend feature
β β βββ Schemas and other
β β βββ Database types/ Enums/Table definitions
β β βββ schema/Table definitions
β β βββ Database functions/queries
β β βββ Database transactions (calling other functions)
β β βββ Database seed
β βββ types
β βββ supabase.public.modified.ts: Overwritten supabase types since generation is not always correct
β βββ supabase.authenticated.shorthand-types.ts: Short-handed supabase types for easier usage in Front End code
β βββ supabase.ts: Auto-generated supabase types
β
βββ copy_sql_files_to_migration_folder: A windows bat file to copy supabase files into the migration directory so that automatic migration can be executed
βββ package.json: Project dependencies
βββ cypress.config.ts: Cypress configuration
Polity includes an angular schematic package which can be used to generate store modules or database elements/features. This can speed up development and ease the implementation of the strict and required naming conventions for PostgreSQL migration files.
To start with polity schematics run:
-
Switch to schematics directory
src\polity-signal-store\schematics\supabase
-
Build the schematic
npm run build
-
Switch to the root component or open a new terminal
cd ../../../..
-
Link the schematic
npm link src\polity-signal-store\schematics\supabase --force
-
Use the schematic
ng g db:<schematic_name> <arguments> ng g db:array profiles read_profiles src/app/features
Available commands
Comman | Description | Arguments |
---|---|---|
array | Generating an angular signal array store. | name rpc_name path |
object | Generating an angular signal object store. | name rpc_name path |
enum | Generates a postgres enum. | name supabse_feature_id path |
feature | Generates a postgres feature (table, policies, CRUD functions). | name supabse_feature_id path |
policies | Generates a postgres policy set. | name supabse_feature_id path |
rpc | Generates a postgres rpc. | name supabse_feature_id path |
storage | Generates a postgres storage. | name supabse_feature_id path |
table | Generates a postgres table. | name supabse_feature_id path |
trigger | Generates a postgres trigger. | name supabse_feature_id path |
PostgreSQL code and code or variables that are used to call PostgreSQL functions should be written in lowercase with underscores e.g. a_variable_for_a_postgres functions.
. Parameters and return table variables of postgres functions should be prefixed with a "_" (e.g. _parameter
) to avoid ambitous naming conflicts. Prefer table returns and table composite type above defining custom types, since custom types do not allow constraints (e.g. not null and supabase type generation adds "| null" to each property.
Postgres migration files are applied in descending sequential order, must be unique and named like timestamp_name.sql
. The codestamp must have a minimum of 6 digits and a maximum of 14 digits. After that an "_" must follow. Files which determine the initial setup do not use a timestamp. Instead, a numbercode is used to gurantee a certain execution order.
numbercode = 000 + two digit number implying the postgres object type _ three digit running number _ six digit number determining the featuere.
The two digit number code is mapped to postgres types to ensure that postgres objects are created in the correct order (since objects on lower table lines depend on higher table lines):
Code | Description |
---|---|
00 | Pre migration tasks (types, resets, schemas...) |
01 | Enums |
10 | Tables, indexes, row level security settings |
20 | Row level security policies |
30 | Triggers and trigger related functions |
60 | Functions |
80 | Transactions (functions that depend on other functions) |
98 | Post migration tasks (bug fixes...) |
99 | Database/table seeding |
The three digit running number is simply a running number except for the Standalone Functions (Code 60). Here, functions should order by Create, read, update, delete and others.
Code | Description |
---|---|
0xx | Functions for create operations (insert or upsert) |
1xx | Functions for read operations |
2xx | Functions for update operations |
3xx | Functions for delete operations |
4xx | Other functions (checks, increments, decrements...) |
The feature is a unique 6 diigt code. Main features should be "thousand steps" (for example: 001000 = profiles).
The leading "000" prefix is added by a scipt.
For purely frontend-related variables code camelCase is used e.g. aVariableForTheFrontend
.
HTML elements used for testing should contain the attribute [attr.data-cy]="'element-name'"
. The attribute name uses "-" seperators.
In general, use speaking names and choose a longer more specific name over a short unspecific name.
Document public functions (especially in services).
Polity applies a database security layer concept with the following layers
Layername | Description |
---|---|
public | A public layer available to all roles. An API is generated automatically. |
authenticated | A layer available to the "authenticated" role (logged in users). An API is generated automatically. |
hidden | A hidden layer that can only be accessed by functions (e.g. available to "authenticated" role but no API generated. This includes all tables. |
security | A hidden layer that can only be accessed by the postgres role. It is used to store helper functions for row level security checks. |
postgres | A hidden layer that can only be accessed by the postgres role. Used to store triggers. |
Additionally, each table is secured with row level security policies. Row level security policies check data access for each row. While this architecture disables the quick usage of the autogenerated supabase api, it decreases the attack vector to the parameters of functions of the public/authenticated layer. To strengthen this even further, it should be avoided to pass the id of the authenticated user as argument to a function. Instead, supabase heper functions like auth.uid() should be used directly in the database function
Functions from the hidden layer should always raise an error if they fail.
The project uses no unit tests so far. However, all features should be committed with a working Cypress end-to-end test that covers at least the expected positive base-line scenario, e.g. the workflows possible in the GUI
To facilitate database security, negative scenarios (e.g. database calls which should not return results because they are not allowed) should be tested using Cypress api call tests.
This is not mandatory - but a guideline:
- Check naming conventions
- Document public functions
- Implement end-to-end-tests
- Pass all existing end-to-end-test to ensure code compatibility
Run ng serve
for a dev server. Navigate to http://localhost:4200/
.
Run supabase start
for a local dev server. Navigate to http://localhost:54323/
. Follow the local development guide of Supabase to reset or reload your environment
npm run e2e_open
to open the Cypress test runner and execute tests without resetting your Supabase environment. Run npm run e2e_run
to run the e2e test in your command line.