Skip to content

Commit

Permalink
Isaac/merge main release (#92)
Browse files Browse the repository at this point in the history
* Add Jest testing (#63)

* Initial boilerplate setup

* Add ArticleFactory, TestingDBConnection, FactoryUtils, placeholder article test file

* Add test cases on getAllArticles

* Create PublicationFactory, add tests for getAllArticles, getArticlesByPublicationID(s), getArticlesByPublicationSlug(s)

* Add hooks

* Update README

* Add pr changes

* Add pr changes

* Add tests for incrementShoutouts, searchArticle, getArticlesAfterDate

* Add publication tests and refactor factory files

* Remove test db open message

* Update src/tests/article.test.ts

Co-authored-by: Archit Mehta <4architmehta@gmail.com>

* Update src/tests/data/ArticleFactory.ts

Co-authored-by: Archit Mehta <4architmehta@gmail.com>

* Refactor code

* Refactor byDate

Co-authored-by: Kidus Zegeye <kmz25@cornell.edu>
Co-authored-by: Archit Mehta <4architmehta@gmail.com>

* Add Docstrings to Test Factory Functions (#64)

* Initial boilerplate setup

* Add ArticleFactory, TestingDBConnection, FactoryUtils, placeholder article test file

* Add test cases on getAllArticles

* Create PublicationFactory, add tests for getAllArticles, getArticlesByPublicationID(s), getArticlesByPublicationSlug(s)

* Add hooks

* Update README

* Add pr changes

* Add pr changes

* Add tests for incrementShoutouts, searchArticle, getArticlesAfterDate

* Add publication tests and refactor factory files

* Remove test db open message

* Update src/tests/article.test.ts

Co-authored-by: Archit Mehta <4architmehta@gmail.com>

* Update src/tests/data/ArticleFactory.ts

Co-authored-by: Archit Mehta <4architmehta@gmail.com>

* Refactor code

* Refactor byDate

* Add docstrings

Co-authored-by: Kidus Zegeye <kmz25@cornell.edu>
Co-authored-by: Archit Mehta <4architmehta@gmail.com>

* Update README.md

* Implement community board models (#65)

* Implemented community board models

- implemented Flyer and Organization models for community board feature
- modified User model
- updated typescript version to 4.0.5 to resolve es lint issue

* Revert package.json changes

- reverted package.json changes that are addressed in a different pr

* Add magazines to weekly debrief (#66)

* Implemented community board models

- implemented Flyer and Organization models for community board feature
- modified User model
- updated typescript version to 4.0.5 to resolve es lint issue

* Implemented weekly debrief magazines

- added magazines to weekly debrief feature
- updated user and weekly debrief models to allow tracking of read magazines
- updated logic in user and weekly debrief controllers to enable reading of magazines

* Revert package.json changes

- reverted package.json changes back to the original file
- added additional rules to eslintrc to suppress es lint warnings

* Create jest testing for magazines

- added jest test cases for magazines

* Implement jest testing for User

- implemented unit testing for UserRepo
- indirectly test Weekly Debrief with User unit tests

* Update gitignore

- updated gitignore file to include secrets folder

* Implement reshuffle articles (#68)

* Add Docstrings to Test Factory Functions (#64)

* Initial boilerplate setup

* Add ArticleFactory, TestingDBConnection, FactoryUtils, placeholder article test file

* Add test cases on getAllArticles

* Create PublicationFactory, add tests for getAllArticles, getArticlesByPublicationID(s), getArticlesByPublicationSlug(s)

* Add hooks

* Update README

* Add pr changes

* Add pr changes

* Add tests for incrementShoutouts, searchArticle, getArticlesAfterDate

* Add publication tests and refactor factory files

* Remove test db open message

* Update src/tests/article.test.ts

Co-authored-by: Archit Mehta <4architmehta@gmail.com>

* Update src/tests/data/ArticleFactory.ts

Co-authored-by: Archit Mehta <4architmehta@gmail.com>

* Refactor code

* Refactor byDate

* Add docstrings

Co-authored-by: Kidus Zegeye <kmz25@cornell.edu>
Co-authored-by: Archit Mehta <4architmehta@gmail.com>

Implement shuffle article function

try to implement reshuffling with aggregate

* Implement reshuffling of articles

* Revert package.json, update eslint and update .gitignore to match main branch

---------

Co-authored-by: Kidus Zegeye <51487468+kidzegeye@users.noreply.github.com>

* Implement magazine search (#69)

* Implement magazine search

- implemented searching for magazines

* update magazinerepo

* Add unit test for magazine search

- created unit test for MagazineRepo.searchMagazine

* Convert result of shuffling resolver to ArticleModel (#71)

* Add 3 new publications (Cornell Daily Sun, Collective X, and Cornell Healthcare Review) to publications.js (#70)

* Resolve merge conflicts in publications.json (keep release version) (#76)

* Kidus/filter articles (#48)

* Add covid filter to all article query functions

* Add filtering capability

* Fix getArticleById filter

* Create filter migration script

* Finish migration script

* Add toggle for filtering enforcement

* Implement PR changes

Co-authored-by: Kidus Zegeye <kmz25@cornell.edu>

* Fix BRSN's url

* Migrate bookmark resolver and update User Repo & Entity

* Resolve remaining merge conflicts

* Implement chronological sorting

* Update prod deployment script

* Remove unused MagazineURL index from Magazine DB

* Add no rules to publications.json

* Remove trailing comma

---------

Co-authored-by: Kidus Zegeye <51487468+kidzegeye@users.noreply.github.com>
Co-authored-by: Kidus Zegeye <kmz25@cornell.edu>
Co-authored-by: Archit404Error <4architmehta@gmail.com>

* Revert "Resolve merge conflicts in publications.json (keep release version) (#76)" (#77)

This reverts commit eccf00c.

* fix merge conflicts (#78)

* Kidus/filter articles (#48)

* Add covid filter to all article query functions

* Add filtering capability

* Fix getArticleById filter

* Create filter migration script

* Finish migration script

* Add toggle for filtering enforcement

* Implement PR changes

Co-authored-by: Kidus Zegeye <kmz25@cornell.edu>

* Fix BRSN's url

* Migrate bookmark resolver and update User Repo & Entity

* Resolve remaining merge conflicts

* Implement chronological sorting

* Update prod deployment script

* Remove unused MagazineURL index from Magazine DB

* Add no rules to publications.json

* Remove trailing comma

---------

Co-authored-by: Kidus Zegeye <51487468+kidzegeye@users.noreply.github.com>
Co-authored-by: Kidus Zegeye <kmz25@cornell.edu>
Co-authored-by: Archit404Error <4architmehta@gmail.com>

* Implement community board [1/7] (#81)

* Begin implementing FlyerRepo

- implemented basic FlyerRepo functions
- updated Flyer model to support uploading in app instead of through a google form

* Implement flyers for community board

- alphabetized imports and fields
- finished implementing flyers for community board

* Address pr review comments

- addressed pr review comments
- removed redundant filtering in FlyerRepo
- added checks for null return values in FlyerRepo
- alphabetized imports and exports

* Add imageURL field to Magazine entity. (#73)

* Add no rules to volume-backend's publications.json (#82)

* Implement community board (flyer tests) [2/7] (#84)

* Begin implementing FlyerRepo

- implemented basic FlyerRepo functions
- updated Flyer model to support uploading in app instead of through a google form

* Implement flyers for community board

- alphabetized imports and fields
- finished implementing flyers for community board

* Implement jest testing for flyers

- created jest unit testing for FlyerRepo
- created FlyerFactory to help with jest testing

* Remove isFiltered field from Flyer entity

- removed redundant isFiltered field from Flyer entity (already checked in microservice)
- updated FlyerRepo and jest testing to reflect changes

* Alphabetize imports and exports

- alphabetized imports and exports in FlyerRepo, flyer.test, and FlyerFactory

* Revert merge conflict changes

- reverted merge conflict changes (accept incoming when actually wanted to keep current)

* Address review comments

- alphabetize imports
- add unit test cases for FlyerRepo including searching for 0 flyers and maximum limit in response
- added unit test case for FlyerRepo.getTrendingFlyers

* Address review comments (Shungo)

- added spaces between test cases

* Implement community board organizations (#85)

- resolve merge conflicts in Flyer.ts and FlyerFactory.ts
- update Organization model
- implement logic in OrganizationRepo
- implement queries and mutations in OrganizationResolver

* Implement community board (organization tests) [4/7] (#86)

* Implement community board organizations

- resolve merge conflicts in Flyer.ts and FlyerFactory.ts
- update Organization model
- implement logic in OrganizationRepo
- implement queries and mutations in OrganizationResolver

* Implement jest testing for organizations

- implemented jest testing for community board organizations
- created organizations.json with 3 onboarded organizations
- added FlyerResolver and OrganizationResolver to app.ts

* Implement community board (user queries/mutations) [5/7] (#87)

* Kidus/filter articles (#48)

* Add covid filter to all article query functions

* Add filtering capability

* Fix getArticleById filter

* Create filter migration script

* Finish migration script

* Add toggle for filtering enforcement

* Implement PR changes

Co-authored-by: Kidus Zegeye <kmz25@cornell.edu>

* Fix BRSN's url

* Migrate bookmark resolver and update User Repo & Entity

* Resolve remaining merge conflicts

* Implement chronological sorting

* Update prod deployment script

* Remove unused MagazineURL index from Magazine DB

* Add no rules to publications.json

* Remove trailing comma

* Begin implementing FlyerRepo

- implemented basic FlyerRepo functions
- updated Flyer model to support uploading in app instead of through a google form

* Implement basic community board features

- finished implementing basic organization and flyer repos/resolvers
- created jest testing for organization and flyer repos

* Remove unnecessary OrganizationRepo function

- removed content types route from organization repo and organization resolver

* Implement user routes for community board

- implemented user routes for community board to allow following orgs and reading flyers
- added jest unit test cases for new UserRepo functions

* Implement mutations for following and unfollowing organizations

- added resolvers to UserResolver for following and unfollowing organizations
- updated documentation in UserRepo
- added import of FlyerRepo in app.ts

* Address pr review comments

- fix spelling

* Address pr review comments

- made style consistent in UserRepo by checking for if(user) in appendReadFlyer, appendReadArticle, and appendReadMagazine

---------

Co-authored-by: Kidus Zegeye <51487468+kidzegeye@users.noreply.github.com>
Co-authored-by: Kidus Zegeye <kmz25@cornell.edu>
Co-authored-by: Archit404Error <4architmehta@gmail.com>

* Onboard organizations (#90)

- add organizations from volume-microservice onto organizations.json

* Implement community board (categories and org lists) [6/7] (#89)

* Implement community board organizations

- resolve merge conflicts in Flyer.ts and FlyerFactory.ts
- update Organization model
- implement logic in OrganizationRepo
- implement queries and mutations in OrganizationResolver

* Implement jest testing for organizations

- implemented jest testing for community board organizations
- created organizations.json with 3 onboarded organizations
- added FlyerResolver and OrganizationResolver to app.ts

* initial commit

* Implement organizations as list for flyer model

- updated flyer model to allow a list of organizations and organizationSlugs to be associated with each flyer
- updated flyer and organization queries accordingly
- added unit jest test cases and update old ones accordingly

* Address pr review comments

- removed redundant resolvers
- updated getOrganizationByCategory test

* Address pr review comments (kate)

- updated docs in OrganizationResolver

---------

Co-authored-by: Kidus Zegeye <51487468+kidzegeye@users.noreply.github.com>
Co-authored-by: Kidus Zegeye <kmz25@cornell.edu>
Co-authored-by: Archit Mehta <4architmehta@gmail.com>
Co-authored-by: Shungo Najima <sn685@cornell.edu>
Co-authored-by: Sasha Loayza <104698418+SashaLoayza@users.noreply.github.com>
  • Loading branch information
6 people authored May 3, 2023
1 parent feec506 commit 1e16a30
Show file tree
Hide file tree
Showing 14 changed files with 741 additions and 88 deletions.
446 changes: 439 additions & 7 deletions organizations.json

Large diffs are not rendered by default.

10 changes: 7 additions & 3 deletions src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,20 @@ import admin from 'firebase-admin';
import Express from 'express';
import { buildSchema } from 'type-graphql';


import { ApolloServer } from 'apollo-server-express';
import ArticleResolver from './resolvers/ArticleResolver';
import ArticleRepo from './repos/ArticleRepo';
import { dbConnection } from './db/DBConnection';
import FlyerRepo from './repos/FlyerRepo';
import FlyerResolver from './resolvers/FlyerResolver';
import MagazineRepo from './repos/MagazineRepo';
import MagazineResolver from './resolvers/MagazineResolver';
import NotificationRepo from './repos/NotificationRepo';
import OrganizationResolver from './resolvers/OrganizationResolver';
import PublicationResolver from './resolvers/PublicationResolver';
import WeeklyDebriefRepo from './repos/WeeklyDebriefRepo';
import UserResolver from './resolvers/UserResolver';
import WeeklyDebriefRepo from './repos/WeeklyDebriefRepo';

const main = async () => {
const schema = await buildSchema({
Expand Down Expand Up @@ -76,13 +78,15 @@ const main = async () => {
}

async function setupTrendingArticleRefreshCron() {
// Refresh trending articles once
// Refresh trending articles, magazines, and flyers once
ArticleRepo.refreshTrendingArticles();
MagazineRepo.refreshFeaturedMagazines();
// Refresh trending articles 12 hours
FlyerRepo.refreshTrendingFlyers();
// Refresh trending articles, magazines, and flyers every 12 hours
cron.schedule('0 */12 * * *', async () => {
ArticleRepo.refreshTrendingArticles();
MagazineRepo.refreshFeaturedMagazines();
FlyerRepo.refreshTrendingFlyers();
});
}

Expand Down
18 changes: 18 additions & 0 deletions src/common/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,21 @@ export class PublicationSlug {
this.slug = slug;
}
}

@ObjectType({ description: 'ID of an Organization' })
export class OrganizationID {
@Field()
@Property()
id: string;
}

@ObjectType({ description: 'Slug of an Organization' })
export class OrganizationSlug {
@Field()
@Property()
slug: string;

constructor(slug: string) {
this.slug = slug;
}
}
12 changes: 6 additions & 6 deletions src/entities/Flyer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,13 @@ export class Flyer {
@Property({ default: false })
nsfw: boolean;

@Field((type) => Organization)
@Property({ type: () => Organization })
organization: Organization;
@Field((type) => [Organization])
@Property({ type: () => [Organization] })
organizations: [Organization];

@Field()
@Property()
organizationSlug: string;
@Field((type) => [String])
@Property({ type: () => [String] })
organizationSlugs: [string];

@Field()
@Property({ default: 0 })
Expand Down
4 changes: 4 additions & 0 deletions src/entities/Organization.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ export class Organization {
@Property()
bio: string;

@Field()
@Property()
categorySlug: string;

@Field()
@Property()
name: string;
Expand Down
6 changes: 3 additions & 3 deletions src/repos/FlyerRepo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ const getFlyersByOrganizationSlug = async (
limit: number = DEFAULT_LIMIT,
offset: number = DEFAULT_OFFSET,
): Promise<Flyer[]> => {
return FlyerModel.find({ 'organization.slug': slug })
return FlyerModel.find({ organizationSlugs: slug })
.sort({ date: 'desc' })
.skip(offset)
.limit(limit)
Expand All @@ -68,7 +68,7 @@ const getFlyersByOrganizationSlugs = async (
offset: number = DEFAULT_OFFSET,
): Promise<Flyer[]> => {
const uniqueSlugs = [...new Set(slugs)];
return FlyerModel.find({ 'organization.slug': { $in: uniqueSlugs } })
return FlyerModel.find({ organizationSlugs: { $in: uniqueSlugs } })
.sort({ date: 'desc' })
.skip(offset)
.limit(limit)
Expand All @@ -83,7 +83,7 @@ const getFlyersByOrganizationID = async (
offset: number = DEFAULT_OFFSET,
): Promise<Flyer[]> => {
const organization = await (await OrganizationModel.findById(organizationID)).execPopulate();
return FlyerModel.find({ 'organization.slug': organization.slug })
return FlyerModel.find({ organizationSlugs: organization.slug })
.sort({ date: 'desc' })
.skip(offset)
.limit(limit)
Expand Down
16 changes: 11 additions & 5 deletions src/repos/OrganizationRepo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,13 @@ const addOrganizationsToDB = async (): Promise<void> => {
const orgDocUpdates = [];

for (const organization of organizationsJSON.organizations) {
const { bio, name, slug, websiteURL } = organization;
const { bio, name, slug, websiteURL, categorySlug } = organization;
const orgDoc = Object.assign(new Organization(), {
bio,
name,
slug,
websiteURL,
categorySlug,
});

// Add or update the organization created from the JSON
Expand All @@ -29,6 +30,10 @@ const addOrganizationsToDB = async (): Promise<void> => {
await Promise.all(orgDocUpdates);
};

const getOrganizationsByCategory = async (categorySlug: string): Promise<Organization[]> => {
return OrganizationModel.find({ categorySlug });
};

const getOrganizationByID = async (id: string): Promise<Organization> => {
return OrganizationModel.findById(new ObjectId(id));
};
Expand Down Expand Up @@ -61,7 +66,7 @@ const getMostRecentFlyer = async (organization: Organization): Promise<Flyer> =>
// Due to the way Mongo interprets 'Organization' object,
// Organization['_doc'] must be used to access fields of a Organization object
return FlyerModel.findOne({
organizationSlug: organization['_doc'].slug, // eslint-disable-line
organizationSlugs: organization['_doc'].slug, // eslint-disable-line
}).sort({ date: 'desc' });
};

Expand All @@ -75,7 +80,7 @@ const getShoutouts = async (organization: Organization): Promise<number> => {
// Due to the way Mongo interprets 'Organization' object,
// Organization['_doc'] must be used to access fields of a Organization object
const orgFlyers = await FlyerModel.find({
organizationSlug: organization['_doc'].slug, // eslint-disable-line
organizationSlugs: organization['_doc'].slug, // eslint-disable-line
});

return orgFlyers.reduce((acc, flyer) => {
Expand All @@ -93,7 +98,7 @@ const getNumFlyers = async (organization: Organization): Promise<number> => {
// Due to the way Mongo interprets 'Organization' object,
// Organization['_doc'] must be used to access fields of a Organization object
const orgFlyers = await FlyerModel.find({
organizationSlug: organization['_doc'].slug, // eslint-disable-line
organizationSlugs: organization['_doc'].slug, // eslint-disable-line
});

return orgFlyers.length;
Expand All @@ -104,8 +109,9 @@ export default {
getAllOrganizations,
getMostRecentFlyer,
getNumFlyers,
getOrganizationsByCategory,
getOrganizationByID,
getOrganizationBySlug,
getOrganizationsByIDs,
getOrganizationBySlug,
getShoutouts,
};
95 changes: 80 additions & 15 deletions src/repos/UserRepo.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { v4 as uuidv4 } from 'uuid';
import { Article } from '../entities/Article';
import ArticleRepo from './ArticleRepo';
import { PublicationSlug } from '../common/types';
import { OrganizationSlug, PublicationSlug } from '../common/types';
import { User, UserModel } from '../entities/User';
import { Magazine } from '../entities/Magazine';
import MagazineRepo from './MagazineRepo';

import { Flyer } from '../entities/Flyer';
import FlyerRepo from './FlyerRepo';
/**
* Create new user associated with deviceToken and followedPublicationsSlugs of deviceType.
*/
Expand Down Expand Up @@ -65,6 +66,37 @@ const unfollowPublication = async (uuid: string, pubSlug: string): Promise<User>
return user;
};

/**
* Adds organization slug to user's followedOrganizations.
* Requires: the user is not already following the organization.
*/
const followOrganization = async (uuid: string, slug: string): Promise<User> => {
const user = await UserModel.findOne({ uuid });

if (user) {
user.followedOrganizations.push(new OrganizationSlug(slug));
return user.save();
}
return user;
};

/**
* Deletes organization slug from user's followedOrganizations.
*/
const unfollowOrganization = async (uuid: string, orgSlug: string): Promise<User> => {
const user = await UserModel.findOne({ uuid });
if (user) {
const orgSlugs = user.followedOrganizations.map((orgSlugObj) => orgSlugObj.slug);
const orgIndex = orgSlugs.indexOf(orgSlug);

if (orgIndex === -1) return user;

user.followedOrganizations.splice(orgIndex, 1);
return user.save();
}
return user;
};

const getUserByUUID = async (uuid: string): Promise<User> => {
return UserModel.findOne({ uuid });
};
Expand All @@ -79,41 +111,70 @@ const getUsersFollowingPublication = async (pubSlug: string): Promise<User[]> =>
return matchedUsers;
};

/**
* Return all users who follow an organization
*/
const getUsersFollowingOrganization = async (orgSlug: string): Promise<User[]> => {
const matchedUsers = await UserModel.find({
followedOrganizations: { $elemMatch: { slug: orgSlug } },
});
return matchedUsers;
};

/**
* Add article to a user's readArticles
*/
const appendReadArticle = async (uuid: string, articleID: string): Promise<User> => {
const user = await UserModel.findOne({ uuid });

if (!user) return user;

const article = await ArticleRepo.getArticleByID(articleID);
const checkDuplicates = (prev: boolean, cur: Article) => prev || cur.id === articleID;
if (user) {
const article = await ArticleRepo.getArticleByID(articleID);
const checkDuplicates = (prev: boolean, cur: Article) => prev || cur.id === articleID;

if (article && !user.readArticles.reduce(checkDuplicates, false)) {
user.readArticles.push(article);
}

user.save();
user.save();
}
return user;
};

/**
* Add a magazine to a user's readMagazines
* Add a flyer to a user's readFlyers
*/
const appendReadMagazine = async (uuid: string, magazineID: string): Promise<User> => {
const appendReadFlyer = async (uuid: string, flyerID: string): Promise<User> => {
const user = await UserModel.findOne({ uuid });

if (!user) return user;
if (user) {
const flyer = await FlyerRepo.getFlyerByID(flyerID);
const checkDuplicates = (prev: boolean, cur: Flyer) => prev || cur.id === flyerID;

const magazine = await MagazineRepo.getMagazineByID(magazineID);
const checkDuplicates = (prev: boolean, cur: Magazine) => prev || cur.id === magazineID;
if (flyer && !user.readFlyers.reduce(checkDuplicates, false)) {
user.readFlyers.push(flyer);
}

if (magazine && !user.readMagazines.reduce(checkDuplicates, false)) {
user.readMagazines.push(magazine);
user.save();
}
return user;
};

/**
* Add a magazine to a user's read
*/
const appendReadMagazine = async (uuid: string, magazineID: string): Promise<User> => {
const user = await UserModel.findOne({ uuid });

if (user) {
const magazine = await MagazineRepo.getMagazineByID(magazineID);
const checkDuplicates = (prev: boolean, cur: Magazine) => prev || cur.id === magazineID;

user.save();
if (magazine && !user.readMagazines.reduce(checkDuplicates, false)) {
user.readMagazines.push(magazine);
}

user.save();
}
return user;
};

Expand Down Expand Up @@ -148,10 +209,14 @@ const incrementBookmarks = async (uuid: string): Promise<User> => {
export default {
appendReadArticle,
appendReadMagazine,
appendReadFlyer,
createUser,
followPublication,
unfollowOrganization,
followOrganization,
getUserByUUID,
getUsersFollowingPublication,
getUsersFollowingOrganization,
incrementBookmarks,
incrementShoutouts,
unfollowPublication,
Expand Down
Loading

0 comments on commit 1e16a30

Please sign in to comment.