test: refactor test in database and add more (#17)
This commit is contained in:
parent
eb93f0734f
commit
85e42f95a5
@ -36,7 +36,7 @@ const connection = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
logging: false,
|
logging: process.env.DB_LOG_SQL === 'on' ? console.log : false,
|
||||||
},
|
},
|
||||||
database: null,
|
database: null,
|
||||||
create() {
|
create() {
|
||||||
|
@ -18,7 +18,8 @@ describe('list', () => {
|
|||||||
for (let index = 0; index < 2; index++) {
|
for (let index = 0; index < 2; index++) {
|
||||||
items.push({
|
items.push({
|
||||||
title: `title${index}`,
|
title: `title${index}`,
|
||||||
status: index % 2 ? 'published' : 'draft'
|
status: index % 2 ? 'published' : 'draft',
|
||||||
|
published_at: index % 2 ? new Date() : null
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
await Post.bulkCreate(items);
|
await Post.bulkCreate(items);
|
||||||
@ -58,6 +59,48 @@ describe('list', () => {
|
|||||||
expect(response.body.rows[0].title).toBe(drafts[0]);
|
expect(response.body.rows[0].title).toBe(drafts[0]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('null', () => {
|
||||||
|
it('filter[published_at.is]', async () => {
|
||||||
|
const Post = db.getModel('posts');
|
||||||
|
const expected = await Post.findAll({
|
||||||
|
where: {
|
||||||
|
published_at: {
|
||||||
|
[Op.is]: null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const response = await agent.get('/posts?filter[published_at.is]');
|
||||||
|
expect(response.body.count).toBe(expected.length);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('filter[published_at.not]', async () => {
|
||||||
|
const Post = db.getModel('posts');
|
||||||
|
const expected = await Post.findAll({
|
||||||
|
where: {
|
||||||
|
published_at: {
|
||||||
|
[Op.not]: null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const response = await agent.get('/posts?filter[published_at.not]');
|
||||||
|
expect(response.body.count).toBe(expected.length);
|
||||||
|
});
|
||||||
|
|
||||||
|
// TODO(bug): should use `user.is`
|
||||||
|
it('filter[user_id.is]', async () => {
|
||||||
|
const Post = db.getModel('posts');
|
||||||
|
const expected = await Post.findAll({
|
||||||
|
where: {
|
||||||
|
user_id: {
|
||||||
|
[Op.is]: null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const response = await agent.get('/posts?filter[user_id.is]');
|
||||||
|
expect(response.body.count).toBe(expected.length);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('page', () => {
|
describe('page', () => {
|
||||||
|
@ -13,6 +13,10 @@ export default {
|
|||||||
name: 'status',
|
name: 'status',
|
||||||
defaultValue: 'publish',
|
defaultValue: 'publish',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
type: 'date',
|
||||||
|
name: 'published_at'
|
||||||
|
},
|
||||||
{
|
{
|
||||||
type: 'belongsTo',
|
type: 'belongsTo',
|
||||||
name: 'user',
|
name: 'user',
|
||||||
|
@ -37,10 +37,6 @@ export async function list(ctx: Context, next: Next) {
|
|||||||
context: ctx,
|
context: ctx,
|
||||||
});
|
});
|
||||||
let data = {};
|
let data = {};
|
||||||
if (page || perPage) {
|
|
||||||
options.limit = 1*(perPage||20);
|
|
||||||
options.offset = options.limit * (page > 0 ? page - 1 : 0);
|
|
||||||
}
|
|
||||||
if (associated && resourceField) {
|
if (associated && resourceField) {
|
||||||
const AssociatedModel = ctx.db.getModel(associatedName);
|
const AssociatedModel = ctx.db.getModel(associatedName);
|
||||||
if (!(associated instanceof AssociatedModel)) {
|
if (!(associated instanceof AssociatedModel)) {
|
||||||
|
@ -1,7 +1,14 @@
|
|||||||
import Database from '../database';
|
import Database from '../database';
|
||||||
import { Dialect } from 'sequelize';
|
import { Dialect } from 'sequelize';
|
||||||
|
|
||||||
|
function getTestKey() {
|
||||||
|
const { id } = require.main;
|
||||||
|
const key = id
|
||||||
|
.replace(process.env.PWD, '')
|
||||||
|
.replace('.test.ts', '')
|
||||||
|
.replace(/[^\w]/g, '_');
|
||||||
|
return key
|
||||||
|
}
|
||||||
|
|
||||||
const config = {
|
const config = {
|
||||||
username: process.env.DB_USER,
|
username: process.env.DB_USER,
|
||||||
@ -21,5 +28,12 @@ const config = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export function getDatabase() {
|
export function getDatabase() {
|
||||||
return new Database(config);
|
return new Database({
|
||||||
|
...config,
|
||||||
|
hooks: {
|
||||||
|
beforeDefine(columns, model) {
|
||||||
|
model.tableName = `${getTestKey()}_${model.tableName || model.name.plural}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
@ -1,15 +1,13 @@
|
|||||||
import Database from '..';
|
import Database from '../..';
|
||||||
import { getDatabase } from '.';
|
import { getDatabase } from '..';
|
||||||
import { Op, Sequelize } from 'sequelize';
|
import Model, { ModelCtor } from '../../model';
|
||||||
import Model, { ModelCtor } from '../model';
|
|
||||||
import { BelongsToMany } from '../fields';
|
|
||||||
import { Mode } from 'fs';
|
|
||||||
|
|
||||||
|
|
||||||
let db: Database;
|
let db: Database;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
db = await getDatabase();
|
db = getDatabase();
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(async () => {
|
afterEach(async () => {
|
||||||
@ -269,137 +267,7 @@ describe('actions', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('parseApiJson', () => {
|
describe('query', () => {
|
||||||
let db: Database;
|
|
||||||
let Foo: ModelCtor<Model>;
|
|
||||||
beforeEach(() => {
|
|
||||||
db = getDatabase();
|
|
||||||
db.table({
|
|
||||||
name: 'bazs',
|
|
||||||
tableName: 'bazs_model'
|
|
||||||
});
|
|
||||||
db.table({
|
|
||||||
name: 'bays',
|
|
||||||
tableName: 'bays_model'
|
|
||||||
});
|
|
||||||
db.table({
|
|
||||||
name: 'bars',
|
|
||||||
tableName: 'bars_model',
|
|
||||||
fields: [
|
|
||||||
{
|
|
||||||
type: 'belongsTo',
|
|
||||||
name: 'baz',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'belongsTo',
|
|
||||||
name: 'bay',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
db.table({
|
|
||||||
name: 'foos',
|
|
||||||
tableName: 'foos_model',
|
|
||||||
fields: [
|
|
||||||
{
|
|
||||||
type: 'hasMany',
|
|
||||||
name: 'bars',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'hasMany',
|
|
||||||
name: 'fozs',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'hasMany',
|
|
||||||
name: 'coos',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
db.table({
|
|
||||||
name: 'fozs',
|
|
||||||
tableName: 'fozs_model',
|
|
||||||
});
|
|
||||||
db.table({
|
|
||||||
name: 'coos',
|
|
||||||
tableName: 'coos_model',
|
|
||||||
});
|
|
||||||
Foo = db.getModel('foos');
|
|
||||||
});
|
|
||||||
|
|
||||||
afterEach(() => db.close());
|
|
||||||
|
|
||||||
it('parseApiJson', () => {
|
|
||||||
const data = Foo.parseApiJson({
|
|
||||||
filter: {
|
|
||||||
col1: 'co2',
|
|
||||||
}
|
|
||||||
});
|
|
||||||
expect(data).toEqual({ where: { col1: 'co2' } });
|
|
||||||
});
|
|
||||||
|
|
||||||
it('parseApiJson', () => {
|
|
||||||
const data = Foo.parseApiJson({
|
|
||||||
fields: ['col1'],
|
|
||||||
});
|
|
||||||
expect(data).toEqual({ attributes: ['col1'] });
|
|
||||||
});
|
|
||||||
|
|
||||||
it('parseApiJson', () => {
|
|
||||||
const data = Foo.parseApiJson({
|
|
||||||
fields: ['col1'],
|
|
||||||
filter: {
|
|
||||||
col1: 'co2',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
expect(data).toEqual({ attributes: ['col1'], where: { col1: 'co2' } });
|
|
||||||
});
|
|
||||||
|
|
||||||
it('parseApiJson', () => {
|
|
||||||
const data = Foo.parseApiJson({
|
|
||||||
fields: ['col1'],
|
|
||||||
filter: {
|
|
||||||
col1: 'val1',
|
|
||||||
bars: {
|
|
||||||
col1: 'val1',
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
expect(data).toEqual({
|
|
||||||
attributes: ['col1'],
|
|
||||||
where: { col1: 'val1' },
|
|
||||||
include: [
|
|
||||||
{
|
|
||||||
association: 'bars',
|
|
||||||
where: { col1: 'val1' },
|
|
||||||
}
|
|
||||||
],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('parseApiJson', () => {
|
|
||||||
const data = Foo.parseApiJson({
|
|
||||||
fields: ['col1', 'bars.col1'],
|
|
||||||
filter: {
|
|
||||||
col1: 'val1',
|
|
||||||
bars: {
|
|
||||||
col1: 'val1',
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
expect(data).toEqual({
|
|
||||||
attributes: ['col1'],
|
|
||||||
where: { col1: 'val1' },
|
|
||||||
include: [
|
|
||||||
{
|
|
||||||
association: 'bars',
|
|
||||||
attributes: ['col1'],
|
|
||||||
where: { col1: 'val1' },
|
|
||||||
}
|
|
||||||
],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('findByApiJson', () => {
|
|
||||||
it('q', async () => {
|
it('q', async () => {
|
||||||
db.getModel('tags').addScope('scopeName', (name, ctx) => {
|
db.getModel('tags').addScope('scopeName', (name, ctx) => {
|
||||||
expect(ctx.scopeName).toBe(name);
|
expect(ctx.scopeName).toBe(name);
|
||||||
@ -477,17 +345,15 @@ describe('actions', () => {
|
|||||||
expect(row.get('title')).toBe('title112233');
|
expect(row.get('title')).toBe('title112233');
|
||||||
expect(row.user.get('name')).toBe('name112233');
|
expect(row.user.get('name')).toBe('name112233');
|
||||||
});
|
});
|
||||||
} catch(error) {
|
} catch(error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
// console.log(count);
|
// console.log(count);
|
||||||
|
|
||||||
// expect(count).toBe(1);
|
// expect(count).toBe(1);
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
|
||||||
describe('query', () => {
|
|
||||||
it('to be explained', async () => {
|
it('to be explained', async () => {
|
||||||
const [User, Post] = db.getModels(['users', 'posts']);
|
const [User, Post] = db.getModels(['users', 'posts']);
|
||||||
const postData = [];
|
const postData = [];
|
206
packages/database/src/__tests__/model/parseApiJson.test.ts
Normal file
206
packages/database/src/__tests__/model/parseApiJson.test.ts
Normal file
@ -0,0 +1,206 @@
|
|||||||
|
import { literal } from 'sequelize';
|
||||||
|
import { getDatabase } from '..';
|
||||||
|
import Database from '../../database';
|
||||||
|
import Model, { ModelCtor } from '../../model';
|
||||||
|
|
||||||
|
|
||||||
|
let db: Database;
|
||||||
|
let Bar: ModelCtor<Model>;
|
||||||
|
let Foo: ModelCtor<Model>;
|
||||||
|
|
||||||
|
beforeAll(() => {
|
||||||
|
db = getDatabase();
|
||||||
|
db.table({
|
||||||
|
name: 'bazs',
|
||||||
|
});
|
||||||
|
db.table({
|
||||||
|
name: 'bays',
|
||||||
|
});
|
||||||
|
db.table({
|
||||||
|
name: 'bars',
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
type: 'belongsTo',
|
||||||
|
name: 'baz',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'belongsTo',
|
||||||
|
name: 'bay',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
db.table({
|
||||||
|
name: 'foos',
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
type: 'hasMany',
|
||||||
|
name: 'bars',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'hasMany',
|
||||||
|
name: 'fozs',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'hasMany',
|
||||||
|
name: 'coos',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
db.table({
|
||||||
|
name: 'fozs',
|
||||||
|
});
|
||||||
|
db.table({
|
||||||
|
name: 'coos',
|
||||||
|
});
|
||||||
|
Bar = db.getModel('bars');
|
||||||
|
Foo = db.getModel('foos');
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(() => db.close());
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
describe('parseApiJson', () => {
|
||||||
|
describe('self table', () => {
|
||||||
|
it('filter', () => {
|
||||||
|
const data = Foo.parseApiJson({
|
||||||
|
filter: {
|
||||||
|
col1: 'co2',
|
||||||
|
}
|
||||||
|
});
|
||||||
|
expect(data).toEqual({ where: { col1: 'co2' } });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('fields', () => {
|
||||||
|
const data = Foo.parseApiJson({
|
||||||
|
fields: ['col1'],
|
||||||
|
});
|
||||||
|
expect(data).toEqual({ attributes: ['col1'] });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('filter and fields', () => {
|
||||||
|
const data = Foo.parseApiJson({
|
||||||
|
fields: ['col1'],
|
||||||
|
filter: {
|
||||||
|
col1: 'co2',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
expect(data).toEqual({ attributes: ['col1'], where: { col1: 'co2' } });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('pagination: page and perPage', () => {
|
||||||
|
expect(Foo.parseApiJson({
|
||||||
|
page: 1,
|
||||||
|
perPage: 10
|
||||||
|
})).toEqual({
|
||||||
|
offset: 0,
|
||||||
|
limit: 10,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('pagination: only page', () => {
|
||||||
|
expect(Foo.parseApiJson({
|
||||||
|
page: 1
|
||||||
|
})).toEqual({
|
||||||
|
offset: 0,
|
||||||
|
limit: 20,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('sort: single self field', () => {
|
||||||
|
expect(Foo.parseApiJson({
|
||||||
|
sort: 'a'
|
||||||
|
})).toEqual({
|
||||||
|
order: [
|
||||||
|
['a', 'ASC']
|
||||||
|
]
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// TODO(bug): should not contain additional attributes
|
||||||
|
it('sort: multiple self fields', () => {
|
||||||
|
expect(Foo.parseApiJson({
|
||||||
|
sort: 'a,-b'
|
||||||
|
})).toEqual({
|
||||||
|
order: [
|
||||||
|
['a', 'ASC'],
|
||||||
|
['b', 'DESC'],
|
||||||
|
]
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// TODO(feature): order by association should be ok
|
||||||
|
it.skip('sort: association field', () => {
|
||||||
|
expect(Bar.parseApiJson({
|
||||||
|
sort: '-foo.a'
|
||||||
|
})).toEqual({
|
||||||
|
order: [
|
||||||
|
[{ model: Foo, as: 'foo' }, 'a', 'DESC'],
|
||||||
|
]
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('page + sort + fields', () => {
|
||||||
|
expect(Foo.parseApiJson({
|
||||||
|
fields: ['a', 'b'],
|
||||||
|
page: 2,
|
||||||
|
perPage: 10,
|
||||||
|
sort: 'a'
|
||||||
|
})).toEqual({
|
||||||
|
attributes: ['a', 'b'],
|
||||||
|
offset: 10,
|
||||||
|
limit: 10,
|
||||||
|
order: [
|
||||||
|
['a', 'ASC']
|
||||||
|
]
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('associations', () => {
|
||||||
|
it('filter on hasMany field', () => {
|
||||||
|
const data = Foo.parseApiJson({
|
||||||
|
fields: ['col1'],
|
||||||
|
filter: {
|
||||||
|
col1: 'val1',
|
||||||
|
bars: {
|
||||||
|
col1: 'val1',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
expect(data).toEqual({
|
||||||
|
attributes: ['col1'],
|
||||||
|
where: { col1: 'val1' },
|
||||||
|
include: [
|
||||||
|
{
|
||||||
|
association: 'bars',
|
||||||
|
where: { col1: 'val1' },
|
||||||
|
}
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('filter and fields to fetch hasMany field', () => {
|
||||||
|
const data = Foo.parseApiJson({
|
||||||
|
fields: ['col1', 'bars.col1'],
|
||||||
|
filter: {
|
||||||
|
col1: 'val1',
|
||||||
|
bars: {
|
||||||
|
col1: 'val1',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
expect(data).toEqual({
|
||||||
|
attributes: ['col1'],
|
||||||
|
where: { col1: 'val1' },
|
||||||
|
include: [
|
||||||
|
{
|
||||||
|
association: 'bars',
|
||||||
|
attributes: ['col1'],
|
||||||
|
where: { col1: 'val1' },
|
||||||
|
}
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -6,7 +6,7 @@ import { getDatabase } from './';
|
|||||||
let db: Database;
|
let db: Database;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
db = await getDatabase();
|
db = getDatabase();
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(async () => {
|
afterEach(async () => {
|
||||||
|
@ -6,7 +6,7 @@ import Model from '../model';
|
|||||||
let db: Database;
|
let db: Database;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
db = await getDatabase();
|
db = getDatabase();
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(async () => {
|
afterEach(async () => {
|
||||||
@ -47,7 +47,8 @@ describe('tables', () => {
|
|||||||
name: 'foos',
|
name: 'foos',
|
||||||
});
|
});
|
||||||
expect(db.getModel('foos').name).toBe('foos');
|
expect(db.getModel('foos').name).toBe('foos');
|
||||||
expect(db.getModel('foos').getTableName()).toBe('foos');
|
// hooked by beforeDefine in index.ts
|
||||||
|
expect(db.getModel('foos').getTableName()).toBe('_packages_database_src___tests___tables_foos');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be custom when tableName is defined', async () => {
|
it('should be custom when tableName is defined', async () => {
|
||||||
@ -56,7 +57,8 @@ describe('tables', () => {
|
|||||||
tableName: 'bar_v2'
|
tableName: 'bar_v2'
|
||||||
});
|
});
|
||||||
expect(db.getModel('bar').name).toBe('bar');
|
expect(db.getModel('bar').name).toBe('bar');
|
||||||
expect(db.getModel('bar').getTableName()).toBe('bar_v2');
|
// hooked by beforeDefine in index.ts
|
||||||
|
expect(db.getModel('bar').getTableName()).toBe('_packages_database_src___tests___tables_bar_v2');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be custom when timestamps is defined', async () => {
|
it('should be custom when timestamps is defined', async () => {
|
||||||
|
@ -86,6 +86,14 @@ describe('utils.toWhere', () => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('Op.lt', () => {
|
||||||
|
expect(toWhere({
|
||||||
|
'id.lt': 1
|
||||||
|
})).toEqual({
|
||||||
|
id: { [Op.lt]: 1 }
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('Op.between', () => {
|
it('Op.between', () => {
|
||||||
expect(toWhere({
|
expect(toWhere({
|
||||||
@ -94,6 +102,14 @@ describe('utils.toWhere', () => {
|
|||||||
id: { [Op.between]: [1, 2] }
|
id: { [Op.between]: [1, 2] }
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('Op.between date', () => {
|
||||||
|
expect(toWhere({
|
||||||
|
'id.between': ['2020-11-01T00:00:00.000Z', '2020-12-01T00:00:00.000Z']
|
||||||
|
})).toEqual({
|
||||||
|
id: { [Op.between]: ['2020-11-01T00:00:00.000Z', '2020-12-01T00:00:00.000Z'] }
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('group by logical operator', () => {
|
describe('group by logical operator', () => {
|
||||||
@ -149,6 +165,30 @@ describe('utils.toWhere', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('or in or (not fully parsed)', () => {
|
||||||
|
expect(toWhere({
|
||||||
|
or: [{ a: 1 }, { 'b.or': [3, 4] }],
|
||||||
|
})).toEqual({
|
||||||
|
[Op.or]: [{ a: 1 }, { b: { [Op.or]: [3, 4] } }],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('or in or (parsed)', () => {
|
||||||
|
expect(toWhere({
|
||||||
|
or: [{ a: 1 }, { b: { or: [3, 4] } }],
|
||||||
|
})).toEqual({
|
||||||
|
[Op.or]: [{ a: 1 }, { b: { [Op.or]: [3, 4] } }],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('or in or in or', () => {
|
||||||
|
expect(toWhere({
|
||||||
|
or: [{ a: 1 }, { or: { b: 3, c: { or: [5, 6] } } }],
|
||||||
|
})).toEqual({
|
||||||
|
[Op.or]: [{ a: 1 }, { [Op.or]: { b: 3, c: { [Op.or]: [5, 6] } } }],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('or in and', () => {
|
it('or in and', () => {
|
||||||
expect(toWhere({
|
expect(toWhere({
|
||||||
and: [{ a: 1 }, { 'b.or': [3, 4] }],
|
and: [{ a: 1 }, { 'b.or': [3, 4] }],
|
||||||
@ -165,8 +205,16 @@ describe('utils.toWhere', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('logical and other comparation', () => {
|
||||||
|
expect(toWhere({
|
||||||
|
or: [{ a: 1 }, { b: { gt: 2 } }],
|
||||||
|
})).toEqual({
|
||||||
|
[Op.or]: [{ a: 1 }, { b: { [Op.gt]: 2 } }],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
// TODO: bug
|
// TODO: bug
|
||||||
it.skip('field as or', () => {
|
it.skip('field as "or"', () => {
|
||||||
expect(toWhere({
|
expect(toWhere({
|
||||||
or: 1,
|
or: 1,
|
||||||
})).toEqual({
|
})).toEqual({
|
||||||
@ -175,7 +223,7 @@ describe('utils.toWhere', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// TODO: bug
|
// TODO: bug
|
||||||
it.skip('or for field as or', () => {
|
it.skip('or for field as "or"', () => {
|
||||||
expect(toWhere({
|
expect(toWhere({
|
||||||
'or.or': [1, 2],
|
'or.or': [1, 2],
|
||||||
})).toEqual({
|
})).toEqual({
|
||||||
@ -186,60 +234,59 @@ describe('utils.toWhere', () => {
|
|||||||
|
|
||||||
describe('association', () => {
|
describe('association', () => {
|
||||||
let db: Database;
|
let db: Database;
|
||||||
let Foo: ModelCtor<Model>;
|
|
||||||
beforeAll(() => {
|
beforeAll(() => {
|
||||||
db = getDatabase();
|
db = getDatabase();
|
||||||
db.table({
|
db.table({
|
||||||
name: 'bazs',
|
name: 'users',
|
||||||
});
|
});
|
||||||
db.table({
|
db.table({
|
||||||
name: 'bars',
|
name: 'posts',
|
||||||
fields: [
|
fields: [
|
||||||
{
|
{
|
||||||
type: 'belongsTo',
|
type: 'belongsTo',
|
||||||
name: 'baz',
|
name: 'user',
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
db.table({
|
db.table({
|
||||||
name: 'foos',
|
name: 'categories',
|
||||||
fields: [
|
fields: [
|
||||||
{
|
{
|
||||||
type: 'hasMany',
|
type: 'hasMany',
|
||||||
name: 'bars',
|
name: 'posts',
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
Foo = db.getModel('foos');
|
|
||||||
});
|
});
|
||||||
afterAll(() => db.close());
|
afterAll(() => db.close());
|
||||||
|
|
||||||
const toWhereExpect = (options, logging = false) => {
|
const toWhereExpect = (options: any) => {
|
||||||
|
const Category = db.getModel('categories');
|
||||||
const where = toWhere(options, {
|
const where = toWhere(options, {
|
||||||
associations: Foo.associations,
|
associations: Category.associations,
|
||||||
});
|
});
|
||||||
return expect(where);
|
return expect(where);
|
||||||
}
|
}
|
||||||
|
|
||||||
it('association', () => {
|
it('with included association where', () => {
|
||||||
toWhereExpect({
|
toWhereExpect({
|
||||||
col1: 'val1',
|
col1: 'val1',
|
||||||
bars: {
|
posts: {
|
||||||
name: {
|
name: {
|
||||||
ilike: 'aa',
|
ilike: 'aa',
|
||||||
},
|
},
|
||||||
col2: {
|
col2: {
|
||||||
lt: 2,
|
lt: 2,
|
||||||
},
|
},
|
||||||
baz: {
|
user: {
|
||||||
col1: 12,
|
col1: 12,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
'bars.col3.ilike': 'aa',
|
'posts.col3.ilike': 'aa',
|
||||||
}).toEqual({
|
}).toEqual({
|
||||||
col1: 'val1',
|
col1: 'val1',
|
||||||
$__include: {
|
$__include: {
|
||||||
bars: {
|
posts: {
|
||||||
name: {
|
name: {
|
||||||
[Op.iLike]: 'aa',
|
[Op.iLike]: 'aa',
|
||||||
},
|
},
|
||||||
@ -250,7 +297,7 @@ describe('utils.toWhere', () => {
|
|||||||
[Op.iLike]: 'aa',
|
[Op.iLike]: 'aa',
|
||||||
},
|
},
|
||||||
$__include: {
|
$__include: {
|
||||||
baz: {
|
user: {
|
||||||
col1: 12
|
col1: 12
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -258,5 +305,19 @@ describe('utils.toWhere', () => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// TODO: 是否要考虑跨关联表的逻辑分组这种情况
|
||||||
|
it.skip('logical across association', () => {
|
||||||
|
toWhereExpect({
|
||||||
|
or: {
|
||||||
|
status: 1,
|
||||||
|
posts: {
|
||||||
|
status: 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).toEqual({
|
||||||
|
|
||||||
|
})
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -82,6 +82,12 @@ export interface ApiJsonOptions {
|
|||||||
*/
|
*/
|
||||||
sort?: any;
|
sort?: any;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 页码
|
||||||
|
*/
|
||||||
|
page?: number;
|
||||||
|
perPage?: number;
|
||||||
|
|
||||||
context?: any;
|
context?: any;
|
||||||
|
|
||||||
[key: string]: any;
|
[key: string]: any;
|
||||||
@ -226,13 +232,17 @@ export abstract class Model extends SequelizeModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static parseApiJson(options: ApiJsonOptions) {
|
static parseApiJson(options: ApiJsonOptions) {
|
||||||
const { fields, filter, sort, context, ...restOptions } = options;
|
const { fields, filter, sort, context, page, perPage } = options;
|
||||||
const data = toInclude({fields, filter, sort}, {
|
const data = toInclude({fields, filter, sort}, {
|
||||||
Model: this,
|
Model: this,
|
||||||
associations: this.associations,
|
associations: this.associations,
|
||||||
dialect: this.sequelize.getDialect(),
|
dialect: this.sequelize.getDialect(),
|
||||||
ctx: context,
|
ctx: context,
|
||||||
});
|
});
|
||||||
|
if (page || perPage) {
|
||||||
|
data.limit = perPage || 20;
|
||||||
|
data.offset = data.limit * (page > 0 ? page - 1 : 0);
|
||||||
|
}
|
||||||
if (data.attributes && data.attributes.length === 0) {
|
if (data.attributes && data.attributes.length === 0) {
|
||||||
delete data.attributes;
|
delete data.attributes;
|
||||||
}
|
}
|
||||||
|
@ -111,11 +111,12 @@ export function toInclude(options: any, context: ToIncludeContext = {}) {
|
|||||||
if (Array.isArray(key)) {
|
if (Array.isArray(key)) {
|
||||||
order.push(key);
|
order.push(key);
|
||||||
} else {
|
} else {
|
||||||
const direction = key.indexOf('-') === 0 ? 'DESC' : 'ASC';
|
const direction = key[0] === '-' ? 'DESC' : 'ASC';
|
||||||
const field = key.replace(/^-/, '');
|
const field = key.replace(/^-/, '');
|
||||||
items.appends.push(field);
|
// TODO(verify): 理论上只是排序并不一定要输出相关字段
|
||||||
// TODO:暂时只支持 postgresql
|
// items.appends.push(field);
|
||||||
order.push([Sequelize.literal(dialect === 'mysql' ? `\`${field}\`` : `"${field}"`), direction]);
|
// TODO: 暂时只支持主表排序,后续需要按`.`分隔符拆分 field 为关联排序
|
||||||
|
order.push([field, direction]);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -257,7 +257,10 @@ export class Resourcer {
|
|||||||
ctx.action = this.getAction(nameRule(params), params.actionName).clone();
|
ctx.action = this.getAction(nameRule(params), params.actionName).clone();
|
||||||
ctx.action.setContext(ctx);
|
ctx.action.setContext(ctx);
|
||||||
// 自带 query 处理的不太给力,需要用 qs 转一下
|
// 自带 query 处理的不太给力,需要用 qs 转一下
|
||||||
const query = qs.parse(qs.stringify(ctx.query));
|
const query = qs.parse(ctx.request.querystring, {
|
||||||
|
// 原始 query string 中如果一个键连等号“=”都没有可以被认为是 null 类型
|
||||||
|
strictNullHandling: true
|
||||||
|
});
|
||||||
// filter 支持 json string
|
// filter 支持 json string
|
||||||
if (typeof query.filter === 'string') {
|
if (typeof query.filter === 'string') {
|
||||||
query.filter = JSON.parse(query.filter);
|
query.filter = JSON.parse(query.filter);
|
||||||
|
@ -87,7 +87,10 @@ export function middleware(options: MiddlewareOptions = {}) {
|
|||||||
ctx.action = resourcer.getAction(resourceName, params.actionName).clone();
|
ctx.action = resourcer.getAction(resourceName, params.actionName).clone();
|
||||||
ctx.action.setContext(ctx);
|
ctx.action.setContext(ctx);
|
||||||
// 自带 query 处理的不太给力,需要用 qs 转一下
|
// 自带 query 处理的不太给力,需要用 qs 转一下
|
||||||
const query = qs.parse(qs.stringify(ctx.query));
|
const query = qs.parse(ctx.request.querystring, {
|
||||||
|
// 原始 query string 中如果一个键连等号“=”都没有可以被认为是 null 类型
|
||||||
|
strictNullHandling: true
|
||||||
|
});
|
||||||
// filter 支持 json string
|
// filter 支持 json string
|
||||||
if (typeof query.filter === 'string') {
|
if (typeof query.filter === 'string') {
|
||||||
query.filter = JSON.parse(query.filter);
|
query.filter = JSON.parse(query.filter);
|
||||||
|
Loading…
Reference in New Issue
Block a user