diff --git a/.dockerignore b/.dockerignore deleted file mode 100644 index 1bd7bf6..0000000 --- a/.dockerignore +++ /dev/null @@ -1,16 +0,0 @@ -node_modules -Dockerfile* -docker-compose* -.dockerignore -.git -.gitignore -README.md -LICENSE -.vscode -Makefile -helm-charts -.env -.env.local -.idea -.vscode -coverage* \ No newline at end of file diff --git a/.eslintrc.json b/.eslintrc.json index 955a0a1..bf64e90 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -12,5 +12,6 @@ "noUnusedLocals": true, "noUnusedParameters": true }, - "rules": {} + "rules": {}, + "ignorePatterns": ["node_modules", "documentation"] } diff --git a/.github/workflows/static.yml b/.github/workflows/static.yml new file mode 100644 index 0000000..5028034 --- /dev/null +++ b/.github/workflows/static.yml @@ -0,0 +1,43 @@ +# Simple workflow for deploying static content to GitHub Pages +name: Deploy static documentation to Pages + +on: + # Runs on pushes targeting the default branch + push: + branches: ['main'] + + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages +permissions: + contents: read + pages: write + id-token: write + +# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. +# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. +concurrency: + group: 'pages' + cancel-in-progress: false + +jobs: + # Single deploy job since we're just deploying + deploy: + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Setup Pages + uses: actions/configure-pages@v5 + - name: Upload artifact + uses: actions/upload-pages-artifact@v3 + with: + # Upload entire repository + path: './documentation/generated' + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 diff --git a/.gitignore b/.gitignore index fe8805a..621d643 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ .env.local .VSCodeCounter node_modules +.DS_Store diff --git a/.husky/pre-commit b/.husky/pre-commit index 00a9d3c..3334bfb 100644 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -1 +1 @@ -bun lint-staged +bun lint-staged \ No newline at end of file diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..b40af7a --- /dev/null +++ b/.prettierignore @@ -0,0 +1 @@ +documentation/generated diff --git a/README.md b/README.md index 9625b7c..0900fd9 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,19 @@ # neuland.app-backend -GraphQL API for the neuland.app and Neuland Next app using Apollo Server and Bun. +Neuland API uses GraphQL to provide a flexible and powerful API for neuland.app and Neuland Next. + +## API Documentation + +The API documentation is available [here](https://neuland-ingolstadt.github.io/neuland.app-backend/). + +### Endpoint + +https://api.neuland.app + +> [!IMPORTANT] +> Using the provided API endpoint outside of the neuland.app and Neuland Next app is not allowed. + +## Development ### Getting Started @@ -9,12 +22,15 @@ bun i ``` Set the necessary environment variables in a `.env.local` file. +You can use the `.env.local.example` file as a template. + +````bash ### Start the Development Server ```bash bun start -``` +```` > [!TIP] > Use `bun dev` to start the development server with hot reloading. @@ -31,8 +47,19 @@ http://localhost:4000/ - `index.ts` - The entry point for the API. - `src/` - Contains the source code for the GraphQL API. -- `src/data/` - Contains the static data for the API. -- `src/resolvers/` - Contains the resolvers for the API. -- `src/schema/` - Contains the type definitions for the API. +- `src/schema.gql` - Contains the GraphQL schema. +- `src/data/` - Contains the static data. +- `src/resolvers/` - Contains the resolvers. - `src/types/` - Contains the types for TypeScript. -- `src/utils/` - Contains utility functions for the API. +- `src/scrapers/` - Contains the scrapers for the data used by the resolvers. +- `src/utils/` - Contains utility functions. + +#### Commiting Changes + +We use husky to lint and test the code before it is committed, that's why we recommend using the ESLint and Prettier extensions in VSCode. + +When changing the schema file husky will automatically generate the html documentation. Therefore, you need to have the `spectaql` package installed globally. + +```bash +npm install -g spectaql +``` diff --git a/docker-compose.yaml b/docker-compose.yaml deleted file mode 100644 index 54dec8a..0000000 --- a/docker-compose.yaml +++ /dev/null @@ -1,13 +0,0 @@ -version: '3.8' - -services: - app: - build: - context: . - dockerfile: Dockerfile - environment: - - NODE_ENV=production - ports: - - "4000:4000" - volumes: - - ./.env.local:/usr/src/app/.env.local diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..26238bb --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,13 @@ +version: '3.8' + +services: + app: + build: + context: . + dockerfile: Dockerfile + environment: + - NODE_ENV=production + ports: + - '4000:4000' + volumes: + - ./.env.local:/usr/src/app/.env.local diff --git a/documentation/config.yml b/documentation/config.yml new file mode 100644 index 0000000..4bbbe8c --- /dev/null +++ b/documentation/config.yml @@ -0,0 +1,27 @@ +introspection: + schemaFile: + - ./src/schema.gql + +servers: + - url: https://api.neuland.app + description: Neuland API Server + production: true +info: + title: Neuland API Documentation + description: + Neuland API uses GraphQL to provide a flexible and powerful API for neuland.app and Neuland Next. + This documentation provides a detailed overview of the API and its capabilities. + Using the provided API endpoint outside of the neuland.app and Neuland Next app is not allowed. + termsOfService: 'https://assets.neuland.app/datenschutzerklaerung-app.pdf' + contact: + name: Neuland Ingolstadt e.V. + email: info@neuland-ingolstadt.de + url: https://github.com/neuland-ingolstadt/neuland.app-backend + license: + name: AGPL-3.0 + url: 'https://www.gnu.org/licenses/agpl-3.0.html' +spectaql: + faviconFile: ./documentation/icon.svg + theme: spectaql + logoFile: ./documentation/icon.svg + targetDir: ./documentation/generated diff --git a/documentation/generated/images/favicon.svg b/documentation/generated/images/favicon.svg new file mode 100644 index 0000000..db8c27d --- /dev/null +++ b/documentation/generated/images/favicon.svg @@ -0,0 +1,106 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + diff --git a/documentation/generated/images/logo.svg b/documentation/generated/images/logo.svg new file mode 100644 index 0000000..db8c27d --- /dev/null +++ b/documentation/generated/images/logo.svg @@ -0,0 +1,106 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + diff --git a/documentation/generated/index.html b/documentation/generated/index.html new file mode 100644 index 0000000..8486099 --- /dev/null +++ b/documentation/generated/index.html @@ -0,0 +1,1846 @@ + + + + + + + Neuland API Documentation + + + + +
+ +
+
+ +
+
+

