f7a56a0bed
Furthermore, submodule link to new public repo
198 lines
4.3 KiB
Markdown
198 lines
4.3 KiB
Markdown
# Kandimat Backend
|
|
|
|
The kandimat backend consists of a postgres database and a [postgraphile](https://www.graphile.org/postgraphile/introduction/) instance.
|
|
Postgraphile generates a Graphql backend based on the underlying postgres schema.
|
|
|
|
## Introduction
|
|
|
|
### Basic structure
|
|
|
|
There are three "data" tables:
|
|
* category
|
|
* question
|
|
* answer
|
|
|
|
Questions can belong to categories.
|
|
Answers belong to questions and candidates.
|
|
|
|
### User management
|
|
|
|
There are four types of roles:
|
|
* editor
|
|
* candidate
|
|
* user
|
|
* anonymous
|
|
|
|
Editors handle questions and categories, candidates handle their specific answers and users are only important
|
|
in setups where there is no public access to the data.
|
|
|
|
#### Authentication
|
|
|
|
Authentication is handled via jwt.
|
|
The kandimat setup roughly follows the instructions in the [postgraphile docu](https://www.graphile.org/postgraphile/postgresql-schema-design/#authentication-and-authorization).
|
|
|
|
## Manually test the backend
|
|
|
|
To test the backend manually an (enhanced) graphiql instance is started in dev mode.
|
|
To access it navigate to http://localhost:5433/graphiql.
|
|
|
|
#### Authenticate (or how to pose as a member of one of the roles)
|
|
|
|
To pose as one of the three roles authenticate as
|
|
* `erika@musterman.de` (editor)
|
|
* `max@mustermann.de` (candidate)
|
|
* `happy@user.de` (normal user)
|
|
|
|
The password is always "password".
|
|
|
|
Use following graphQL mutation to get the jwtToken of an editor:
|
|
```
|
|
mutation Authenticate {
|
|
__typename
|
|
authenticate(input: {email: "erika@mustermann.de", password: "password"}) {
|
|
jwtToken
|
|
}
|
|
}
|
|
```
|
|
|
|
The jwtToken in the response has to be added to the headers in the following way:
|
|
```
|
|
{
|
|
"Authorization": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiY2FuZHltYXRfY2FuZGlkYXRlIiwicGVyc29uX2lkIjoyLCJleHAiOjE1OTEwNDgzMzgsImlhdCI6MTU5MDg3NTUzNywiYXVkIjoicG9zdGdyYXBoaWxlIiwiaXNzIjoicG9zdGdyYXBoaWxlIn0.21Lu51_suJ5O2RU-UKN2Y6fvKw4SYe-oqx_QqlU0-GE"
|
|
}
|
|
```
|
|
|
|
#### Query the data tables
|
|
|
|
This is possible as member of any role, including no role (anonymous).
|
|
|
|
You can use the schema to get familiar with possible queries.
|
|
As an example, here is a query which retrieves all questions including the category they belong to:
|
|
```graphql
|
|
{
|
|
allQuestions {
|
|
nodes {
|
|
categoryByCategoryId {
|
|
title
|
|
description
|
|
}
|
|
text
|
|
description
|
|
}
|
|
}
|
|
}
|
|
```
|
|
Example response:
|
|
```json
|
|
{
|
|
"data": {
|
|
"allQuestions": {
|
|
"nodes": [
|
|
{
|
|
"categoryByCategoryId": {
|
|
"title": "Umwelt",
|
|
"description": "Themen rund um Naturschutz usw."
|
|
},
|
|
"text": "Was sagen Sie zur 10H Regel?",
|
|
"description": "In Bayern dürfen Windräder nur ..."
|
|
}
|
|
]
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
#### Creating new users
|
|
|
|
aka "register"
|
|
|
|
Only possible if no bearer token is set in the headers.
|
|
```graphql
|
|
mutation Register {
|
|
registerPerson(input: {firstName: "Ford", lastName: "Prefect", email: "ford@prefect.com", password: "password"}) {
|
|
person {
|
|
id
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
#### Creating questions
|
|
|
|
This is only possible as "editor". Use the following mutation:
|
|
```
|
|
mutation CreateQuestion($text: String!) {
|
|
createQuestion(input: {question: {text: $text}}) {
|
|
question {
|
|
text
|
|
id
|
|
}
|
|
}
|
|
}
|
|
```
|
|
with the variables
|
|
```
|
|
{
|
|
"text": "Die Antwort auf die Frage nach dem Leben, dem Universum und dem ganzen Rest?"
|
|
}
|
|
```
|
|
|
|
#### Creating categories
|
|
|
|
This is only possible as "editor".
|
|
|
|
Mutation:
|
|
```
|
|
mutation CreateCategory($title: String!) {
|
|
createCategory(input: {category: {title: $title}}) {
|
|
category {
|
|
title
|
|
id
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
Variables
|
|
```
|
|
{
|
|
"title": "Verkehr"
|
|
}
|
|
```
|
|
|
|
|
|
##### Creating answers
|
|
|
|
This is only possible as "candidate".
|
|
Also the `personId` needs to be `2` (the id of Max Mustermann).
|
|
It is impossible for a candidate to pose as a different candidate when answering a question.
|
|
|
|
Mutation:
|
|
```
|
|
mutation CreateAnswer($position: Int!, $questionId: Int!, $personId: Int!) {
|
|
createAnswer(input: {answer: {position: $position, questionId: $questionId, personId: $personId}}) {
|
|
answer {
|
|
position
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
```
|
|
|
|
Variables
|
|
```
|
|
{
|
|
"questionId": 1,
|
|
"personId": 2,
|
|
"position": 2
|
|
}
|
|
```
|
|
Also change the `personId` to see that the candidate can only answer for themself.
|
|
|
|
|
|
##### Updating, Deleting
|
|
|
|
Works the same as creating and has the same restrictions for the specific roles.
|
|
The exact mutations can be inferred looking at the schema definitions.
|