mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-12-17 21:41:49 +03:00
Fixed recommendation order (#18060)
fixes https://github.com/TryGhost/Product/issues/3851 - Order was not applied via the CRUD plugin - Removed usage of CRUD findAll, and swapped to Bookshelf fetchAll instead, to decrease dependencies of invisible Bookshelf plugins logic - Reverted page and limit options possibility via findAll method
This commit is contained in:
parent
acae53d9ed
commit
f566729ed6
@ -1,3 +1,5 @@
|
|||||||
|
import {Knex} from 'knex';
|
||||||
|
|
||||||
type Entity<T> = {
|
type Entity<T> = {
|
||||||
id: T;
|
id: T;
|
||||||
deleted: boolean;
|
deleted: boolean;
|
||||||
@ -11,9 +13,12 @@ type Order<T> = {
|
|||||||
export type ModelClass<T> = {
|
export type ModelClass<T> = {
|
||||||
destroy: (data: {id: T}) => Promise<void>;
|
destroy: (data: {id: T}) => Promise<void>;
|
||||||
findOne: (data: {id: T}, options?: {require?: boolean}) => Promise<ModelInstance<T> | null>;
|
findOne: (data: {id: T}, options?: {require?: boolean}) => Promise<ModelInstance<T> | null>;
|
||||||
findAll: (options: {filter?: string; order?: string, page?: number, limit?: number | 'all'}) => Promise<ModelInstance<T>[]>;
|
|
||||||
add: (data: object) => Promise<ModelInstance<T>>;
|
add: (data: object) => Promise<ModelInstance<T>>;
|
||||||
getFilteredCollection: (options: {filter?: string}) => {count(): Promise<number>};
|
getFilteredCollection: (options: {filter?: string}) => {
|
||||||
|
count(): Promise<number>,
|
||||||
|
query: (f: (q: Knex.QueryBuilder) => void) => void,
|
||||||
|
fetchAll: () => Promise<ModelInstance<T>[]>
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ModelInstance<T> = {
|
export type ModelInstance<T> = {
|
||||||
@ -64,22 +69,44 @@ export abstract class BookshelfRepository<IDType, T extends Entity<IDType>> {
|
|||||||
return model ? this.modelToEntity(model) : null;
|
return model ? this.modelToEntity(model) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
async getAll({filter, order}: { filter?: string; order?: OrderOption<T> } = {}): Promise<T[]> {
|
async #fetchAll({filter, order, page, limit}: { filter?: string; order?: OrderOption<T>; page?: number; limit?: number }): Promise<T[]> {
|
||||||
const models = await this.Model.findAll({
|
const collection = this.Model.getFilteredCollection({filter});
|
||||||
filter,
|
const orderString = this.#orderToString(order);
|
||||||
order: this.#orderToString(order)
|
|
||||||
}) as ModelInstance<IDType>[];
|
if ((limit && page) || orderString) {
|
||||||
|
collection
|
||||||
|
.query((q) => {
|
||||||
|
if (limit && page) {
|
||||||
|
q.limit(limit);
|
||||||
|
q.offset(limit * (page - 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (orderString) {
|
||||||
|
q.orderByRaw(
|
||||||
|
orderString
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const models = await collection.fetchAll();
|
||||||
return (await Promise.all(models.map(model => this.modelToEntity(model)))).filter(entity => !!entity) as T[];
|
return (await Promise.all(models.map(model => this.modelToEntity(model)))).filter(entity => !!entity) as T[];
|
||||||
}
|
}
|
||||||
|
|
||||||
async getPage({filter, order, page, limit}: { filter?: string; order?: OrderOption<T>; page: number; limit: number }): Promise<T[]> {
|
async getAll({filter, order}: { filter?: string; order?: OrderOption<T> } = {}): Promise<T[]> {
|
||||||
const models = await this.Model.findAll({
|
return this.#fetchAll({
|
||||||
filter,
|
filter,
|
||||||
order: this.#orderToString(order),
|
order
|
||||||
limit,
|
});
|
||||||
page
|
}
|
||||||
|
|
||||||
|
async getPage({filter, order, page, limit}: { filter?: string; order?: OrderOption<T>; page: number; limit: number }): Promise<T[]> {
|
||||||
|
return this.#fetchAll({
|
||||||
|
filter,
|
||||||
|
order,
|
||||||
|
page,
|
||||||
|
limit
|
||||||
});
|
});
|
||||||
return (await Promise.all(models.map(model => this.modelToEntity(model)))).filter(entity => !!entity) as T[];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async getCount({filter}: { filter?: string } = {}): Promise<number> {
|
async getCount({filter}: { filter?: string } = {}): Promise<number> {
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import assert from 'assert';
|
import assert from 'assert';
|
||||||
import {BookshelfRepository, ModelClass, ModelInstance} from '../src/index';
|
import {BookshelfRepository, ModelClass, ModelInstance} from '../src/index';
|
||||||
|
import {Knex} from 'knex';
|
||||||
|
|
||||||
type SimpleEntity = {
|
type SimpleEntity = {
|
||||||
id: string;
|
id: string;
|
||||||
@ -37,6 +38,10 @@ class SimpleBookshelfRepository extends BookshelfRepository<string, SimpleEntity
|
|||||||
class Model implements ModelClass<string> {
|
class Model implements ModelClass<string> {
|
||||||
items: ModelInstance<string>[] = [];
|
items: ModelInstance<string>[] = [];
|
||||||
|
|
||||||
|
orderRaw?: string;
|
||||||
|
limit?: number;
|
||||||
|
offset?: number;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.items = [];
|
this.items = [];
|
||||||
}
|
}
|
||||||
@ -53,9 +58,10 @@ class Model implements ModelClass<string> {
|
|||||||
}
|
}
|
||||||
return Promise.resolve(item ?? null);
|
return Promise.resolve(item ?? null);
|
||||||
}
|
}
|
||||||
findAll(options: {filter?: string | undefined; order?: string | undefined; page?: number; limit?: number | 'all'}): Promise<ModelInstance<string>[]> {
|
|
||||||
|
fetchAll(): Promise<ModelInstance<string>[]> {
|
||||||
const sorted = this.items.slice().sort((a, b) => {
|
const sorted = this.items.slice().sort((a, b) => {
|
||||||
for (const order of options.order?.split(',') ?? []) {
|
for (const order of this.orderRaw?.split(',') ?? []) {
|
||||||
const [field, direction] = order.split(' ');
|
const [field, direction] = order.split(' ');
|
||||||
|
|
||||||
const aValue = a.get(field as string) as number;
|
const aValue = a.get(field as string) as number;
|
||||||
@ -68,7 +74,7 @@ class Model implements ModelClass<string> {
|
|||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
});
|
});
|
||||||
return Promise.resolve(sorted);
|
return Promise.resolve(sorted.slice(this.offset ?? 0, (this.offset ?? 0) + (this.limit ?? sorted.length)));
|
||||||
}
|
}
|
||||||
|
|
||||||
add(data: object): Promise<ModelInstance<string>> {
|
add(data: object): Promise<ModelInstance<string>> {
|
||||||
@ -100,6 +106,24 @@ class Model implements ModelClass<string> {
|
|||||||
count() {
|
count() {
|
||||||
return Promise.resolve(this.items.length);
|
return Promise.resolve(this.items.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line no-unused-vars
|
||||||
|
query(f: (q: Knex.QueryBuilder) => void) {
|
||||||
|
return f({
|
||||||
|
limit: (limit: number) => {
|
||||||
|
this.limit = limit;
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
offset: (offset: number) => {
|
||||||
|
this.offset = offset;
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
orderByRaw: (order: string) => {
|
||||||
|
this.orderRaw = order;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
} as any as Knex.QueryBuilder);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('BookshelfRepository', function () {
|
describe('BookshelfRepository', function () {
|
||||||
|
@ -44,12 +44,6 @@ module.exports = function (Bookshelf) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.page && options.limit) {
|
|
||||||
itemCollection
|
|
||||||
.query('limit', options.limit)
|
|
||||||
.query('offset', options.limit * (options.page - 1));
|
|
||||||
}
|
|
||||||
|
|
||||||
const result = await itemCollection.fetchAll(options);
|
const result = await itemCollection.fetchAll(options);
|
||||||
if (options.withRelated) {
|
if (options.withRelated) {
|
||||||
_.each(result.models, function each(item) {
|
_.each(result.models, function each(item) {
|
||||||
|
@ -45,7 +45,7 @@ module.exports = function (Bookshelf) {
|
|||||||
case 'findOne':
|
case 'findOne':
|
||||||
return baseOptions.concat(extraOptions, ['columns', 'require', 'mongoTransformer']);
|
return baseOptions.concat(extraOptions, ['columns', 'require', 'mongoTransformer']);
|
||||||
case 'findAll':
|
case 'findAll':
|
||||||
return baseOptions.concat(extraOptions, ['filter', 'columns', 'mongoTransformer', 'page', 'limit']);
|
return baseOptions.concat(extraOptions, ['filter', 'columns', 'mongoTransformer']);
|
||||||
case 'findPage':
|
case 'findPage':
|
||||||
return baseOptions.concat(extraOptions, ['filter', 'order', 'autoOrder', 'page', 'limit', 'columns', 'mongoTransformer']);
|
return baseOptions.concat(extraOptions, ['filter', 'order', 'autoOrder', 'page', 'limit', 'columns', 'mongoTransformer']);
|
||||||
default:
|
default:
|
||||||
|
@ -221,10 +221,10 @@ Object {
|
|||||||
"featured_image": null,
|
"featured_image": null,
|
||||||
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
|
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
|
||||||
"one_click_subscribe": false,
|
"one_click_subscribe": false,
|
||||||
"reason": "Reason 0",
|
"reason": "Reason 14",
|
||||||
"title": "Recommendation 0",
|
"title": "Recommendation 14",
|
||||||
"updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
|
"updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
|
||||||
"url": "https://recommendation0.com/",
|
"url": "https://recommendation14.com/",
|
||||||
},
|
},
|
||||||
Object {
|
Object {
|
||||||
"created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
|
"created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
|
||||||
@ -233,10 +233,10 @@ Object {
|
|||||||
"featured_image": null,
|
"featured_image": null,
|
||||||
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
|
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
|
||||||
"one_click_subscribe": false,
|
"one_click_subscribe": false,
|
||||||
"reason": "Reason 1",
|
"reason": "Reason 13",
|
||||||
"title": "Recommendation 1",
|
"title": "Recommendation 13",
|
||||||
"updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
|
"updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
|
||||||
"url": "https://recommendation1.com/",
|
"url": "https://recommendation13.com/",
|
||||||
},
|
},
|
||||||
Object {
|
Object {
|
||||||
"created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
|
"created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
|
||||||
@ -245,10 +245,10 @@ Object {
|
|||||||
"featured_image": null,
|
"featured_image": null,
|
||||||
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
|
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
|
||||||
"one_click_subscribe": false,
|
"one_click_subscribe": false,
|
||||||
"reason": "Reason 2",
|
"reason": "Reason 12",
|
||||||
"title": "Recommendation 2",
|
"title": "Recommendation 12",
|
||||||
"updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
|
"updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
|
||||||
"url": "https://recommendation2.com/",
|
"url": "https://recommendation12.com/",
|
||||||
},
|
},
|
||||||
Object {
|
Object {
|
||||||
"created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
|
"created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
|
||||||
@ -257,10 +257,10 @@ Object {
|
|||||||
"featured_image": null,
|
"featured_image": null,
|
||||||
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
|
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
|
||||||
"one_click_subscribe": false,
|
"one_click_subscribe": false,
|
||||||
"reason": "Reason 3",
|
"reason": "Reason 11",
|
||||||
"title": "Recommendation 3",
|
"title": "Recommendation 11",
|
||||||
"updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
|
"updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
|
||||||
"url": "https://recommendation3.com/",
|
"url": "https://recommendation11.com/",
|
||||||
},
|
},
|
||||||
Object {
|
Object {
|
||||||
"created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
|
"created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
|
||||||
@ -269,10 +269,10 @@ Object {
|
|||||||
"featured_image": null,
|
"featured_image": null,
|
||||||
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
|
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
|
||||||
"one_click_subscribe": false,
|
"one_click_subscribe": false,
|
||||||
"reason": "Reason 4",
|
"reason": "Reason 10",
|
||||||
"title": "Recommendation 4",
|
"title": "Recommendation 10",
|
||||||
"updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
|
"updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
|
||||||
"url": "https://recommendation4.com/",
|
"url": "https://recommendation10.com/",
|
||||||
},
|
},
|
||||||
Object {
|
Object {
|
||||||
"created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
|
"created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
|
||||||
@ -281,10 +281,10 @@ Object {
|
|||||||
"featured_image": null,
|
"featured_image": null,
|
||||||
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
|
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
|
||||||
"one_click_subscribe": false,
|
"one_click_subscribe": false,
|
||||||
"reason": "Reason 5",
|
"reason": "Reason 9",
|
||||||
"title": "Recommendation 5",
|
"title": "Recommendation 9",
|
||||||
"updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
|
"updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
|
||||||
"url": "https://recommendation5.com/",
|
"url": "https://recommendation9.com/",
|
||||||
},
|
},
|
||||||
Object {
|
Object {
|
||||||
"created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
|
"created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
|
||||||
@ -293,10 +293,10 @@ Object {
|
|||||||
"featured_image": null,
|
"featured_image": null,
|
||||||
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
|
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
|
||||||
"one_click_subscribe": false,
|
"one_click_subscribe": false,
|
||||||
"reason": "Reason 6",
|
"reason": "Reason 8",
|
||||||
"title": "Recommendation 6",
|
"title": "Recommendation 8",
|
||||||
"updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
|
"updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
|
||||||
"url": "https://recommendation6.com/",
|
"url": "https://recommendation8.com/",
|
||||||
},
|
},
|
||||||
Object {
|
Object {
|
||||||
"created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
|
"created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
|
||||||
@ -317,10 +317,10 @@ Object {
|
|||||||
"featured_image": null,
|
"featured_image": null,
|
||||||
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
|
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
|
||||||
"one_click_subscribe": false,
|
"one_click_subscribe": false,
|
||||||
"reason": "Reason 8",
|
"reason": "Reason 6",
|
||||||
"title": "Recommendation 8",
|
"title": "Recommendation 6",
|
||||||
"updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
|
"updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
|
||||||
"url": "https://recommendation8.com/",
|
"url": "https://recommendation6.com/",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
@ -330,7 +330,7 @@ exports[`Recommendations Admin API Can request pages 2: [headers] 1`] = `
|
|||||||
Object {
|
Object {
|
||||||
"access-control-allow-origin": "http://127.0.0.1:2369",
|
"access-control-allow-origin": "http://127.0.0.1:2369",
|
||||||
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
|
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
|
||||||
"content-length": "2964",
|
"content-length": "2979",
|
||||||
"content-type": "application/json; charset=utf-8",
|
"content-type": "application/json; charset=utf-8",
|
||||||
"content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/,
|
"content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/,
|
||||||
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
|
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
|
||||||
@ -359,10 +359,10 @@ Object {
|
|||||||
"featured_image": null,
|
"featured_image": null,
|
||||||
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
|
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
|
||||||
"one_click_subscribe": false,
|
"one_click_subscribe": false,
|
||||||
"reason": "Reason 9",
|
"reason": "Reason 5",
|
||||||
"title": "Recommendation 9",
|
"title": "Recommendation 5",
|
||||||
"updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
|
"updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
|
||||||
"url": "https://recommendation9.com/",
|
"url": "https://recommendation5.com/",
|
||||||
},
|
},
|
||||||
Object {
|
Object {
|
||||||
"created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
|
"created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
|
||||||
@ -371,10 +371,10 @@ Object {
|
|||||||
"featured_image": null,
|
"featured_image": null,
|
||||||
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
|
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
|
||||||
"one_click_subscribe": false,
|
"one_click_subscribe": false,
|
||||||
"reason": "Reason 10",
|
"reason": "Reason 4",
|
||||||
"title": "Recommendation 10",
|
"title": "Recommendation 4",
|
||||||
"updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
|
"updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
|
||||||
"url": "https://recommendation10.com/",
|
"url": "https://recommendation4.com/",
|
||||||
},
|
},
|
||||||
Object {
|
Object {
|
||||||
"created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
|
"created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
|
||||||
@ -383,10 +383,10 @@ Object {
|
|||||||
"featured_image": null,
|
"featured_image": null,
|
||||||
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
|
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
|
||||||
"one_click_subscribe": false,
|
"one_click_subscribe": false,
|
||||||
"reason": "Reason 11",
|
"reason": "Reason 3",
|
||||||
"title": "Recommendation 11",
|
"title": "Recommendation 3",
|
||||||
"updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
|
"updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
|
||||||
"url": "https://recommendation11.com/",
|
"url": "https://recommendation3.com/",
|
||||||
},
|
},
|
||||||
Object {
|
Object {
|
||||||
"created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
|
"created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
|
||||||
@ -395,10 +395,10 @@ Object {
|
|||||||
"featured_image": null,
|
"featured_image": null,
|
||||||
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
|
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
|
||||||
"one_click_subscribe": false,
|
"one_click_subscribe": false,
|
||||||
"reason": "Reason 12",
|
"reason": "Reason 2",
|
||||||
"title": "Recommendation 12",
|
"title": "Recommendation 2",
|
||||||
"updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
|
"updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
|
||||||
"url": "https://recommendation12.com/",
|
"url": "https://recommendation2.com/",
|
||||||
},
|
},
|
||||||
Object {
|
Object {
|
||||||
"created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
|
"created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
|
||||||
@ -407,10 +407,10 @@ Object {
|
|||||||
"featured_image": null,
|
"featured_image": null,
|
||||||
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
|
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
|
||||||
"one_click_subscribe": false,
|
"one_click_subscribe": false,
|
||||||
"reason": "Reason 13",
|
"reason": "Reason 1",
|
||||||
"title": "Recommendation 13",
|
"title": "Recommendation 1",
|
||||||
"updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
|
"updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
|
||||||
"url": "https://recommendation13.com/",
|
"url": "https://recommendation1.com/",
|
||||||
},
|
},
|
||||||
Object {
|
Object {
|
||||||
"created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
|
"created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
|
||||||
@ -419,10 +419,10 @@ Object {
|
|||||||
"featured_image": null,
|
"featured_image": null,
|
||||||
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
|
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
|
||||||
"one_click_subscribe": false,
|
"one_click_subscribe": false,
|
||||||
"reason": "Reason 14",
|
"reason": "Reason 0",
|
||||||
"title": "Recommendation 14",
|
"title": "Recommendation 0",
|
||||||
"updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
|
"updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
|
||||||
"url": "https://recommendation14.com/",
|
"url": "https://recommendation0.com/",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
@ -432,7 +432,7 @@ exports[`Recommendations Admin API Can request pages 4: [headers] 1`] = `
|
|||||||
Object {
|
Object {
|
||||||
"access-control-allow-origin": "http://127.0.0.1:2369",
|
"access-control-allow-origin": "http://127.0.0.1:2369",
|
||||||
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
|
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
|
||||||
"content-length": "1790",
|
"content-length": "1775",
|
||||||
"content-type": "application/json; charset=utf-8",
|
"content-type": "application/json; charset=utf-8",
|
||||||
"content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/,
|
"content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/,
|
||||||
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
|
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
|
||||||
@ -507,7 +507,7 @@ exports[`Recommendations Admin API Uses default limit of 5 1: [headers] 1`] = `
|
|||||||
Object {
|
Object {
|
||||||
"access-control-allow-origin": "http://127.0.0.1:2369",
|
"access-control-allow-origin": "http://127.0.0.1:2369",
|
||||||
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
|
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
|
||||||
"content-length": "1573",
|
"content-length": "1585",
|
||||||
"content-type": "application/json; charset=utf-8",
|
"content-type": "application/json; charset=utf-8",
|
||||||
"content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/,
|
"content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/,
|
||||||
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
|
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
|
||||||
|
@ -201,7 +201,8 @@ describe('Recommendations Admin API', function () {
|
|||||||
favicon: null,
|
favicon: null,
|
||||||
featuredImage: null,
|
featuredImage: null,
|
||||||
excerpt: null,
|
excerpt: null,
|
||||||
oneClickSubscribe: false
|
oneClickSubscribe: false,
|
||||||
|
createdAt: new Date(i * 5000) // Reliable ordering
|
||||||
});
|
});
|
||||||
|
|
||||||
await recommendationsService.repository.save(recommendation);
|
await recommendationsService.repository.save(recommendation);
|
||||||
|
@ -20,10 +20,10 @@ Object {
|
|||||||
"featured_image": null,
|
"featured_image": null,
|
||||||
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
|
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
|
||||||
"one_click_subscribe": false,
|
"one_click_subscribe": false,
|
||||||
"reason": "Reason 0",
|
"reason": "Reason 6",
|
||||||
"title": "Recommendation 0",
|
"title": "Recommendation 6",
|
||||||
"updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
|
"updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
|
||||||
"url": "https://recommendation0.com/",
|
"url": "https://recommendation6.com/",
|
||||||
},
|
},
|
||||||
Object {
|
Object {
|
||||||
"created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
|
"created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
|
||||||
@ -32,10 +32,10 @@ Object {
|
|||||||
"featured_image": null,
|
"featured_image": null,
|
||||||
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
|
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
|
||||||
"one_click_subscribe": false,
|
"one_click_subscribe": false,
|
||||||
"reason": "Reason 1",
|
"reason": "Reason 5",
|
||||||
"title": "Recommendation 1",
|
"title": "Recommendation 5",
|
||||||
"updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
|
"updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
|
||||||
"url": "https://recommendation1.com/",
|
"url": "https://recommendation5.com/",
|
||||||
},
|
},
|
||||||
Object {
|
Object {
|
||||||
"created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
|
"created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
|
||||||
@ -44,10 +44,10 @@ Object {
|
|||||||
"featured_image": null,
|
"featured_image": null,
|
||||||
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
|
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
|
||||||
"one_click_subscribe": false,
|
"one_click_subscribe": false,
|
||||||
"reason": "Reason 2",
|
"reason": "Reason 4",
|
||||||
"title": "Recommendation 2",
|
"title": "Recommendation 4",
|
||||||
"updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
|
"updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
|
||||||
"url": "https://recommendation2.com/",
|
"url": "https://recommendation4.com/",
|
||||||
},
|
},
|
||||||
Object {
|
Object {
|
||||||
"created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
|
"created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
|
||||||
@ -68,10 +68,10 @@ Object {
|
|||||||
"featured_image": null,
|
"featured_image": null,
|
||||||
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
|
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
|
||||||
"one_click_subscribe": false,
|
"one_click_subscribe": false,
|
||||||
"reason": "Reason 4",
|
"reason": "Reason 2",
|
||||||
"title": "Recommendation 4",
|
"title": "Recommendation 2",
|
||||||
"updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
|
"updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
|
||||||
"url": "https://recommendation4.com/",
|
"url": "https://recommendation2.com/",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
@ -110,10 +110,10 @@ Object {
|
|||||||
"featured_image": null,
|
"featured_image": null,
|
||||||
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
|
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
|
||||||
"one_click_subscribe": false,
|
"one_click_subscribe": false,
|
||||||
"reason": "Reason 5",
|
"reason": "Reason 1",
|
||||||
"title": "Recommendation 5",
|
"title": "Recommendation 1",
|
||||||
"updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
|
"updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
|
||||||
"url": "https://recommendation5.com/",
|
"url": "https://recommendation1.com/",
|
||||||
},
|
},
|
||||||
Object {
|
Object {
|
||||||
"created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
|
"created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
|
||||||
@ -122,10 +122,10 @@ Object {
|
|||||||
"featured_image": null,
|
"featured_image": null,
|
||||||
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
|
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
|
||||||
"one_click_subscribe": false,
|
"one_click_subscribe": false,
|
||||||
"reason": "Reason 6",
|
"reason": "Reason 0",
|
||||||
"title": "Recommendation 6",
|
"title": "Recommendation 0",
|
||||||
"updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
|
"updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
|
||||||
"url": "https://recommendation6.com/",
|
"url": "https://recommendation0.com/",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,8 @@ describe('Recommendations Content API', function () {
|
|||||||
favicon: null,
|
favicon: null,
|
||||||
featuredImage: null,
|
featuredImage: null,
|
||||||
excerpt: null,
|
excerpt: null,
|
||||||
oneClickSubscribe: false
|
oneClickSubscribe: false,
|
||||||
|
createdAt: new Date(i * 5000) // Reliable ordering
|
||||||
});
|
});
|
||||||
|
|
||||||
await recommendationsService.repository.save(recommendation);
|
await recommendationsService.repository.save(recommendation);
|
||||||
|
Loading…
Reference in New Issue
Block a user