Neuland API Documentation

+
+
+
+

Neuland API uses GraphQL to provide a flexible and powerful API for neuland.app and Neuland Next. This documentation provides a detailed overview of the API and its capabilities. Using the provided API endpoint outside of the neuland.app and Neuland Next app is not allowed.

+
+
+ + + +
+
API Endpoints
+
https://api.neuland.app
+
+
+
+
+

Queries

+
+

+ announcements +

+
+
+
+
Description
+

Get the current announcements

+
+
+
+
+
+
+
Response
+

Returns [Announcement!]! +

+
+
+
+

Example

+
+
Query
+ + +
query announcements {
+  announcements {
+    title {
+      ...MultiLanguageStringFragment
+    }
+    description {
+      ...MultiLanguageStringFragment
+    }
+    startDateTime
+    endDateTime
+    priority
+    url
+  }
+}
+
+ + +
+
+
Response
+ + +
{
+  "data": {
+    "announcements": [
+      {
+        "title": MultiLanguageString,
+        "description": MultiLanguageString,
+        "startDateTime": "xyz789",
+        "endDateTime": "xyz789",
+        "priority": 123,
+        "url": "abc123"
+      }
+    ]
+  }
+}
+
+ + +
+
+
+
+
+
+ Queries +
+

+ bus +

+
+
+
+
Description
+

Get the current bus departures at a specific station

+
+
+
+
+
+
+
Response
+

Returns [Bus!]! +

+
+
+
Arguments
+ + + + + + + + + + + + + +
NameDescription
+ station - String! + +
+
+
+
+

Example

+
+
Query
+ + +
query bus($station: String!) {
+  bus(station: $station) {
+    route
+    destination
+    time
+  }
+}
+
+ + +
+
+
Variables
+ + +
{"station": "xyz789"}
+
+ + +
+
+
Response
+ + +
{
+  "data": {
+    "bus": [
+      {
+        "route": "xyz789",
+        "destination": "xyz789",
+        "time": "abc123"
+      }
+    ]
+  }
+}
+
+ + +
+
+
+
+
+
+ Queries +
+

+ charging +

+
+
+
+
Description
+

Get the current electric vehicle charging stations in Ingolstadt

+
+
+
+
+
+
+
Response
+

Returns [ChargingStation!]! +

+
+
+
+

Example

+
+
Query
+ + +
query charging {
+  charging {
+    id
+    name
+    address
+    city
+    latitude
+    longitude
+    available
+    total
+    freeParking
+    operator
+  }
+}
+
+ + +
+
+
Response
+ + +
{
+  "data": {
+    "charging": [
+      {
+        "id": 987,
+        "name": "abc123",
+        "address": "abc123",
+        "city": "xyz789",
+        "latitude": 123.45,
+        "longitude": 987.65,
+        "available": 123,
+        "total": 987,
+        "freeParking": false,
+        "operator": "abc123"
+      }
+    ]
+  }
+}
+
+ + +
+
+
+
+
+
+ Queries +
+

+ clEvents +

+
+
+
+
Description
+

Get the campus life events

+
+
+
+
+
+
+
Response
+

Returns [ClEvent!]! +

+
+
+
+

Example

+
+
Query
+ + +
query clEvents {
+  clEvents {
+    id
+    organizer
+    title
+    begin
+    end
+  }
+}
+
+ + +
+
+
Response
+ + +
{
+  "data": {
+    "clEvents": [
+      {
+        "id": 4,
+        "organizer": "abc123",
+        "title": "abc123",
+        "begin": "abc123",
+        "end": "xyz789"
+      }
+    ]
+  }
+}
+
+ + +
+
+
+
+
+
+ Queries +
+

+ food +

+
+
+
+
Description
+

Get the meal plan for a specific restaurant

+
+
+
+
+
+
+
Response
+

Returns [Food!]! +

+
+
+
Arguments
+ + + + + + + + + + + + + +
NameDescription
+ locations - [String!] + +
+
+
+
+

Example

+
+
Query
+ + +
query food($locations: [String!]) {
+  food(locations: $locations) {
+    timestamp
+    meals {
+      ...MealFragment
+    }
+  }
+}
+
+ + +
+
+
Variables
+ + +
{"locations": ["abc123"]}
+
+ + +
+
+
Response
+ + +
{
+  "data": {
+    "food": [
+      {
+        "timestamp": "xyz789",
+        "meals": [Meal]
+      }
+    ]
+  }
+}
+
+ + +
+
+
+
+
+
+ Queries +
+

+ parking +

+
+
+
+
Description
+

Get the current parking data in Ingolstadt

+
+
+
+
+
+
+
Response
+

Returns a ParkingData +

+
+
+
+

Example

+
+
Query
+ + +
query parking {
+  parking {
+    updated
+    lots {
+      ...ParkingLotFragment
+    }
+  }
+}
+
+ + +
+
+
Response
+ + +
{
+  "data": {
+    "parking": {
+      "updated": "abc123",
+      "lots": [ParkingLot]
+    }
+  }
+}
+
+ + +
+
+
+
+
+
+ Queries +
+

+ train +

+
+
+
+
Description
+

Get the current train departures at a specific station

+
+
+
+
+
+
+
Response
+

Returns [Train!]! +

+
+
+
Arguments
+ + + + + + + + + + + + + +
NameDescription
+ station - String! + +
+
+
+
+

Example

+
+
Query
+ + +
query train($station: String!) {
+  train(station: $station) {
+    name
+    destination
+    plannedTime
+    actualTime
+    canceled
+    track
+    url
+  }
+}
+
+ + +
+
+
Variables
+ + +
{"station": "xyz789"}
+
+ + +
+
+
Response
+ + +
{
+  "data": {
+    "train": [
+      {
+        "name": "xyz789",
+        "destination": "xyz789",
+        "plannedTime": "abc123",
+        "actualTime": "xyz789",
+        "canceled": true,
+        "track": "abc123",
+        "url": "xyz789"
+      }
+    ]
+  }
+}
+
+ + +
+
+
+
+

Types

+
+

Announcement

+
+
+
+
Description
+

Announcement data to display on top of the apps dashboard

