Add extracted tools: CitrineOS, OpenOCPP, ShapeShifter
- CitrineOS core extracted (CSMS OCPP 2.0.1) - OpenOCPP extracted (firmware OCPP 1.6J/2.0.1) - ShapeShifter library installed (pip install -e) - ShapeShifter specification extracted - EVerest extracted TODO updated with progress
This commit is contained in:
6
tools/citrineos-core-main/apps/Server/.sequelizerc
Normal file
6
tools/citrineos-core-main/apps/Server/.sequelizerc
Normal file
@@ -0,0 +1,6 @@
|
||||
const path = require('path');
|
||||
|
||||
module.exports = {
|
||||
config: path.resolve('dist', 'config', 'sequelize.bridge.config.js'),
|
||||
'migrations-path': path.resolve('dist/migrations'),
|
||||
};
|
||||
295
tools/citrineos-core-main/apps/Server/README.md
Normal file
295
tools/citrineos-core-main/apps/Server/README.md
Normal file
@@ -0,0 +1,295 @@
|
||||
<!--
|
||||
SPDX-FileCopyrightText: 2025 Contributors to the CitrineOS Project
|
||||
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
-->
|
||||
|
||||

|
||||

|
||||
|
||||
# CitrineOS Server (`@citrineos/server`)
|
||||
|
||||
This is the OCPP server application for CitrineOS — the runnable entrypoint that wires together
|
||||
[`@citrineos/base`](../../packages/base) and [`@citrineos/core`](../../packages/core) into a deployable
|
||||
service. It hosts the WebSocket endpoints that charging stations connect to, the OCPP message router, the
|
||||
HTTP/REST Data and Message APIs, and the Sequelize database migrations.
|
||||
|
||||
It is one workspace member of the `citrineos-core` pnpm monorepo. For repository-wide setup (cloning,
|
||||
`pnpm install`, building, the full-stack Docker Compose files, and the operator UI), see the
|
||||
[root README](../../README.md).
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [Running the Server](#running-the-server)
|
||||
- [With Docker (backend only)](#with-docker-backend-only)
|
||||
- [Without Docker](#without-docker)
|
||||
- [Attaching a Debugger](#attaching-a-debugger)
|
||||
- [Server Ports](#server-ports)
|
||||
- [Database Sync vs. Migration](#database-sync-vs-migration)
|
||||
- [Runtime Configuration](#runtime-configuration)
|
||||
- [Bootstrap Configuration Environment Variables](#bootstrap-configuration-environment-variables)
|
||||
- [Generating OCPP Interfaces](#generating-ocpp-interfaces)
|
||||
- [Validating Custom OCPP DataTransfer Messages](#validating-custom-ocpp-datatransfer-messages)
|
||||
- [Allow Unknown Charging Stations & Auto-Commissioning](#allow-unknown-charging-stations--auto-commissioning)
|
||||
- [Hasura Metadata](#hasura-metadata)
|
||||
- [Testing with EVerest](#testing-with-everest)
|
||||
|
||||
## Running the Server
|
||||
|
||||
Make sure the workspace has been installed and built first (from the repository root: `pnpm install && pnpm run build`).
|
||||
|
||||
### With Docker (backend only)
|
||||
|
||||
`apps/Server/docker-compose.yml` brings up the server plus its supporting services — RabbitMQ, PostgreSQL, MinIO,
|
||||
and Hasura — but **not** the operator UI. The server image is built from local source and the source tree is mounted
|
||||
as volumes for live reload. Run it from this directory:
|
||||
|
||||
```shell
|
||||
cd apps/Server
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
To run the full stack including the operator UI, use one of the root-level Compose files instead — see the
|
||||
[root README](../../README.md#information-on-docker-setup).
|
||||
|
||||
### Without Docker
|
||||
|
||||
To start the server directly with pnpm, run from the repository root:
|
||||
|
||||
```shell
|
||||
pnpm run start
|
||||
```
|
||||
|
||||
Or from this directory:
|
||||
|
||||
```shell
|
||||
cd apps/Server
|
||||
pnpm run start
|
||||
```
|
||||
|
||||
This launches the server via `nodemon` (see `nodemon.json`), which builds the workspace, runs database migrations,
|
||||
and then starts the process with the Node.js inspector listening on port 9229.
|
||||
|
||||
CitrineOS requires configuration to allow your OCPP 1.6 and OCPP 2.0.1 compliant charging stations to connect.
|
||||
To change the configuration used outside of Docker, adjust the configuration file at
|
||||
`apps/Server/src/config/envs/local.ts`. Make sure any changes to the local configuration do not make it into your PR.
|
||||
|
||||
## Attaching a Debugger
|
||||
|
||||
Whether you run the application with Docker or locally with pnpm, you can attach a debugger to port 9229 and set
|
||||
breakpoints in the TypeScript code directly from your IDE.
|
||||
|
||||
To make the process **wait for the debugger to attach** before executing, modify the `nodemon.json` exec command from:
|
||||
|
||||
```shell
|
||||
pnpm run build --prefix ../../ && pnpm run migrate && node --inspect=0.0.0.0:9229 ./dist/index.js
|
||||
```
|
||||
|
||||
to:
|
||||
|
||||
```shell
|
||||
pnpm run build --prefix ../../ && pnpm run migrate && node --inspect-brk=0.0.0.0:9229 ./dist/index.js
|
||||
```
|
||||
|
||||
## Server Ports
|
||||
|
||||
When running, the server container exposes the following ports (see `docker-compose.yml`):
|
||||
|
||||
- `8080`: webserver HTTP — [Swagger](http://localhost:8080/docs)
|
||||
- `8081`: websocket server TCP connection without auth
|
||||
- `8082`: websocket server TCP connection with basic HTTP auth
|
||||
- `8083`: additional websocket server
|
||||
- `8443` / `8444`: TLS websocket servers
|
||||
- `9229`: Node.js debugger
|
||||
|
||||
## Database Sync vs. Migration
|
||||
|
||||
By default, CitrineOS uses migrations to manage database schema changes. This is the recommended approach for
|
||||
production environments. The `pnpm run migrate` script (run automatically on start via `nodemon.json`) applies
|
||||
Sequelize migrations.
|
||||
|
||||
For development purposes, you can also use `sync` to automatically synchronize your database schema with the models.
|
||||
Two sync scripts are available at the repository root:
|
||||
|
||||
- `pnpm run sync-db`: synchronizes the database schema with the models without altering existing tables. Useful for
|
||||
development when you want to quickly update the schema without losing data.
|
||||
- `pnpm run force-sync-db`: drops all tables and recreates them based on the models. Useful when you want to start
|
||||
with a fresh database.
|
||||
|
||||
**Disclaimer:** Using `sync` in a production environment is not recommended as it can lead to data loss. Always use
|
||||
migrations for production deployments.
|
||||
|
||||
## Runtime Configuration
|
||||
|
||||
Values from configuration files (`local.ts`, `docker.ts`, `swarm.docker.ts`) may be overridden at runtime via
|
||||
environment variables. Environment variables prefixed with `citrineos_` and hierarchically separated by an
|
||||
underscore will override the corresponding value. For example, the amqp URL:
|
||||
|
||||
```json
|
||||
util: {
|
||||
(...)
|
||||
messageBroker: {
|
||||
amqp: {
|
||||
url: 'amqp://guest:guest@localhost:5672'
|
||||
(...)
|
||||
}
|
||||
(...)
|
||||
}
|
||||
(...)
|
||||
}
|
||||
```
|
||||
|
||||
may be overridden by setting the environment variable `CITRINEOS_util_messageBroker_amqp_url` (case-insensitive).
|
||||
|
||||
## Bootstrap Configuration Environment Variables
|
||||
|
||||
All environment variables use the `CITRINEOS_` prefix.
|
||||
Additional prefixes can be added by passing the `--env-prefix` argument to nodemon (see `start:instance1` in
|
||||
`package.json`).
|
||||
Here's the complete list of environment variables used in bootstrapping the application (this is not the full system
|
||||
configuration):
|
||||
|
||||
### Basic Bootstrap Configuration
|
||||
|
||||
- `BOOTSTRAP_CITRINEOS_CONFIG_FILENAME` - Name of the main config file (default: `config.json`)
|
||||
- `BOOTSTRAP_CITRINEOS_CONFIG_DIR` - Directory containing the config file (optional)
|
||||
- `BOOTSTRAP_CITRINEOS_FILE_ACCESS_TYPE` - Type of file access: `local`, `s3`, or `gcp`
|
||||
|
||||
### Database Configuration
|
||||
|
||||
Database connection details (moved from system config to bootstrap config for better security and 12-factor compliance):
|
||||
|
||||
- `BOOTSTRAP_CITRINEOS_DATABASE_HOST` - Database host (default: `localhost`)
|
||||
- `BOOTSTRAP_CITRINEOS_DATABASE_PORT` - Database port (default: `5432`)
|
||||
- `BOOTSTRAP_CITRINEOS_DATABASE_NAME` - Database name (default: `citrine`)
|
||||
- `BOOTSTRAP_CITRINEOS_DATABASE_DIALECT` - Database dialect (default: `postgres`)
|
||||
- `BOOTSTRAP_CITRINEOS_DATABASE_USERNAME` - Database username (optional)
|
||||
- `BOOTSTRAP_CITRINEOS_DATABASE_PASSWORD` - Database password (optional)
|
||||
- `BOOTSTRAP_CITRINEOS_DATABASE_SYNC` - Enable database sync (via sequelize) (true/false, default: `false`)
|
||||
- `BOOTSTRAP_CITRINEOS_DATABASE_ALTER` - Enable database alter (via sequelize) (true/false, default: `false`)
|
||||
- `BOOTSTRAP_CITRINEOS_DATABASE_MAX_RETRIES` - Maximum connection retries (default: `3`)
|
||||
- `BOOTSTRAP_CITRINEOS_DATABASE_RETRY_DELAY` - Retry delay in milliseconds (default: `1000`)
|
||||
|
||||
### Local File Access
|
||||
|
||||
When `BOOTSTRAP_CITRINEOS_FILE_ACCESS_TYPE=local`:
|
||||
|
||||
- `BOOTSTRAP_CITRINEOS_FILE_ACCESS_LOCAL_DEFAULT_FILE_PATH` - Default file path (default: `/data`)
|
||||
|
||||
### S3 File Access
|
||||
|
||||
When `BOOTSTRAP_CITRINEOS_FILE_ACCESS_TYPE=s3`:
|
||||
|
||||
- `BOOTSTRAP_CITRINEOS_FILE_ACCESS_S3_REGION` - AWS region (optional)
|
||||
- `BOOTSTRAP_CITRINEOS_FILE_ACCESS_S3_ENDPOINT` - S3 endpoint URL (for MinIO or custom S3)
|
||||
- `BOOTSTRAP_CITRINEOS_FILE_ACCESS_S3_DEFAULT_BUCKET_NAME` - S3 bucket name (default: `citrineos-s3-bucket`)
|
||||
- `BOOTSTRAP_CITRINEOS_FILE_ACCESS_S3_FORCE_PATH_STYLE` - Force path style (true/false, default: `true`)
|
||||
- `BOOTSTRAP_CITRINEOS_FILE_ACCESS_S3_ACCESS_KEY_ID` - S3 access key ID
|
||||
- `BOOTSTRAP_CITRINEOS_FILE_ACCESS_S3_SECRET_ACCESS_KEY` - S3 secret access key
|
||||
|
||||
### GCP File Access
|
||||
|
||||
When `BOOTSTRAP_CITRINEOS_FILE_ACCESS_TYPE=gcp`:
|
||||
|
||||
- `BOOTSTRAP_CITRINEOS_FILE_ACCESS_GCP_PROJECTID` - Project ID
|
||||
- `BOOTSTRAP_CITRINEOS_FILE_ACCESS_GCP_CREDENTIALS` - GCP Credentials object (Optional, if not set will use Application Default Credentials such as the `GOOGLE_APPLICATION_CREDENTIALS` environment variable or gcloud CLI credentials)
|
||||
|
||||
## Generating OCPP Interfaces
|
||||
|
||||
All CitrineOS interfaces for OCPP 1.6, 2.0.1, and 2.1-defined schemas were procedurally generated using a processing
|
||||
script. Schemas are sourced from official OCPP JSON files.
|
||||
As of release 1.8.0, the schema files used by CitrineOS are not the raw output of this function; we have added
|
||||
field-level validation that the official schemas lack.
|
||||
|
||||
## Validating Custom OCPP DataTransfer Messages
|
||||
|
||||
It is possible to add custom JSON schemas to validate the data fields of DataTransfer messages, which are supported by
|
||||
all OCPP versions.
|
||||
In the `apps/Server/src/index.ts` code, there is a function `ajvInstance()` that creates the AJV instance. Here, you
|
||||
could register DataTransfer schemas:
|
||||
|
||||
```
|
||||
import { MyDataTransferRequestSchema } from './path'
|
||||
...
|
||||
ajvInstance.compile(MyDataTransferRequestSchema);
|
||||
```
|
||||
|
||||
Note: The schema's `$id` field must follow this format:
|
||||
|
||||
```
|
||||
${protocol}-${dataTransferRequest.vendorId}${dataTransferRequest.messageId ? `-${dataTransferRequest.messageId}` : ''}
|
||||
```
|
||||
|
||||
'Protocol' is the OCPP websocket subprotocol, i.e. "ocpp1.6", "ocpp2.0.1", or so on.
|
||||
|
||||
CitrineOS's validation logic assumes that the data field is a string field with JSON structure, and uses `JSON.parse`
|
||||
before validation. Other approaches to custom DataTransfer message types are not supported.
|
||||
|
||||
## Allow Unknown Charging Stations & Auto-Commissioning
|
||||
|
||||
The System Configuration defines websocket servers with certain properties, one of which is 'Allow Unknown Charging
|
||||
Stations', a boolean that permits charging stations which are not commissioned to connect to CitrineOS.
|
||||
This triggers an auto-commissioning flow which creates the station on its first connection, and creates evses and
|
||||
connectors for that station in response to StatusNotifications.
|
||||
This is not recommended for production; it is exclusively for testing and is enabled by the default configuration only
|
||||
on the websocket server at port 8081 — which also has no security.
|
||||
Since not all information on the charger is necessarily available in the OCPP messages, commissioning may be wrong and
|
||||
will be incomplete. In 1.6 in particular, multi-evse stations will not commission properly because 1.6 does not have a
|
||||
concept of 'evses'. This will lead to improper behavior if a 1.6 station with multiple evses is auto-commissioned:
|
||||
CitrineOS will assume each new transaction is on the same evse and will automatically mark older transactions on that
|
||||
evse as inactive, leading to an inconsistent state with the charging station.
|
||||
|
||||
## Hasura Metadata
|
||||
|
||||
In order for Hasura to track the existing Citrine tables and relationships, this repository comes with Hasura metadata
|
||||
already exported into the `apps/Server/hasura-metadata` folder.
|
||||
Running the Docker container will automatically import this metadata and track all tables and relationships.
|
||||
|
||||
Unfortunately, Hasura doesn't currently support importing metadata from a JSON (which is the format if you export your
|
||||
metadata from the Hasura UI or API).
|
||||
Refer to this issue for more information: https://github.com/hasura/graphql-engine/issues/8423#issuecomment-1115996153.
|
||||
|
||||
Therefore, you must use the Hasura CLI to re-export your metadata, should something change with it. As explained in the
|
||||
Hasura docs https://hasura.io/docs/2.0/migrations-metadata-seeds/auto-apply-migrations/#auto-apply-metadata,
|
||||
Hasura provides an image called `hasura/graphql-engine:<version>.cli-migrations-v3` that will process and import the
|
||||
metadata first before starting the server and runs the Hasura CLI internally. This is the image CitrineOS normally uses
|
||||
in order to automatically load accurate metadata. However, if you want to capture the current state of your database,
|
||||
you should use a normal version tag (such as `v2.40.3` instead of `v2.40.3.cli-migrations-v3`). Then proceed to the
|
||||
Hasura console at `localhost:8090`, go to the data tab, use the sidebar to navigate to the database schema at
|
||||
default>public, and track all of the tables, relationships, and functions you need. Then proceed with the below
|
||||
instructions.
|
||||
|
||||
You can follow these steps to re-export your metadata via the Hasura CLI in the `graphql-engine` container:
|
||||
|
||||
- (if the hasura cli isn't installed):
|
||||
|
||||
```
|
||||
curl -L https://github.com/hasura/graphql-engine/raw/stable/cli/get.sh | bash
|
||||
```
|
||||
|
||||
- (If not yet initialized) Initialize the Hasura project in the `graphql-engine` container (you can do this via the Docker Desktop `exec` view):
|
||||
|
||||
```
|
||||
hasura-cli init
|
||||
OR
|
||||
hasura init
|
||||
|
||||
enter any name you wish for the project (i.e. citrine)
|
||||
```
|
||||
|
||||
- Export the metadata by executing this command in the `graphql-engine` container:
|
||||
|
||||
```
|
||||
hasura-cli metadata export
|
||||
OR
|
||||
hasura metadata export
|
||||
```
|
||||
|
||||
- Find the exported files in the `graphql-engine` container's files in the metadata filepath `<name of project i.e. citrine>/metadata` and pull that metadata backup onto your local machine
|
||||
- Copy the contents of the copied `metadata` folder into the `apps/Server/hasura-metadata` folder in this repository
|
||||
|
||||
## Testing with EVerest
|
||||
|
||||
In case you don't have a charger that supports OCPP to experiment with, you can run the EVerest charger simulator
|
||||
locally and point it at CitrineOS. Helper scripts (`pnpm run start-everest` and `pnpm run start-everest-16`) and full
|
||||
instructions live in [`everest/README.md`](./everest/README.md).
|
||||
58
tools/citrineos-core-main/apps/Server/copy-assets.ts
Normal file
58
tools/citrineos-core-main/apps/Server/copy-assets.ts
Normal file
@@ -0,0 +1,58 @@
|
||||
// SPDX-FileCopyrightText: 2025 Contributors to the CitrineOS Project
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
import * as path from 'path';
|
||||
import { promises as fs } from 'fs';
|
||||
|
||||
const source = process.argv[2];
|
||||
const target = process.argv[3];
|
||||
|
||||
console.log(`Copying assets from ${source} to ${target}`);
|
||||
|
||||
if (!source || !target) {
|
||||
console.error(
|
||||
`Error: need valid source and target. Received source: ${source}, target: ${target}`,
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
async function copyDirectory(src: string, dest: string) {
|
||||
try {
|
||||
await fs.mkdir(dest, { recursive: true });
|
||||
console.log(`Created "${dest}" directory`);
|
||||
|
||||
const entries = await fs.readdir(src, { withFileTypes: true });
|
||||
|
||||
for (const entry of entries) {
|
||||
const srcPath = path.join(src, entry.name);
|
||||
const destPath = path.join(dest, entry.name);
|
||||
|
||||
if (entry.isDirectory()) {
|
||||
await copyDirectory(srcPath, destPath);
|
||||
} else {
|
||||
await fs.copyFile(srcPath, destPath);
|
||||
console.log(`Copied ${srcPath} to ${destPath}`);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Error copying directory:', err);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
async function copy(s: string, t: string) {
|
||||
const srcDir = path.resolve(s);
|
||||
const destDir = path.resolve(t);
|
||||
await copyDirectory(srcDir, destDir);
|
||||
console.log(`Successfully copied assets from ${srcDir} to ${destDir}`);
|
||||
}
|
||||
|
||||
copy(source, target)
|
||||
.then(() => {
|
||||
console.log(`Completed copying assets from ${source} to ${target}`);
|
||||
process.exit(0);
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error('Unhandled error:', err);
|
||||
process.exit(1);
|
||||
});
|
||||
30
tools/citrineos-core-main/apps/Server/deploy.Dockerfile
Normal file
30
tools/citrineos-core-main/apps/Server/deploy.Dockerfile
Normal file
@@ -0,0 +1,30 @@
|
||||
# SPDX-FileCopyrightText: 2025 Contributors to the CitrineOS Project
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
# Use a specific base image with platform support
|
||||
FROM --platform=${BUILDPLATFORM:-linux/amd64} node:24.16.0 AS build
|
||||
|
||||
RUN corepack enable
|
||||
|
||||
WORKDIR /usr/local/apps/citrineos
|
||||
|
||||
COPY . .
|
||||
RUN pnpm install --frozen-lockfile
|
||||
RUN pnpm --filter "@citrineos/server..." build
|
||||
|
||||
# The final stage, which copies built files and prepares the run environment
|
||||
# Using a slim image to reduce the final image size
|
||||
FROM node:24.16.0-slim
|
||||
|
||||
RUN corepack enable
|
||||
|
||||
COPY --from=build /usr/local/apps/citrineos /usr/local/apps/citrineos
|
||||
|
||||
WORKDIR /usr/local/apps/citrineos
|
||||
|
||||
RUN chmod +x /usr/local/apps/citrineos/apps/Server/entrypoint.sh
|
||||
|
||||
EXPOSE ${PORT}
|
||||
|
||||
ENTRYPOINT ["/usr/local/apps/citrineos/apps/Server/entrypoint.sh"]
|
||||
181
tools/citrineos-core-main/apps/Server/docker-compose.yml
Executable file
181
tools/citrineos-core-main/apps/Server/docker-compose.yml
Executable file
@@ -0,0 +1,181 @@
|
||||
# SPDX-FileCopyrightText: 2025 Contributors to the CitrineOS Project
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
services:
|
||||
amqp-broker:
|
||||
image: rabbitmq:3-management
|
||||
ports:
|
||||
- 15672:15672
|
||||
- 5672:5672
|
||||
environment:
|
||||
RABBITMQ_DEFAULT_USER: "guest"
|
||||
RABBITMQ_DEFAULT_PASS: "guest"
|
||||
volumes:
|
||||
- ./data/rabbitmq:/var/lib/rabbitmq
|
||||
healthcheck:
|
||||
test: rabbitmq-diagnostics -q check_port_connectivity
|
||||
interval: 10s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
|
||||
ocpp-db:
|
||||
image: postgis/postgis:16-3.5
|
||||
platform: linux/amd64
|
||||
ports:
|
||||
- 5432:5432
|
||||
volumes:
|
||||
- ./data/postgresql/pgdata:/var/lib/postgresql/data
|
||||
environment:
|
||||
POSTGRES_DB: citrine
|
||||
POSTGRES_USER: citrine
|
||||
POSTGRES_PASSWORD: "citrine"
|
||||
healthcheck:
|
||||
test: "pg_isready --username=citrine"
|
||||
interval: 5s
|
||||
timeout: 10s
|
||||
retries: 5
|
||||
|
||||
citrine:
|
||||
image: citrineos-core-citrine:latest
|
||||
build:
|
||||
context: ../../
|
||||
dockerfile: ./apps/Server/deploy.Dockerfile
|
||||
volumes:
|
||||
- ../../pnpm-lock.yaml:/usr/local/apps/citrineos/pnpm-lock.yaml
|
||||
- ../../package.json:/usr/local/apps/citrineos/package.json
|
||||
- ../../tsconfig.json:/usr/local/apps/citrineos/tsconfig.json
|
||||
- ../../tsconfig.build.json:/usr/local/apps/citrineos/tsconfig.build.json
|
||||
- ./:/usr/local/apps/citrineos/apps/Server
|
||||
- ../../packages/base:/usr/local/apps/citrineos/packages/base
|
||||
- ../../packages/core:/usr/local/apps/citrineos/packages/core
|
||||
- /usr/local/apps/citrineos/node_modules
|
||||
- /usr/local/apps/citrineos/apps/Server/node_modules
|
||||
- /usr/local/apps/citrineos/packages/base/node_modules
|
||||
- /usr/local/apps/citrineos/packages/core/node_modules
|
||||
- /usr/local/apps/citrineos/dist/
|
||||
- /usr/local/apps/citrineos/apps/Server/dist/
|
||||
- /usr/local/apps/citrineos/packages/base/dist/
|
||||
- /usr/local/apps/citrineos/packages/core/dist/
|
||||
environment:
|
||||
APP_NAME: "all"
|
||||
APP_ENV: "docker"
|
||||
# Add AWS region (required by the SDK)
|
||||
AWS_REGION: us-east-1
|
||||
# Skip authentication completely - use public access
|
||||
AWS_ACCESS_KEY_ID: minioadmin
|
||||
AWS_SECRET_ACCESS_KEY: minioadmin
|
||||
# Database Strategy
|
||||
DB_STRATEGY: "migrate"
|
||||
# Bootstrap Configuration Environment Variables
|
||||
BOOTSTRAP_CITRINEOS_DATABASE_HOST: "ocpp-db"
|
||||
BOOTSTRAP_CITRINEOS_CONFIG_FILENAME: "config.json"
|
||||
# CITRINEOS_CONFIG_DIR: '/custom/config/path' # Optional - uncomment if needed
|
||||
BOOTSTRAP_CITRINEOS_FILE_ACCESS_TYPE: "local"
|
||||
BOOTSTRAP_CITRINEOS_FILE_ACCESS_LOCAL_DEFAULT_FILE_PATH: "/data"
|
||||
# No System Config file persistence respected, to avoid local conflicts across version updates - recommended to remove for production
|
||||
CONFIG_CITRINEOS_WIPE_FILE_ON_START: "true"
|
||||
depends_on:
|
||||
ocpp-db:
|
||||
condition: service_healthy
|
||||
amqp-broker:
|
||||
condition: service_healthy
|
||||
minio-init:
|
||||
condition: service_completed_successfully
|
||||
ports:
|
||||
- 8080:8080
|
||||
- 8081:8081
|
||||
- 8082:8082
|
||||
- 8083:8083
|
||||
- 8443:8443
|
||||
- 8444:8444
|
||||
- 9229:9229
|
||||
healthcheck:
|
||||
test: [
|
||||
"CMD-SHELL",
|
||||
"node -e \"const net = require(\\\"net\\\"); const client =
|
||||
net.createConnection(8080, \\\"127.0.0.1\\\", () => { client.end();
|
||||
process.exit(0); }); client.on(\\\"error\\\", () =>
|
||||
process.exit(1)); client.setTimeout(5000, () => { client.destroy();
|
||||
process.exit(1); });\"",
|
||||
]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 5
|
||||
start_period: 60s
|
||||
|
||||
minio:
|
||||
image: minio/minio
|
||||
container_name: minio
|
||||
ports:
|
||||
- "9000:9000"
|
||||
- "9001:9001"
|
||||
environment:
|
||||
# Use default credentials for simplicity
|
||||
MINIO_ROOT_USER: minioadmin
|
||||
MINIO_ROOT_PASSWORD: minioadmin
|
||||
# Enable public buckets
|
||||
MINIO_BROWSER_REDIRECT_URL: http://localhost:9001
|
||||
MINIO_SERVER_URL: http://localhost:9000
|
||||
volumes:
|
||||
- ./data/minio:/data
|
||||
command: server /data --console-address ":9001"
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 5
|
||||
start_period: 20s
|
||||
|
||||
# Initialize MinIO
|
||||
minio-init:
|
||||
image: minio/mc
|
||||
depends_on:
|
||||
minio:
|
||||
condition: service_healthy
|
||||
entrypoint: >
|
||||
/bin/sh -c " # Enable command echoing and error reporting set -x
|
||||
|
||||
echo 'Setting up MinIO alias' mc alias set myminio http://minio:9000
|
||||
minioadmin minioadmin || echo 'FAILED: alias setup' "
|
||||
|
||||
graphql-engine:
|
||||
image: hasura/graphql-engine:v2.40.3.cli-migrations-v3
|
||||
volumes:
|
||||
- ./hasura-metadata:/hasura-metadata
|
||||
ports:
|
||||
- 8090:8080
|
||||
restart: always
|
||||
depends_on:
|
||||
citrine:
|
||||
# ensures hasura migrations will run only after citrine is healthy,
|
||||
# meaning 01_Data/dist/layers/sequelize/util.js was executed and logged
|
||||
# CitrineOS Logger:DefaultSequelizeInstance Database altered,
|
||||
# which means the database is ready to be used by hasura
|
||||
condition: service_healthy
|
||||
environment:
|
||||
## postgres database to store Hasura metadata
|
||||
HASURA_GRAPHQL_DATABASE_URL: postgres://citrine:citrine@ocpp-db:5432/citrine
|
||||
## enable the console served by server
|
||||
HASURA_GRAPHQL_ENABLE_CONSOLE: "true" # set to "false" to disable console
|
||||
## enable debugging mode. It is recommended to disable this in production
|
||||
HASURA_GRAPHQL_DEV_MODE: "true"
|
||||
HASURA_GRAPHQL_ENABLED_LOG_TYPES: startup, http-log, webhook-log, websocket-log, query-log
|
||||
HASURA_GRAPHQL_ENABLE_TELEMETRY: "false"
|
||||
## uncomment next line to run console offline (i.e load console assets from server instead of CDN)
|
||||
# HASURA_GRAPHQL_CONSOLE_ASSETS_DIR: /srv/console-assets
|
||||
## uncomment next line to set an admin secret - warning, without configuration this will cause upstream services to fail
|
||||
# HASURA_GRAPHQL_ADMIN_SECRET: CitrineOS!
|
||||
HASURA_GRAPHQL_METADATA_DEFAULTS:
|
||||
"{\"backend_configs\":{\"dataconnector\":{\"a\
|
||||
thena\":{\"uri\":\"http://data-connector-agent:8081/api/v1/athena\"},\"\
|
||||
mariadb\":{\"uri\":\"http://data-connector-agent:8081/api/v1/mariadb\"},\
|
||||
\"mysql8\":{\"uri\":\"http://data-connector-agent:8081/api/v1/mysql\"},\
|
||||
\"oracle\":{\"uri\":\"http://data-connector-agent:8081/api/v1/oracle\"},\
|
||||
\"snowflake\":{\"uri\":\"http://data-connector-agent:8081/api/v1/snowfl\
|
||||
ake\"}}}}"
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://graphql-engine:8080/healthz"]
|
||||
interval: 5s
|
||||
timeout: 10s
|
||||
retries: 20
|
||||
start_period: 5s
|
||||
20
tools/citrineos-core-main/apps/Server/entrypoint.sh
Executable file
20
tools/citrineos-core-main/apps/Server/entrypoint.sh
Executable file
@@ -0,0 +1,20 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
# Default to migrate if DB_STRATEGY is not set
|
||||
DB_STRATEGY=${DB_STRATEGY:-migrate}
|
||||
|
||||
echo "Executing DB strategy: $DB_STRATEGY"
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
|
||||
if [ "$DB_STRATEGY" = "migrate" ]; then
|
||||
(cd "$SCRIPT_DIR" && pnpm run migrate)
|
||||
else
|
||||
echo "Unknown DB_STRATEGY: $DB_STRATEGY. Defaulting to migrate."
|
||||
(cd "$SCRIPT_DIR" && pnpm run migrate)
|
||||
fi
|
||||
|
||||
echo "Starting application..."
|
||||
exec node "$SCRIPT_DIR/dist/index.js"
|
||||
|
||||
26
tools/citrineos-core-main/apps/Server/eslint.config.js
Normal file
26
tools/citrineos-core-main/apps/Server/eslint.config.js
Normal file
@@ -0,0 +1,26 @@
|
||||
// SPDX-FileCopyrightText: 2025 Contributors to the CitrineOS Project
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { fileURLToPath } from 'url';
|
||||
import { dirname } from 'path';
|
||||
import tseslint from 'typescript-eslint';
|
||||
import { sharedConfigs, sharedIgnores } from '../../eslint.config.base.js';
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
export default tseslint.config(
|
||||
...sharedConfigs,
|
||||
{
|
||||
languageOptions: {
|
||||
parserOptions: {
|
||||
project: ['./tsconfig.eslint.json'],
|
||||
tsconfigRootDir: __dirname,
|
||||
},
|
||||
},
|
||||
rules: {
|
||||
'@typescript-eslint/no-floating-promises': 'error',
|
||||
},
|
||||
},
|
||||
sharedIgnores,
|
||||
);
|
||||
11
tools/citrineos-core-main/apps/Server/everest/Dockerfile
Normal file
11
tools/citrineos-core-main/apps/Server/everest/Dockerfile
Normal file
@@ -0,0 +1,11 @@
|
||||
ARG EVEREST_IMAGE_TAG="2025.6.1-dt-esdp"
|
||||
FROM ghcr.io/everest/everest-demo/manager:${EVEREST_IMAGE_TAG}
|
||||
|
||||
WORKDIR /workspace
|
||||
|
||||
RUN npm i -g http-server
|
||||
EXPOSE 8888
|
||||
|
||||
COPY ./start.sh /tmp/start.sh
|
||||
RUN chmod +x /tmp/start.sh
|
||||
ENTRYPOINT ["/tmp/start.sh"]
|
||||
62
tools/citrineos-core-main/apps/Server/everest/README.md
Normal file
62
tools/citrineos-core-main/apps/Server/everest/README.md
Normal file
@@ -0,0 +1,62 @@
|
||||
# Testing with EVerest
|
||||
|
||||
In the case you don't have a charger that supports OCPP 2.0.1 to experiment with, we can recommend using the Linux
|
||||
Foundation Energy project EVerest. [See here](https://github.com/EVerest) for the repository. They have built an open source version of
|
||||
charger firmware and also allow for using it as a simulator. They support OCPP 2.0.1 which makes it a great testing
|
||||
opportunity with CitrineOS. For the long route of setting up EVerst you can follow their documentation and build
|
||||
the project yourself. [See here for Docs](https://everest.github.io/latest/how-to-guides/getting-started/index.html)
|
||||
|
||||
# Running EVerest
|
||||
|
||||
In order to alleviate some of the complexities that may arise when starting EVerest, we have created
|
||||
some helpful commands that should help in getting the EVerest charger simulator running locally and targeting
|
||||
CitrineOS.
|
||||
|
||||
You will notice in the `apps/Server/everest` directory the files created to support running EVerest within Docker.
|
||||
In addition, we created some helpful package scripts (run from the `apps/Server` directory):
|
||||
|
||||
- `pnpm run start-everest` — starts EVerest for OCPP 2.x (defaults to `OCPP_VERSION=2.1`)
|
||||
- `pnpm run start-everest-16` — starts EVerest for OCPP 1.6
|
||||
|
||||
Both scripts `cd` into `everest` and trigger the `docker compose up` command (below) from within the
|
||||
`apps/Server/everest` directory so that it can pick up the `Dockerfile` and the `docker-compose.yml` files.
|
||||
|
||||
You will notice that there are two args that are configurable:
|
||||
|
||||
- `EVEREST_IMAGE_TAG` - The image tag that will be used for the EVerest image (ghcr.io/everest/everest-demo/manager).
|
||||
- `OCPP_VERSION` - The version of OCPP to run EVerest for. `start-everest` handles all 2.x versions for the given 2.x `OCPP_VERSION`, and `start-everest-16` is for OCPP 1.6 only.
|
||||
|
||||
After running `pnpm run start-everest` (or `pnpm run start-everest-16`), you should see 3 running EVerest containers
|
||||
and the `manager` container should have the appropriate EVerest logs.
|
||||
|
||||
### EVerest UI
|
||||
|
||||
Now that the 3 containers are running in Docker, you should be able to navigate to `[localhost|ip]:1880/ui/` to view
|
||||
the EVerest simulator UI. There, you should be able to simulate the pause/resume and plug/unplug events among others.
|
||||
|
||||
### EVerest NodeRed
|
||||
|
||||
You can also view the EVerest NodeRed UI `[localhost|ip]:1880/`, but it is not advisable to make any adjustments here
|
||||
unless you have a good understanding of this configuration.
|
||||
|
||||
### Viewing OCPP logs in EVerest
|
||||
|
||||
To view the OCPP logs in EVerest, we have utilized Node `http-server`, which you will see being initialized
|
||||
in the Dockerfile. We initialize a simple HTTP server on port `8888` and expose this port so that it is
|
||||
mapped in the compose file allowing you to navigate to `localhost:8888`. This HTTP server is configured to
|
||||
serve the contents of the `/tmp/everest_ocpp_logs` which is where EVerest stores the OCPP logs in the
|
||||
Docker container. Conveniently, the logs are in HTML format, so we can easily view them in the browser.
|
||||
|
||||
# Running EVerest Manually
|
||||
|
||||
You can also use their demo repository that hosts a Docker packaged EVerest image. [See here for Github Repo](https://github.com/EVerest/everest-demo)
|
||||
|
||||
To get EVerest running on the side while developing and making changes, you can follow the steps below.
|
||||
|
||||
1. Run your CitrineOS instance locally with `docker compose up -d` in the CitrineOS repository.
|
||||
1. Clone the [EVerest Demo](https://github.com/EVerest/everest-demo) repository and `cd` into the repo.
|
||||
1. With CitrineOS running execute an "add charger" script at `./citrineos/add-charger.sh` This adds a charger, location and password for the charger to CitrineOS.
|
||||
1. Bring up EVerest with `docker compose --project-name everest-ac-demo --file "docker-compose.ocpp201.yml" up -d`.
|
||||
1. Copy over the appropriate device model with `docker cp manager/device_model_storage_citrineos_sp1.db \
|
||||
everest-ac-demo-manager-1:/ext/source/build/dist/share/everest/modules/OCPP201/device_model_storage.db`.
|
||||
1. Start EVerst having OCPP2.0.1 support with `docker exec everest-ac-demo-manager-1 sh /ext/source/build/run-scripts/run-sil-ocpp201.sh`.
|
||||
@@ -0,0 +1,56 @@
|
||||
# SPDX-FileCopyrightText: 2025 Contributors to the CitrineOS Project
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
version: '3.6'
|
||||
|
||||
services:
|
||||
mqtt-server:
|
||||
image: ghcr.io/everest/everest-demo/mqtt-server:${EVEREST_IMAGE_TAG}
|
||||
platform: linux/x86_64
|
||||
logging:
|
||||
driver: none
|
||||
networks:
|
||||
- everest_net
|
||||
|
||||
manager:
|
||||
build:
|
||||
dockerfile: Dockerfile
|
||||
args:
|
||||
- EVEREST_IMAGE_TAG=${EVEREST_IMAGE_TAG}
|
||||
platform: linux/x86_64
|
||||
ports:
|
||||
- 8888:8888
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: '2'
|
||||
memory: '4G'
|
||||
depends_on:
|
||||
- mqtt-server
|
||||
environment:
|
||||
- MQTT_SERVER_ADDRESS=mqtt-server
|
||||
- EVEREST_IMAGE_TAG=${EVEREST_IMAGE_TAG}
|
||||
- OCPP_VERSION=${OCPP_VERSION}
|
||||
sysctls:
|
||||
- net.ipv6.conf.all.disable_ipv6=0
|
||||
extra_hosts:
|
||||
- 'host.docker.internal:host-gateway'
|
||||
networks:
|
||||
- everest_net
|
||||
|
||||
nodered:
|
||||
image: ghcr.io/everest/everest-demo/nodered:${EVEREST_IMAGE_TAG}
|
||||
depends_on:
|
||||
- mqtt-server
|
||||
ports:
|
||||
- 1880:1880
|
||||
environment:
|
||||
- MQTT_SERVER_ADDRESS=mqtt-server
|
||||
- FLOWS=/config/config-sil-two-evse-flow.json
|
||||
networks:
|
||||
- everest_net
|
||||
|
||||
networks:
|
||||
everest_net:
|
||||
driver: bridge
|
||||
enable_ipv6: true
|
||||
75
tools/citrineos-core-main/apps/Server/everest/start.sh
Normal file
75
tools/citrineos-core-main/apps/Server/everest/start.sh
Normal file
@@ -0,0 +1,75 @@
|
||||
#!/bin/sh
|
||||
# SPDX-FileCopyrightText: 2025 Contributors to the CitrineOS Project
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
_OCPP_VERSION=$OCPP_VERSION
|
||||
OCPP_VERSION_ENUM="OCPP201"
|
||||
EVEREST_TARGET_URL="ws://host.docker.internal:8081/cp001"
|
||||
|
||||
case "$_OCPP_VERSION" in
|
||||
"1.6")
|
||||
OCPP_VERSION_ENUM="OCPP16"
|
||||
;;
|
||||
"2.0.1")
|
||||
OCPP_VERSION_ENUM="OCPP201"
|
||||
;;
|
||||
"2.1")
|
||||
OCPP_VERSION_ENUM="OCPP21"
|
||||
;;
|
||||
*)
|
||||
# NOT in the list
|
||||
_OCPP_VERSION="2.0.1"
|
||||
OCPP_VERSION_ENUM="OCPP201"
|
||||
;;
|
||||
esac
|
||||
|
||||
echo $OCPP_VERSION_ENUM
|
||||
|
||||
if [ "$_OCPP_VERSION" != "1.6" ]; then
|
||||
#There are two different configs in Everest that default their settings that we need to override for Citrine.
|
||||
#The first is a temp file that may or may not be used by Everest, but to be safe we change the config there.
|
||||
#The second is the config used by Everest when setting up their image, and this is the primary driver for the configuration of the image.
|
||||
|
||||
CONFIG="$(cat <<JSON
|
||||
[{"configurationSlot": 1, "connectionData": {"messageTimeout": 30, "ocppCsmsUrl": "$EVEREST_TARGET_URL", "ocppInterface": "Wired0", "ocppTransport": "JSON", "ocppVersion": "$OCPP_VERSION_ENUM", "securityProfile": 1}}, {"configurationSlot": 2, "connectionData": {"messageTimeout": 30, "ocppCsmsUrl": "$EVEREST_TARGET_URL", "ocppInterface": "Wired0", "ocppTransport": "JSON", "ocppVersion": "$OCPP_VERSION_ENUM", "securityProfile": 2}}]
|
||||
JSON
|
||||
)"
|
||||
|
||||
chmod +x /tmp/config.json
|
||||
jq --argjson config "$CONFIG" '
|
||||
(.[]
|
||||
| select(.name == "InternalCtrlr")
|
||||
| .variables.NetworkConnectionProfiles.attributes.Actual
|
||||
) = $config
|
||||
' "/tmp/config.json" > /tmp/config_citrine.json && mv /tmp/config_citrine.json "/tmp/config.json"
|
||||
chmod -x /tmp/config.json
|
||||
|
||||
chmod +x /ext/dist/share/everest/modules/OCPP201/component_config/standardized/InternalCtrlr.json
|
||||
jq --argjson config "$CONFIG" '
|
||||
(.
|
||||
| .properties
|
||||
| .NetworkConnectionProfiles
|
||||
| .attributes[]
|
||||
| select(.type == "Actual")
|
||||
| .value
|
||||
) = $config
|
||||
' "/ext/dist/share/everest/modules/OCPP201/component_config/standardized/InternalCtrlr.json" \
|
||||
> /tmp/config_citrine_dist.json && mv /tmp/config_citrine_dist.json "/ext/dist/share/everest/modules/OCPP201/component_config/standardized/InternalCtrlr.json"
|
||||
chmod -x /ext/dist/share/everest/modules/OCPP201/component_config/standardized/InternalCtrlr.json
|
||||
fi
|
||||
|
||||
/entrypoint.sh
|
||||
http-server /tmp/everest_ocpp_logs -p 8888 &
|
||||
|
||||
if [ "$_OCPP_VERSION" = "1.6" ]; then
|
||||
chmod +x /ext/build/run-scripts/run-sil-ocpp.sh
|
||||
sed -i "0,/127.0.0.1:8180\/steve\/websocket\/CentralSystemService\// s|127.0.0.1:8180/steve/websocket/CentralSystemService/|${EVEREST_TARGET_URL}|" /ext/dist/share/everest/modules/OCPP/config-docker.json
|
||||
/ext/build/run-scripts/run-sil-ocpp.sh
|
||||
else
|
||||
#Works for all 2.x versions
|
||||
rm /ext/dist/share/everest/modules/OCPP201/component_config/custom/EVSE_2.json
|
||||
rm /ext/dist/share/everest/modules/OCPP201/component_config/custom/Connector_2_1.json
|
||||
chmod +x /ext/build/run-scripts/run-sil-ocpp201-pnc.sh
|
||||
/ext/build/run-scripts/run-sil-ocpp201-pnc.sh
|
||||
fi
|
||||
@@ -0,0 +1,9 @@
|
||||
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
actions: []
|
||||
custom_types:
|
||||
enums: []
|
||||
input_objects: []
|
||||
objects: []
|
||||
scalars: []
|
||||
@@ -0,0 +1,4 @@
|
||||
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
[]
|
||||
@@ -0,0 +1,4 @@
|
||||
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
{}
|
||||
@@ -0,0 +1,4 @@
|
||||
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
{}
|
||||
@@ -0,0 +1,4 @@
|
||||
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
[]
|
||||
@@ -0,0 +1,17 @@
|
||||
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
- name: default
|
||||
kind: postgres
|
||||
configuration:
|
||||
connection_info:
|
||||
database_url:
|
||||
from_env: HASURA_GRAPHQL_DATABASE_URL
|
||||
isolation_level: read-committed
|
||||
pool_settings:
|
||||
connection_lifetime: 600
|
||||
idle_timeout: 180
|
||||
max_connections: 50
|
||||
retries: 1
|
||||
use_prepared_statements: true
|
||||
tables: "!include default/tables/tables.yaml"
|
||||
@@ -0,0 +1,10 @@
|
||||
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
table:
|
||||
name: AsyncJobStatuses
|
||||
schema: public
|
||||
object_relationships:
|
||||
- name: TenantPartner
|
||||
using:
|
||||
foreign_key_constraint_on: tenantPartnerId
|
||||
@@ -0,0 +1,83 @@
|
||||
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
table:
|
||||
name: Authorizations
|
||||
schema: public
|
||||
object_relationships:
|
||||
- name: GroupAuthorization
|
||||
using:
|
||||
foreign_key_constraint_on: groupAuthorizationId
|
||||
- name: Tariff
|
||||
using:
|
||||
foreign_key_constraint_on: tariffId
|
||||
- name: Tenant
|
||||
using:
|
||||
foreign_key_constraint_on: tenantId
|
||||
- name: TenantPartner
|
||||
using:
|
||||
foreign_key_constraint_on: tenantPartnerId
|
||||
array_relationships:
|
||||
- name: Authorizations
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: groupAuthorizationId
|
||||
table:
|
||||
name: Authorizations
|
||||
schema: public
|
||||
- name: LocalListAuthorizations
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: authorizationId
|
||||
table:
|
||||
name: LocalListAuthorizations
|
||||
schema: public
|
||||
- name: Transactions
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: authorizationId
|
||||
table:
|
||||
name: Transactions
|
||||
schema: public
|
||||
- name: localListAuthorizationsByGroupauthorizationid
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: groupAuthorizationId
|
||||
table:
|
||||
name: LocalListAuthorizations
|
||||
schema: public
|
||||
insert_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
check:
|
||||
tenantId:
|
||||
_eq: x-hasura-tenant-id
|
||||
columns: '*'
|
||||
comment: ""
|
||||
select_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
columns: '*'
|
||||
filter:
|
||||
tenantId:
|
||||
_eq: x-hasura-tenant-id
|
||||
allow_aggregations: true
|
||||
comment: ""
|
||||
update_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
columns: '*'
|
||||
filter:
|
||||
tenantId:
|
||||
_eq: x-hasura-tenant-id
|
||||
check:
|
||||
tenantId:
|
||||
_eq: x-hasura-tenant-id
|
||||
comment: ""
|
||||
delete_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
filter:
|
||||
tenantId:
|
||||
_eq: x-hasura-tenant-id
|
||||
comment: ""
|
||||
@@ -0,0 +1,53 @@
|
||||
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
table:
|
||||
name: Boots
|
||||
schema: public
|
||||
object_relationships:
|
||||
- name: Tenant
|
||||
using:
|
||||
foreign_key_constraint_on: tenantId
|
||||
array_relationships:
|
||||
- name: VariableAttributes
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: bootConfigId
|
||||
table:
|
||||
name: VariableAttributes
|
||||
schema: public
|
||||
insert_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
check:
|
||||
tenantId:
|
||||
_eq: x-hasura-tenant-id
|
||||
columns: '*'
|
||||
comment: ""
|
||||
select_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
columns: '*'
|
||||
filter:
|
||||
tenantId:
|
||||
_eq: x-hasura-tenant-id
|
||||
allow_aggregations: true
|
||||
comment: ""
|
||||
update_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
columns: '*'
|
||||
filter:
|
||||
tenantId:
|
||||
_eq: x-hasura-tenant-id
|
||||
check:
|
||||
tenantId:
|
||||
_eq: x-hasura-tenant-id
|
||||
comment: ""
|
||||
delete_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
filter:
|
||||
tenantId:
|
||||
_eq: x-hasura-tenant-id
|
||||
comment: ""
|
||||
@@ -0,0 +1,70 @@
|
||||
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
table:
|
||||
name: Certificates
|
||||
schema: public
|
||||
object_relationships:
|
||||
- name: Certificate
|
||||
using:
|
||||
foreign_key_constraint_on: signedBy
|
||||
- name: Tenant
|
||||
using:
|
||||
foreign_key_constraint_on: tenantId
|
||||
array_relationships:
|
||||
- name: Certificates
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: signedBy
|
||||
table:
|
||||
name: Certificates
|
||||
schema: public
|
||||
- name: InstallCertificateAttempts
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: certificateId
|
||||
table:
|
||||
name: InstallCertificateAttempts
|
||||
schema: public
|
||||
- name: InstalledCertificates
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: certificateId
|
||||
table:
|
||||
name: InstalledCertificates
|
||||
schema: public
|
||||
insert_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
check:
|
||||
tenantId:
|
||||
_eq: x-hasura-tenant-id
|
||||
columns: '*'
|
||||
comment: ""
|
||||
select_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
columns: '*'
|
||||
filter:
|
||||
tenantId:
|
||||
_eq: x-hasura-tenant-id
|
||||
allow_aggregations: true
|
||||
comment: ""
|
||||
update_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
columns: '*'
|
||||
filter:
|
||||
tenantId:
|
||||
_eq: x-hasura-tenant-id
|
||||
check:
|
||||
tenantId:
|
||||
_eq: x-hasura-tenant-id
|
||||
comment: ""
|
||||
delete_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
filter:
|
||||
tenantId:
|
||||
_eq: x-hasura-tenant-id
|
||||
comment: ""
|
||||
@@ -0,0 +1,45 @@
|
||||
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
table:
|
||||
name: ChangeConfigurations
|
||||
schema: public
|
||||
object_relationships:
|
||||
- name: Tenant
|
||||
using:
|
||||
foreign_key_constraint_on: tenantId
|
||||
insert_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
check:
|
||||
tenantId:
|
||||
_eq: x-hasura-tenant-id
|
||||
columns: '*'
|
||||
comment: ""
|
||||
select_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
columns: '*'
|
||||
filter:
|
||||
tenantId:
|
||||
_eq: x-hasura-tenant-id
|
||||
allow_aggregations: true
|
||||
comment: ""
|
||||
update_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
columns: '*'
|
||||
filter:
|
||||
tenantId:
|
||||
_eq: x-hasura-tenant-id
|
||||
check:
|
||||
tenantId:
|
||||
_eq: x-hasura-tenant-id
|
||||
comment: ""
|
||||
delete_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
filter:
|
||||
tenantId:
|
||||
_eq: x-hasura-tenant-id
|
||||
comment: ""
|
||||
@@ -0,0 +1,25 @@
|
||||
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
table:
|
||||
name: ChargingNeeds
|
||||
schema: public
|
||||
object_relationships:
|
||||
- name: Evse
|
||||
using:
|
||||
foreign_key_constraint_on: evseId
|
||||
- name: Tenant
|
||||
using:
|
||||
foreign_key_constraint_on: tenantId
|
||||
- name: Transaction
|
||||
using:
|
||||
foreign_key_constraint_on: transactionDatabaseId
|
||||
select_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
columns: '*'
|
||||
filter:
|
||||
tenantId:
|
||||
_eq: x-hasura-tenant-id
|
||||
allow_aggregations: true
|
||||
comment: ""
|
||||
@@ -0,0 +1,56 @@
|
||||
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
table:
|
||||
name: ChargingProfiles
|
||||
schema: public
|
||||
object_relationships:
|
||||
- name: Tenant
|
||||
using:
|
||||
foreign_key_constraint_on: tenantId
|
||||
- name: Transaction
|
||||
using:
|
||||
foreign_key_constraint_on: transactionDatabaseId
|
||||
array_relationships:
|
||||
- name: ChargingSchedules
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: chargingProfileDatabaseId
|
||||
table:
|
||||
name: ChargingSchedules
|
||||
schema: public
|
||||
insert_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
check:
|
||||
tenantId:
|
||||
_eq: x-hasura-tenant-id
|
||||
columns: '*'
|
||||
comment: ""
|
||||
select_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
columns: '*'
|
||||
filter:
|
||||
tenantId:
|
||||
_eq: x-hasura-tenant-id
|
||||
allow_aggregations: true
|
||||
comment: ""
|
||||
update_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
columns: '*'
|
||||
filter:
|
||||
tenantId:
|
||||
_eq: x-hasura-tenant-id
|
||||
check:
|
||||
tenantId:
|
||||
_eq: x-hasura-tenant-id
|
||||
comment: ""
|
||||
delete_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
filter:
|
||||
tenantId:
|
||||
_eq: x-hasura-tenant-id
|
||||
comment: ""
|
||||
@@ -0,0 +1,56 @@
|
||||
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
table:
|
||||
name: ChargingSchedules
|
||||
schema: public
|
||||
object_relationships:
|
||||
- name: ChargingProfile
|
||||
using:
|
||||
foreign_key_constraint_on: chargingProfileDatabaseId
|
||||
- name: Tenant
|
||||
using:
|
||||
foreign_key_constraint_on: tenantId
|
||||
array_relationships:
|
||||
- name: SalesTariffs
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: chargingScheduleDatabaseId
|
||||
table:
|
||||
name: SalesTariffs
|
||||
schema: public
|
||||
insert_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
check:
|
||||
tenantId:
|
||||
_eq: x-hasura-tenant-id
|
||||
columns: '*'
|
||||
comment: ""
|
||||
select_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
columns: '*'
|
||||
filter:
|
||||
tenantId:
|
||||
_eq: x-hasura-tenant-id
|
||||
allow_aggregations: true
|
||||
comment: ""
|
||||
update_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
columns: '*'
|
||||
filter:
|
||||
tenantId:
|
||||
_eq: x-hasura-tenant-id
|
||||
check:
|
||||
tenantId:
|
||||
_eq: x-hasura-tenant-id
|
||||
comment: ""
|
||||
delete_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
filter:
|
||||
tenantId:
|
||||
_eq: x-hasura-tenant-id
|
||||
comment: ""
|
||||
@@ -0,0 +1,28 @@
|
||||
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
table:
|
||||
name: ChargingStationNetworkProfiles
|
||||
schema: public
|
||||
object_relationships:
|
||||
- name: ChargingStation
|
||||
using:
|
||||
foreign_key_constraint_on: stationId
|
||||
- name: ServerNetworkProfile
|
||||
using:
|
||||
foreign_key_constraint_on: websocketServerConfigId
|
||||
- name: SetNetworkProfile
|
||||
using:
|
||||
foreign_key_constraint_on: setNetworkProfileId
|
||||
- name: Tenant
|
||||
using:
|
||||
foreign_key_constraint_on: tenantId
|
||||
select_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
columns: '*'
|
||||
filter:
|
||||
tenantId:
|
||||
_eq: x-hasura-tenant-id
|
||||
allow_aggregations: true
|
||||
comment: ""
|
||||
@@ -0,0 +1,22 @@
|
||||
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
table:
|
||||
name: ChargingStationSecurityInfos
|
||||
schema: public
|
||||
object_relationships:
|
||||
- name: ChargingStation
|
||||
using:
|
||||
foreign_key_constraint_on: stationId
|
||||
- name: Tenant
|
||||
using:
|
||||
foreign_key_constraint_on: tenantId
|
||||
select_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
columns: '*'
|
||||
filter:
|
||||
tenantId:
|
||||
_eq: x-hasura-tenant-id
|
||||
allow_aggregations: true
|
||||
comment: ""
|
||||
@@ -0,0 +1,22 @@
|
||||
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
table:
|
||||
name: ChargingStationSequences
|
||||
schema: public
|
||||
object_relationships:
|
||||
- name: ChargingStation
|
||||
using:
|
||||
foreign_key_constraint_on: stationId
|
||||
- name: Tenant
|
||||
using:
|
||||
foreign_key_constraint_on: tenantId
|
||||
select_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
columns: '*'
|
||||
filter:
|
||||
tenantId:
|
||||
_eq: x-hasura-tenant-id
|
||||
allow_aggregations: true
|
||||
comment: ""
|
||||
@@ -0,0 +1,161 @@
|
||||
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
table:
|
||||
name: ChargingStations
|
||||
schema: public
|
||||
object_relationships:
|
||||
- name: Location
|
||||
using:
|
||||
foreign_key_constraint_on: locationId
|
||||
- name: Tenant
|
||||
using:
|
||||
foreign_key_constraint_on: tenantId
|
||||
array_relationships:
|
||||
- name: ChargingStationNetworkProfiles
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: stationId
|
||||
table:
|
||||
name: ChargingStationNetworkProfiles
|
||||
schema: public
|
||||
- name: ChargingStationSecurityInfos
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: stationId
|
||||
table:
|
||||
name: ChargingStationSecurityInfos
|
||||
schema: public
|
||||
- name: ChargingStationSequences
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: stationId
|
||||
table:
|
||||
name: ChargingStationSequences
|
||||
schema: public
|
||||
- name: Connectors
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: stationId
|
||||
table:
|
||||
name: Connectors
|
||||
schema: public
|
||||
- name: DeleteCertificateAttempts
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: stationId
|
||||
table:
|
||||
name: DeleteCertificateAttempts
|
||||
schema: public
|
||||
- name: EventData
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: stationId
|
||||
table:
|
||||
name: EventData
|
||||
schema: public
|
||||
- name: Evses
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: stationId
|
||||
table:
|
||||
name: Evses
|
||||
schema: public
|
||||
- name: InstallCertificateAttempts
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: stationId
|
||||
table:
|
||||
name: InstallCertificateAttempts
|
||||
schema: public
|
||||
- name: InstalledCertificates
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: stationId
|
||||
table:
|
||||
name: InstalledCertificates
|
||||
schema: public
|
||||
- name: LatestStatusNotifications
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: stationId
|
||||
table:
|
||||
name: LatestStatusNotifications
|
||||
schema: public
|
||||
- name: OCPPMessages
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: stationId
|
||||
table:
|
||||
name: OCPPMessages
|
||||
schema: public
|
||||
- name: SetNetworkProfiles
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: stationId
|
||||
table:
|
||||
name: SetNetworkProfiles
|
||||
schema: public
|
||||
- name: StatusNotifications
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: stationId
|
||||
table:
|
||||
name: StatusNotifications
|
||||
schema: public
|
||||
- name: Transactions
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: stationId
|
||||
table:
|
||||
name: Transactions
|
||||
schema: public
|
||||
- name: VariableAttributes
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: stationId
|
||||
table:
|
||||
name: VariableAttributes
|
||||
schema: public
|
||||
- name: VariableMonitorings
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: stationId
|
||||
table:
|
||||
name: VariableMonitorings
|
||||
schema: public
|
||||
insert_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
check:
|
||||
tenantId:
|
||||
_eq: x-hasura-tenant-id
|
||||
columns: '*'
|
||||
comment: ""
|
||||
select_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
columns: '*'
|
||||
filter:
|
||||
tenantId:
|
||||
_eq: x-hasura-tenant-id
|
||||
allow_aggregations: true
|
||||
comment: ""
|
||||
update_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
columns: '*'
|
||||
filter:
|
||||
tenantId:
|
||||
_eq: x-hasura-tenant-id
|
||||
check:
|
||||
tenantId:
|
||||
_eq: x-hasura-tenant-id
|
||||
comment: ""
|
||||
delete_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
filter:
|
||||
tenantId:
|
||||
_eq: x-hasura-tenant-id
|
||||
comment: ""
|
||||
@@ -0,0 +1,51 @@
|
||||
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
table:
|
||||
name: ComponentVariables
|
||||
schema: public
|
||||
object_relationships:
|
||||
- name: Component
|
||||
using:
|
||||
foreign_key_constraint_on: componentId
|
||||
- name: Tenant
|
||||
using:
|
||||
foreign_key_constraint_on: tenantId
|
||||
- name: Variable
|
||||
using:
|
||||
foreign_key_constraint_on: variableId
|
||||
insert_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
check:
|
||||
tenantId:
|
||||
_eq: x-hasura-tenant-id
|
||||
columns: '*'
|
||||
comment: ""
|
||||
select_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
columns: '*'
|
||||
filter:
|
||||
tenantId:
|
||||
_eq: x-hasura-tenant-id
|
||||
allow_aggregations: true
|
||||
comment: ""
|
||||
update_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
columns: '*'
|
||||
filter:
|
||||
tenantId:
|
||||
_eq: x-hasura-tenant-id
|
||||
check:
|
||||
tenantId:
|
||||
_eq: x-hasura-tenant-id
|
||||
comment: ""
|
||||
delete_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
filter:
|
||||
tenantId:
|
||||
_eq: x-hasura-tenant-id
|
||||
comment: ""
|
||||
@@ -0,0 +1,84 @@
|
||||
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
table:
|
||||
name: Components
|
||||
schema: public
|
||||
object_relationships:
|
||||
- name: EvseType
|
||||
using:
|
||||
foreign_key_constraint_on: evseDatabaseId
|
||||
- name: Tenant
|
||||
using:
|
||||
foreign_key_constraint_on: tenantId
|
||||
array_relationships:
|
||||
- name: ComponentVariables
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: componentId
|
||||
table:
|
||||
name: ComponentVariables
|
||||
schema: public
|
||||
- name: EventData
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: componentId
|
||||
table:
|
||||
name: EventData
|
||||
schema: public
|
||||
- name: MessageInfos
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: displayComponentId
|
||||
table:
|
||||
name: MessageInfos
|
||||
schema: public
|
||||
- name: VariableAttributes
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: componentId
|
||||
table:
|
||||
name: VariableAttributes
|
||||
schema: public
|
||||
- name: VariableMonitorings
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: componentId
|
||||
table:
|
||||
name: VariableMonitorings
|
||||
schema: public
|
||||
insert_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
check:
|
||||
tenantId:
|
||||
_eq: x-hasura-tenant-id
|
||||
columns: '*'
|
||||
comment: ""
|
||||
select_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
columns: '*'
|
||||
filter:
|
||||
tenantId:
|
||||
_eq: x-hasura-tenant-id
|
||||
allow_aggregations: true
|
||||
comment: ""
|
||||
update_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
columns: '*'
|
||||
filter:
|
||||
tenantId:
|
||||
_eq: x-hasura-tenant-id
|
||||
check:
|
||||
tenantId:
|
||||
_eq: x-hasura-tenant-id
|
||||
comment: ""
|
||||
delete_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
filter:
|
||||
tenantId:
|
||||
_eq: x-hasura-tenant-id
|
||||
comment: ""
|
||||
@@ -0,0 +1,45 @@
|
||||
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
table:
|
||||
name: CompositeSchedules
|
||||
schema: public
|
||||
object_relationships:
|
||||
- name: Tenant
|
||||
using:
|
||||
foreign_key_constraint_on: tenantId
|
||||
insert_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
check:
|
||||
tenantId:
|
||||
_eq: x-hasura-tenant-id
|
||||
columns: '*'
|
||||
comment: ""
|
||||
select_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
columns: '*'
|
||||
filter:
|
||||
tenantId:
|
||||
_eq: x-hasura-tenant-id
|
||||
allow_aggregations: true
|
||||
comment: ""
|
||||
update_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
columns: '*'
|
||||
filter:
|
||||
tenantId:
|
||||
_eq: x-hasura-tenant-id
|
||||
check:
|
||||
tenantId:
|
||||
_eq: x-hasura-tenant-id
|
||||
comment: ""
|
||||
delete_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
filter:
|
||||
tenantId:
|
||||
_eq: x-hasura-tenant-id
|
||||
comment: ""
|
||||
@@ -0,0 +1,69 @@
|
||||
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
table:
|
||||
name: Connectors
|
||||
schema: public
|
||||
object_relationships:
|
||||
- name: ChargingStation
|
||||
using:
|
||||
foreign_key_constraint_on: stationId
|
||||
- name: Evse
|
||||
using:
|
||||
foreign_key_constraint_on: evseId
|
||||
- name: Tariff
|
||||
using:
|
||||
foreign_key_constraint_on: tariffId
|
||||
- name: Tenant
|
||||
using:
|
||||
foreign_key_constraint_on: tenantId
|
||||
array_relationships:
|
||||
- name: StartTransactions
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: connectorDatabaseId
|
||||
table:
|
||||
name: StartTransactions
|
||||
schema: public
|
||||
- name: Transactions
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: connectorId
|
||||
table:
|
||||
name: Transactions
|
||||
schema: public
|
||||
insert_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
check:
|
||||
tenantId:
|
||||
_eq: x-hasura-tenant-id
|
||||
columns: '*'
|
||||
comment: ""
|
||||
select_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
columns: '*'
|
||||
filter:
|
||||
tenantId:
|
||||
_eq: x-hasura-tenant-id
|
||||
allow_aggregations: true
|
||||
comment: ""
|
||||
update_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
columns: '*'
|
||||
filter:
|
||||
tenantId:
|
||||
_eq: x-hasura-tenant-id
|
||||
check:
|
||||
tenantId:
|
||||
_eq: x-hasura-tenant-id
|
||||
comment: ""
|
||||
delete_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
filter:
|
||||
tenantId:
|
||||
_eq: x-hasura-tenant-id
|
||||
comment: ""
|
||||
@@ -0,0 +1,13 @@
|
||||
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
table:
|
||||
name: DeleteCertificateAttempts
|
||||
schema: public
|
||||
object_relationships:
|
||||
- name: ChargingStation
|
||||
using:
|
||||
foreign_key_constraint_on: stationId
|
||||
- name: Tenant
|
||||
using:
|
||||
foreign_key_constraint_on: tenantId
|
||||
@@ -0,0 +1,28 @@
|
||||
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
table:
|
||||
name: EventData
|
||||
schema: public
|
||||
object_relationships:
|
||||
- name: ChargingStation
|
||||
using:
|
||||
foreign_key_constraint_on: stationId
|
||||
- name: Component
|
||||
using:
|
||||
foreign_key_constraint_on: componentId
|
||||
- name: Tenant
|
||||
using:
|
||||
foreign_key_constraint_on: tenantId
|
||||
- name: Variable
|
||||
using:
|
||||
foreign_key_constraint_on: variableId
|
||||
select_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
columns: '*'
|
||||
filter:
|
||||
tenantId:
|
||||
_eq: x-hasura-tenant-id
|
||||
allow_aggregations: true
|
||||
comment: ""
|
||||
@@ -0,0 +1,48 @@
|
||||
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
table:
|
||||
name: EvseTypes
|
||||
schema: public
|
||||
object_relationships:
|
||||
- name: Tenant
|
||||
using:
|
||||
foreign_key_constraint_on: tenantId
|
||||
array_relationships:
|
||||
- name: Components
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: evseDatabaseId
|
||||
table:
|
||||
name: Components
|
||||
schema: public
|
||||
- name: Reservations
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: evseId
|
||||
table:
|
||||
name: Reservations
|
||||
schema: public
|
||||
- name: TransactionEvents
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: evseId
|
||||
table:
|
||||
name: TransactionEvents
|
||||
schema: public
|
||||
- name: VariableAttributes
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: evseDatabaseId
|
||||
table:
|
||||
name: VariableAttributes
|
||||
schema: public
|
||||
select_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
columns: '*'
|
||||
filter:
|
||||
tenantId:
|
||||
_eq: x-hasura-tenant-id
|
||||
allow_aggregations: true
|
||||
comment: ""
|
||||
@@ -0,0 +1,70 @@
|
||||
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
table:
|
||||
name: Evses
|
||||
schema: public
|
||||
object_relationships:
|
||||
- name: ChargingStation
|
||||
using:
|
||||
foreign_key_constraint_on: stationId
|
||||
- name: Tenant
|
||||
using:
|
||||
foreign_key_constraint_on: tenantId
|
||||
array_relationships:
|
||||
- name: ChargingNeeds
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: evseId
|
||||
table:
|
||||
name: ChargingNeeds
|
||||
schema: public
|
||||
- name: Connectors
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: evseId
|
||||
table:
|
||||
name: Connectors
|
||||
schema: public
|
||||
- name: Transactions
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: evseId
|
||||
table:
|
||||
name: Transactions
|
||||
schema: public
|
||||
insert_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
check:
|
||||
tenantId:
|
||||
_eq: x-hasura-tenant-id
|
||||
columns: '*'
|
||||
comment: ""
|
||||
select_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
columns: '*'
|
||||
filter:
|
||||
tenantId:
|
||||
_eq: x-hasura-tenant-id
|
||||
allow_aggregations: true
|
||||
comment: ""
|
||||
update_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
columns: '*'
|
||||
filter:
|
||||
tenantId:
|
||||
_eq: x-hasura-tenant-id
|
||||
check:
|
||||
tenantId:
|
||||
_eq: x-hasura-tenant-id
|
||||
comment: ""
|
||||
delete_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
filter:
|
||||
tenantId:
|
||||
_eq: x-hasura-tenant-id
|
||||
comment: ""
|
||||
@@ -0,0 +1,16 @@
|
||||
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
table:
|
||||
name: InstallCertificateAttempts
|
||||
schema: public
|
||||
object_relationships:
|
||||
- name: Certificate
|
||||
using:
|
||||
foreign_key_constraint_on: certificateId
|
||||
- name: ChargingStation
|
||||
using:
|
||||
foreign_key_constraint_on: stationId
|
||||
- name: Tenant
|
||||
using:
|
||||
foreign_key_constraint_on: tenantId
|
||||
@@ -0,0 +1,25 @@
|
||||
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
table:
|
||||
name: InstalledCertificates
|
||||
schema: public
|
||||
object_relationships:
|
||||
- name: Certificate
|
||||
using:
|
||||
foreign_key_constraint_on: certificateId
|
||||
- name: ChargingStation
|
||||
using:
|
||||
foreign_key_constraint_on: stationId
|
||||
- name: Tenant
|
||||
using:
|
||||
foreign_key_constraint_on: tenantId
|
||||
select_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
columns: '*'
|
||||
filter:
|
||||
tenantId:
|
||||
_eq: x-hasura-tenant-id
|
||||
allow_aggregations: true
|
||||
comment: ""
|
||||
@@ -0,0 +1,25 @@
|
||||
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
table:
|
||||
name: LatestStatusNotifications
|
||||
schema: public
|
||||
object_relationships:
|
||||
- name: ChargingStation
|
||||
using:
|
||||
foreign_key_constraint_on: stationId
|
||||
- name: StatusNotification
|
||||
using:
|
||||
foreign_key_constraint_on: statusNotificationId
|
||||
- name: Tenant
|
||||
using:
|
||||
foreign_key_constraint_on: tenantId
|
||||
select_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
columns: '*'
|
||||
filter:
|
||||
tenantId:
|
||||
_eq: x-hasura-tenant-id
|
||||
allow_aggregations: true
|
||||
comment: ""
|
||||
@@ -0,0 +1,40 @@
|
||||
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
table:
|
||||
name: LocalListAuthorizations
|
||||
schema: public
|
||||
object_relationships:
|
||||
- name: Authorization
|
||||
using:
|
||||
foreign_key_constraint_on: authorizationId
|
||||
- name: Tenant
|
||||
using:
|
||||
foreign_key_constraint_on: tenantId
|
||||
- name: authorizationByGroupauthorizationid
|
||||
using:
|
||||
foreign_key_constraint_on: groupAuthorizationId
|
||||
array_relationships:
|
||||
- name: LocalListVersionAuthorizations
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: authorizationId
|
||||
table:
|
||||
name: LocalListVersionAuthorizations
|
||||
schema: public
|
||||
- name: SendLocalListAuthorizations
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: authorizationId
|
||||
table:
|
||||
name: SendLocalListAuthorizations
|
||||
schema: public
|
||||
select_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
columns: '*'
|
||||
filter:
|
||||
tenantId:
|
||||
_eq: x-hasura-tenant-id
|
||||
allow_aggregations: true
|
||||
comment: ""
|
||||
@@ -0,0 +1,25 @@
|
||||
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
table:
|
||||
name: LocalListVersionAuthorizations
|
||||
schema: public
|
||||
object_relationships:
|
||||
- name: LocalListAuthorization
|
||||
using:
|
||||
foreign_key_constraint_on: authorizationId
|
||||
- name: LocalListVersion
|
||||
using:
|
||||
foreign_key_constraint_on: localListVersionId
|
||||
- name: Tenant
|
||||
using:
|
||||
foreign_key_constraint_on: tenantId
|
||||
select_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
columns: '*'
|
||||
filter:
|
||||
tenantId:
|
||||
_eq: x-hasura-tenant-id
|
||||
allow_aggregations: true
|
||||
comment: ""
|
||||
@@ -0,0 +1,27 @@
|
||||
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
table:
|
||||
name: LocalListVersions
|
||||
schema: public
|
||||
object_relationships:
|
||||
- name: Tenant
|
||||
using:
|
||||
foreign_key_constraint_on: tenantId
|
||||
array_relationships:
|
||||
- name: LocalListVersionAuthorizations
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: localListVersionId
|
||||
table:
|
||||
name: LocalListVersionAuthorizations
|
||||
schema: public
|
||||
select_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
columns: '*'
|
||||
filter:
|
||||
tenantId:
|
||||
_eq: x-hasura-tenant-id
|
||||
allow_aggregations: true
|
||||
comment: ""
|
||||
@@ -0,0 +1,60 @@
|
||||
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
table:
|
||||
name: Locations
|
||||
schema: public
|
||||
object_relationships:
|
||||
- name: Tenant
|
||||
using:
|
||||
foreign_key_constraint_on: tenantId
|
||||
array_relationships:
|
||||
- name: ChargingStations
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: locationId
|
||||
table:
|
||||
name: ChargingStations
|
||||
schema: public
|
||||
- name: Transactions
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: locationId
|
||||
table:
|
||||
name: Transactions
|
||||
schema: public
|
||||
insert_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
check:
|
||||
tenantId:
|
||||
_eq: x-hasura-tenant-id
|
||||
columns: '*'
|
||||
comment: ""
|
||||
select_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
columns: '*'
|
||||
filter:
|
||||
tenantId:
|
||||
_eq: x-hasura-tenant-id
|
||||
allow_aggregations: true
|
||||
comment: ""
|
||||
update_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
columns: '*'
|
||||
filter:
|
||||
tenantId:
|
||||
_eq: x-hasura-tenant-id
|
||||
check:
|
||||
tenantId:
|
||||
_eq: x-hasura-tenant-id
|
||||
comment: ""
|
||||
delete_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
filter:
|
||||
tenantId:
|
||||
_eq: x-hasura-tenant-id
|
||||
comment: ""
|
||||
@@ -0,0 +1,22 @@
|
||||
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
table:
|
||||
name: MessageInfos
|
||||
schema: public
|
||||
object_relationships:
|
||||
- name: Component
|
||||
using:
|
||||
foreign_key_constraint_on: displayComponentId
|
||||
- name: Tenant
|
||||
using:
|
||||
foreign_key_constraint_on: tenantId
|
||||
select_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
columns: '*'
|
||||
filter:
|
||||
tenantId:
|
||||
_eq: x-hasura-tenant-id
|
||||
allow_aggregations: true
|
||||
comment: ""
|
||||
@@ -0,0 +1,31 @@
|
||||
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
table:
|
||||
name: MeterValues
|
||||
schema: public
|
||||
object_relationships:
|
||||
- name: StopTransaction
|
||||
using:
|
||||
foreign_key_constraint_on: stopTransactionDatabaseId
|
||||
- name: Tariff
|
||||
using:
|
||||
foreign_key_constraint_on: tariffId
|
||||
- name: Tenant
|
||||
using:
|
||||
foreign_key_constraint_on: tenantId
|
||||
- name: Transaction
|
||||
using:
|
||||
foreign_key_constraint_on: transactionDatabaseId
|
||||
- name: TransactionEvent
|
||||
using:
|
||||
foreign_key_constraint_on: transactionEventId
|
||||
select_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
columns: '*'
|
||||
filter:
|
||||
tenantId:
|
||||
_eq: x-hasura-tenant-id
|
||||
allow_aggregations: true
|
||||
comment: ""
|
||||
@@ -0,0 +1,33 @@
|
||||
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
table:
|
||||
name: OCPPMessages
|
||||
schema: public
|
||||
object_relationships:
|
||||
- name: ChargingStation
|
||||
using:
|
||||
foreign_key_constraint_on: stationId
|
||||
- name: OCPPMessage
|
||||
using:
|
||||
foreign_key_constraint_on: requestMessageId
|
||||
- name: Tenant
|
||||
using:
|
||||
foreign_key_constraint_on: tenantId
|
||||
array_relationships:
|
||||
- name: OCPPMessages
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: requestMessageId
|
||||
table:
|
||||
name: OCPPMessages
|
||||
schema: public
|
||||
select_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
columns: '*'
|
||||
filter:
|
||||
tenantId:
|
||||
_eq: x-hasura-tenant-id
|
||||
allow_aggregations: true
|
||||
comment: ""
|
||||
@@ -0,0 +1,22 @@
|
||||
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
table:
|
||||
name: Reservations
|
||||
schema: public
|
||||
object_relationships:
|
||||
- name: EvseType
|
||||
using:
|
||||
foreign_key_constraint_on: evseId
|
||||
- name: Tenant
|
||||
using:
|
||||
foreign_key_constraint_on: tenantId
|
||||
select_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
columns: '*'
|
||||
filter:
|
||||
tenantId:
|
||||
_eq: x-hasura-tenant-id
|
||||
allow_aggregations: true
|
||||
comment: ""
|
||||
@@ -0,0 +1,22 @@
|
||||
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
table:
|
||||
name: SalesTariffs
|
||||
schema: public
|
||||
object_relationships:
|
||||
- name: ChargingSchedule
|
||||
using:
|
||||
foreign_key_constraint_on: chargingScheduleDatabaseId
|
||||
- name: Tenant
|
||||
using:
|
||||
foreign_key_constraint_on: tenantId
|
||||
select_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
columns: '*'
|
||||
filter:
|
||||
tenantId:
|
||||
_eq: x-hasura-tenant-id
|
||||
allow_aggregations: true
|
||||
comment: ""
|
||||
@@ -0,0 +1,19 @@
|
||||
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
table:
|
||||
name: SecurityEvents
|
||||
schema: public
|
||||
object_relationships:
|
||||
- name: Tenant
|
||||
using:
|
||||
foreign_key_constraint_on: tenantId
|
||||
select_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
columns: '*'
|
||||
filter:
|
||||
tenantId:
|
||||
_eq: x-hasura-tenant-id
|
||||
allow_aggregations: true
|
||||
comment: ""
|
||||
@@ -0,0 +1,25 @@
|
||||
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
table:
|
||||
name: SendLocalListAuthorizations
|
||||
schema: public
|
||||
object_relationships:
|
||||
- name: LocalListAuthorization
|
||||
using:
|
||||
foreign_key_constraint_on: authorizationId
|
||||
- name: SendLocalList
|
||||
using:
|
||||
foreign_key_constraint_on: sendLocalListId
|
||||
- name: Tenant
|
||||
using:
|
||||
foreign_key_constraint_on: tenantId
|
||||
select_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
columns: '*'
|
||||
filter:
|
||||
tenantId:
|
||||
_eq: x-hasura-tenant-id
|
||||
allow_aggregations: true
|
||||
comment: ""
|
||||
@@ -0,0 +1,27 @@
|
||||
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
table:
|
||||
name: SendLocalLists
|
||||
schema: public
|
||||
object_relationships:
|
||||
- name: Tenant
|
||||
using:
|
||||
foreign_key_constraint_on: tenantId
|
||||
array_relationships:
|
||||
- name: SendLocalListAuthorizations
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: sendLocalListId
|
||||
table:
|
||||
name: SendLocalListAuthorizations
|
||||
schema: public
|
||||
select_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
columns: '*'
|
||||
filter:
|
||||
tenantId:
|
||||
_eq: x-hasura-tenant-id
|
||||
allow_aggregations: true
|
||||
comment: ""
|
||||
@@ -0,0 +1,6 @@
|
||||
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
table:
|
||||
name: SequelizeMeta
|
||||
schema: public
|
||||
@@ -0,0 +1,34 @@
|
||||
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
table:
|
||||
name: ServerNetworkProfiles
|
||||
schema: public
|
||||
object_relationships:
|
||||
- name: Tenant
|
||||
using:
|
||||
foreign_key_constraint_on: tenantId
|
||||
array_relationships:
|
||||
- name: ChargingStationNetworkProfiles
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: websocketServerConfigId
|
||||
table:
|
||||
name: ChargingStationNetworkProfiles
|
||||
schema: public
|
||||
- name: SetNetworkProfiles
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: websocketServerConfigId
|
||||
table:
|
||||
name: SetNetworkProfiles
|
||||
schema: public
|
||||
select_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
columns: '*'
|
||||
filter:
|
||||
tenantId:
|
||||
_eq: x-hasura-tenant-id
|
||||
allow_aggregations: true
|
||||
comment: ""
|
||||
@@ -0,0 +1,33 @@
|
||||
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
table:
|
||||
name: SetNetworkProfiles
|
||||
schema: public
|
||||
object_relationships:
|
||||
- name: ChargingStation
|
||||
using:
|
||||
foreign_key_constraint_on: stationId
|
||||
- name: ServerNetworkProfile
|
||||
using:
|
||||
foreign_key_constraint_on: websocketServerConfigId
|
||||
- name: Tenant
|
||||
using:
|
||||
foreign_key_constraint_on: tenantId
|
||||
array_relationships:
|
||||
- name: ChargingStationNetworkProfiles
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: setNetworkProfileId
|
||||
table:
|
||||
name: ChargingStationNetworkProfiles
|
||||
schema: public
|
||||
select_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
columns: '*'
|
||||
filter:
|
||||
tenantId:
|
||||
_eq: x-hasura-tenant-id
|
||||
allow_aggregations: true
|
||||
comment: ""
|
||||
@@ -0,0 +1,51 @@
|
||||
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
table:
|
||||
name: StartTransactions
|
||||
schema: public
|
||||
object_relationships:
|
||||
- name: Connector
|
||||
using:
|
||||
foreign_key_constraint_on: connectorDatabaseId
|
||||
- name: Tenant
|
||||
using:
|
||||
foreign_key_constraint_on: tenantId
|
||||
- name: Transaction
|
||||
using:
|
||||
foreign_key_constraint_on: transactionDatabaseId
|
||||
insert_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
check:
|
||||
tenantId:
|
||||
_eq: x-hasura-tenant-id
|
||||
columns: '*'
|
||||
comment: ""
|
||||
select_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
columns: '*'
|
||||
filter:
|
||||
tenantId:
|
||||
_eq: x-hasura-tenant-id
|
||||
allow_aggregations: true
|
||||
comment: ""
|
||||
update_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
columns: '*'
|
||||
filter:
|
||||
tenantId:
|
||||
_eq: x-hasura-tenant-id
|
||||
check:
|
||||
tenantId:
|
||||
_eq: x-hasura-tenant-id
|
||||
comment: ""
|
||||
delete_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
filter:
|
||||
tenantId:
|
||||
_eq: x-hasura-tenant-id
|
||||
comment: ""
|
||||
@@ -0,0 +1,30 @@
|
||||
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
table:
|
||||
name: StatusNotifications
|
||||
schema: public
|
||||
object_relationships:
|
||||
- name: ChargingStation
|
||||
using:
|
||||
foreign_key_constraint_on: stationId
|
||||
- name: Tenant
|
||||
using:
|
||||
foreign_key_constraint_on: tenantId
|
||||
array_relationships:
|
||||
- name: LatestStatusNotifications
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: statusNotificationId
|
||||
table:
|
||||
name: LatestStatusNotifications
|
||||
schema: public
|
||||
select_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
columns: '*'
|
||||
filter:
|
||||
tenantId:
|
||||
_eq: x-hasura-tenant-id
|
||||
allow_aggregations: true
|
||||
comment: ""
|
||||
@@ -0,0 +1,30 @@
|
||||
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
table:
|
||||
name: StopTransactions
|
||||
schema: public
|
||||
object_relationships:
|
||||
- name: Tenant
|
||||
using:
|
||||
foreign_key_constraint_on: tenantId
|
||||
- name: Transaction
|
||||
using:
|
||||
foreign_key_constraint_on: transactionDatabaseId
|
||||
array_relationships:
|
||||
- name: MeterValues
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: stopTransactionDatabaseId
|
||||
table:
|
||||
name: MeterValues
|
||||
schema: public
|
||||
select_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
columns: '*'
|
||||
filter:
|
||||
tenantId:
|
||||
_eq: x-hasura-tenant-id
|
||||
allow_aggregations: true
|
||||
comment: ""
|
||||
@@ -0,0 +1,19 @@
|
||||
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
table:
|
||||
name: Subscriptions
|
||||
schema: public
|
||||
object_relationships:
|
||||
- name: Tenant
|
||||
using:
|
||||
foreign_key_constraint_on: tenantId
|
||||
select_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
columns: '*'
|
||||
filter:
|
||||
tenantId:
|
||||
_eq: x-hasura-tenant-id
|
||||
allow_aggregations: true
|
||||
comment: ""
|
||||
@@ -0,0 +1,74 @@
|
||||
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
table:
|
||||
name: Tariffs
|
||||
schema: public
|
||||
object_relationships:
|
||||
- name: Tenant
|
||||
using:
|
||||
foreign_key_constraint_on: tenantId
|
||||
array_relationships:
|
||||
- name: Authorizations
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: tariffId
|
||||
table:
|
||||
name: Authorizations
|
||||
schema: public
|
||||
- name: Connectors
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: tariffId
|
||||
table:
|
||||
name: Connectors
|
||||
schema: public
|
||||
- name: MeterValues
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: tariffId
|
||||
table:
|
||||
name: MeterValues
|
||||
schema: public
|
||||
- name: Transactions
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: tariffId
|
||||
table:
|
||||
name: Transactions
|
||||
schema: public
|
||||
insert_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
check:
|
||||
tenantId:
|
||||
_eq: x-hasura-tenant-id
|
||||
columns: '*'
|
||||
comment: ""
|
||||
select_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
columns: '*'
|
||||
filter:
|
||||
tenantId:
|
||||
_eq: x-hasura-tenant-id
|
||||
allow_aggregations: true
|
||||
comment: ""
|
||||
update_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
columns: '*'
|
||||
filter:
|
||||
tenantId:
|
||||
_eq: x-hasura-tenant-id
|
||||
check:
|
||||
tenantId:
|
||||
_eq: x-hasura-tenant-id
|
||||
comment: ""
|
||||
delete_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
filter:
|
||||
tenantId:
|
||||
_eq: x-hasura-tenant-id
|
||||
comment: ""
|
||||
@@ -0,0 +1,60 @@
|
||||
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
table:
|
||||
name: TenantPartners
|
||||
schema: public
|
||||
object_relationships:
|
||||
- name: Tenant
|
||||
using:
|
||||
foreign_key_constraint_on: tenantId
|
||||
array_relationships:
|
||||
- name: AsyncJobStatuses
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: tenantPartnerId
|
||||
table:
|
||||
name: AsyncJobStatuses
|
||||
schema: public
|
||||
- name: Authorizations
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: tenantPartnerId
|
||||
table:
|
||||
name: Authorizations
|
||||
schema: public
|
||||
insert_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
check:
|
||||
tenantId:
|
||||
_eq: x-hasura-tenant-id
|
||||
columns: '*'
|
||||
comment: ""
|
||||
select_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
columns: '*'
|
||||
filter:
|
||||
tenantId:
|
||||
_eq: x-hasura-tenant-id
|
||||
allow_aggregations: true
|
||||
comment: ""
|
||||
update_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
columns: '*'
|
||||
filter:
|
||||
tenantId:
|
||||
_eq: x-hasura-tenant-id
|
||||
check:
|
||||
tenantId:
|
||||
_eq: x-hasura-tenant-id
|
||||
comment: ""
|
||||
delete_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
filter:
|
||||
tenantId:
|
||||
_eq: x-hasura-tenant-id
|
||||
comment: ""
|
||||
@@ -0,0 +1,366 @@
|
||||
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
table:
|
||||
name: Tenants
|
||||
schema: public
|
||||
array_relationships:
|
||||
- name: Authorizations
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: tenantId
|
||||
table:
|
||||
name: Authorizations
|
||||
schema: public
|
||||
- name: Boots
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: tenantId
|
||||
table:
|
||||
name: Boots
|
||||
schema: public
|
||||
- name: Certificates
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: tenantId
|
||||
table:
|
||||
name: Certificates
|
||||
schema: public
|
||||
- name: ChangeConfigurations
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: tenantId
|
||||
table:
|
||||
name: ChangeConfigurations
|
||||
schema: public
|
||||
- name: ChargingNeeds
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: tenantId
|
||||
table:
|
||||
name: ChargingNeeds
|
||||
schema: public
|
||||
- name: ChargingProfiles
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: tenantId
|
||||
table:
|
||||
name: ChargingProfiles
|
||||
schema: public
|
||||
- name: ChargingSchedules
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: tenantId
|
||||
table:
|
||||
name: ChargingSchedules
|
||||
schema: public
|
||||
- name: ChargingStationNetworkProfiles
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: tenantId
|
||||
table:
|
||||
name: ChargingStationNetworkProfiles
|
||||
schema: public
|
||||
- name: ChargingStationSecurityInfos
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: tenantId
|
||||
table:
|
||||
name: ChargingStationSecurityInfos
|
||||
schema: public
|
||||
- name: ChargingStationSequences
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: tenantId
|
||||
table:
|
||||
name: ChargingStationSequences
|
||||
schema: public
|
||||
- name: ChargingStations
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: tenantId
|
||||
table:
|
||||
name: ChargingStations
|
||||
schema: public
|
||||
- name: ComponentVariables
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: tenantId
|
||||
table:
|
||||
name: ComponentVariables
|
||||
schema: public
|
||||
- name: Components
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: tenantId
|
||||
table:
|
||||
name: Components
|
||||
schema: public
|
||||
- name: CompositeSchedules
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: tenantId
|
||||
table:
|
||||
name: CompositeSchedules
|
||||
schema: public
|
||||
- name: Connectors
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: tenantId
|
||||
table:
|
||||
name: Connectors
|
||||
schema: public
|
||||
- name: DeleteCertificateAttempts
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: tenantId
|
||||
table:
|
||||
name: DeleteCertificateAttempts
|
||||
schema: public
|
||||
- name: EventData
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: tenantId
|
||||
table:
|
||||
name: EventData
|
||||
schema: public
|
||||
- name: EvseTypes
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: tenantId
|
||||
table:
|
||||
name: EvseTypes
|
||||
schema: public
|
||||
- name: Evses
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: tenantId
|
||||
table:
|
||||
name: Evses
|
||||
schema: public
|
||||
- name: InstallCertificateAttempts
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: tenantId
|
||||
table:
|
||||
name: InstallCertificateAttempts
|
||||
schema: public
|
||||
- name: InstalledCertificates
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: tenantId
|
||||
table:
|
||||
name: InstalledCertificates
|
||||
schema: public
|
||||
- name: LatestStatusNotifications
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: tenantId
|
||||
table:
|
||||
name: LatestStatusNotifications
|
||||
schema: public
|
||||
- name: LocalListAuthorizations
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: tenantId
|
||||
table:
|
||||
name: LocalListAuthorizations
|
||||
schema: public
|
||||
- name: LocalListVersionAuthorizations
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: tenantId
|
||||
table:
|
||||
name: LocalListVersionAuthorizations
|
||||
schema: public
|
||||
- name: LocalListVersions
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: tenantId
|
||||
table:
|
||||
name: LocalListVersions
|
||||
schema: public
|
||||
- name: Locations
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: tenantId
|
||||
table:
|
||||
name: Locations
|
||||
schema: public
|
||||
- name: MessageInfos
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: tenantId
|
||||
table:
|
||||
name: MessageInfos
|
||||
schema: public
|
||||
- name: MeterValues
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: tenantId
|
||||
table:
|
||||
name: MeterValues
|
||||
schema: public
|
||||
- name: OCPPMessages
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: tenantId
|
||||
table:
|
||||
name: OCPPMessages
|
||||
schema: public
|
||||
- name: Reservations
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: tenantId
|
||||
table:
|
||||
name: Reservations
|
||||
schema: public
|
||||
- name: SalesTariffs
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: tenantId
|
||||
table:
|
||||
name: SalesTariffs
|
||||
schema: public
|
||||
- name: SecurityEvents
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: tenantId
|
||||
table:
|
||||
name: SecurityEvents
|
||||
schema: public
|
||||
- name: SendLocalListAuthorizations
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: tenantId
|
||||
table:
|
||||
name: SendLocalListAuthorizations
|
||||
schema: public
|
||||
- name: SendLocalLists
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: tenantId
|
||||
table:
|
||||
name: SendLocalLists
|
||||
schema: public
|
||||
- name: ServerNetworkProfiles
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: tenantId
|
||||
table:
|
||||
name: ServerNetworkProfiles
|
||||
schema: public
|
||||
- name: SetNetworkProfiles
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: tenantId
|
||||
table:
|
||||
name: SetNetworkProfiles
|
||||
schema: public
|
||||
- name: StartTransactions
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: tenantId
|
||||
table:
|
||||
name: StartTransactions
|
||||
schema: public
|
||||
- name: StatusNotifications
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: tenantId
|
||||
table:
|
||||
name: StatusNotifications
|
||||
schema: public
|
||||
- name: StopTransactions
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: tenantId
|
||||
table:
|
||||
name: StopTransactions
|
||||
schema: public
|
||||
- name: Subscriptions
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: tenantId
|
||||
table:
|
||||
name: Subscriptions
|
||||
schema: public
|
||||
- name: Tariffs
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: tenantId
|
||||
table:
|
||||
name: Tariffs
|
||||
schema: public
|
||||
- name: TenantPartners
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: tenantId
|
||||
table:
|
||||
name: TenantPartners
|
||||
schema: public
|
||||
- name: TransactionEvents
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: tenantId
|
||||
table:
|
||||
name: TransactionEvents
|
||||
schema: public
|
||||
- name: Transactions
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: tenantId
|
||||
table:
|
||||
name: Transactions
|
||||
schema: public
|
||||
- name: VariableAttributes
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: tenantId
|
||||
table:
|
||||
name: VariableAttributes
|
||||
schema: public
|
||||
- name: VariableCharacteristics
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: tenantId
|
||||
table:
|
||||
name: VariableCharacteristics
|
||||
schema: public
|
||||
- name: VariableMonitoringStatuses
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: tenantId
|
||||
table:
|
||||
name: VariableMonitoringStatuses
|
||||
schema: public
|
||||
- name: VariableMonitorings
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: tenantId
|
||||
table:
|
||||
name: VariableMonitorings
|
||||
schema: public
|
||||
- name: VariableStatuses
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: tenantId
|
||||
table:
|
||||
name: VariableStatuses
|
||||
schema: public
|
||||
- name: Variables
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: tenantId
|
||||
table:
|
||||
name: Variables
|
||||
schema: public
|
||||
select_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
columns: '*'
|
||||
filter:
|
||||
id:
|
||||
_eq: x-hasura-tenant-id
|
||||
allow_aggregations: true
|
||||
comment: ""
|
||||
@@ -0,0 +1,33 @@
|
||||
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
table:
|
||||
name: TransactionEvents
|
||||
schema: public
|
||||
object_relationships:
|
||||
- name: EvseType
|
||||
using:
|
||||
foreign_key_constraint_on: evseId
|
||||
- name: Tenant
|
||||
using:
|
||||
foreign_key_constraint_on: tenantId
|
||||
- name: Transaction
|
||||
using:
|
||||
foreign_key_constraint_on: transactionDatabaseId
|
||||
array_relationships:
|
||||
- name: MeterValues
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: transactionEventId
|
||||
table:
|
||||
name: MeterValues
|
||||
schema: public
|
||||
select_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
columns: '*'
|
||||
filter:
|
||||
tenantId:
|
||||
_eq: x-hasura-tenant-id
|
||||
allow_aggregations: true
|
||||
comment: ""
|
||||
@@ -0,0 +1,80 @@
|
||||
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
table:
|
||||
name: Transactions
|
||||
schema: public
|
||||
object_relationships:
|
||||
- name: Authorization
|
||||
using:
|
||||
foreign_key_constraint_on: authorizationId
|
||||
- name: ChargingStation
|
||||
using:
|
||||
foreign_key_constraint_on: stationId
|
||||
- name: Connector
|
||||
using:
|
||||
foreign_key_constraint_on: connectorId
|
||||
- name: Evse
|
||||
using:
|
||||
foreign_key_constraint_on: evseId
|
||||
- name: Location
|
||||
using:
|
||||
foreign_key_constraint_on: locationId
|
||||
- name: StartTransaction
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: transactionDatabaseId
|
||||
table:
|
||||
name: StartTransactions
|
||||
schema: public
|
||||
- name: StopTransaction
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: transactionDatabaseId
|
||||
table:
|
||||
name: StopTransactions
|
||||
schema: public
|
||||
- name: Tariff
|
||||
using:
|
||||
foreign_key_constraint_on: tariffId
|
||||
- name: Tenant
|
||||
using:
|
||||
foreign_key_constraint_on: tenantId
|
||||
array_relationships:
|
||||
- name: ChargingNeeds
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: transactionDatabaseId
|
||||
table:
|
||||
name: ChargingNeeds
|
||||
schema: public
|
||||
- name: ChargingProfiles
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: transactionDatabaseId
|
||||
table:
|
||||
name: ChargingProfiles
|
||||
schema: public
|
||||
- name: MeterValues
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: transactionDatabaseId
|
||||
table:
|
||||
name: MeterValues
|
||||
schema: public
|
||||
- name: TransactionEvents
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: transactionDatabaseId
|
||||
table:
|
||||
name: TransactionEvents
|
||||
schema: public
|
||||
select_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
columns: '*'
|
||||
filter:
|
||||
tenantId:
|
||||
_eq: x-hasura-tenant-id
|
||||
allow_aggregations: true
|
||||
comment: ""
|
||||
@@ -0,0 +1,68 @@
|
||||
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
table:
|
||||
name: VariableAttributes
|
||||
schema: public
|
||||
object_relationships:
|
||||
- name: Boot
|
||||
using:
|
||||
foreign_key_constraint_on: bootConfigId
|
||||
- name: ChargingStation
|
||||
using:
|
||||
foreign_key_constraint_on: stationId
|
||||
- name: Component
|
||||
using:
|
||||
foreign_key_constraint_on: componentId
|
||||
- name: EvseType
|
||||
using:
|
||||
foreign_key_constraint_on: evseDatabaseId
|
||||
- name: Tenant
|
||||
using:
|
||||
foreign_key_constraint_on: tenantId
|
||||
- name: Variable
|
||||
using:
|
||||
foreign_key_constraint_on: variableId
|
||||
array_relationships:
|
||||
- name: VariableStatuses
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: variableAttributeId
|
||||
table:
|
||||
name: VariableStatuses
|
||||
schema: public
|
||||
insert_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
check:
|
||||
tenantId:
|
||||
_eq: x-hasura-tenant-id
|
||||
columns: '*'
|
||||
comment: ""
|
||||
select_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
columns: '*'
|
||||
filter:
|
||||
tenantId:
|
||||
_eq: x-hasura-tenant-id
|
||||
allow_aggregations: true
|
||||
comment: ""
|
||||
update_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
columns: '*'
|
||||
filter:
|
||||
tenantId:
|
||||
_eq: x-hasura-tenant-id
|
||||
check:
|
||||
tenantId:
|
||||
_eq: x-hasura-tenant-id
|
||||
comment: ""
|
||||
delete_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
filter:
|
||||
tenantId:
|
||||
_eq: x-hasura-tenant-id
|
||||
comment: ""
|
||||
@@ -0,0 +1,22 @@
|
||||
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
table:
|
||||
name: VariableCharacteristics
|
||||
schema: public
|
||||
object_relationships:
|
||||
- name: Tenant
|
||||
using:
|
||||
foreign_key_constraint_on: tenantId
|
||||
- name: Variable
|
||||
using:
|
||||
foreign_key_constraint_on: variableId
|
||||
select_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
columns: '*'
|
||||
filter:
|
||||
tenantId:
|
||||
_eq: x-hasura-tenant-id
|
||||
allow_aggregations: true
|
||||
comment: ""
|
||||
@@ -0,0 +1,22 @@
|
||||
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
table:
|
||||
name: VariableMonitoringStatuses
|
||||
schema: public
|
||||
object_relationships:
|
||||
- name: Tenant
|
||||
using:
|
||||
foreign_key_constraint_on: tenantId
|
||||
- name: VariableMonitoring
|
||||
using:
|
||||
foreign_key_constraint_on: variableMonitoringId
|
||||
select_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
columns: '*'
|
||||
filter:
|
||||
tenantId:
|
||||
_eq: x-hasura-tenant-id
|
||||
allow_aggregations: true
|
||||
comment: ""
|
||||
@@ -0,0 +1,36 @@
|
||||
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
table:
|
||||
name: VariableMonitorings
|
||||
schema: public
|
||||
object_relationships:
|
||||
- name: ChargingStation
|
||||
using:
|
||||
foreign_key_constraint_on: stationId
|
||||
- name: Component
|
||||
using:
|
||||
foreign_key_constraint_on: componentId
|
||||
- name: Tenant
|
||||
using:
|
||||
foreign_key_constraint_on: tenantId
|
||||
- name: Variable
|
||||
using:
|
||||
foreign_key_constraint_on: variableId
|
||||
array_relationships:
|
||||
- name: VariableMonitoringStatuses
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: variableMonitoringId
|
||||
table:
|
||||
name: VariableMonitoringStatuses
|
||||
schema: public
|
||||
select_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
columns: '*'
|
||||
filter:
|
||||
tenantId:
|
||||
_eq: x-hasura-tenant-id
|
||||
allow_aggregations: true
|
||||
comment: ""
|
||||
@@ -0,0 +1,22 @@
|
||||
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
table:
|
||||
name: VariableStatuses
|
||||
schema: public
|
||||
object_relationships:
|
||||
- name: Tenant
|
||||
using:
|
||||
foreign_key_constraint_on: tenantId
|
||||
- name: VariableAttribute
|
||||
using:
|
||||
foreign_key_constraint_on: variableAttributeId
|
||||
select_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
columns: '*'
|
||||
filter:
|
||||
tenantId:
|
||||
_eq: x-hasura-tenant-id
|
||||
allow_aggregations: true
|
||||
comment: ""
|
||||
@@ -0,0 +1,81 @@
|
||||
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
table:
|
||||
name: Variables
|
||||
schema: public
|
||||
object_relationships:
|
||||
- name: Tenant
|
||||
using:
|
||||
foreign_key_constraint_on: tenantId
|
||||
- name: VariableCharacteristic
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: variableId
|
||||
table:
|
||||
name: VariableCharacteristics
|
||||
schema: public
|
||||
array_relationships:
|
||||
- name: ComponentVariables
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: variableId
|
||||
table:
|
||||
name: ComponentVariables
|
||||
schema: public
|
||||
- name: EventData
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: variableId
|
||||
table:
|
||||
name: EventData
|
||||
schema: public
|
||||
- name: VariableAttributes
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: variableId
|
||||
table:
|
||||
name: VariableAttributes
|
||||
schema: public
|
||||
- name: VariableMonitorings
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: variableId
|
||||
table:
|
||||
name: VariableMonitorings
|
||||
schema: public
|
||||
insert_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
check:
|
||||
tenantId:
|
||||
_eq: x-hasura-tenant-id
|
||||
columns: '*'
|
||||
comment: ""
|
||||
select_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
columns: '*'
|
||||
filter:
|
||||
tenantId:
|
||||
_eq: x-hasura-tenant-id
|
||||
allow_aggregations: true
|
||||
comment: ""
|
||||
update_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
columns: '*'
|
||||
filter:
|
||||
tenantId:
|
||||
_eq: x-hasura-tenant-id
|
||||
check:
|
||||
tenantId:
|
||||
_eq: x-hasura-tenant-id
|
||||
comment: ""
|
||||
delete_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
filter:
|
||||
tenantId:
|
||||
_eq: x-hasura-tenant-id
|
||||
comment: ""
|
||||
@@ -0,0 +1,6 @@
|
||||
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
table:
|
||||
name: geography_columns
|
||||
schema: public
|
||||
@@ -0,0 +1,6 @@
|
||||
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
table:
|
||||
name: geometry_columns
|
||||
schema: public
|
||||
@@ -0,0 +1,6 @@
|
||||
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
table:
|
||||
name: spatial_ref_sys
|
||||
schema: public
|
||||
@@ -0,0 +1,59 @@
|
||||
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
- "!include public_AsyncJobStatuses.yaml"
|
||||
- "!include public_Authorizations.yaml"
|
||||
- "!include public_Boots.yaml"
|
||||
- "!include public_Certificates.yaml"
|
||||
- "!include public_ChangeConfigurations.yaml"
|
||||
- "!include public_ChargingNeeds.yaml"
|
||||
- "!include public_ChargingProfiles.yaml"
|
||||
- "!include public_ChargingSchedules.yaml"
|
||||
- "!include public_ChargingStationNetworkProfiles.yaml"
|
||||
- "!include public_ChargingStationSecurityInfos.yaml"
|
||||
- "!include public_ChargingStationSequences.yaml"
|
||||
- "!include public_ChargingStations.yaml"
|
||||
- "!include public_ComponentVariables.yaml"
|
||||
- "!include public_Components.yaml"
|
||||
- "!include public_CompositeSchedules.yaml"
|
||||
- "!include public_Connectors.yaml"
|
||||
- "!include public_DeleteCertificateAttempts.yaml"
|
||||
- "!include public_EventData.yaml"
|
||||
- "!include public_EvseTypes.yaml"
|
||||
- "!include public_Evses.yaml"
|
||||
- "!include public_InstallCertificateAttempts.yaml"
|
||||
- "!include public_InstalledCertificates.yaml"
|
||||
- "!include public_LatestStatusNotifications.yaml"
|
||||
- "!include public_LocalListAuthorizations.yaml"
|
||||
- "!include public_LocalListVersionAuthorizations.yaml"
|
||||
- "!include public_LocalListVersions.yaml"
|
||||
- "!include public_Locations.yaml"
|
||||
- "!include public_MessageInfos.yaml"
|
||||
- "!include public_MeterValues.yaml"
|
||||
- "!include public_OCPPMessages.yaml"
|
||||
- "!include public_Reservations.yaml"
|
||||
- "!include public_SalesTariffs.yaml"
|
||||
- "!include public_SecurityEvents.yaml"
|
||||
- "!include public_SendLocalListAuthorizations.yaml"
|
||||
- "!include public_SendLocalLists.yaml"
|
||||
- "!include public_SequelizeMeta.yaml"
|
||||
- "!include public_ServerNetworkProfiles.yaml"
|
||||
- "!include public_SetNetworkProfiles.yaml"
|
||||
- "!include public_StartTransactions.yaml"
|
||||
- "!include public_StatusNotifications.yaml"
|
||||
- "!include public_StopTransactions.yaml"
|
||||
- "!include public_Subscriptions.yaml"
|
||||
- "!include public_Tariffs.yaml"
|
||||
- "!include public_TenantPartners.yaml"
|
||||
- "!include public_Tenants.yaml"
|
||||
- "!include public_TransactionEvents.yaml"
|
||||
- "!include public_Transactions.yaml"
|
||||
- "!include public_VariableAttributes.yaml"
|
||||
- "!include public_VariableCharacteristics.yaml"
|
||||
- "!include public_VariableMonitoringStatuses.yaml"
|
||||
- "!include public_VariableMonitorings.yaml"
|
||||
- "!include public_VariableStatuses.yaml"
|
||||
- "!include public_Variables.yaml"
|
||||
- "!include public_geography_columns.yaml"
|
||||
- "!include public_geometry_columns.yaml"
|
||||
- "!include public_spatial_ref_sys.yaml"
|
||||
@@ -0,0 +1,4 @@
|
||||
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
disabled_for_roles: []
|
||||
@@ -0,0 +1,4 @@
|
||||
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
[]
|
||||
@@ -0,0 +1,4 @@
|
||||
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
{}
|
||||
@@ -0,0 +1,4 @@
|
||||
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
{}
|
||||
@@ -0,0 +1,4 @@
|
||||
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
{}
|
||||
@@ -0,0 +1,4 @@
|
||||
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
[]
|
||||
@@ -0,0 +1,4 @@
|
||||
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
[]
|
||||
@@ -0,0 +1,4 @@
|
||||
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
[]
|
||||
@@ -0,0 +1,4 @@
|
||||
# SPDX-FileCopyrightText: 2026 Contributors to the CitrineOS Project
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
version: 3
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,40 @@
|
||||
// SPDX-FileCopyrightText: 2025 Contributors to the CitrineOS Project
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
'use strict';
|
||||
|
||||
/** @type {import('sequelize-cli').Migration} */
|
||||
import { DataTypes, QueryInterface } from 'sequelize';
|
||||
|
||||
const TENANTS_TABLE = `Tenants`;
|
||||
|
||||
export default {
|
||||
up: async (queryInterface: QueryInterface) => {
|
||||
await queryInterface.createTable(TENANTS_TABLE, {
|
||||
id: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: false,
|
||||
autoIncrement: true,
|
||||
primaryKey: true,
|
||||
},
|
||||
name: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false,
|
||||
},
|
||||
createdAt: {
|
||||
type: DataTypes.DATE,
|
||||
allowNull: false,
|
||||
defaultValue: DataTypes.NOW,
|
||||
},
|
||||
updatedAt: {
|
||||
type: DataTypes.DATE,
|
||||
allowNull: false,
|
||||
defaultValue: DataTypes.NOW,
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
down: async (queryInterface: QueryInterface) => {
|
||||
await queryInterface.dropTable(TENANTS_TABLE);
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,32 @@
|
||||
// SPDX-FileCopyrightText: 2025 Contributors to the CitrineOS Project
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
'use strict';
|
||||
|
||||
import { DEFAULT_TENANT_ID } from '@citrineos/base';
|
||||
import { QueryInterface } from 'sequelize';
|
||||
|
||||
const TENANTS_TABLE = `Tenants`;
|
||||
|
||||
export default {
|
||||
up: async (queryInterface: QueryInterface) => {
|
||||
const [[existingTenant]] = await queryInterface.sequelize.query(
|
||||
`SELECT 1 FROM "${TENANTS_TABLE}" WHERE id = ${DEFAULT_TENANT_ID} LIMIT 1`,
|
||||
);
|
||||
|
||||
if (!existingTenant) {
|
||||
await queryInterface.bulkInsert(TENANTS_TABLE, [
|
||||
{
|
||||
id: DEFAULT_TENANT_ID,
|
||||
name: 'Default Tenant',
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
},
|
||||
]);
|
||||
}
|
||||
},
|
||||
|
||||
down: async (queryInterface: QueryInterface) => {
|
||||
await queryInterface.bulkDelete(TENANTS_TABLE, { id: DEFAULT_TENANT_ID });
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,94 @@
|
||||
// SPDX-FileCopyrightText: 2025 Contributors to the CitrineOS Project
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
'use strict';
|
||||
|
||||
/** @type {import('sequelize-cli').Migration} */
|
||||
import { DEFAULT_TENANT_ID } from '@citrineos/base';
|
||||
import { DataTypes, QueryInterface } from 'sequelize';
|
||||
|
||||
const TABLES = [
|
||||
'AdditionalInfos',
|
||||
'IdTokens',
|
||||
'IdTokenInfos',
|
||||
'Authorizations',
|
||||
'Boots',
|
||||
'Certificates',
|
||||
'InstalledCertificates',
|
||||
'ChangeConfigurations',
|
||||
'Evses',
|
||||
'Locations',
|
||||
'ChargingStations',
|
||||
'Transactions',
|
||||
'ChargingNeeds',
|
||||
'ChargingProfiles',
|
||||
'ChargingSchedules',
|
||||
'ServerNetworkProfiles',
|
||||
'SetNetworkProfiles',
|
||||
'ChargingStationNetworkProfiles',
|
||||
'ChargingStationSecurityInfos',
|
||||
'ChargingStationSequences',
|
||||
'Components',
|
||||
'Variables',
|
||||
'ComponentVariables',
|
||||
'CompositeSchedules',
|
||||
'Connectors',
|
||||
'EventData',
|
||||
'IdTokenAdditionalInfos',
|
||||
'TransactionEvents',
|
||||
'StopTransactions',
|
||||
'MeterValues',
|
||||
'MessageInfos',
|
||||
'OCPPMessages',
|
||||
'Reservations',
|
||||
'SalesTariffs',
|
||||
'SecurityEvents',
|
||||
'StartTransactions',
|
||||
'StatusNotifications',
|
||||
'LatestStatusNotifications',
|
||||
'Subscriptions',
|
||||
'Tariffs',
|
||||
'VariableAttributes',
|
||||
'VariableCharacteristics',
|
||||
'VariableMonitorings',
|
||||
'VariableMonitoringStatuses',
|
||||
'VariableStatuses',
|
||||
'LocalListAuthorizations',
|
||||
'LocalListVersions',
|
||||
'LocalListVersionAuthorizations',
|
||||
'SendLocalLists',
|
||||
'SendLocalListAuthorizations',
|
||||
];
|
||||
|
||||
const TENANT_COLUMN = 'tenantId';
|
||||
const TENANTS_TABLE = `Tenants`;
|
||||
|
||||
export default {
|
||||
up: async (queryInterface: QueryInterface) => {
|
||||
for (const table of TABLES) {
|
||||
const tableDescription = await queryInterface.describeTable(table);
|
||||
if (!tableDescription[TENANT_COLUMN]) {
|
||||
await queryInterface.addColumn(table, TENANT_COLUMN, {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: false,
|
||||
defaultValue: DEFAULT_TENANT_ID,
|
||||
references: {
|
||||
model: TENANTS_TABLE,
|
||||
key: 'id',
|
||||
},
|
||||
onUpdate: 'CASCADE', // update tenantId if the tenant primary key is updated (should never happen)
|
||||
onDelete: 'RESTRICT', // ensure tenant row cannot be deleted if there are existing records using it
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
down: async (queryInterface: QueryInterface) => {
|
||||
for (const table of TABLES) {
|
||||
const tableDescription = await queryInterface.describeTable(table);
|
||||
if (tableDescription[TENANT_COLUMN]) {
|
||||
await queryInterface.removeColumn(table, TENANT_COLUMN);
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,31 @@
|
||||
// SPDX-FileCopyrightText: 2025 Contributors to the CitrineOS Project
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
'use strict';
|
||||
|
||||
/** @type {import('sequelize-cli').Migration} */
|
||||
import { QueryInterface } from 'sequelize';
|
||||
import { DataType } from 'sequelize-typescript';
|
||||
|
||||
const TABLE_NAME = 'Authorizations';
|
||||
const COLUMN_NAME = 'concurrentTransaction';
|
||||
|
||||
export default {
|
||||
up: async (queryInterface: QueryInterface) => {
|
||||
const tableDescription = await queryInterface.describeTable(TABLE_NAME);
|
||||
if (!tableDescription[COLUMN_NAME]) {
|
||||
await queryInterface.addColumn(TABLE_NAME, COLUMN_NAME, {
|
||||
type: DataType.BOOLEAN,
|
||||
allowNull: true,
|
||||
defaultValue: false,
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
down: async (queryInterface: QueryInterface) => {
|
||||
const tableDescription = await queryInterface.describeTable(TABLE_NAME);
|
||||
if (tableDescription[COLUMN_NAME]) {
|
||||
await queryInterface.removeColumn(TABLE_NAME, COLUMN_NAME);
|
||||
}
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,48 @@
|
||||
// SPDX-FileCopyrightText: 2025 Contributors to the CitrineOS Project
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
'use strict';
|
||||
|
||||
/** @type {import('sequelize-cli').Migration} */
|
||||
import { AuthorizationWhitelistEnum } from '@citrineos/base';
|
||||
import { QueryInterface } from 'sequelize';
|
||||
import { DataType } from 'sequelize-typescript';
|
||||
|
||||
const TABLE_NAME = 'Authorizations';
|
||||
const COLUMNS = [
|
||||
{
|
||||
name: 'realTimeAuth',
|
||||
attributes: {
|
||||
type: DataType.STRING,
|
||||
allowNull: false,
|
||||
defaultValue: AuthorizationWhitelistEnum.Never,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'realTimeAuthUrl',
|
||||
attributes: {
|
||||
type: DataType.STRING,
|
||||
allowNull: true,
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
export default {
|
||||
up: async (queryInterface: QueryInterface) => {
|
||||
const tableDescription = await queryInterface.describeTable(TABLE_NAME);
|
||||
for (const column of COLUMNS) {
|
||||
if (!tableDescription[column.name]) {
|
||||
await queryInterface.addColumn(TABLE_NAME, column.name, column.attributes);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
down: async (queryInterface: QueryInterface) => {
|
||||
const tableDescription = await queryInterface.describeTable(TABLE_NAME);
|
||||
for (const column of COLUMNS) {
|
||||
if (tableDescription[column.name]) {
|
||||
await queryInterface.removeColumn(TABLE_NAME, column.name);
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,425 @@
|
||||
// SPDX-FileCopyrightText: 2025 Contributors to the CitrineOS Project
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
import { QueryInterface } from 'sequelize';
|
||||
|
||||
export default {
|
||||
up: async (queryInterface: QueryInterface) => {
|
||||
// 1. Drop any existing temp tables first, then create temp tables to preserve data from related tables
|
||||
await queryInterface.sequelize.query('DROP TABLE IF EXISTS "IdTokens_temp"');
|
||||
await queryInterface.sequelize.query('DROP TABLE IF EXISTS "IdTokenInfos_temp"');
|
||||
await queryInterface.sequelize.query('DROP TABLE IF EXISTS "AdditionalInfos_temp"');
|
||||
await queryInterface.sequelize.query('DROP TABLE IF EXISTS "Authorizations_temp"');
|
||||
|
||||
await queryInterface.sequelize.query(`
|
||||
CREATE TABLE "IdTokens_temp" AS TABLE "IdTokens";
|
||||
`);
|
||||
await queryInterface.sequelize.query(`
|
||||
CREATE TABLE "IdTokenInfos_temp" AS TABLE "IdTokenInfos";
|
||||
`);
|
||||
await queryInterface.sequelize.query(`
|
||||
CREATE TABLE "AdditionalInfos_temp" AS TABLE "AdditionalInfos";
|
||||
`);
|
||||
await queryInterface.sequelize.query(`
|
||||
CREATE TABLE "Authorizations_temp" AS TABLE "Authorizations";
|
||||
`);
|
||||
|
||||
// 2. Alter the Authorizations table: add new flat columns, but do not drop old columns yet
|
||||
// Check if columns exist before adding them
|
||||
const tableDescription = await queryInterface.describeTable('Authorizations');
|
||||
|
||||
if (!tableDescription['idToken']) {
|
||||
await queryInterface.addColumn('Authorizations', 'idToken', {
|
||||
type: 'VARCHAR(255)',
|
||||
allowNull: true,
|
||||
});
|
||||
}
|
||||
if (!tableDescription['idTokenType']) {
|
||||
await queryInterface.addColumn('Authorizations', 'idTokenType', {
|
||||
type: 'VARCHAR(255)',
|
||||
allowNull: true,
|
||||
});
|
||||
}
|
||||
if (!tableDescription['additionalInfo']) {
|
||||
await queryInterface.addColumn('Authorizations', 'additionalInfo', {
|
||||
type: 'JSONB',
|
||||
allowNull: true,
|
||||
});
|
||||
}
|
||||
if (!tableDescription['status']) {
|
||||
await queryInterface.addColumn('Authorizations', 'status', {
|
||||
type: 'VARCHAR(255)',
|
||||
allowNull: true,
|
||||
});
|
||||
}
|
||||
if (!tableDescription['cacheExpiryDateTime']) {
|
||||
await queryInterface.addColumn('Authorizations', 'cacheExpiryDateTime', {
|
||||
type: 'TIMESTAMP WITH TIME ZONE',
|
||||
allowNull: true,
|
||||
});
|
||||
}
|
||||
if (!tableDescription['chargingPriority']) {
|
||||
await queryInterface.addColumn('Authorizations', 'chargingPriority', {
|
||||
type: 'INTEGER',
|
||||
allowNull: true,
|
||||
});
|
||||
}
|
||||
if (!tableDescription['language1']) {
|
||||
await queryInterface.addColumn('Authorizations', 'language1', {
|
||||
type: 'VARCHAR(255)',
|
||||
allowNull: true,
|
||||
});
|
||||
}
|
||||
if (!tableDescription['language2']) {
|
||||
await queryInterface.addColumn('Authorizations', 'language2', {
|
||||
type: 'VARCHAR(255)',
|
||||
allowNull: true,
|
||||
});
|
||||
}
|
||||
if (!tableDescription['personalMessage']) {
|
||||
await queryInterface.addColumn('Authorizations', 'personalMessage', {
|
||||
type: 'JSON',
|
||||
allowNull: true,
|
||||
});
|
||||
}
|
||||
if (!tableDescription['groupIdTokenId']) {
|
||||
await queryInterface.addColumn('Authorizations', 'groupIdTokenId', {
|
||||
type: 'INTEGER',
|
||||
allowNull: true,
|
||||
references: { model: 'Authorizations', key: 'id' },
|
||||
onUpdate: 'CASCADE',
|
||||
onDelete: 'SET NULL',
|
||||
});
|
||||
}
|
||||
// concurrentTransaction already exists from previous migration, skip it
|
||||
if (!tableDescription['customData']) {
|
||||
await queryInterface.addColumn('Authorizations', 'customData', {
|
||||
type: 'JSONB',
|
||||
allowNull: true,
|
||||
});
|
||||
}
|
||||
if (!tableDescription['groupAuthorizationId']) {
|
||||
await queryInterface.addColumn('Authorizations', 'groupAuthorizationId', {
|
||||
type: 'INTEGER',
|
||||
allowNull: true,
|
||||
});
|
||||
}
|
||||
// Ensure personalMessage is JSONB
|
||||
if (
|
||||
tableDescription['personalMessage'] &&
|
||||
tableDescription['personalMessage'].type !== 'JSONB'
|
||||
) {
|
||||
await queryInterface.changeColumn('Authorizations', 'personalMessage', {
|
||||
type: 'JSONB',
|
||||
allowNull: true,
|
||||
});
|
||||
}
|
||||
// Ensure concurrentTransaction is BOOLEAN
|
||||
if (
|
||||
tableDescription['concurrentTransaction'] &&
|
||||
tableDescription['concurrentTransaction'].type !== 'BOOLEAN'
|
||||
) {
|
||||
await queryInterface.changeColumn('Authorizations', 'concurrentTransaction', {
|
||||
type: 'BOOLEAN',
|
||||
allowNull: true,
|
||||
});
|
||||
}
|
||||
|
||||
// 3. Copy/transform data from old columns/related tables into new flat columns
|
||||
await queryInterface.sequelize.query(`
|
||||
UPDATE "Authorizations"
|
||||
SET
|
||||
"idToken" = subq."idToken",
|
||||
"idTokenType" = subq."idTokenType",
|
||||
"additionalInfo" = subq."additionalInfo",
|
||||
"status" = subq."status",
|
||||
"cacheExpiryDateTime" = subq."cacheExpiryDateTime",
|
||||
"chargingPriority" = subq."chargingPriority",
|
||||
"language1" = subq."language1",
|
||||
"language2" = subq."language2",
|
||||
"personalMessage" = subq."personalMessage",
|
||||
"groupIdTokenId" = subq."groupIdTokenId",
|
||||
"concurrentTransaction" = subq."concurrentTransaction",
|
||||
"customData" = NULL
|
||||
FROM (
|
||||
SELECT
|
||||
auth."id" as auth_id,
|
||||
t."idToken",
|
||||
t."type" as "idTokenType",
|
||||
COALESCE(
|
||||
(
|
||||
SELECT jsonb_agg(
|
||||
jsonb_build_object(
|
||||
'additionalIdToken', ai."additionalIdToken",
|
||||
'type', ai."type"
|
||||
)
|
||||
)
|
||||
FROM "AdditionalInfos" ai
|
||||
INNER JOIN "IdTokenAdditionalInfos" itai ON ai."id" = itai."additionalInfoId"
|
||||
WHERE itai."idTokenId" = t."id"
|
||||
),
|
||||
NULL
|
||||
) as "additionalInfo",
|
||||
COALESCE(info."status", 'Accepted') as "status",
|
||||
info."cacheExpiryDateTime",
|
||||
info."chargingPriority",
|
||||
info."language1",
|
||||
info."language2",
|
||||
info."personalMessage",
|
||||
CASE
|
||||
WHEN info."groupIdTokenId" IS NOT NULL THEN (
|
||||
SELECT auth2."id"
|
||||
FROM "Authorizations" auth2
|
||||
WHERE auth2."idTokenId" = info."groupIdTokenId"
|
||||
LIMIT 1
|
||||
)
|
||||
ELSE NULL
|
||||
END as "groupIdTokenId",
|
||||
COALESCE(auth."concurrentTransaction", false) as "concurrentTransaction"
|
||||
FROM "Authorizations" auth
|
||||
INNER JOIN "IdTokens" t ON auth."idTokenId" = t."id"
|
||||
LEFT JOIN "IdTokenInfos" info ON auth."idTokenInfoId" = info."id"
|
||||
) subq
|
||||
WHERE "Authorizations"."id" = subq.auth_id
|
||||
`);
|
||||
|
||||
// 4. Set NOT NULL and default constraints on new columns as needed
|
||||
await queryInterface.changeColumn('Authorizations', 'idToken', {
|
||||
type: 'VARCHAR(255)',
|
||||
allowNull: false,
|
||||
});
|
||||
await queryInterface.changeColumn('Authorizations', 'status', {
|
||||
type: 'VARCHAR(255)',
|
||||
allowNull: false,
|
||||
defaultValue: 'Accepted',
|
||||
});
|
||||
|
||||
// 5. Drop old columns and tables
|
||||
await queryInterface
|
||||
.removeConstraint('Authorizations', 'Authorizations_idTokenId_fkey')
|
||||
.catch(() => {});
|
||||
await queryInterface
|
||||
.removeConstraint('Authorizations', 'Authorizations_idTokenInfoId_fkey')
|
||||
.catch(() => {});
|
||||
await queryInterface.removeColumn('Authorizations', 'idTokenId').catch(() => {});
|
||||
await queryInterface.removeColumn('Authorizations', 'idTokenInfoId').catch(() => {});
|
||||
|
||||
// Drop all foreign key constraints systematically
|
||||
const constraintsToRemove = [
|
||||
// IdTokens table constraints
|
||||
['IdTokenInfos', 'IdTokenInfos_groupIdTokenId_fkey'],
|
||||
['IdTokenAdditionalInfos', 'IdTokenAdditionalInfos_idTokenId_fkey'],
|
||||
['TransactionEvents', 'TransactionEvents_idTokenId_fkey'],
|
||||
['StopTransactions', 'StopTransactions_idTokenDatabaseId_fkey'],
|
||||
['StartTransactions', 'StartTransactions_idTokenDatabaseId_fkey'],
|
||||
['LocalListAuthorizations', 'LocalListAuthorizations_idTokenId_fkey'],
|
||||
// IdTokenInfos table constraints
|
||||
['LocalListAuthorizations', 'LocalListAuthorizations_idTokenInfoId_fkey'],
|
||||
// AdditionalInfos table constraints
|
||||
['IdTokenAdditionalInfos', 'IdTokenAdditionalInfos_additionalInfoId_fkey'],
|
||||
];
|
||||
|
||||
for (const [tableName, constraintName] of constraintsToRemove) {
|
||||
await queryInterface.removeConstraint(tableName, constraintName).catch(() => {});
|
||||
}
|
||||
|
||||
// Drop junction table first
|
||||
await queryInterface.sequelize.query('DROP TABLE IF EXISTS "IdTokenAdditionalInfos"');
|
||||
|
||||
// Drop temp tables and old tables in correct order
|
||||
const tablesToDrop = [
|
||||
'Authorizations_temp',
|
||||
'IdTokens_temp',
|
||||
'IdTokenInfos_temp',
|
||||
'AdditionalInfos_temp',
|
||||
'IdTokenInfos',
|
||||
'AdditionalInfos',
|
||||
'IdTokens',
|
||||
];
|
||||
|
||||
for (const tableName of tablesToDrop) {
|
||||
await queryInterface.sequelize.query(`DROP TABLE IF EXISTS "${tableName}"`);
|
||||
}
|
||||
},
|
||||
|
||||
down: async (queryInterface: QueryInterface) => {
|
||||
// 1. Add back the old columns to Authorizations table
|
||||
await queryInterface.addColumn('Authorizations', 'idTokenId', {
|
||||
type: 'INTEGER',
|
||||
allowNull: true,
|
||||
});
|
||||
await queryInterface.addColumn('Authorizations', 'idTokenInfoId', {
|
||||
type: 'INTEGER',
|
||||
allowNull: true,
|
||||
});
|
||||
|
||||
// 2. Recreate old tables structure
|
||||
await queryInterface.sequelize.query(`
|
||||
CREATE TABLE "IdTokens" (
|
||||
"id" SERIAL PRIMARY KEY,
|
||||
"idToken" VARCHAR(255) NOT NULL,
|
||||
"type" VARCHAR(255),
|
||||
"createdAt" TIMESTAMP WITH TIME ZONE DEFAULT now(),
|
||||
"updatedAt" TIMESTAMP WITH TIME ZONE DEFAULT now()
|
||||
);
|
||||
`);
|
||||
await queryInterface.sequelize.query(`
|
||||
CREATE TABLE "IdTokenInfos" (
|
||||
"id" SERIAL PRIMARY KEY,
|
||||
"info" JSONB,
|
||||
"status" VARCHAR(255),
|
||||
"cacheExpiryDateTime" TIMESTAMP WITH TIME ZONE,
|
||||
"chargingPriority" INTEGER,
|
||||
"language1" VARCHAR(255),
|
||||
"language2" VARCHAR(255),
|
||||
"personalMessage" JSON,
|
||||
"groupIdTokenId" INTEGER,
|
||||
"concurrentTransaction" BOOLEAN,
|
||||
"createdAt" TIMESTAMP WITH TIME ZONE DEFAULT now(),
|
||||
"updatedAt" TIMESTAMP WITH TIME ZONE DEFAULT now()
|
||||
);
|
||||
`);
|
||||
await queryInterface.sequelize.query(`
|
||||
CREATE TABLE "AdditionalInfos" (
|
||||
"id" SERIAL PRIMARY KEY,
|
||||
"additionalIdToken" VARCHAR(255) NOT NULL,
|
||||
"type" VARCHAR(255) NOT NULL,
|
||||
"createdAt" TIMESTAMP WITH TIME ZONE DEFAULT now(),
|
||||
"updatedAt" TIMESTAMP WITH TIME ZONE DEFAULT now(),
|
||||
UNIQUE("additionalIdToken", "type")
|
||||
);
|
||||
`);
|
||||
|
||||
await queryInterface.sequelize.query(`
|
||||
CREATE TABLE "IdTokenAdditionalInfos" (
|
||||
"id" SERIAL PRIMARY KEY,
|
||||
"idTokenId" INTEGER REFERENCES "IdTokens"("id") ON DELETE CASCADE,
|
||||
"additionalInfoId" INTEGER REFERENCES "AdditionalInfos"("id") ON DELETE CASCADE,
|
||||
"createdAt" TIMESTAMP WITH TIME ZONE DEFAULT now(),
|
||||
"updatedAt" TIMESTAMP WITH TIME ZONE DEFAULT now(),
|
||||
UNIQUE("idTokenId", "additionalInfoId")
|
||||
);
|
||||
`);
|
||||
|
||||
// 3. Restore data to the recreated tables from flattened Authorization data
|
||||
// Insert IdTokens from flattened data
|
||||
await queryInterface.sequelize.query(`
|
||||
INSERT INTO "IdTokens" ("idToken", "type", "createdAt", "updatedAt")
|
||||
SELECT DISTINCT "idToken", "idTokenType", "createdAt", "updatedAt"
|
||||
FROM "Authorizations"
|
||||
WHERE "idToken" IS NOT NULL
|
||||
ON CONFLICT ("idToken", "type") DO NOTHING
|
||||
`);
|
||||
|
||||
// Insert IdTokenInfos from flattened data (with proper groupIdTokenId handling)
|
||||
await queryInterface.sequelize.query(`
|
||||
INSERT INTO "IdTokenInfos" (
|
||||
"status", "cacheExpiryDateTime", "chargingPriority", "language1", "language2",
|
||||
"personalMessage", "groupIdTokenId", "concurrentTransaction", "createdAt", "updatedAt"
|
||||
)
|
||||
SELECT DISTINCT
|
||||
"status", "cacheExpiryDateTime", "chargingPriority", "language1", "language2",
|
||||
"personalMessage",
|
||||
CASE
|
||||
WHEN "groupIdTokenId" IS NOT NULL THEN (
|
||||
SELECT t."id"
|
||||
FROM "IdTokens" t
|
||||
INNER JOIN "Authorizations" auth ON auth."idToken" = t."idToken" AND auth."idTokenType" = t."type"
|
||||
WHERE auth."id" = "Authorizations"."groupIdTokenId"
|
||||
LIMIT 1
|
||||
)
|
||||
ELSE NULL
|
||||
END as "groupIdTokenId",
|
||||
"concurrentTransaction", "createdAt", "updatedAt"
|
||||
FROM "Authorizations"
|
||||
WHERE "status" IS NOT NULL
|
||||
`);
|
||||
|
||||
// Insert AdditionalInfos from flattened additionalInfo JSONB array
|
||||
await queryInterface.sequelize.query(`
|
||||
INSERT INTO "AdditionalInfos" ("additionalIdToken", "type", "createdAt", "updatedAt")
|
||||
SELECT DISTINCT
|
||||
(jsonb_array_elements("additionalInfo")->>'additionalIdToken')::VARCHAR(255),
|
||||
(jsonb_array_elements("additionalInfo")->>'type')::VARCHAR(255),
|
||||
"createdAt",
|
||||
"updatedAt"
|
||||
FROM "Authorizations"
|
||||
WHERE "additionalInfo" IS NOT NULL
|
||||
AND jsonb_array_length("additionalInfo") > 0
|
||||
ON CONFLICT ("additionalIdToken", "type") DO NOTHING
|
||||
`);
|
||||
|
||||
// Create IdTokenAdditionalInfo junction table relationships
|
||||
await queryInterface.sequelize.query(`
|
||||
INSERT INTO "IdTokenAdditionalInfos" ("idTokenId", "additionalInfoId", "createdAt", "updatedAt")
|
||||
SELECT DISTINCT
|
||||
t."id" as "idTokenId",
|
||||
ai."id" as "additionalInfoId",
|
||||
a."createdAt",
|
||||
a."updatedAt"
|
||||
FROM "Authorizations" a
|
||||
INNER JOIN "IdTokens" t ON a."idToken" = t."idToken" AND a."idTokenType" = t."type"
|
||||
CROSS JOIN LATERAL jsonb_array_elements(a."additionalInfo") as elem
|
||||
INNER JOIN "AdditionalInfos" ai ON
|
||||
ai."additionalIdToken" = (elem->>'additionalIdToken')::VARCHAR(255) AND
|
||||
ai."type" = (elem->>'type')::VARCHAR(255)
|
||||
WHERE a."additionalInfo" IS NOT NULL
|
||||
AND jsonb_array_length(a."additionalInfo") > 0
|
||||
ON CONFLICT ("idTokenId", "additionalInfoId") DO NOTHING
|
||||
`);
|
||||
|
||||
// 4. Update Authorizations with foreign key references
|
||||
await queryInterface.sequelize.query(`
|
||||
UPDATE "Authorizations"
|
||||
SET
|
||||
"idTokenId" = t."id",
|
||||
"idTokenInfoId" = (
|
||||
SELECT info."id"
|
||||
FROM "IdTokenInfos" info
|
||||
WHERE COALESCE("Authorizations"."status", 'Accepted') = info."status"
|
||||
AND COALESCE("Authorizations"."cacheExpiryDateTime", '1970-01-01'::timestamp) = COALESCE(info."cacheExpiryDateTime", '1970-01-01'::timestamp)
|
||||
AND COALESCE("Authorizations"."chargingPriority", -999) = COALESCE(info."chargingPriority", -999)
|
||||
AND COALESCE("Authorizations"."language1", '') = COALESCE(info."language1", '')
|
||||
AND COALESCE("Authorizations"."language2", '') = COALESCE(info."language2", '')
|
||||
LIMIT 1
|
||||
)
|
||||
FROM "IdTokens" t
|
||||
WHERE "Authorizations"."idToken" = t."idToken"
|
||||
AND COALESCE("Authorizations"."idTokenType", '') = COALESCE(t."type", '')
|
||||
`);
|
||||
|
||||
// 5. Add foreign key constraints back
|
||||
await queryInterface.addConstraint('Authorizations', {
|
||||
fields: ['idTokenId'],
|
||||
type: 'foreign key',
|
||||
name: 'Authorizations_idTokenId_fkey',
|
||||
references: { table: 'IdTokens', field: 'id' },
|
||||
onUpdate: 'CASCADE',
|
||||
onDelete: 'CASCADE',
|
||||
});
|
||||
|
||||
await queryInterface.addConstraint('Authorizations', {
|
||||
fields: ['idTokenInfoId'],
|
||||
type: 'foreign key',
|
||||
name: 'Authorizations_idTokenInfoId_fkey',
|
||||
references: { table: 'IdTokenInfos', field: 'id' },
|
||||
onUpdate: 'CASCADE',
|
||||
onDelete: 'SET NULL',
|
||||
});
|
||||
|
||||
// 6. Remove flattened columns
|
||||
await queryInterface.removeColumn('Authorizations', 'idToken').catch(() => {});
|
||||
await queryInterface.removeColumn('Authorizations', 'idTokenType').catch(() => {});
|
||||
await queryInterface.removeColumn('Authorizations', 'additionalInfo').catch(() => {});
|
||||
await queryInterface.removeColumn('Authorizations', 'status').catch(() => {});
|
||||
await queryInterface.removeColumn('Authorizations', 'cacheExpiryDateTime').catch(() => {});
|
||||
await queryInterface.removeColumn('Authorizations', 'chargingPriority').catch(() => {});
|
||||
await queryInterface.removeColumn('Authorizations', 'language1').catch(() => {});
|
||||
await queryInterface.removeColumn('Authorizations', 'language2').catch(() => {});
|
||||
await queryInterface.removeColumn('Authorizations', 'personalMessage').catch(() => {});
|
||||
await queryInterface.removeColumn('Authorizations', 'groupIdTokenId').catch(() => {});
|
||||
await queryInterface.removeColumn('Authorizations', 'concurrentTransaction').catch(() => {});
|
||||
await queryInterface.removeColumn('Authorizations', 'customData').catch(() => {});
|
||||
await queryInterface.removeColumn('Authorizations', 'groupAuthorizationId').catch(() => {});
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,53 @@
|
||||
// SPDX-FileCopyrightText: 2025 Contributors to the CitrineOS Project
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
import { DEFAULT_TENANT_ID } from '@citrineos/base';
|
||||
import { DataTypes, QueryInterface } from 'sequelize';
|
||||
|
||||
export default {
|
||||
up: async (queryInterface: QueryInterface) => {
|
||||
await queryInterface.createTable('TenantPartners', {
|
||||
id: {
|
||||
type: DataTypes.INTEGER,
|
||||
primaryKey: true,
|
||||
autoIncrement: true,
|
||||
allowNull: false,
|
||||
},
|
||||
partyId: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false,
|
||||
},
|
||||
countryCode: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false,
|
||||
},
|
||||
tenantId: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: false,
|
||||
references: {
|
||||
model: 'Tenants',
|
||||
key: 'id',
|
||||
},
|
||||
defaultValue: DEFAULT_TENANT_ID,
|
||||
onUpdate: 'CASCADE',
|
||||
onDelete: 'CASCADE',
|
||||
},
|
||||
partnerProfileOCPI: {
|
||||
type: DataTypes.JSONB,
|
||||
allowNull: true,
|
||||
},
|
||||
createdAt: {
|
||||
type: DataTypes.DATE,
|
||||
allowNull: false,
|
||||
},
|
||||
updatedAt: {
|
||||
type: DataTypes.DATE,
|
||||
allowNull: false,
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
down: async (queryInterface: QueryInterface) => {
|
||||
await queryInterface.dropTable('TenantPartners');
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,34 @@
|
||||
// SPDX-FileCopyrightText: 2025 Contributors to the CitrineOS Project
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
import { DataTypes, QueryInterface } from 'sequelize';
|
||||
|
||||
export default {
|
||||
up: async (queryInterface: QueryInterface) => {
|
||||
await queryInterface.addColumn('Tenants', 'partyId', {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false,
|
||||
defaultValue: 'default',
|
||||
});
|
||||
await queryInterface.addColumn('Tenants', 'countryCode', {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false,
|
||||
defaultValue: 'US',
|
||||
});
|
||||
await queryInterface.addColumn('Tenants', 'url', {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: true,
|
||||
});
|
||||
await queryInterface.addColumn('Tenants', 'serverProfileOCPI', {
|
||||
type: DataTypes.JSONB,
|
||||
allowNull: true,
|
||||
});
|
||||
},
|
||||
|
||||
down: async (queryInterface: QueryInterface) => {
|
||||
await queryInterface.removeColumn('Tenants', 'partyId');
|
||||
await queryInterface.removeColumn('Tenants', 'countryCode');
|
||||
await queryInterface.removeColumn('Tenants', 'url');
|
||||
await queryInterface.removeColumn('Tenants', 'serverProfileOCPI');
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,78 @@
|
||||
// SPDX-FileCopyrightText: 2025 Contributors to the CitrineOS Project
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
'use strict';
|
||||
|
||||
/** @type {import('sequelize-cli').Migration} */
|
||||
import { DataTypes, QueryInterface } from 'sequelize';
|
||||
|
||||
const TABLE_NAME = 'AsyncJobStatuses';
|
||||
|
||||
export default {
|
||||
up: async (queryInterface: QueryInterface) => {
|
||||
await queryInterface.createTable(TABLE_NAME, {
|
||||
jobId: {
|
||||
type: DataTypes.STRING,
|
||||
primaryKey: true,
|
||||
allowNull: false,
|
||||
},
|
||||
jobName: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false,
|
||||
},
|
||||
tenantPartnerId: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: false,
|
||||
references: {
|
||||
model: 'TenantPartners',
|
||||
key: 'id',
|
||||
},
|
||||
onUpdate: 'CASCADE',
|
||||
onDelete: 'RESTRICT',
|
||||
},
|
||||
finishedAt: {
|
||||
type: DataTypes.DATE,
|
||||
allowNull: true,
|
||||
},
|
||||
stoppedAt: {
|
||||
type: DataTypes.DATE,
|
||||
allowNull: true,
|
||||
},
|
||||
stopScheduled: {
|
||||
type: DataTypes.BOOLEAN,
|
||||
allowNull: false,
|
||||
defaultValue: false,
|
||||
},
|
||||
isFailed: {
|
||||
type: DataTypes.BOOLEAN,
|
||||
allowNull: false,
|
||||
defaultValue: false,
|
||||
},
|
||||
paginationParams: {
|
||||
type: DataTypes.JSON,
|
||||
allowNull: false,
|
||||
},
|
||||
totalObjects: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: true,
|
||||
},
|
||||
createdAt: {
|
||||
type: DataTypes.DATE,
|
||||
allowNull: false,
|
||||
defaultValue: DataTypes.NOW,
|
||||
},
|
||||
updatedAt: {
|
||||
type: DataTypes.DATE,
|
||||
allowNull: false,
|
||||
defaultValue: DataTypes.NOW,
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
down: async (queryInterface: QueryInterface) => {
|
||||
await queryInterface.dropTable(TABLE_NAME);
|
||||
await queryInterface.sequelize.query(`
|
||||
DROP TYPE IF EXISTS "enum_AsyncJobStatuses_jobName";
|
||||
`);
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,603 @@
|
||||
// SPDX-FileCopyrightText: 2025 Contributors to the CitrineOS Project
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
'use strict';
|
||||
|
||||
/** @type {import('sequelize-cli').Migration} */
|
||||
import { DataTypes, QueryInterface } from 'sequelize';
|
||||
|
||||
export default {
|
||||
up: async (queryInterface: QueryInterface) => {
|
||||
// Helper to check if a constraint exists (robust for schema/casing)
|
||||
const constraintExists = async (
|
||||
tableName: string,
|
||||
constraintName: string,
|
||||
): Promise<boolean> => {
|
||||
const [results] = await queryInterface.sequelize.query(
|
||||
`SELECT constraint_name FROM information_schema.table_constraints WHERE table_schema = 'public' AND table_name = '${tableName}' AND constraint_name = '${constraintName}';`,
|
||||
);
|
||||
return results.length > 0;
|
||||
};
|
||||
|
||||
// 1. Create EvseTypes table
|
||||
await queryInterface.createTable('EvseTypes', {
|
||||
databaseId: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: false,
|
||||
autoIncrement: true,
|
||||
primaryKey: true,
|
||||
},
|
||||
id: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: true,
|
||||
},
|
||||
connectorId: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: true,
|
||||
},
|
||||
tenantId: {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: false,
|
||||
references: {
|
||||
model: 'Tenants',
|
||||
key: 'id',
|
||||
},
|
||||
onUpdate: 'CASCADE',
|
||||
onDelete: 'RESTRICT',
|
||||
},
|
||||
createdAt: {
|
||||
type: DataTypes.DATE,
|
||||
allowNull: false,
|
||||
defaultValue: DataTypes.NOW,
|
||||
},
|
||||
updatedAt: {
|
||||
type: DataTypes.DATE,
|
||||
allowNull: false,
|
||||
defaultValue: DataTypes.NOW,
|
||||
},
|
||||
});
|
||||
|
||||
// 2. Add all missing columns
|
||||
await queryInterface.renameColumn('ChargingNeeds', 'evseDatabaseId', 'evseId');
|
||||
await queryInterface.addColumn('Evses', 'stationId', {
|
||||
type: DataTypes.STRING(36),
|
||||
allowNull: true,
|
||||
});
|
||||
await queryInterface.addColumn('TransactionEvents', 'idTokenValue', {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: true,
|
||||
});
|
||||
await queryInterface.removeColumn('Authorizations', 'groupIdTokenId');
|
||||
await queryInterface.addColumn('Authorizations', 'tenantPartnerId', {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: true,
|
||||
});
|
||||
await queryInterface.addColumn('Connectors', 'evseId', {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: true,
|
||||
});
|
||||
await queryInterface.addColumn('Tariffs', 'connectorId', {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: true,
|
||||
});
|
||||
await queryInterface.addColumn('LocalListAuthorizations', 'groupAuthorizationId', {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: true,
|
||||
});
|
||||
await queryInterface.removeColumn('LocalListAuthorizations', 'idTokenId');
|
||||
await queryInterface.addColumn('LocalListAuthorizations', 'idToken', {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: true,
|
||||
});
|
||||
await queryInterface.sequelize.query(
|
||||
'UPDATE "LocalListAuthorizations" SET "idToken" = \'\' WHERE "idToken" IS NULL;',
|
||||
);
|
||||
await queryInterface.changeColumn('LocalListAuthorizations', 'idToken', {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false,
|
||||
});
|
||||
await queryInterface.addColumn('LocalListAuthorizations', 'idTokenType', {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: true,
|
||||
});
|
||||
await queryInterface.addColumn('LocalListAuthorizations', 'additionalInfo', {
|
||||
type: DataTypes.JSONB,
|
||||
allowNull: true,
|
||||
});
|
||||
await queryInterface.addColumn('LocalListAuthorizations', 'status', {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false,
|
||||
defaultValue: 'Accepted',
|
||||
});
|
||||
await queryInterface.addColumn('LocalListAuthorizations', 'cacheExpiryDateTime', {
|
||||
type: DataTypes.DATE,
|
||||
allowNull: true,
|
||||
});
|
||||
await queryInterface.addColumn('LocalListAuthorizations', 'chargingPriority', {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: true,
|
||||
});
|
||||
await queryInterface.addColumn('LocalListAuthorizations', 'language1', {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: true,
|
||||
});
|
||||
await queryInterface.addColumn('LocalListAuthorizations', 'language2', {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: true,
|
||||
});
|
||||
await queryInterface.addColumn('LocalListAuthorizations', 'personalMessage', {
|
||||
type: DataTypes.JSON,
|
||||
allowNull: true,
|
||||
});
|
||||
await queryInterface.removeColumn('LocalListAuthorizations', 'idTokenInfoId');
|
||||
await queryInterface.addColumn('LocalListAuthorizations', 'customData', {
|
||||
type: DataTypes.JSONB,
|
||||
allowNull: true,
|
||||
});
|
||||
await queryInterface.addColumn('Evses', 'evseTypeId', {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: true,
|
||||
});
|
||||
await queryInterface.addColumn('TransactionEvents', 'idTokenType', {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: true,
|
||||
});
|
||||
await queryInterface.removeColumn('TransactionEvents', 'idTokenId');
|
||||
await queryInterface.addColumn('Evses', 'evseId', {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: true,
|
||||
});
|
||||
await queryInterface.addColumn('Evses', 'physicalReference', {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: true,
|
||||
});
|
||||
await queryInterface.addColumn('Evses', 'removed', {
|
||||
type: DataTypes.BOOLEAN,
|
||||
allowNull: true,
|
||||
});
|
||||
await queryInterface.addColumn('StopTransactions', 'idTokenValue', {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: true,
|
||||
});
|
||||
await queryInterface.addColumn('StopTransactions', 'idTokenType', {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: true,
|
||||
});
|
||||
await queryInterface.removeColumn('StopTransactions', 'idTokenDatabaseId');
|
||||
// ChargingStation: Add missing columns
|
||||
await queryInterface.addColumn('ChargingStations', 'coordinates', {
|
||||
type: DataTypes.GEOMETRY('POINT'),
|
||||
allowNull: true,
|
||||
});
|
||||
await queryInterface.addColumn('ChargingStations', 'floorLevel', {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: true,
|
||||
});
|
||||
await queryInterface.addColumn('ChargingStations', 'parkingRestrictions', {
|
||||
type: DataTypes.JSONB,
|
||||
allowNull: true,
|
||||
});
|
||||
await queryInterface.addColumn('ChargingStations', 'capabilities', {
|
||||
type: DataTypes.JSONB,
|
||||
allowNull: true,
|
||||
});
|
||||
await queryInterface.addColumn('Locations', 'publishUpstream', {
|
||||
type: DataTypes.BOOLEAN,
|
||||
defaultValue: true,
|
||||
});
|
||||
await queryInterface.addColumn('Locations', 'timeZone', {
|
||||
type: DataTypes.STRING,
|
||||
defaultValue: 'UTC',
|
||||
});
|
||||
await queryInterface.addColumn('Locations', 'parkingType', {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: true,
|
||||
});
|
||||
await queryInterface.addColumn('Locations', 'facilities', {
|
||||
type: DataTypes.JSONB,
|
||||
allowNull: true,
|
||||
});
|
||||
await queryInterface.addColumn('Locations', 'openingHours', {
|
||||
type: DataTypes.JSONB,
|
||||
allowNull: true,
|
||||
});
|
||||
// Tariff: Add missing column
|
||||
await queryInterface.addColumn('Tariffs', 'tariffAltText', {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: true,
|
||||
});
|
||||
|
||||
await queryInterface.addColumn('MeterValues', 'customData', {
|
||||
type: DataTypes.JSONB,
|
||||
allowNull: true,
|
||||
});
|
||||
await queryInterface.addColumn('MeterValues', 'tariffId', {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: true,
|
||||
references: {
|
||||
model: 'Tariffs',
|
||||
key: 'id',
|
||||
},
|
||||
onUpdate: 'CASCADE',
|
||||
onDelete: 'SET NULL',
|
||||
});
|
||||
await queryInterface.addColumn('MeterValues', 'transactionId', {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: true,
|
||||
});
|
||||
|
||||
await queryInterface.removeColumn('Transactions', 'evseDatabaseId');
|
||||
await queryInterface.addColumn('Transactions', 'locationId', {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: true,
|
||||
});
|
||||
await queryInterface.addColumn('Transactions', 'evseId', {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: true,
|
||||
});
|
||||
await queryInterface.addColumn('Transactions', 'connectorId', {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: true,
|
||||
});
|
||||
await queryInterface.addColumn('Transactions', 'authorizationId', {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: true,
|
||||
});
|
||||
await queryInterface.addColumn('Transactions', 'tariffId', {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: true,
|
||||
});
|
||||
await queryInterface.addColumn('Transactions', 'startTime', {
|
||||
type: DataTypes.DATE,
|
||||
allowNull: true,
|
||||
});
|
||||
await queryInterface.addColumn('Transactions', 'endTime', {
|
||||
type: DataTypes.DATE,
|
||||
allowNull: true,
|
||||
});
|
||||
await queryInterface.addColumn('Transactions', 'customData', {
|
||||
type: DataTypes.JSONB,
|
||||
allowNull: true,
|
||||
});
|
||||
|
||||
// 3. Drop dependent foreign key constraints
|
||||
await queryInterface.sequelize.query(
|
||||
'ALTER TABLE "Transactions" DROP CONSTRAINT IF EXISTS "Transactions_evseDatabaseId_fkey";',
|
||||
);
|
||||
await queryInterface.sequelize.query(
|
||||
'ALTER TABLE "ChargingNeeds" DROP CONSTRAINT IF EXISTS "ChargingNeeds_evseDatabaseId_fkey";',
|
||||
);
|
||||
await queryInterface.sequelize.query(
|
||||
'ALTER TABLE "Components" DROP CONSTRAINT IF EXISTS "Components_evseDatabaseId_fkey";',
|
||||
);
|
||||
await queryInterface.sequelize.query(
|
||||
'ALTER TABLE "TransactionEvents" DROP CONSTRAINT IF EXISTS "TransactionEvents_evseId_fkey";',
|
||||
);
|
||||
await queryInterface.sequelize.query(
|
||||
'ALTER TABLE "Reservations" DROP CONSTRAINT IF EXISTS "Reservations_evseId_fkey";',
|
||||
);
|
||||
await queryInterface.sequelize.query(
|
||||
'ALTER TABLE "VariableAttributes" DROP CONSTRAINT IF EXISTS "VariableAttributes_evseDatabaseId_fkey";',
|
||||
);
|
||||
await queryInterface.sequelize.query(
|
||||
'ALTER TABLE "Components" DROP CONSTRAINT IF EXISTS "Components_evseTypeId_fkey";',
|
||||
);
|
||||
await queryInterface.sequelize.query(
|
||||
'ALTER TABLE "Reservations" DROP CONSTRAINT IF EXISTS "Reservations_evseTypeId_fkey";',
|
||||
);
|
||||
await queryInterface.sequelize.query(
|
||||
'ALTER TABLE "TransactionEvents" DROP CONSTRAINT IF EXISTS "TransactionEvents_evseTypeId_fkey";',
|
||||
);
|
||||
await queryInterface.sequelize.query(
|
||||
'ALTER TABLE "VariableAttributes" DROP CONSTRAINT IF EXISTS "VariableAttributes_evseTypeId_fkey";',
|
||||
);
|
||||
|
||||
// 4. Fix the Evses table
|
||||
await queryInterface.sequelize.query(
|
||||
'ALTER TABLE "Evses" DROP CONSTRAINT IF EXISTS "Evses_pkey";',
|
||||
);
|
||||
|
||||
// Populate EvseTypes from existing data before adding foreign keys
|
||||
await queryInterface.sequelize.query(`
|
||||
INSERT INTO "EvseTypes" ("id", "tenantId", "connectorId", "createdAt", "updatedAt")
|
||||
SELECT "id", "tenantId", "connectorId", NOW(), NOW()
|
||||
FROM "Evses";
|
||||
`);
|
||||
|
||||
// Truncate Evses table after migration
|
||||
await queryInterface.sequelize.query('TRUNCATE TABLE "Evses" CASCADE;');
|
||||
|
||||
await queryInterface.removeColumn('Evses', 'databaseId');
|
||||
await queryInterface.removeColumn('Evses', 'connectorId');
|
||||
await queryInterface.removeColumn('Evses', 'id');
|
||||
// Sequelize does not support adding a primary key via addColumn, so we do it in two steps
|
||||
await queryInterface.addColumn('Evses', 'id', {
|
||||
type: DataTypes.INTEGER,
|
||||
autoIncrement: true,
|
||||
});
|
||||
await queryInterface.sequelize.query(
|
||||
'ALTER TABLE "Evses" ADD CONSTRAINT "Evses_pkey" PRIMARY KEY (id);',
|
||||
);
|
||||
|
||||
await queryInterface.addColumn('Connectors', 'evseTypeConnectorId', {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: true,
|
||||
});
|
||||
await queryInterface.addColumn('Connectors', 'type', {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: true,
|
||||
});
|
||||
await queryInterface.addColumn('Connectors', 'format', {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: true,
|
||||
});
|
||||
await queryInterface.addColumn('Connectors', 'powerType', {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: true,
|
||||
});
|
||||
await queryInterface.addColumn('Connectors', 'maximumAmperage', {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: true,
|
||||
});
|
||||
await queryInterface.addColumn('Connectors', 'maximumVoltage', {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: true,
|
||||
});
|
||||
await queryInterface.addColumn('Connectors', 'maximumPowerWatts', {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: true,
|
||||
});
|
||||
await queryInterface.addColumn('Connectors', 'termsAndConditionsUrl', {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: true,
|
||||
});
|
||||
// Foreign key constraints for relationships (add only if not exists, drop if exists first)
|
||||
if (await constraintExists('Connectors', 'Connectors_evseId_fkey')) {
|
||||
await queryInterface.sequelize.query(
|
||||
'ALTER TABLE "Connectors" DROP CONSTRAINT IF EXISTS "Connectors_evseId_fkey";',
|
||||
);
|
||||
}
|
||||
if (!(await constraintExists('Connectors', 'Connectors_evseId_fkey'))) {
|
||||
await queryInterface.sequelize.query(
|
||||
'ALTER TABLE "Connectors" ADD CONSTRAINT "Connectors_evseId_fkey" FOREIGN KEY ("evseId") REFERENCES "Evses" (id) ON UPDATE CASCADE ON DELETE SET NULL;',
|
||||
);
|
||||
}
|
||||
if (!(await constraintExists('Connectors', 'Connectors_stationId_fkey'))) {
|
||||
await queryInterface.sequelize.query(
|
||||
'ALTER TABLE "Connectors" ADD CONSTRAINT "Connectors_stationId_fkey" FOREIGN KEY ("stationId") REFERENCES "ChargingStations" (id) ON UPDATE CASCADE ON DELETE SET NULL;',
|
||||
);
|
||||
}
|
||||
if (!(await constraintExists('ChargingStations', 'ChargingStations_locationId_fkey'))) {
|
||||
await queryInterface.sequelize.query(
|
||||
'ALTER TABLE "ChargingStations" ADD CONSTRAINT "ChargingStations_locationId_fkey" FOREIGN KEY ("locationId") REFERENCES "Locations" (id) ON UPDATE CASCADE ON DELETE SET NULL;',
|
||||
);
|
||||
}
|
||||
if (!(await constraintExists('Authorizations', 'Authorizations_groupAuthorizationId_fkey'))) {
|
||||
await queryInterface.sequelize.query(
|
||||
'ALTER TABLE "Authorizations" ADD CONSTRAINT "Authorizations_groupAuthorizationId_fkey" FOREIGN KEY ("groupAuthorizationId") REFERENCES "Authorizations" (id) ON UPDATE CASCADE ON DELETE SET NULL;',
|
||||
);
|
||||
}
|
||||
if (!(await constraintExists('ChargingNeeds', 'ChargingNeeds_transactionDatabaseId_fkey'))) {
|
||||
await queryInterface.sequelize.query(
|
||||
'ALTER TABLE "ChargingNeeds" ADD CONSTRAINT "ChargingNeeds_transactionDatabaseId_fkey" FOREIGN KEY ("transactionDatabaseId") REFERENCES "Transactions" (id) ON UPDATE CASCADE ON DELETE SET NULL;',
|
||||
);
|
||||
}
|
||||
|
||||
// Rename partnerProfile to partnerProfileOCPI in TenantPartners ---
|
||||
const tenantPartnersTable = 'TenantPartners';
|
||||
const oldColumn = 'partnerProfile';
|
||||
const newColumn = 'partnerProfileOCPI';
|
||||
const tenantPartnersDesc = await queryInterface.describeTable(tenantPartnersTable);
|
||||
if (tenantPartnersDesc[oldColumn] && !tenantPartnersDesc[newColumn]) {
|
||||
await queryInterface.renameColumn(tenantPartnersTable, oldColumn, newColumn);
|
||||
}
|
||||
|
||||
// 5. Re-create all foreign key constraints
|
||||
await queryInterface.sequelize.query(
|
||||
'ALTER TABLE "Transactions" ADD CONSTRAINT "Transactions_evseId_fkey" FOREIGN KEY ("evseId") REFERENCES "Evses" (id) ON UPDATE CASCADE ON DELETE SET NULL;',
|
||||
);
|
||||
await queryInterface.sequelize.query(
|
||||
'ALTER TABLE "ChargingNeeds" ADD CONSTRAINT "ChargingNeeds_evseId_fkey" FOREIGN KEY ("evseId") REFERENCES "Evses" (id) ON UPDATE CASCADE ON DELETE SET NULL;',
|
||||
);
|
||||
await queryInterface.sequelize.query(
|
||||
'ALTER TABLE "Authorizations" ADD CONSTRAINT "Authorizations_tenantPartnerId_fkey" FOREIGN KEY ("tenantPartnerId") REFERENCES "TenantPartners" (id) ON UPDATE CASCADE ON DELETE SET NULL;',
|
||||
);
|
||||
await queryInterface.sequelize.query(
|
||||
'ALTER TABLE "Evses" ADD CONSTRAINT "Evses_stationId_fkey" FOREIGN KEY ("stationId") REFERENCES "ChargingStations" (id) ON UPDATE CASCADE ON DELETE SET NULL;',
|
||||
);
|
||||
await queryInterface.sequelize.query(
|
||||
'ALTER TABLE "Tariffs" ADD CONSTRAINT "Tariffs_connectorId_fkey" FOREIGN KEY ("connectorId") REFERENCES "Connectors" (id) ON UPDATE CASCADE ON DELETE SET NULL;',
|
||||
);
|
||||
await queryInterface.sequelize.query(
|
||||
'ALTER TABLE "Transactions" ADD CONSTRAINT "Transactions_tariffId_fkey" FOREIGN KEY ("tariffId") REFERENCES "Tariffs" (id) ON UPDATE CASCADE ON DELETE SET NULL;',
|
||||
);
|
||||
await queryInterface.sequelize.query(
|
||||
'ALTER TABLE "Transactions" ADD CONSTRAINT "Transactions_authorizationId_fkey" FOREIGN KEY ("authorizationId") REFERENCES "Authorizations" (id) ON UPDATE CASCADE ON DELETE SET NULL;',
|
||||
);
|
||||
await queryInterface.sequelize.query(
|
||||
'ALTER TABLE "Transactions" ADD CONSTRAINT "Transactions_connectorId_fkey" FOREIGN KEY ("connectorId") REFERENCES "Connectors" (id) ON UPDATE CASCADE ON DELETE SET NULL;',
|
||||
);
|
||||
await queryInterface.sequelize.query(
|
||||
'ALTER TABLE "Transactions" ADD CONSTRAINT "Transactions_locationId_fkey" FOREIGN KEY ("locationId") REFERENCES "Locations" (id) ON UPDATE CASCADE ON DELETE SET NULL;',
|
||||
);
|
||||
await queryInterface.sequelize.query(
|
||||
'ALTER TABLE "LocalListAuthorizations" ADD CONSTRAINT "LocalListAuthorizations_groupAuthorizationId_fkey" FOREIGN KEY ("groupAuthorizationId") REFERENCES "Authorizations" (id) ON UPDATE CASCADE ON DELETE SET NULL;',
|
||||
);
|
||||
await queryInterface.sequelize.query(
|
||||
'ALTER TABLE "Components" ADD CONSTRAINT "Components_evseTypeId_fkey" FOREIGN KEY ("evseDatabaseId") REFERENCES "EvseTypes" ("databaseId") ON UPDATE CASCADE ON DELETE SET NULL;',
|
||||
);
|
||||
await queryInterface.sequelize.query(
|
||||
'ALTER TABLE "Reservations" ADD CONSTRAINT "Reservations_evseTypeId_fkey" FOREIGN KEY ("evseId") REFERENCES "EvseTypes" ("databaseId") ON UPDATE CASCADE ON DELETE SET NULL;',
|
||||
);
|
||||
await queryInterface.sequelize.query(
|
||||
'ALTER TABLE "TransactionEvents" ADD CONSTRAINT "TransactionEvents_evseTypeId_fkey" FOREIGN KEY ("evseId") REFERENCES "EvseTypes" ("databaseId") ON UPDATE CASCADE ON DELETE SET NULL;',
|
||||
);
|
||||
await queryInterface.sequelize.query(
|
||||
'ALTER TABLE "VariableAttributes" ADD CONSTRAINT "VariableAttributes_evseTypeId_fkey" FOREIGN KEY ("evseDatabaseId") REFERENCES "EvseTypes" ("databaseId") ON UPDATE CASCADE ON DELETE SET NULL;',
|
||||
);
|
||||
},
|
||||
|
||||
down: async (queryInterface: QueryInterface) => {
|
||||
// 1. Drop all foreign key constraints added in up
|
||||
await queryInterface.sequelize.query(
|
||||
'ALTER TABLE "Transactions" DROP CONSTRAINT IF EXISTS "Transactions_evseId_fkey";',
|
||||
);
|
||||
await queryInterface.sequelize.query(
|
||||
'ALTER TABLE "Transactions" DROP CONSTRAINT IF EXISTS "Transactions_tariffId_fkey";',
|
||||
);
|
||||
await queryInterface.sequelize.query(
|
||||
'ALTER TABLE "Transactions" DROP CONSTRAINT IF EXISTS "Transactions_authorizationId_fkey";',
|
||||
);
|
||||
await queryInterface.sequelize.query(
|
||||
'ALTER TABLE "Transactions" DROP CONSTRAINT IF EXISTS "Transactions_connectorId_fkey";',
|
||||
);
|
||||
await queryInterface.sequelize.query(
|
||||
'ALTER TABLE "Transactions" DROP CONSTRAINT IF EXISTS "Transactions_locationId_fkey";',
|
||||
);
|
||||
await queryInterface.sequelize.query(
|
||||
'ALTER TABLE "ChargingNeeds" DROP CONSTRAINT IF EXISTS "ChargingNeeds_evseId_fkey";',
|
||||
);
|
||||
await queryInterface.sequelize.query(
|
||||
'ALTER TABLE "Authorizations" DROP CONSTRAINT IF EXISTS "Authorizations_tenantPartnerId_fkey";',
|
||||
);
|
||||
await queryInterface.sequelize.query(
|
||||
'ALTER TABLE "Authorizations" DROP CONSTRAINT IF EXISTS "Authorizations_groupAuthorizationId_fkey";',
|
||||
);
|
||||
await queryInterface.sequelize.query(
|
||||
'ALTER TABLE "Evses" DROP CONSTRAINT IF EXISTS "Evses_stationId_fkey";',
|
||||
);
|
||||
await queryInterface.sequelize.query(
|
||||
'ALTER TABLE "Connectors" DROP CONSTRAINT IF EXISTS "Connectors_evseId_fkey";',
|
||||
);
|
||||
await queryInterface.sequelize.query(
|
||||
'ALTER TABLE "Tariffs" DROP CONSTRAINT IF EXISTS "Tariffs_connectorId_fkey";',
|
||||
);
|
||||
await queryInterface.sequelize.query(
|
||||
'ALTER TABLE "LocalListAuthorizations" DROP CONSTRAINT IF EXISTS "LocalListAuthorizations_groupAuthorizationId_fkey";',
|
||||
);
|
||||
await queryInterface.sequelize.query(
|
||||
'ALTER TABLE "Components" DROP CONSTRAINT IF EXISTS "Components_evseTypeId_fkey";',
|
||||
);
|
||||
await queryInterface.sequelize.query(
|
||||
'ALTER TABLE "Reservations" DROP CONSTRAINT IF EXISTS "Reservations_evseTypeId_fkey";',
|
||||
);
|
||||
await queryInterface.sequelize.query(
|
||||
'ALTER TABLE "TransactionEvents" DROP CONSTRAINT IF EXISTS "TransactionEvents_evseTypeId_fkey";',
|
||||
);
|
||||
await queryInterface.sequelize.query(
|
||||
'ALTER TABLE "VariableAttributes" DROP CONSTRAINT IF EXISTS "VariableAttributes_evseTypeId_fkey";',
|
||||
);
|
||||
|
||||
// 2. Remove all columns added in up
|
||||
await queryInterface.renameColumn('ChargingNeeds', 'evseId', 'evseDatabaseId');
|
||||
await queryInterface.removeColumn('Evses', 'stationId');
|
||||
await queryInterface.removeColumn('TransactionEvents', 'idTokenValue');
|
||||
await queryInterface.removeColumn('Transactions', 'evseId');
|
||||
await queryInterface.removeColumn('Authorizations', 'tenantPartnerId');
|
||||
await queryInterface.removeColumn('Connectors', 'evseId');
|
||||
await queryInterface.removeColumn('Tariffs', 'connectorId');
|
||||
await queryInterface.removeColumn('Transactions', 'tariffId');
|
||||
await queryInterface.removeColumn('Transactions', 'authorizationId');
|
||||
await queryInterface.removeColumn('Transactions', 'connectorId');
|
||||
await queryInterface.removeColumn('Transactions', 'locationId');
|
||||
await queryInterface.removeColumn('LocalListAuthorizations', 'groupAuthorizationId');
|
||||
await queryInterface.addColumn('LocalListAuthorizations', 'idTokenId', {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: true,
|
||||
references: {
|
||||
model: 'IdTokens',
|
||||
key: 'id',
|
||||
},
|
||||
onUpdate: 'CASCADE',
|
||||
onDelete: 'SET NULL',
|
||||
});
|
||||
await queryInterface.removeColumn('LocalListAuthorizations', 'idToken');
|
||||
await queryInterface.addColumn('LocalListAuthorizations', 'idTokenType', {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: true,
|
||||
});
|
||||
await queryInterface.addColumn('LocalListAuthorizations', 'additionalInfo', {
|
||||
type: DataTypes.JSONB,
|
||||
allowNull: true,
|
||||
});
|
||||
await queryInterface.addColumn('LocalListAuthorizations', 'status', {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false,
|
||||
defaultValue: 'Accepted',
|
||||
});
|
||||
await queryInterface.addColumn('LocalListAuthorizations', 'cacheExpiryDateTime', {
|
||||
type: DataTypes.DATE,
|
||||
allowNull: true,
|
||||
});
|
||||
await queryInterface.addColumn('LocalListAuthorizations', 'chargingPriority', {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: true,
|
||||
});
|
||||
await queryInterface.addColumn('LocalListAuthorizations', 'language1', {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: true,
|
||||
});
|
||||
await queryInterface.addColumn('LocalListAuthorizations', 'language2', {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: true,
|
||||
});
|
||||
await queryInterface.addColumn('LocalListAuthorizations', 'personalMessage', {
|
||||
type: DataTypes.JSON,
|
||||
allowNull: true,
|
||||
});
|
||||
await queryInterface.removeColumn('LocalListAuthorizations', 'idTokenInfoId');
|
||||
await queryInterface.addColumn('LocalListAuthorizations', 'customData', {
|
||||
type: DataTypes.JSONB,
|
||||
allowNull: true,
|
||||
});
|
||||
await queryInterface.addColumn('Evses', 'evseTypeId', {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: true,
|
||||
});
|
||||
await queryInterface.addColumn('TransactionEvents', 'idTokenType', {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: true,
|
||||
});
|
||||
await queryInterface.removeColumn('TransactionEvents', 'idTokenId');
|
||||
await queryInterface.addColumn('Evses', 'evseId', {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: true,
|
||||
});
|
||||
await queryInterface.addColumn('Evses', 'physicalReference', {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: true,
|
||||
});
|
||||
await queryInterface.addColumn('Evses', 'removed', {
|
||||
type: DataTypes.BOOLEAN,
|
||||
allowNull: true,
|
||||
});
|
||||
// ChargingStation: Remove columns
|
||||
await queryInterface.removeColumn('ChargingStations', 'coordinates');
|
||||
await queryInterface.removeColumn('ChargingStations', 'floorLevel');
|
||||
await queryInterface.removeColumn('ChargingStations', 'parkingRestrictions');
|
||||
await queryInterface.removeColumn('ChargingStations', 'capabilities');
|
||||
// Tariff: Remove column
|
||||
await queryInterface.removeColumn('Tariffs', 'tariffAltText');
|
||||
|
||||
// 3. Drop EvseTypes table
|
||||
await queryInterface.dropTable('EvseTypes');
|
||||
|
||||
// 4. Restore Evses table PK/index as needed
|
||||
await queryInterface.addColumn('Evses', 'databaseId', {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: true,
|
||||
});
|
||||
await queryInterface.sequelize.query(
|
||||
'ALTER TABLE "Evses" DROP CONSTRAINT IF EXISTS "Evses_pkey";',
|
||||
);
|
||||
|
||||
// Revert partnerProfileOCPI to partnerProfile in TenantPartners ---
|
||||
const tenantPartnersTable = 'TenantPartners';
|
||||
const oldColumn = 'partnerProfile';
|
||||
const newColumn = 'partnerProfileOCPI';
|
||||
const tenantPartnersDesc = await queryInterface.describeTable(tenantPartnersTable);
|
||||
if (tenantPartnersDesc[newColumn] && !tenantPartnersDesc[oldColumn]) {
|
||||
await queryInterface.renameColumn(tenantPartnersTable, newColumn, oldColumn);
|
||||
}
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,68 @@
|
||||
// SPDX-FileCopyrightText: 2025 Contributors to the CitrineOS Project
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
'use strict';
|
||||
|
||||
/** @type {import('sequelize-cli').Migration} */
|
||||
import { QueryInterface } from 'sequelize';
|
||||
|
||||
export default {
|
||||
up: async (queryInterface: QueryInterface) => {
|
||||
// First, let's get all the stations that have EvseTypes associated with them
|
||||
// through VariableAttributes
|
||||
const [stationEvseTypes] = await queryInterface.sequelize.query(`
|
||||
SELECT DISTINCT
|
||||
cs.id as "stationId",
|
||||
cs."tenantId" as "tenantId",
|
||||
et.id as "evseTypeId",
|
||||
ROW_NUMBER() OVER (PARTITION BY cs.id, et.id ORDER BY va.id) as "evseSequence"
|
||||
FROM "ChargingStations" cs
|
||||
INNER JOIN "VariableAttributes" va ON va."stationId" = cs.id
|
||||
INNER JOIN "EvseTypes" et ON va."evseDatabaseId" = et."databaseId"
|
||||
WHERE cs.id IS NOT NULL
|
||||
AND et.id IS NOT NULL
|
||||
ORDER BY cs.id, et.id
|
||||
`);
|
||||
|
||||
// Now create Evse records for each station-evseType combination
|
||||
let id = 1;
|
||||
const evseInserts = stationEvseTypes.map((row: any, index: number) => {
|
||||
// Generate evseId in the format US*TST*C*01234567*8
|
||||
// Using the station's stationId and a sequence number
|
||||
const paddedSequence = row.evseSequence.toString().padStart(8, '0');
|
||||
const evseId = `US*TST*C*${paddedSequence}*${index % 10}`;
|
||||
|
||||
const evse = {
|
||||
id,
|
||||
stationId: row.stationId,
|
||||
evseTypeId: row.evseTypeId,
|
||||
evseId: evseId,
|
||||
tenantId: row.tenantId,
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
};
|
||||
id++;
|
||||
return evse;
|
||||
});
|
||||
|
||||
// Bulk insert the new Evse records
|
||||
if (evseInserts.length > 0) {
|
||||
await queryInterface.bulkInsert('Evses', evseInserts);
|
||||
|
||||
console.log(`Created ${evseInserts.length} Evse records from EvseType associations`);
|
||||
} else {
|
||||
console.log('No EvseType associations found to migrate');
|
||||
}
|
||||
},
|
||||
|
||||
down: async (queryInterface: QueryInterface) => {
|
||||
// Remove all Evse records that were created by this migration
|
||||
// We'll identify them by the fact that they have evseIds matching our pattern
|
||||
await queryInterface.sequelize.query(`
|
||||
DELETE FROM "Evses"
|
||||
WHERE "evseId" LIKE 'US*TST*C*%'
|
||||
`);
|
||||
|
||||
console.log('Rolled back Evse creation migration');
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,157 @@
|
||||
// SPDX-FileCopyrightText: 2025 Contributors to the CitrineOS Project
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
'use strict';
|
||||
|
||||
/** @type {import('sequelize-cli').Migration} */
|
||||
import { DataTypes, QueryInterface } from 'sequelize';
|
||||
|
||||
export default {
|
||||
up: async (queryInterface: QueryInterface) => {
|
||||
await queryInterface.changeColumn('InstalledCertificates', 'certificateType', {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false,
|
||||
});
|
||||
await queryInterface.sequelize.query(`
|
||||
DROP TYPE "enum_InstalledCertificates_certificateType";
|
||||
`);
|
||||
|
||||
await queryInterface.changeColumn('Connectors', 'status', {
|
||||
type: DataTypes.STRING,
|
||||
});
|
||||
await queryInterface.sequelize.query(`
|
||||
DROP TYPE "enum_Connectors_status";
|
||||
`);
|
||||
// Default value references enum type, since default value is changed before column type, so we had to change the column to STRING first.
|
||||
await queryInterface.changeColumn('Connectors', 'status', {
|
||||
type: DataTypes.STRING,
|
||||
defaultValue: 'Unknown',
|
||||
});
|
||||
|
||||
await queryInterface.changeColumn('Connectors', 'errorCode', {
|
||||
type: DataTypes.STRING,
|
||||
});
|
||||
await queryInterface.sequelize.query(`
|
||||
DROP TYPE "enum_Connectors_errorCode";
|
||||
`);
|
||||
// Default value references enum type, since default value is changed before column type, so we had to change the column to STRING first.
|
||||
await queryInterface.changeColumn('Connectors', 'errorCode', {
|
||||
type: DataTypes.STRING,
|
||||
defaultValue: 'NoError',
|
||||
});
|
||||
},
|
||||
|
||||
down: async (queryInterface: QueryInterface) => {
|
||||
await queryInterface.sequelize.query(`
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM pg_type WHERE typname = 'enum_InstalledCertificates_certificateType'
|
||||
) THEN
|
||||
CREATE TYPE "enum_InstalledCertificates_certificateType" AS ENUM (
|
||||
'V2GRootCertificate',
|
||||
'MORootCertificate',
|
||||
'CSMSRootCertificate',
|
||||
'V2GCertificateChain',
|
||||
'ManufacturerRootCertificate'
|
||||
);
|
||||
END IF;
|
||||
END$$;
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM pg_type WHERE typname = 'enum_Connectors_status'
|
||||
) THEN
|
||||
CREATE TYPE "enum_Connectors_status" AS ENUM (
|
||||
'Available',
|
||||
'Preparing',
|
||||
'Charging',
|
||||
'SuspendedEVSE',
|
||||
'SuspendedEV',
|
||||
'Finishing',
|
||||
'Reserved',
|
||||
'Unavailable',
|
||||
'Faulted'
|
||||
);
|
||||
END IF;
|
||||
END$$;
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM pg_type WHERE typname = 'enum_Connectors_errorCode'
|
||||
) THEN
|
||||
CREATE TYPE "enum_Connectors_errorCode" AS ENUM (
|
||||
'ConnectorLockFailure',
|
||||
'EVCommunicationError',
|
||||
'GroundFailure',
|
||||
'HighTemperature',
|
||||
'InternalError',
|
||||
'LocalListConflict',
|
||||
'NoError',
|
||||
'OtherError',
|
||||
'OverCurrentFailure',
|
||||
'PowerMeterFailure',
|
||||
'PowerSwitchFailure',
|
||||
'ReaderFailure',
|
||||
'ResetFailure',
|
||||
'UnderVoltage',
|
||||
'OverVoltage',
|
||||
'WeakSignal'
|
||||
);
|
||||
END IF;
|
||||
END$$;
|
||||
`);
|
||||
|
||||
await queryInterface.changeColumn('InstalledCertificates', 'certificateType', {
|
||||
type: DataTypes.ENUM(
|
||||
'V2GRootCertificate',
|
||||
'MORootCertificate',
|
||||
'CSMSRootCertificate',
|
||||
'V2GCertificateChain',
|
||||
'ManufacturerRootCertificate',
|
||||
),
|
||||
allowNull: false,
|
||||
});
|
||||
|
||||
await queryInterface.changeColumn('Connectors', 'status', {
|
||||
type: DataTypes.ENUM(
|
||||
'Available',
|
||||
'Preparing',
|
||||
'Charging',
|
||||
'SuspendedEVSE',
|
||||
'SuspendedEV',
|
||||
'Finishing',
|
||||
'Reserved',
|
||||
'Unavailable',
|
||||
'Faulted',
|
||||
),
|
||||
allowNull: false,
|
||||
defaultValue: 'Unknown',
|
||||
});
|
||||
|
||||
await queryInterface.changeColumn('Connectors', 'errorCode', {
|
||||
type: DataTypes.ENUM(
|
||||
'ConnectorLockFailure',
|
||||
'EVCommunicationError',
|
||||
'GroundFailure',
|
||||
'HighTemperature',
|
||||
'InternalError',
|
||||
'LocalListConflict',
|
||||
'NoError',
|
||||
'OtherError',
|
||||
'OverCurrentFailure',
|
||||
'PowerMeterFailure',
|
||||
'PowerSwitchFailure',
|
||||
'ReaderFailure',
|
||||
'ResetFailure',
|
||||
'UnderVoltage',
|
||||
'OverVoltage',
|
||||
'WeakSignal',
|
||||
),
|
||||
allowNull: false,
|
||||
defaultValue: 'NoError',
|
||||
});
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,54 @@
|
||||
// SPDX-FileCopyrightText: 2025 Contributors to the CitrineOS Project
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
'use strict';
|
||||
|
||||
/** @type {import('sequelize-cli').Migration} */
|
||||
import { DataTypes, QueryInterface } from 'sequelize';
|
||||
|
||||
export default {
|
||||
up: async (queryInterface: QueryInterface) => {
|
||||
// Add isUserTenant column
|
||||
await queryInterface.addColumn('Tenants', 'isUserTenant', {
|
||||
type: DataTypes.BOOLEAN,
|
||||
allowNull: false,
|
||||
defaultValue: false,
|
||||
comment: 'Indicates if this tenant is a user tenant',
|
||||
});
|
||||
|
||||
// Make url, partyId, countryCode optional
|
||||
await queryInterface.changeColumn('Tenants', 'url', {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: true,
|
||||
});
|
||||
|
||||
await queryInterface.changeColumn('Tenants', 'partyId', {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: true,
|
||||
});
|
||||
|
||||
await queryInterface.changeColumn('Tenants', 'countryCode', {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: true,
|
||||
});
|
||||
},
|
||||
|
||||
down: async (queryInterface: QueryInterface) => {
|
||||
await queryInterface.removeColumn('Tenants', 'isUserTenant');
|
||||
|
||||
await queryInterface.changeColumn('Tenants', 'url', {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false,
|
||||
});
|
||||
|
||||
await queryInterface.changeColumn('Tenants', 'partyId', {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false,
|
||||
});
|
||||
|
||||
await queryInterface.changeColumn('Tenants', 'countryCode', {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false,
|
||||
});
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,25 @@
|
||||
// SPDX-FileCopyrightText: 2025 Contributors to the CitrineOS Project
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
'use strict';
|
||||
|
||||
/** @type {import('sequelize-cli').Migration} */
|
||||
import { DataTypes, QueryInterface } from 'sequelize';
|
||||
|
||||
export default {
|
||||
up: async (queryInterface: QueryInterface) => {
|
||||
await queryInterface.addColumn('Authorizations', 'realTimeAuthLastAttempt', {
|
||||
type: DataTypes.JSONB,
|
||||
allowNull: true,
|
||||
});
|
||||
await queryInterface.addColumn('Authorizations', 'realTimeAuthTimeout', {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: true,
|
||||
});
|
||||
},
|
||||
|
||||
down: async (queryInterface: QueryInterface) => {
|
||||
await queryInterface.removeColumn('Authorizations', 'realTimeAuthLastAttempt');
|
||||
await queryInterface.removeColumn('Authorizations', 'realTimeAuthTimeout');
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,28 @@
|
||||
// SPDX-FileCopyrightText: 2025 Contributors to the CitrineOS Project
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
'use strict';
|
||||
|
||||
/** @type {import('sequelize-cli').Migration} */
|
||||
import { DataTypes, QueryInterface } from 'sequelize';
|
||||
|
||||
export default {
|
||||
up: async (queryInterface: QueryInterface) => {
|
||||
console.log('Creating citext extension if not exists...');
|
||||
await queryInterface.sequelize.query('CREATE EXTENSION IF NOT EXISTS citext;');
|
||||
|
||||
console.log('Changing column to use CITEXT type...');
|
||||
await queryInterface.changeColumn('Authorizations', 'idToken', {
|
||||
type: DataTypes.CITEXT,
|
||||
allowNull: false,
|
||||
});
|
||||
},
|
||||
|
||||
down: async (queryInterface: QueryInterface) => {
|
||||
await queryInterface.changeColumn('Authorizations', 'idToken', {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false,
|
||||
});
|
||||
// Note: Not dropping the extension in case other tables use it
|
||||
},
|
||||
};
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user