Skip to content

Commit

Permalink
feat: highlighted views and queries (#1953)
Browse files Browse the repository at this point in the history
  • Loading branch information
capJavert authored May 30, 2024
1 parent e030f8b commit c0758a0
Show file tree
Hide file tree
Showing 18 changed files with 752 additions and 65 deletions.
4 changes: 4 additions & 0 deletions .infra/crons.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,4 +85,8 @@ export const crons: Cron[] = [
memory: '1Gi',
},
},
{
name: 'update-highlighted-views',
schedule: '15 4 * * 0',
},
];
22 changes: 0 additions & 22 deletions __tests__/__snapshots__/tags.ts.snap
Original file line number Diff line number Diff line change
@@ -1,27 +1,5 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`query popularTags should return most popular tags ordered by value 1`] = `
Object {
"popularTags": Array [
Object {
"name": "development",
},
Object {
"name": "fullstack",
},
Object {
"name": "golang",
},
Object {
"name": "rust",
},
Object {
"name": "webdev",
},
],
}
`;

exports[`query searchTags should search for tags and order by value 1`] = `
Object {
"searchTags": Object {
Expand Down
10 changes: 10 additions & 0 deletions __tests__/cron/updateHighlightedViews.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { crons } from '../../src/cron/index';
import cron from '../../src/cron/updateHighlightedViews';

describe('updateHighlightedViews cron', () => {
it('should be registered', () => {
const registeredWorker = crons.find((item) => item.name === cron.name);

expect(registeredWorker).toBeDefined();
});
});
178 changes: 178 additions & 0 deletions __tests__/sources.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
NotificationPreferenceSource,
Post,
PostKeyword,
PostType,
SharePost,
Source,
SourceFeed,
Expand Down Expand Up @@ -232,6 +233,183 @@ describe('query sources', () => {
});
});

describe('query mostRecentSources', () => {
const QUERY = `
query MostRecentSources {
mostRecentSources {
id
name
image
public
}
}
`;

it('should return most recent sources', async () => {
const res = await client.query(QUERY);
expect(res.errors).toBeFalsy();
expect(res.data).toMatchObject({
mostRecentSources: [
{ id: 'a', name: 'A', image: 'http://image.com/a', public: true },
{ id: 'b', name: 'B', image: 'http://image.com/b', public: true },
],
});
});
});

describe('query trendingSources', () => {
const QUERY = `
query TrendingSources {
trendingSources {
id
name
image
public
}
}
`;

it('should return most trending sources', async () => {
await con.getRepository(Post).save(
new Array(5).fill('a').map((item, index) => {
return {
id: `post_${index}`,
shortId: `post_${index}`,
title: `Post ${index}`,
tagsStr: 'tag1',
upvotes: 10 + index,
createdAt: new Date(),
sourceId: 'a',
};
}),
);
await con.getRepository(Post).save({
id: `post_6`,
shortId: `post_6`,
title: `Post 6`,
tagsStr: 'tag1',
upvotes: 10,
createdAt: new Date(),
sourceId: 'b',
});
await con.query(`REFRESH MATERIALIZED VIEW trending_post`);
await con.query(`REFRESH MATERIALIZED VIEW trending_source`);

const res = await client.query(QUERY);
expect(res.errors).toBeFalsy();
expect(res.data).toMatchObject({
trendingSources: [
{ id: 'a', name: 'A', image: 'http://image.com/a', public: null },
],
});
});
});

describe('query popularSources', () => {
const QUERY = `
query PopularSources {
popularSources {
id
name
image
public
}
}
`;

it('should return most popular sources', async () => {
await con.getRepository(Post).save(
new Array(6).fill('a').map((item, index) => {
return {
id: `post_${index}`,
shortId: `post_${index}`,
title: `Post ${index}`,
tagsStr: 'tag1',
upvotes: 10 + index,
createdAt: new Date(),
sourceId: 'a',
};
}),
);
await con.getRepository(Post).save(
new Array(5).fill('b').map((item, index) => {
return {
id: `post_${index}`,
shortId: `post_${index}`,
title: `Post ${index}`,
tagsStr: 'tag1',
upvotes: 10 + index,
createdAt: new Date(),
sourceId: 'a',
};
}),
);
await con.query(`REFRESH MATERIALIZED VIEW popular_post`);
await con.query(`REFRESH MATERIALIZED VIEW popular_source`);

const res = await client.query(QUERY);
expect(res.errors).toBeFalsy();
expect(res.data).toMatchObject({
popularSources: [
{ id: 'a', name: 'A', image: 'http://image.com/a', public: null },
],
});
});
});

describe('query topVideoSources', () => {
const QUERY = `
query TopVideoSources {
topVideoSources {
id
name
image
public
}
}
`;

it('should return top video sources', async () => {
await con.getRepository(Post).save(
new Array(6).fill('a').map((item, index) => {
return {
id: `post_a_${index}`,
shortId: `post_a_${index}`,
title: `Post ${index}`,
tagsStr: 'tag1',
upvotes: 10 + index,
createdAt: new Date(),
sourceId: 'a',
type: PostType.VideoYouTube,
};
}),
);
await con.getRepository(Post).save(
new Array(6).fill('b').map((item, index) => {
return {
id: `post_b_${index}`,
shortId: `post_b_${index}`,
title: `Post ${index}`,
tagsStr: 'tag1',
upvotes: 10 + index,
createdAt: new Date(),
sourceId: 'b',
};
}),
);
await con.query(`REFRESH MATERIALIZED VIEW popular_video_post`);
await con.query(`REFRESH MATERIALIZED VIEW popular_video_source`);

const res = await client.query(QUERY);
expect(res.errors).toBeFalsy();
expect(res.data).toMatchObject({
topVideoSources: [
{ id: 'a', name: 'A', image: 'http://image.com/a', public: null },
],
});
});
});

describe('query sourceByFeed', () => {
const QUERY = `
query SourceByFeed($data: String!) {
Expand Down
88 changes: 67 additions & 21 deletions __tests__/tags.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
saveFixtures,
testQueryError,
} from './helpers';
import { ArticlePost, Keyword, PostKeyword, Source } from '../src/entity';
import { ArticlePost, Keyword, Post, PostKeyword, Source } from '../src/entity';
import {
keywordsFixture,
postRecommendedKeywordsFixture,
Expand Down Expand Up @@ -68,27 +68,46 @@ describe('query trendingTags', () => {
}
}`;

beforeEach(async () => {
await saveFixtures(con, Source, sourcesFixture);
let tags = 'tag1';
await con.getRepository(Post).save(
new Array(20).fill('tag').map((item, index) => {
tags += `,tag${index + 1}`;
return {
id: `post_${index}`,
shortId: `post_${index}`,
title: `Post ${index}`,
tagsStr: tags,
upvotes: 10 + index,
createdAt: new Date(),
sourceId: 'a',
};
}),
);
await con.query(`REFRESH MATERIALIZED VIEW trending_post`);
await con.query(`REFRESH MATERIALIZED VIEW trending_tag`);
});

it('should return most trending tags ordered by value', async () => {
const res = await client.query(QUERY);
expect(res.data).toMatchObject({
trendingTags: [
{ name: 'development' },
{ name: 'fullstack' },
{ name: 'golang' },
{ name: 'rust' },
{ name: 'webdev' },
{ name: 'tag1' },
{ name: 'tag2' },
{ name: 'tag3' },
{ name: 'tag4' },
{ name: 'tag5' },
{ name: 'tag6' },
{ name: 'tag7' },
{ name: 'tag8' },
{ name: 'tag9' },
{ name: 'tag10' },
],
});
});

it('should return limit of 10 by default', async () => {
await con.getRepository(Keyword).save(
new Array(20).fill('tag').map((item, index) => ({
value: item + index,
occurances: 0,
status: 'allow',
})),
);
const res = await client.query(QUERY);
expect(res.data.trendingTags.length).toBe(10);
});
Expand All @@ -101,19 +120,46 @@ describe('query popularTags', () => {
}
}`;

beforeEach(async () => {
await saveFixtures(con, Source, sourcesFixture);
let tags = 'tag1';
await con.getRepository(Post).save(
new Array(20).fill('tag').map((item, index) => {
tags += `,tag${index + 1}`;
return {
id: `post_${index}`,
shortId: `post_${index}`,
title: `Post ${index}`,
tagsStr: tags,
upvotes: 10 + index,
createdAt: new Date(),
sourceId: 'a',
};
}),
);
await con.query(`REFRESH MATERIALIZED VIEW popular_post`);
await con.query(`REFRESH MATERIALIZED VIEW popular_tag`);
});

it('should return most popular tags ordered by value', async () => {
const res = await client.query(QUERY);
expect(res.data).toMatchSnapshot();
expect(res.data).toMatchObject({
popularTags: [
{ name: 'tag1' },
{ name: 'tag2' },
{ name: 'tag3' },
{ name: 'tag4' },
{ name: 'tag5' },
{ name: 'tag6' },
{ name: 'tag7' },
{ name: 'tag8' },
{ name: 'tag9' },
{ name: 'tag10' },
],
});
});

it('should return limit of 10 by default', async () => {
await con.getRepository(Keyword).save(
new Array(20).fill('tag').map((item, index) => ({
value: item + index,
occurances: 0,
status: 'allow',
})),
);
const res = await client.query(QUERY);
expect(res.data.popularTags.length).toBe(10);
});
Expand Down
2 changes: 2 additions & 0 deletions src/cron/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import personalizedDigest from './personalizedDigest';
import generateSearchInvites from './generateSearchInvites';
import checkReferralReminder from './checkReferralReminder';
import dailyDigest from './dailyDigest';
import updateHighlightedViews from './updateHighlightedViews';

export const crons: Cron[] = [
updateViews,
Expand All @@ -25,4 +26,5 @@ export const crons: Cron[] = [
generateSearchInvites,
checkReferralReminder,
dailyDigest,
updateHighlightedViews,
];
Loading

0 comments on commit c0758a0

Please sign in to comment.