+
+
+
Fields
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Field NameDescription
title - MultiLanguageString! + Title of the announcement in different languages
description - MultiLanguageString! + Description of the announcement in different languages
startDateTime - String! + Start date and time when the announcement is displayed
endDateTime - String! + End date and time when the announcement is displayed
priority - Int! + Priority of the announcement, higher are more important
url - String + URL to the announcement
+
+
+
+
+
Example
+ + +
{
+  "title": MultiLanguageString,
+  "description": MultiLanguageString,
+  "startDateTime": "xyz789",
+  "endDateTime": "xyz789",
+  "priority": 123,
+  "url": "xyz789"
+}
+
+ + +
+
+
+
+
+
+ Types +
+

Boolean

+
+
+
+
Description
+

The Boolean scalar type represents true or false.

+
+
+
+
+
+
+
+
+ Types +
+

Bus

+
+
+
+
Description
+

Charging station data

+
+
+
Fields
+ + + + + + + + + + + + + + + + + + + + + +
Field NameDescription
route - String! + Code of the bus route, like 10, N1, etc.
destination - String! + Destination of the bus route
time - String! + Planned time at the station
+
+
+
+
+
Example
+ + +
{
+  "route": "xyz789",
+  "destination": "abc123",
+  "time": "xyz789"
+}
+
+ + +
+
+
+
+
+
+ Types +
+

ChargingStation

+
+
+
+
Description
+

Charging station data

+
+
+
Fields
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Field NameDescription
id - Int! + Unique identifier of the charging station
name - String! + Name of the charging station
address - String! + Address of the charging station
city - String! + City of the charging station
latitude - Float! + Latitude of the charging station
longitude - Float! + Longitude of the charging station
available - Int! + Number of available charging points
total - Int! + Number of total charging points
freeParking - Boolean + True if the charging is labeled as free parking
operator - String + Operator of the charging station
+
+
+
+
+
Example
+ + +
{
+  "id": 987,
+  "name": "xyz789",
+  "address": "abc123",
+  "city": "xyz789",
+  "latitude": 987.65,
+  "longitude": 123.45,
+  "available": 123,
+  "total": 123,
+  "freeParking": true,
+  "operator": "xyz789"
+}
+
+ + +
+
+
+
+
+
+ Types +
+

ClEvent

+
+
+
+
Description
+

Campus Life Event

+
+
+
Fields
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Field NameDescription
id - ID! + Unique identifier of the event
organizer - String! + Organizer of the event
title - String! + Title of the event in German
begin - String + Begin of the event
end - String + End of the event
+
+
+
+
+
Example
+ + +
{
+  "id": "4",
+  "organizer": "abc123",
+  "title": "abc123",
+  "begin": "abc123",
+  "end": "xyz789"
+}
+
+ + +
+
+
+
+
+
+ Types +
+

Float

+
+
+
+
Description
+

The Float scalar type represents signed double-precision fractional values as specified by IEEE 754.

+
+
+
+
+
Example
+ + +
987.65
+
+ + +
+
+
+
+
+
+ Types +
+

Food

+
+
+
+
Description
+

Provides a list of meals for a specific day

+
+
+
Fields
+ + + + + + + + + + + + + + + + + +
Field NameDescription
timestamp - String! + Date of the meal list
meals - [Meal!] + List of meals
+
+
+
+
+
Example
+ + +
{
+  "timestamp": "xyz789",
+  "meals": [Meal]
+}
+
+ + +
+
+
+
+
+
+ Types +
+

ID

+
+
+
+
Description
+

The ID scalar type represents a unique identifier, often used to refetch an object or as key for a cache. The ID type appears in a JSON response as a String; however, it is not intended to be human-readable. When expected as an input type, any string (such as "4") or integer (such as 4) input value will be accepted as an ID.

+
+
+
+
+
Example
+ + +
4
+
+ + +
+
+
+
+
+
+ Types +
+

Int

+
+
+
+
Description
+

The Int scalar type represents non-fractional signed whole numeric values. Int can represent values between -(2^31) and 2^31 - 1.

+
+
+
+
+
Example
+ + +
987
+
+ + +
+
+
+
+
+
+ Types +
+

Meal

+
+
+
+
Description
+

Meal data

+
+
+
Fields
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Field NameDescription
name - MultiLanguageString! + Name of the meal in different languages
id - ID! + Unique identifier of the meal
category - String! + Category of the meal (main, soup or salad)
prices - Prices! + Prices for different types of customers (student, employee, guest)
allergens - [String] + List of allergens (e.g. gluten, lactose, etc.)
flags - [String] + List of flags (e.g. vegan, vegetarian, etc.)
nutrition - Nutrition + Nutritional values for the meal
variants - [Variation!] + Variants or toppings of the meal, like bread, sauce, etc.
originalLanguage - OriginalLanguage! + Original language of the meal name
static - Boolean! + Static meals are always available, non-static meals are only available on specific days
restaurant - String! + Restaurant where the meal is available (mensa, reimanns or canisius)
+
+
+
+
+
Example
+ + +
{
+  "name": MultiLanguageString,
+  "id": 4,
+  "category": "abc123",
+  "prices": Prices,
+  "allergens": ["abc123"],
+  "flags": ["xyz789"],
+  "nutrition": Nutrition,
+  "variants": [Variation],
+  "originalLanguage": "de",
+  "static": false,
+  "restaurant": "abc123"
+}
+
+ + +
+
+
+
+
+
+ Types +
+

MultiLanguageString

+
+
+
+
Description
+

String in multiple languages (German and English)

+
+
+
Fields
+ + + + + + + + + + + + + + + + + +
Field NameDescription
de - String + German language code
en - String + English language code
+
+
+
+
+
Example
+ + +
{
+  "de": "xyz789",
+  "en": "abc123"
+}
+
+ + +
+
+
+
+
+
+ Types +
+

Nutrition

+
+
+
+
Description
+

Nutritional values for a meal. Currently only available at Mensa. Values are per average portion.

+
+
+
Fields
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Field NameDescription
kj - Float! + Energy in kilojoules
kcal - Float! + Energy in kilocalories
fat - Float! + Fat in grams
fatSaturated - Float! + Saturated fat in grams
carbs - Float! + Carbohydrates in grams
sugar - Float! + Sugar in grams
fiber - Float! + Fiber in grams
protein - Float! + Protein in grams
salt - Float! + Salt in grams
+
+
+
+
+
Example
+ + +
{
+  "kj": 987.65,
+  "kcal": 987.65,
+  "fat": 987.65,
+  "fatSaturated": 123.45,
+  "carbs": 123.45,
+  "sugar": 987.65,
+  "fiber": 987.65,
+  "protein": 123.45,
+  "salt": 123.45
+}
+
+ + +
+
+
+
+
+
+ Types +
+

