Skip to content

Commit

Permalink
add sqlite_drizzle to test.yml, add posts with tests to sqlite example
Browse files Browse the repository at this point in the history
  • Loading branch information
lastmjs committed May 2, 2024
1 parent dbbc81e commit 5d66cfd
Show file tree
Hide file tree
Showing 8 changed files with 470 additions and 107 deletions.
1 change: 1 addition & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@ jobs:
"examples/simple_erc20",
"examples/simple_user_accounts",
"examples/sqlite",
"examples/sqlite_drizzle",
"examples/stable_b_tree_map_instruction_threshold",
"examples/stable_memory",
"examples/stable_structures",
Expand Down
19 changes: 14 additions & 5 deletions examples/sqlite/src/db/migrations/migration_0.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,17 @@
export const migration0 = `
CREATE TABLE users
(
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT NOT NULL UNIQUE,
age INTEGER NOT NULL
);
(
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT NOT NULL UNIQUE,
age INTEGER NOT NULL
);
CREATE TABLE posts
(
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL,
title TEXT NOT NULL,
body TEXT NOT NULL,
FOREIGN KEY (user_id) REFERENCES users(id)
);
`;
85 changes: 7 additions & 78 deletions examples/sqlite/src/server/index.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
import { ic, jsonStringify } from 'azle';
import express, { Request } from 'express';
import { v4 } from 'uuid';
import express from 'express';

import { db, initState, postUpgradeState } from '../';
import { countUsers, createUser, getUser, getUsers } from './users';
import { initState, postUpgradeState } from '../';
import { getRouter as getRouterPosts } from './posts/router';
import { getRouter as getRouterUsers } from './users/router';

export function initServer() {
let app = express();

app.use(express.json());

app.use('/users', getRouterUsers());
app.use('/posts', getRouterPosts());

app.get('/init-state', (_req, res) => {
res.json(initState.value);
});
Expand All @@ -18,78 +20,5 @@ export function initServer() {
res.json(postUpgradeState.value);
});

app.get(
'/users',
(
req: Request<any, any, any, { limit?: string; offset?: string }>,
res
) => {
try {
const limit = Number(req.query.limit ?? -1);
const offset = Number(req.query.offset ?? 0);

const users = getUsers(db, limit, offset);

res.json(users);
} catch (error) {
console.log(error);
}
}
);

app.get('/users/count', (_req, res) => {
res.json(countUsers(db));
});

app.get('/users/:id', (req, res) => {
const { id } = req.params;

const user = getUser(db, Number(id));

res.json(user);
});

app.post(
'/users',
(req: Request<any, any, { username: string; age: number }>, res) => {
const { username, age } = req.body;

const id = createUser(db, {
username,
age
});

res.json(id);
}
);

app.post('/users/batch/:num', (req, res) => {
try {
const start = ic.instructionCounter();

const num = Number(req.params.num);

for (let i = 0; i < Number(req.params.num); i++) {
createUser(db, {
username: `lastmjs${v4()}`,
age: i
});
}

const end = ic.instructionCounter();

res.send(
jsonStringify({
Success: `${num} users created`,
instructions: end - start
})
);
} catch (error) {
console.log('error', error);
}
});

// TODO also do put and patch

return app.listen();
}
89 changes: 89 additions & 0 deletions examples/sqlite/src/server/posts/db.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
// TODO the db stuff should be separated out into the db...
// TODO well actually no, I think users and posts should just be their own
// TODO thing grouped by functionality...so maybe just server needs to be changed...
// TODO to just be a file, and then we have users and posts directories

// TODO how do we deal with joins in our little template tag thing?
// TODO we need to add all of the joins

// TODO figure out joins!!!

import { Database, QueryExecResult, SqlValue } from 'sql.js/dist/sql-asm.js';

type Post = {
id: number;
user_id: number;
title: string;
body: string;
};

type PostCreate = Pick<Post, 'user_id' | 'title' | 'body'>;

export function getPosts(db: Database, limit: number, offset: number): Post[] {
const queryExecResults = db.exec(
`SELECT * FROM posts ORDER BY id LIMIT :limit OFFSET :offset`,
{
':limit': limit,
':offset': offset
}
);
const queryExecResult = queryExecResults[0] as QueryExecResult | undefined;

return (
queryExecResult?.values.map((sqlValues) => {
return convertQueryExecResultToUser(sqlValues);
}) ?? []
);
}

export function getPost(db: Database, id: number): Post | null {
const queryExecResults = db.exec('SELECT * FROM posts WHERE id=:id', {
':id': id
});
const queryExecResult = queryExecResults[0] as QueryExecResult | undefined;

if (queryExecResult === undefined) {
return null;
} else {
return queryExecResult.values.map((sqlValues) => {
return convertQueryExecResultToUser(sqlValues);
})[0];
}
}

export function countPosts(db: Database): number {
const queryExecResults = db.exec(
'SELECT id FROM posts ORDER BY id DESC LIMIT 1'
);
const queryExecResult = queryExecResults[0] as QueryExecResult | undefined;

if (queryExecResult === undefined) {
return 0;
} else {
return queryExecResult.values[0][0] as number;
}
}

export function createPost(db: Database, postCreate: PostCreate): number {
db.run(
'INSERT INTO posts (user_id, title, body) VALUES (:user_id, :title, :body)',
{
':user_id': postCreate.user_id,
':title': postCreate.title,
':body': postCreate.body
}
);

const id = db.exec('SELECT last_insert_rowid()')[0].values[0][0] as number;

return id;
}

export function convertQueryExecResultToUser(sqlValues: SqlValue[]): Post {
return {
id: sqlValues[0] as number,
user_id: sqlValues[1] as number,
title: sqlValues[2] as string,
body: sqlValues[3] as string
};
}
93 changes: 93 additions & 0 deletions examples/sqlite/src/server/posts/router.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import { ic, jsonStringify } from 'azle';
import express, { Request, Router } from 'express';
import { v4 } from 'uuid';

import { db } from '../../';
import { createUser } from '../users/db';
import { countPosts, createPost, getPost, getPosts } from './db';

// TODO also do put and patch
export function getRouter(): Router {
const router = express.Router();

router.get(
'/',
(
req: Request<any, any, any, { limit?: string; offset?: string }>,
res
) => {
const limit = Number(req.query.limit ?? -1);
const offset = Number(req.query.offset ?? 0);

const posts = getPosts(db, limit, offset);

res.json(posts);
}
);

router.get('/count', (_req, res) => {
res.json(countPosts(db));
});

router.get('/:id', (req, res) => {
const { id } = req.params;

const post = getPost(db, Number(id));

res.json(post);
});

router.post(
'/',
(req: Request<any, any, { title: string; body: string }>, res) => {
const { title, body } = req.body;

const userId = createUser(db, {
username: `lastmjs${v4()}`,
age: 33
});

const id = createPost(db, {
user_id: userId,
title,
body
});

res.json(id);
}
);

router.post('/batch/:num', (req, res) => {
try {
const start = ic.instructionCounter();

const num = Number(req.params.num);

for (let i = 0; i < Number(req.params.num); i++) {
const userId = createUser(db, {
username: `lastmjs${v4()}`,
age: i
});

createPost(db, {
user_id: userId,
title: `Post ${v4()}`,
body: `${v4()}${v4()}${v4()}${v4()}`
});
}

const end = ic.instructionCounter();

res.send(
jsonStringify({
Success: `${num} posts created`,
instructions: end - start
})
);
} catch (error) {
console.log('error', error);
}
});

return router;
}
File renamed without changes.
Loading

0 comments on commit 5d66cfd

Please sign in to comment.