Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions docs/DATABASE_SETUP.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Setting up a database

## Docker Dev Environment

### Creating a database

Once your dev containers are running and you are inside the chatbot_server container, it is easy to create a development database.

First, install dependencies.

```bash
pnpm install
```

Then, create the database.

```bash
pnpm db:create
```

Finally, run the migrations to build the most current schema.

```bash
pnpm db:migrate
```

### Dropping a database

You can also delete the whole database, which is sometimes useful. Simply run

```bash
pnpm db:drop
```

**Note:** Any data that was saved will be lost and unrecoverable!
109 changes: 109 additions & 0 deletions docs/MIGRATIONS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
# Database Migrations

Our Chatbot db uses [Sequelize CLI](https://sequelize.org/docs/v6/other-topics/migrations/) to manage database migrations.

## Creating a Migration

To create a database migration skeleton, use the following command found in `package.json`.

``` bash
pnpm db:generate-migration --name <some_informative_name_about_your_model>
```

In `src/migrations`, you should now find a new migration file with an auto-generated timestamp.

Change the new migration's file extension to `.cjs`.

``` bash
202405121234-test.js ==> 202405121234-test.cjs
```

`*.js` files are set up to have a linting error if we use CommonJS style imports and exports, so `*.cjs` is used to be explicit about the fact that we are using a different workflow from the rest of the project.

## Adding Schema Changes

Below is the body of a sample migration to use as an example. Note that
the underlying Postgresql table should have **snake_cased** column names,
but that the table attributes should be **camelCase**.

See the [Sequelize Docs](https://sequelize.org/) for more examples and info on adding indexes, etc.

``` javascript
'use strict';

module.exports = {
up: async (queryInterface, Sequelize) => {
await queryInterface.createTable('tests', {
id: {
type: Sequelize.UUID,
defaultValue: Sequelize.UUIDV4,
primaryKey: true,
allowNull: false
},
name: {
type: Sequelize.STRING(100),
allowNull: false
},
description: {
type: Sequelize.TEXT,
allowNull: true
},
is_active: {
type: Sequelize.BOOLEAN,
defaultValue: true
},
difficulty_level: {
type: Sequelize.ENUM('easy', 'medium', 'hard'),
defaultValue: 'medium'
},
max_score: {
type: Sequelize.INTEGER
},
created_at: {
type: Sequelize.DATE,
allowNull: false,
defaultValue: Sequelize.literal('CURRENT_TIMESTAMP')
},
updated_at: {
type: Sequelize.DATE,
allowNull: false,
defaultValue: Sequelize.literal('CURRENT_TIMESTAMP')
},
deleted_at: {
type: Sequelize.DATE,
allowNull: true
}
});

},

down: async (queryInterface) => {
await queryInterface.dropTable('tests');
}
};
```

## Undoing a Migration

**These commands should be used very carefully!**
**Rolling back a migration may cause data loss!**

You can undo the most recent migration with

```bash
pnpm db:undo-migration
```

or undo the last n migrations with

```bash
pnpm db:rollback-migrations --to <first_migration_of_the_ones_to_rollback>
```

**Note:** The migration specified in the `--to` argument is included in the rollback!

Finally, undo every migration with

```bash
pnpm db:rollback-migrations
```
108 changes: 108 additions & 0 deletions docs/MODELS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
# Creating an ORM Model

Models are the way that we interact with our chatbot_server's underlying database.

First, create a new model in `src/models`.

```bash
touch src/models/myModel.js
```
<!-- Models should be camelCase -->

Second, create the model in the following format

```javascript
'use strict';
const { Model } = require('sequelize');

module.exports = (sequelize, DataTypes) => {
class Test extends Model {
static associate(models) {
// Define associations here (example)
// this.hasMany(models.TestResult, { foreignKey: 'test_id' });
}
}

Test.init({
id: {
type: DataTypes.UUID,
defaultValue: DataTypes.UUIDV4,
primaryKey: true,
allowNull: false
},
name: {
type: DataTypes.STRING(100),
allowNull: false,
validate: {
notEmpty: true,
len: [3, 100]
}
},
description: {
type: DataTypes.TEXT,
allowNull: true
},
is_active: {
type: DataTypes.BOOLEAN,
defaultValue: true
},
difficulty_level: {
type: DataTypes.ENUM('easy', 'medium', 'hard'),
defaultValue: 'medium'
},
max_score: {
type: DataTypes.INTEGER,
validate: {
min: 0,
max: 1000
}
}
}, {
sequelize,
modelName: 'Test',
tableName: 'tests',
timestamps: true,
underscored: true,
paranoid: true, // Enables soft deletes
});

return Test;
};
```



## Using the Model

You can go ahead and import the model in your service, and/or tests file as such and perform operations on the model given the demands of the tasks you're working on as such.

```javascript
// Import the model
const { Test } = require('./models');

// 1. Create a test
const newTest = await Test.create({
name: 'Math Assessment',
description: 'Basic algebra test',
difficulty_level: 'medium',
max_score: 100
});

// 2. Fetch tests
const tests = await Test.findAll({
where: { is_active: true },
limit: 10
});

// 3. Update a test
const [updatedRows] = await Test.update(
{ max_score: 150 },
{ where: { id: testId } }
);

// 4. Soft delete
await Test.destroy({ where: { id: testId } });

```

Once you restart the app, the Sequelize instance will be able to use the model.