OriginalLanguage

+
+
+
+
Description
+

Original language of the meal name

+
+
+
Values
+ + + + + + + + + + + + + + + + + +
Enum ValueDescription
+

de

+
German language code
+

en

+
English language code
+
+
+
+
+
Example
+ + +
"de"
+
+ + +
+
+
+
+
+
+ Types +
+

Parent

+
+
+
+
Description
+

Parent meal for a variant meal

+
+
+
Fields
+ + + + + + + + + + + + + + + + + + + + + +
Field NameDescription
id - ID! + Unique identifier of the parent meal
category - String! + Category of the parent meal (main, soup or salad)
name - MultiLanguageString! + Name of the parent meal in different languages
+
+
+
+
+
Example
+ + +
{
+  "id": "4",
+  "category": "xyz789",
+  "name": MultiLanguageString
+}
+
+ + +
+
+
+
+
+
+ Types +
+

ParkingData

+
+
+
+
Description
+

Parking data

+
+
+
Fields
+ + + + + + + + + + + + + + + + + +
Field NameDescription
updated - String! + Timestamp of the last update from the source
lots - [ParkingLot!]! + List of parking lots
+
+
+
+
+
Example
+ + +
{
+  "updated": "abc123",
+  "lots": [ParkingLot]
+}
+
+ + +
+
+
+
+
+
+ Types +
+

ParkingLot

+
+
+
+
Description
+

Parking lot data

+
+
+
Fields
+ + + + + + + + + + + + + + + + + + + + + +
Field NameDescription
name - String! + Name of the parking lot
available - Int! + Number of available parking spaces
priceLevel - Int + Price level of the parking lot between 0 (free) and 3 (expensive)
+
+
+
+
+
Example
+ + +
{
+  "name": "xyz789",
+  "available": 987,
+  "priceLevel": 987
+}
+
+ + +
+
+
+
+
+
+ Types +
+

Prices

+
+
+
+
Description
+

Prices for different types of customers

+
+
+
Fields
+ + + + + + + + + + + + + + + + + + + + + +
Field NameDescription
student - Float! + Price for students
employee - Float! + Price for employees
guest - Float! + Price for guests
+
+
+
+
+
Example
+ + +
{"student": 987.65, "employee": 987.65, "guest": 987.65}
+
+ + +
+
+
+
+
+
+ Types +
+

String

+
+
+
+
Description
+

The String scalar type represents textual data, represented as UTF-8 character sequences. The String type is most often used by GraphQL to represent free-form human-readable text.

+
+
+
+
+
Example
+ + +
"xyz789"
+
+ + +
+
+
+
+
+
+ Types +
+

Train

+
+
+
+
Description
+

Train data

+
+
+
Fields
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Field NameDescription
name - String! + Name of the train
destination - String! + Destination of the train
plannedTime - String! + Planned departure time
actualTime - String! + Actual departure time
canceled - Boolean! + True if the train is canceled
track - String + Track of the train
url - String + URL to the train information
+
+
+
+
+
Example
+ + +
{
+  "name": "xyz789",
+  "destination": "xyz789",
+  "plannedTime": "abc123",
+  "actualTime": "abc123",
+  "canceled": true,
+  "track": "abc123",
+  "url": "xyz789"
+}
+
+ + +
+
+
+
+
+
+ Types +
+

Variation

+
+
+
+
Description
+

Variants of a meal

+
+
+
Fields
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Field NameDescription
name - MultiLanguageString! + Name of the variant in different languages
additional - Boolean! + True if the variant is an additional topping and not a variant of the meal
prices - Prices! + Prices for different types of customers (student, employee, guest)
id - ID! + Unique identifier of the variant
allergens - [String] + List of allergens (e.g. gluten, lactose, etc.)
flags - [String] + List of flags (e.g. vegan, vegetarian, etc.)
nutrition - Nutrition + Nutritional values for the variant
originalLanguage - OriginalLanguage! + Original language of the variant name
static - Boolean! + Static variants are always available, non-static variants are only available on specific days
restaurant - String + Restaurant where the variant is available (mensa, reimanns or canisius)
parent - Parent + Parent meal for a variant meal
+
+
+
+
+
Example
+ + +
{
+  "name": MultiLanguageString,
+  "additional": true,
+  "prices": Prices,
+  "id": "4",
+  "allergens": ["xyz789"],
+  "flags": ["abc123"],
+  "nutrition": Nutrition,
+  "originalLanguage": "de",
+  "static": false,
+  "restaurant": "xyz789",
+  "parent": Parent
+}
+
+ + +
+
+
+
+ +
+
+
+
+ + \ No newline at end of file diff --git a/documentation/generated/javascripts/spectaql.min.js b/documentation/generated/javascripts/spectaql.min.js new file mode 100644 index 0000000..0b7aea0 --- /dev/null +++ b/documentation/generated/javascripts/spectaql.min.js @@ -0,0 +1 @@ +"use strict";function scrollSpy(){var c=5,e=document.querySelector("html"),l=(e&&(e=window.getComputedStyle(e).scrollPaddingTop)&&"string"==typeof e&&"auto"!==e&&e.endsWith("px")&&(c+=parseInt(e.split("px")[0])),"nav-scroll-active"),i=null,d=[];function t(){i=null;var e=document.querySelectorAll("[data-traverse-target]");Array.prototype.forEach.call(e,function(e){d.push({id:e.id,top:e.offsetTop})})}var n=debounce(function(){t(),o()},500),o=debounce(function(){var e,t,n,o,r=function(e){for(var t=e+c,n=0;n=d[n].top&&(!o||t{toggleMenu(),scrollSpy()}); \ No newline at end of file diff --git a/documentation/generated/stylesheets/spectaql.min.css b/documentation/generated/stylesheets/spectaql.min.css new file mode 100644 index 0000000..5859df8 --- /dev/null +++ b/documentation/generated/stylesheets/spectaql.min.css @@ -0,0 +1 @@ +#spectaql{padding:0;margin:0}#spectaql pre{overflow:auto;margin-top:0;margin-bottom:20px}#spectaql pre code{display:block;background:#ccc}#spectaql table{width:100%;table-layout:fixed;text-align:left;border-collapse:collapse}#spectaql table td,#spectaql table th{margin:0;padding:0}#spectaql #introduction .example-section>*,#spectaql .definition-heading,#spectaql .doc-heading,#spectaql .introduction-item-title,#spectaql .operation-heading{overflow:hidden;text-overflow:ellipsis}#spectaql #page{display:flex}#spectaql #page *{box-sizing:border-box}#spectaql #page.drawer-open #sidebar{z-index:1000;transform:translateX(0)}#spectaql #page.drawer-open .drawer-overlay{display:block;background:rgba(0,0,0,.5);z-index:10}#spectaql #sidebar{position:fixed;min-width:250px;max-width:250px;flex-shrink:0;transition:transform .2s ease-out;transform:translateX(-100%);z-index:10;padding-top:20px;background:#fff}@media (min-width:48em){#spectaql #sidebar{position:relative;transform:none}}@media (min-width:64em){#spectaql #sidebar{min-width:300px;max-width:300px}}#spectaql .sidebar-top-container{display:flex;align-items:center;padding:0 20px}#spectaql #mobile-navbar{display:flex;align-items:center;position:sticky;top:0}@media (min-width:48em){#spectaql #mobile-navbar{display:none}}#spectaql .sidebar-open-button{display:flex;align-items:flex-start;margin:0;padding:0;border:none;background:0 0}#spectaql .sidebar-open-button .hamburger{width:16px;height:14px;cursor:pointer}#spectaql .sidebar-open-button .hamburger::after{display:block;content:"";height:2px;background:#222;box-shadow:0 5px 0 #222,0 10px 0 #222}#spectaql .sidebar-open-button .sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}#spectaql .close-button{display:block}#spectaql .close-button .sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}@media (min-width:48em){#spectaql .close-button{display:none}}#spectaql .drawer-overlay{display:none;position:absolute;top:0;left:0;bottom:0;right:0;background:rgba(0,0,0,0)}@media (min-width:48em){#spectaql .drawer-overlay{display:none!important}}#spectaql #nav{display:flex;flex-direction:column;max-height:calc(100vh - 0px);padding:0 20px;padding-bottom:20px;position:sticky;top:0;overflow:auto}#spectaql #logo{margin-right:auto}#spectaql #logo img{display:block;width:100%;max-width:100%}#spectaql .nav-group-items,#spectaql .nav-group-section-items{padding:0;margin:0}#spectaql .nav-group-items>li,#spectaql .nav-group-section-items>li{list-style:none}#spectaql .nav-group-items .nav-group-section-title,#spectaql .nav-group-items>li,#spectaql .nav-group-section-items .nav-group-section-title,#spectaql .nav-group-section-items>li{overflow:hidden;text-overflow:ellipsis}#spectaql .nav-group-section-items{display:none}#spectaql .nav-scroll-expand .nav-group-section-items{display:block}#spectaql #docs{position:relative;margin:0 auto;min-width:100px;max-width:88em;flex-grow:1;flex-shrink:1;padding:20px}@media (min-width:48em){#spectaql .doc-row{display:flex;flex-wrap:wrap}}#spectaql .doc-row .doc-copy,#spectaql .doc-row .doc-examples{width:100%}@media (min-width:48em){#spectaql .doc-row .doc-copy,#spectaql .doc-row .doc-examples{width:50%}}@media (min-width:48em){#spectaql .doc-row .doc-copy{padding-right:20px}}@media (min-width:48em){#spectaql .doc-row .doc-examples{padding-left:20px}}.hljs{display:block;overflow-x:auto;padding:.5em;background:#23241f}.hljs,.hljs-subst,.hljs-tag{color:#f8f8f2}.hljs-emphasis,.hljs-strong{color:#a8a8a2}.hljs-bullet,.hljs-link,.hljs-literal,.hljs-number,.hljs-quote,.hljs-regexp{color:#ae81ff}.hljs-code,.hljs-section,.hljs-selector-class,.hljs-title{color:#a6e22e}.hljs-strong{font-weight:700}.hljs-emphasis{font-style:italic}.hljs-attr,.hljs-keyword,.hljs-name,.hljs-selector-tag{color:#f92672}.hljs-attribute,.hljs-symbol{color:#66d9ef}.hljs-class .hljs-title,.hljs-params{color:#f8f8f2}.hljs-addition,.hljs-built_in,.hljs-builtin-name,.hljs-selector-attr,.hljs-selector-id,.hljs-selector-pseudo,.hljs-string,.hljs-template-variable,.hljs-type,.hljs-variable{color:#e6db74}.hljs-comment,.hljs-deletion,.hljs-meta{color:#75715e}#spectaql{font-family:-apple-system,BlinkMacSystemFont,system-ui,"Segoe UI",Roboto,Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol";font-size:14px;line-height:1.6;background:#fff;color:#222}@media (min-width:32em){#spectaql{font-size:16px}}#spectaql a{color:#0298bf;text-decoration:none}#spectaql a:hover{color:#0182a2}#spectaql a:active,#spectaql a:focus{color:#0298bf}#spectaql code{font-size:.875em;font-family:Consolas,Monaco,Andale Mono,Ubuntu Mono,monospace}#spectaql pre{color:#fff}#spectaql pre code{background:#222}#spectaql pre code,#spectaql pre code.hljs{font-size:.82em;line-height:1.4;padding:15px 20px}#spectaql .doc-heading{line-height:1.2;font-size:2.25em;margin-top:10px}@media (min-width:48em){#spectaql .doc-heading{margin-top:-10px}}#spectaql .close-button{background:0 0;border:none;padding:5px;font-size:16px;font-weight:700;color:#222}#spectaql #introduction{margin-bottom:60px}#spectaql #introduction .example-section:not(.example-section-is-code){margin-bottom:20px}#spectaql #introduction .example-section:not(.example-section-is-code) h5,#spectaql #introduction .example-section:not(.example-section-is-code) p{margin:0;font-size:1em}#spectaql #docs,#spectaql #mobile-navbar,#spectaql #sidebar{padding-top:20px;padding-bottom:20px}@media (min-width:32em){#spectaql #docs,#spectaql #mobile-navbar,#spectaql #sidebar{padding-top:30px;padding-bottom:30px}}@media (min-width:48em){#spectaql #docs,#spectaql #mobile-navbar,#spectaql #sidebar{padding-top:40px;padding-bottom:40px}}#spectaql #docs,#spectaql #mobile-navbar,#spectaql #nav,#spectaql .sidebar-top-container{padding-left:20px;padding-right:20px}@media (min-width:32em){#spectaql #docs,#spectaql #mobile-navbar,#spectaql #nav,#spectaql .sidebar-top-container{padding-left:30px;padding-right:30px}}@media (min-width:48em){#spectaql #docs,#spectaql #mobile-navbar,#spectaql #nav,#spectaql .sidebar-top-container{padding-left:50px;padding-right:50px}}#spectaql #sidebar{padding-bottom:0;background:#fff}#spectaql #sidebar a{color:#222}#spectaql #sidebar a.nav-scroll-active,#spectaql #sidebar a:hover{font-weight:700}#spectaql #sidebar a.nav-scroll-active{color:#222}#spectaql #sidebar a:hover{color:#0182a2}@media (min-width:48em){#spectaql #sidebar{border-right:2px solid #d8d8d8}}#spectaql #mobile-navbar{background:#fff;margin-top:-20px;margin-left:-20px;margin-right:-20px}@media (min-width:32em){#spectaql #mobile-navbar{margin-top:-30px;margin-left:-30px;margin-right:-30px}}#spectaql #mobile-navbar .sidebar-open-button::after{display:block;content:"All Topics";margin-left:10px;color:#222}#spectaql #nav .nav-group{margin-top:20px}#spectaql #nav .nav-group li{margin-bottom:5px}#spectaql #nav .nav-group-title{font-size:.875em;font-weight:400;margin:0 0 6px 0;color:#999}#spectaql #nav .nav-group-section-title{font-size:inherit;margin:0;margin-bottom:5px;font-weight:400}#spectaql #nav .nav-group-section-items{margin-left:.75em}#spectaql .definition,#spectaql .operation{margin-bottom:60px}#spectaql .definition .definition-heading,#spectaql .definition .operation-heading,#spectaql .operation .definition-heading,#spectaql .operation .operation-heading{font-size:1.72em}#spectaql .definition .definition-heading code,#spectaql .definition .operation-heading code,#spectaql .operation .definition-heading code,#spectaql .operation .operation-heading code{font-family:inherit;font-size:inherit;font-weight:inherit}@media (min-width:32em){#spectaql .definition .definition-heading,#spectaql .definition .operation-heading,#spectaql .operation .definition-heading,#spectaql .operation .operation-heading{font-size:2.25em}}#spectaql .definition-group-name,#spectaql .group-heading,#spectaql .operation-group-name{border-top:2px solid #d8d8d8;padding-top:3px;color:#999;font-size:inherit;font-weight:inherit}#spectaql .definition-group-name a,#spectaql .group-heading a,#spectaql .operation-group-name a{color:#999}#spectaql .definition-group-name a:hover,#spectaql .group-heading a:hover,#spectaql .operation-group-name a:hover{font-weight:700}#spectaql .doc-examples{margin-top:20px}#spectaql .doc-examples .example-heading{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}#spectaql .doc-examples .example-section-is-code h5{color:#999;text-transform:uppercase;background:#000;font-size:.75em;font-weight:700;padding:.6em 0 .6em 20px;margin:0;opacity:1}@media (min-width:48em){#spectaql .doc-examples{margin-top:0}}#spectaql .doc-copy p{margin:0 0 20px 0}#spectaql .doc-copy p:last-child{margin-bottom:0}#spectaql .doc-copy table tr th{font-weight:400;border-bottom:2px solid #d8d8d8}#spectaql .doc-copy table tr td{border-bottom:1px solid #e0e0e0}#spectaql .doc-copy table tr.row-has-field-arguments td,#spectaql .doc-copy table tr:last-child td{border-bottom:none}#spectaql .doc-copy table tr td,#spectaql .doc-copy table tr th{padding:5px}#spectaql .doc-copy table tr td:first-child,#spectaql .doc-copy table tr th:first-child{padding-left:0}#spectaql .doc-copy table tr td:last-child,#spectaql .doc-copy table tr th:last-child{padding-right:0}#spectaql .doc-copy .doc-copy-section{margin-bottom:30px}#spectaql .doc-copy .doc-copy-section>h5{margin:0 0 5px 0;font-size:inherit;font-weight:inherit;color:#999}#spectaql .doc-copy .definition-description>h5,#spectaql .doc-copy .definition-properties>h5,#spectaql .doc-copy .operation-description>h5{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}#spectaql .field-arguments{font-size:.875em;background-color:#fafbfc;border:1px solid #e0e0e0;padding:10px;margin-bottom:5px}#spectaql .field-arguments p{margin:10px 0 0 0}#spectaql .field-arguments h5.field-arguments-heading{margin:0;padding:0 0 10px 0;font-weight:inherit;color:#999}#spectaql .field-arguments .field-argument{border-top:1px #e0e0e0 solid;padding:10px 0}#spectaql .field-arguments .field-argument:last-child{padding-bottom:0}#spectaql .field-arguments .field-argument-name{margin:0;font-size:inherit;font-weight:inherit}#spectaql .deprecation-reason{word-break:break-word}#spectaql .deprecation-reason::before{display:inline;content:"Deprecated";padding:2px 5px;margin-right:5px;background:#fed7d8;color:#c60609;font-weight:700;font-size:.875em} \ No newline at end of file diff --git a/documentation/icon.svg b/documentation/icon.svg new file mode 100644 index 0000000..db8c27d --- /dev/null +++ b/documentation/icon.svg @@ -0,0 +1,106 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + diff --git a/index.ts b/index.ts index 84fe577..5768005 100644 --- a/index.ts +++ b/index.ts @@ -4,10 +4,12 @@ import { ApolloServerPluginLandingPageProductionDefault, } from '@apollo/server/plugin/landingPage/default' import { apolloIntegration } from '@chrisenglert/as-integrations-bun' +import { readFileSync } from 'fs' import NodeCache from 'node-cache' import { resolvers } from './src/resolvers' -import { typeDefs } from './src/schema' + +const typeDefs = readFileSync('./src/schema.gql', { encoding: 'utf-8' }) const apolloServer = new ApolloServer({ typeDefs, diff --git a/package.json b/package.json index d74c5fb..a1031a5 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,8 @@ "scripts": { "start": "bun run index.ts", "dev": "bun --hot run index.ts", - "prepare": "husky" + "prepare": "husky", + "docs": "bunx spectaql ./documentation/config.yml" }, "peerDependencies": { "typescript": "^5.0.0" @@ -42,10 +43,14 @@ "typescript": "^5.4.4" }, "lint-staged": { + "**/*.{gql,graphql}": [ + "bun docs", + "git add documentation/generated" + ], "**/*.{js,jsx,ts,tsx}": [ "bunx eslint --fix" ], - "**/*.{js,jsx,ts,tsx,json,yml}": [ + "**/*.{js,jsx,ts,tsx,json,yml,gql,graphql,md}": [ "bunx prettier --write" ] } diff --git a/src/schema.gql b/src/schema.gql new file mode 100644 index 0000000..5decdd2 --- /dev/null +++ b/src/schema.gql @@ -0,0 +1,439 @@ +""" +Charging station data +""" +type ChargingStation { + """ + Unique identifier of the charging station + """ + id: Int! + """ + Name of the charging station + """ + name: String! + """ + Address of the charging station + """ + address: String! + """ + City of the charging station + """ + city: String! + """ + Latitude of the charging station + """ + latitude: Float! + """ + Longitude of the charging station + """ + longitude: Float! + """ + Number of available charging points + """ + available: Int! + """ + Number of total charging points + """ + total: Int! + """ + True if the charging is labeled as free parking + """ + freeParking: Boolean + """ + Operator of the charging station + """ + operator: String +} + +""" +Parking data +""" +type ParkingData { + """ + Timestamp of the last update from the source + """ + updated: String! + """ + List of parking lots + """ + lots: [ParkingLot!]! +} + +""" +Parking lot data +""" +type ParkingLot { + """ + Name of the parking lot + """ + name: String! + """ + Number of available parking spaces + """ + available: Int! + """ + Price level of the parking lot between 0 (free) and 3 (expensive) + """ + priceLevel: Int +} + +""" +Meal data +""" +type Meal { + """ + Name of the meal in different languages + """ + name: MultiLanguageString! + """ + Unique identifier of the meal + """ + id: ID! + """ + Category of the meal (main, soup or salad) + """ + category: String! + """ + Prices for different types of customers (student, employee, guest) + """ + prices: Prices! + """ + List of allergens (e.g. gluten, lactose, etc.) + """ + allergens: [String] + """ + List of flags (e.g. vegan, vegetarian, etc.) + """ + flags: [String] + """ + Nutritional values for the meal + """ + nutrition: Nutrition + """ + Variants or toppings of the meal, like bread, sauce, etc. + """ + variants: [Variation!] + """ + Original language of the meal name + """ + originalLanguage: OriginalLanguage! + """ + Static meals are always available, non-static meals are only available on specific days + """ + static: Boolean! + """ + Restaurant where the meal is available (mensa, reimanns or canisius) + """ + restaurant: String! +} + +""" +Variants of a meal +""" +type Variation { + """ + Name of the variant in different languages + """ + name: MultiLanguageString! + """ + True if the variant is an additional topping and not a variant of the meal + """ + additional: Boolean! + """ + Prices for different types of customers (student, employee, guest) + """ + prices: Prices! + """ + Unique identifier of the variant + """ + id: ID! + """ + List of allergens (e.g. gluten, lactose, etc.) + """ + allergens: [String] + """ + List of flags (e.g. vegan, vegetarian, etc.) + """ + flags: [String] + """ + Nutritional values for the variant + """ + nutrition: Nutrition + """ + Original language of the variant name + """ + originalLanguage: OriginalLanguage! + """ + Static variants are always available, non-static variants are only available on specific days + """ + static: Boolean! + """ + Restaurant where the variant is available (mensa, reimanns or canisius) + """ + restaurant: String + """ + Parent meal for a variant meal + """ + parent: Parent +} + +""" +Nutritional values for a meal. Currently only available at Mensa. Values are per average portion. +""" +type Nutrition { + """ + Energy in kilojoules + """ + kj: Float! + """ + Energy in kilocalories + """ + kcal: Float! + """ + Fat in grams + """ + fat: Float! + """ + Saturated fat in grams + """ + fatSaturated: Float! + """ + Carbohydrates in grams + """ + carbs: Float! + """ + Sugar in grams + """ + sugar: Float! + """ + Fiber in grams + """ + fiber: Float! + """ + Protein in grams + """ + protein: Float! + """ + Salt in grams + """ + salt: Float! +} + +""" +Original language of the meal name +""" +enum OriginalLanguage { + """ + German language code + """ + de + """ + English language code + """ + en +} + +""" +Prices for different types of customers +""" +type Prices { + """ + Price for students + """ + student: Float! + """ + Price for employees + """ + employee: Float! + """ + Price for guests + """ + guest: Float! +} + +""" +Provides a list of meals for a specific day +""" +type Food { + """ + Date of the meal list + """ + timestamp: String! + """ + List of meals + """ + meals: [Meal!] +} + +""" +Parent meal for a variant meal +""" +type Parent { + """ + Unique identifier of the parent meal + """ + id: ID! + """ + Category of the parent meal (main, soup or salad) + """ + category: String! + """ + Name of the parent meal in different languages + """ + name: MultiLanguageString! +} + +""" +Campus Life Event +""" +type ClEvent { + """ + Unique identifier of the event + """ + id: ID! + """ + Organizer of the event + """ + organizer: String! + """ + Title of the event in German + """ + title: String! + """ + Begin of the event + """ + begin: String + """ + End of the event + """ + end: String +} + +""" +Charging station data +""" +type Bus { + """ + Code of the bus route, like 10, N1, etc. + """ + route: String! + """ + Destination of the bus route + """ + destination: String! + """ + Planned time at the station + """ + time: String! +} + +""" +Train data +""" +type Train { + """ + Name of the train + """ + name: String! + """ + Destination of the train + """ + destination: String! + """ + Planned departure time + """ + plannedTime: String! + """ + Actual departure time + """ + actualTime: String! + """ + True if the train is canceled + """ + canceled: Boolean! + """ + Track of the train + """ + track: String + """ + URL to the train information + """ + url: String +} + +""" +Announcement data to display on top of the apps dashboard +""" +type Announcement { + """ + Title of the announcement in different languages + """ + title: MultiLanguageString! + """ + Description of the announcement in different languages + """ + description: MultiLanguageString! + """ + Start date and time when the announcement is displayed + """ + startDateTime: String! + """ + End date and time when the announcement is displayed + """ + endDateTime: String! + """ + Priority of the announcement, higher are more important + """ + priority: Int! + """ + URL to the announcement + """ + url: String +} + +""" +String in multiple languages (German and English) +""" +type MultiLanguageString { + """ + German language code + """ + de: String + """ + English language code + """ + en: String +} + +""" +Root query +""" +type Query { + """ + Get the current parking data in Ingolstadt + """ + parking: ParkingData + """ + Get the current electric vehicle charging stations in Ingolstadt + """ + charging: [ChargingStation!]! + """ + Get the meal plan for a specific restaurant + """ + food(locations: [String!]): [Food!]! + """ + Get the campus life events + """ + clEvents: [ClEvent!]! + """ + Get the current bus departures at a specific station + """ + bus(station: String!): [Bus!]! + """ + Get the current train departures at a specific station + """ + train(station: String!): [Train!]! + """ + Get the current announcements + """ + announcements: [Announcement!]! +} diff --git a/src/schema/index.ts b/src/schema/index.ts deleted file mode 100644 index 568cc37..0000000 --- a/src/schema/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -import type { DocumentNode } from 'graphql' - -import { queryType } from './query' - -export const typeDefs: DocumentNode[] = [queryType] diff --git a/src/schema/query/announcements.ts b/src/schema/query/announcements.ts deleted file mode 100644 index f7e670a..0000000 --- a/src/schema/query/announcements.ts +++ /dev/null @@ -1,17 +0,0 @@ -import gql from 'graphql-tag' - -export const announcementsType = gql` - type Announcement { - title: MultiLanguageString! - description: MultiLanguageString! - startDateTime: String! - endDateTime: String! - priority: Int! - url: String - } - - type MultiLanguageString { - de: String - en: String - } -` diff --git a/src/schema/query/bus.ts b/src/schema/query/bus.ts deleted file mode 100644 index 89c0198..0000000 --- a/src/schema/query/bus.ts +++ /dev/null @@ -1,10 +0,0 @@ -import gql from 'graphql-tag' - -export const busType = gql` - "Charging station data" - type Bus { - route: String! - destination: String! - time: String! - } -` diff --git a/src/schema/query/charging.ts b/src/schema/query/charging.ts deleted file mode 100644 index eb62f7a..0000000 --- a/src/schema/query/charging.ts +++ /dev/null @@ -1,17 +0,0 @@ -import gql from 'graphql-tag' - -export const chargingType = gql` - "Charging station data" - type ChargingStation { - id: Int! - name: String! - address: String! - city: String! - latitude: Float! - longitude: Float! - available: Int! - total: Int! - freeParking: Boolean - operator: String - } -` diff --git a/src/schema/query/cl-events.ts b/src/schema/query/cl-events.ts deleted file mode 100644 index 7fc5ba8..0000000 --- a/src/schema/query/cl-events.ts +++ /dev/null @@ -1,12 +0,0 @@ -import gql from 'graphql-tag' - -export const clEventsType = gql` - "Campus Life Event" - type ClEvent { - id: ID! - organizer: String! - title: String! - begin: String! - end: String! - } -` diff --git a/src/schema/query/food.ts b/src/schema/query/food.ts deleted file mode 100644 index 3558d85..0000000 --- a/src/schema/query/food.ts +++ /dev/null @@ -1,79 +0,0 @@ -import gql from 'graphql-tag' - -export const foodType = gql` - "Meal data" - type Meal { - name: Name - id: ID! - category: String! - prices: Prices! - allergens: [String] - flags: [String] - nutrition: Nutrition - variants: [Variation!] - originalLanguage: OriginalLanguage! - static: Boolean! - restaurant: String - } - - "Variants of a meal" - type Variation { - name: Name! - additional: Boolean! - prices: Prices! - id: ID! - allergens: [String] - flags: [String] - nutrition: Nutrition - originalLanguage: OriginalLanguage! - static: Boolean! - restaurant: String - parent: Parent - variants: [Variation] - } - - "Name of a meal in different languages" - type Name { - de: String! - en: String! - } - - "Nutritional values for a meal" - type Nutrition { - kj: Float! - kcal: Float! - fat: Float! - fatSaturated: Float! - carbs: Float! - sugar: Float! - fiber: Float! - protein: Float! - salt: Float! - } - - "Original language of the meal name" - enum OriginalLanguage { - de - en - } - - "Prices for different types of customers" - type Prices { - student: Float! - employee: Float! - guest: Float! - } - - "Provides a list of meals for a specific day" - type Food { - timestamp: String! - meals: [Meal!] - } - - "Parent meal for a variant meal" - type Parent { - id: ID! - category: String! - name: Name! - } -` diff --git a/src/schema/query/index.ts b/src/schema/query/index.ts deleted file mode 100644 index 579ce8c..0000000 --- a/src/schema/query/index.ts +++ /dev/null @@ -1,29 +0,0 @@ -import gql from 'graphql-tag' - -import { announcementsType } from './announcements' -import { busType } from './bus' -import { chargingType } from './charging' -import { clEventsType } from './cl-events' -import { foodType } from './food' -import { parkingType } from './parking' -import { trainType } from './train' - -export const queryType = gql` - ${chargingType} - ${parkingType} - ${foodType} - ${clEventsType} - ${busType} - ${trainType} - ${announcementsType} - "Root query" - type Query { - parking: ParkingData - charging: [ChargingStation!]! - food(locations: [String!]): [Food!]! - clEvents: [ClEvent!]! - bus(station: String!): [Bus!]! - train(station: String!): [Train!]! - announcements: [Announcement!]! - } -` diff --git a/src/schema/query/parking.ts b/src/schema/query/parking.ts deleted file mode 100644 index 6a4fb61..0000000 --- a/src/schema/query/parking.ts +++ /dev/null @@ -1,16 +0,0 @@ -import gql from 'graphql-tag' - -export const parkingType = gql` - "Parking data" - type ParkingData { - updated: String! - lots: [ParkingLot!]! - } - - "Parking lot data" - type ParkingLot { - name: String! - available: Int! - priceLevel: Int - } -` diff --git a/src/schema/query/train.ts b/src/schema/query/train.ts deleted file mode 100644 index 329bb70..0000000 --- a/src/schema/query/train.ts +++ /dev/null @@ -1,14 +0,0 @@ -import gql from 'graphql-tag' - -export const trainType = gql` - "Train data" - type Train { - name: String! - destination: String! - plannedTime: String! - actualTime: String! - canceled: Boolean! - track: String - url: String - } -`