Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merge main to release #123

Merged
merged 43 commits into from
Nov 2, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
512c725
Add Jest testing (#63)
kidzegeye Nov 14, 2022
a0c56bb
Add Docstrings to Test Factory Functions (#64)
kidzegeye Nov 17, 2022
f974d1a
Update README.md
snajima Feb 15, 2023
bf2774c
Implement community board models (#65)
isaachan100 Mar 18, 2023
6505521
Add magazines to weekly debrief (#66)
isaachan100 Mar 20, 2023
8cd07aa
Implement reshuffle articles (#68)
SashaLoayza Mar 23, 2023
08705a4
Implement magazine search (#69)
isaachan100 Mar 23, 2023
ca89a00
Convert result of shuffling resolver to ArticleModel (#71)
SashaLoayza Mar 25, 2023
274b344
Add 3 new publications (Cornell Daily Sun, Collective X, and Cornell …
SashaLoayza Apr 4, 2023
eccf00c
Resolve merge conflicts in publications.json (keep release version) (…
SashaLoayza Apr 12, 2023
94ddfa2
Revert "Resolve merge conflicts in publications.json (keep release ve…
isaachan100 Apr 12, 2023
ebd91ec
fix merge conflicts (#78)
SashaLoayza Apr 12, 2023
573b70d
Implement community board [1/7] (#81)
isaachan100 Apr 21, 2023
904c4ae
Add imageURL field to Magazine entity. (#73)
SashaLoayza Apr 21, 2023
e012894
Add no rules to volume-backend's publications.json (#82)
SashaLoayza Apr 23, 2023
34f1bd8
Implement community board (flyer tests) [2/7] (#84)
isaachan100 Apr 25, 2023
ed6913f
Implement community board organizations (#85)
isaachan100 Apr 26, 2023
e0dcd22
Implement community board (organization tests) [4/7] (#86)
isaachan100 Apr 27, 2023
5cd38cd
Implement community board (user queries/mutations) [5/7] (#87)
isaachan100 Apr 28, 2023
048a834
Onboard organizations (#90)
isaachan100 May 1, 2023
b1f9cad
Implement community board (categories and org lists) [6/7] (#89)
isaachan100 May 2, 2023
bce6431
Implement cboard start and end dates (#93)
isaachan100 Jun 3, 2023
8860183
update organization shoutouts query (#94)
isaachan100 Jun 5, 2023
c766c93
Implement trending flyers logic (#95)
isaachan100 Jun 12, 2023
930960f
Fixed notification issue (#97)
vinnie4k Aug 22, 2023
86768bd
Flyer Notifications + Org/Flyer Model Changes (#99)
vinnie4k Sep 1, 2023
9ce89dd
Improve Search Algorithm (#101)
zachseidner1 Sep 10, 2023
d49b504
getFlyersByCategorySlug Query (#100)
vinnie4k Sep 12, 2023
b0976c5
Merge `vin/create-flyer` to `main` (#104)
vinnie4k Sep 13, 2023
d43a4f5
Trendiness Update and Migration Script for New Flyers Model (#106)
zachseidner1 Sep 14, 2023
cc16b13
Zach/trendiness and migration hot fix (#107)
zachseidner1 Sep 14, 2023
ed241dc
Micro PR for Flyers categorySlug migration (#108)
zachseidner1 Sep 20, 2023
ee2aed9
Create `editFlyer` mutation (#105)
vinnie4k Sep 26, 2023
221fe81
`checkAccessCode` query for Organization authentication (#109)
vinnie4k Sep 28, 2023
bf06c7d
Implemented getAllFlyerCategories and tests (#110)
cindy-x-liang Oct 2, 2023
5a0de53
Changing specifications for getFlyersBeforeDate and getFlyersAfterDat…
Aayush-Agnihotri Oct 4, 2023
31a508a
Cindy/flyer categories (#113)
cindy-x-liang Oct 4, 2023
47193d4
Increased HTTP request size (#114)
vinnie4k Oct 8, 2023
926eff8
Add error message (#116)
vinnie4k Oct 8, 2023
5df82ca
Update FlyerMiddleware.ts (#119)
vinnie4k Oct 15, 2023
3cf24c2
Update publications.json (#122)
jjennifergu Oct 23, 2023
889c4aa
Change Create Flyer route to use form data for image upload (#121)
zachseidner1 Oct 31, 2023
cf578dc
Merge remote-tracking branch 'origin/main' into release-copy
vinnie4k Nov 2, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
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
  • Loading branch information
isaachan100 authored May 2, 2023
commit b1f9cad073cdb92b42f6105fa49159fc94e2b167
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,
};
36 changes: 22 additions & 14 deletions src/resolvers/OrganizationResolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,20 +13,20 @@ class OrganizationResolver {
return OrganizationRepo.getAllOrganizations();
}

@Query((_returns) => Organization, {
@Query((_returns) => [Organization], {
nullable: true,
description: 'Returns a single <Organization> via a given <id>',
description: 'Returns a list of <Organization>s via a given <categorySlug>',
})
async getOrganizationByID(@Arg('id') id: string) {
return OrganizationRepo.getOrganizationByID(id);
async getOrganizationsByCategory(@Arg('categorySlug') categorySlug: string) {
return OrganizationRepo.getOrganizationsByCategory(categorySlug);
}

@Query((_returns) => Organization, {
nullable: true,
description: 'Returns a single <Organization> via a given <slug>',
description: 'Returns a single <Organization> via a given <id>',
})
async getOrganizationBySlug(@Arg('slug') slug: string) {
return OrganizationRepo.getOrganizationBySlug(slug);
async getOrganizationByID(@Arg('id') id: string) {
return OrganizationRepo.getOrganizationByID(id);
}

@Query((_returns) => [Organization], {
Expand All @@ -36,26 +36,34 @@ class OrganizationResolver {
return OrganizationRepo.getOrganizationsByIDs(ids);
}

@Query((_returns) => Organization, {
nullable: true,
description: 'Returns a single <Organization> via a given <slug>',
})
async getOrganizationBySlug(@Arg('slug') slug: string) {
return OrganizationRepo.getOrganizationBySlug(slug);
}

@FieldResolver((_returns) => Flyer, {
nullable: true,
description: 'The most recent <Flyer> of an <Organization>',
description: 'Returns the most recent <Flyer> of an <Organization>',
})
async mostRecentFlyer(@Root() organization: Organization): Promise<Flyer> {
return OrganizationRepo.getMostRecentFlyer(organization);
}

@FieldResolver((_returns) => Number, {
description: "The total shoutouts of an <Organization's> <Flyers>",
description: 'Returns the total number of <Flyers> from an <Organization>',
})
async shoutouts(@Root() organization: Organization): Promise<number> {
return OrganizationRepo.getShoutouts(organization);
async numFlyers(@Root() organization: Organization): Promise<number> {
return OrganizationRepo.getNumFlyers(organization);
}

@FieldResolver((_returns) => Number, {
description: 'The total number of <Flyers> from an <Organization>',
description: "Returns the total shoutouts of an <Organization's> <Flyers>",
})
async numFlyers(@Root() organization: Organization): Promise<number> {
return OrganizationRepo.getNumFlyers(organization);
async shoutouts(@Root() organization: Organization): Promise<number> {
return OrganizationRepo.getShoutouts(organization);
}
}

Expand Down
4 changes: 2 additions & 2 deletions src/tests/data/FlyerFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ class FlyerFactory {
fakeFlyer.imageURL = faker.image.cats();
fakeFlyer.flyerURL = faker.datatype.string();
fakeFlyer.location = faker.datatype.string();
fakeFlyer.organization = exampleOrg;
fakeFlyer.organizationSlug = exampleOrg.slug;
fakeFlyer.organizations = [exampleOrg];
fakeFlyer.organizationSlugs = [exampleOrg.slug];
fakeFlyer.title = faker.commerce.productDescription();
fakeFlyer.isTrending = _.sample([true, false]);
fakeFlyer.nsfw = _.sample([true, false]);
Expand Down
29 changes: 17 additions & 12 deletions src/tests/flyer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,8 @@ describe('getFlyersByOrganizationSlug(s) tests', () => {
test('getFlyersByOrganizationSlug - 1 organization, 1 flyer', async () => {
const org = await OrganizationFactory.getRandomOrganization();
const flyers = await FlyerFactory.createSpecific(1, {
organizationSlug: org.slug,
organization: org,
organizationSlugs: [org.slug],
organization: [org],
});
await FlyerModel.insertMany(flyers);

Expand All @@ -101,8 +101,8 @@ describe('getFlyersByOrganizationSlug(s) tests', () => {
const org = await OrganizationFactory.getRandomOrganization();
const flyers = (
await FlyerFactory.createSpecific(3, {
organizationSlug: org.slug,
organization: org,
organizationSlugs: [org.slug],
organizations: [org],
})
).sort(FactoryUtils.compareByDate);

Expand All @@ -114,16 +114,21 @@ describe('getFlyersByOrganizationSlug(s) tests', () => {
);
});

test('getFlyersByOrganizationSlugs - many organizations, 5 flyers', async () => {
const flyers = (await FlyerFactory.create(3)).sort(FactoryUtils.compareByDate);

test('getFlyersByOrganizationSlugs - many organizations, 3 flyers', async () => {
const org1 = await OrganizationFactory.getRandomOrganization();
const org2 = await OrganizationFactory.getRandomOrganization();
const flyers = (
await FlyerFactory.createSpecific(3, {
organizationSlugs: [org1.slug, org2.slug],
})
).sort(FactoryUtils.compareByDate);
await FlyerModel.insertMany(flyers);
const getFlyersResponse = await FlyerRepo.getFlyersByOrganizationSlug(
FactoryUtils.mapToValue(flyers, 'organizationSlug'),
);

expect(FactoryUtils.mapToValue(getFlyersResponse, 'title')).toEqual(
FactoryUtils.mapToValue(flyers, 'title'),
const getFlyersResponse1 = await FlyerRepo.getFlyersByOrganizationSlugs([org1.slug]);
const getFlyersResponse2 = await FlyerRepo.getFlyersByOrganizationSlugs([org2.slug]);

expect(FactoryUtils.mapToValue(getFlyersResponse1, 'title')).toEqual(
FactoryUtils.mapToValue(getFlyersResponse2, 'title'),
);
});
});
Expand Down
57 changes: 36 additions & 21 deletions src/tests/organization.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,27 +27,27 @@ afterAll(async () => {

describe('getAllOrganizations tests:', () => {
test('getAllOrganizations', async () => {
const pubs = await OrganizationFactory.getAllOrganizations();
const orgs = await OrganizationFactory.getAllOrganizations();

const getOrganizationsResponse = await OrganizationRepo.getAllOrganizations();
expect(FactoryUtils.mapToValue(getOrganizationsResponse, 'slug')).toEqual(
FactoryUtils.mapToValue(pubs, 'slug'),
FactoryUtils.mapToValue(orgs, 'slug'),
);
});
});

describe('getMostRecentFlyer tests:', () => {
test('getMostRecentFlyer - Out of 2-10 flyers', async () => {
const pub = await OrganizationRepo.getOrganizationBySlug(
const org = await OrganizationRepo.getOrganizationBySlug(
(await OrganizationFactory.getRandomOrganization()).slug,
);
const flyers = await FlyerFactory.createSpecific(_.random(2, 10), {
organizationSlug: pub.slug,
organization: pub,
organizationSlugs: [org.slug],
organizations: [org],
});
await FlyerModel.insertMany(flyers);

const getOrganizationsResponse = await OrganizationRepo.getMostRecentFlyer(pub);
const getOrganizationsResponse = await OrganizationRepo.getMostRecentFlyer(org);
const respDate = new Date(getOrganizationsResponse.date);

const flyerDates = FactoryUtils.mapToValue(flyers, 'date');
Expand All @@ -62,56 +62,71 @@ describe('getMostRecentFlyer tests:', () => {

describe('getNumFlyer tests:', () => {
test('getNumFlyer - 0 flyers', async () => {
const pub = await OrganizationRepo.getOrganizationBySlug(
const org = await OrganizationRepo.getOrganizationBySlug(
(await OrganizationFactory.getRandomOrganization()).slug,
);

const numResp = await OrganizationRepo.getNumFlyers(pub);
const numResp = await OrganizationRepo.getNumFlyers(org);

expect(numResp).toEqual(0);
});

test('getNumFlyer - Random number of flyers', async () => {
const pub = await OrganizationRepo.getOrganizationBySlug(
const org1 = await OrganizationRepo.getOrganizationBySlug(
(await OrganizationFactory.getRandomOrganization()).slug,
);
const org2 = await OrganizationRepo.getOrganizationBySlug(
(await OrganizationFactory.getRandomOrganization()).slug,
);
const numFlyers = _.random(1, 10);
const flyers = await FlyerFactory.createSpecific(numFlyers, {
organizationSlug: pub.slug,
organization: pub,
organizationSlugs: [org1.slug],
organizations: [org1, org2],
});
await FlyerModel.insertMany(flyers);

const numResp = await OrganizationRepo.getNumFlyers(pub);
const numResp = await OrganizationRepo.getNumFlyers(org1);

expect(numResp).toEqual(numFlyers);
});
});

describe('getOrganizationByCategory tests:', () => {
test('getOrganizationByCategory - 1 category', async () => {
const org = await OrganizationFactory.getRandomOrganization();
const { categorySlug } = org;

const getOrganizationsResponse = await OrganizationRepo.getOrganizationsByCategory(
categorySlug,
);
expect(FactoryUtils.mapToValue(getOrganizationsResponse, 'slug')).toContain(org.slug);
});
});

describe('getOrganizationBySlug tests:', () => {
test('getOrganizationBySlug - 1 pub', async () => {
const pub = await OrganizationFactory.getRandomOrganization();
test('getOrganizationBySlug - 1 org', async () => {
const org = await OrganizationFactory.getRandomOrganization();

const getOrganizationsResponse = await OrganizationRepo.getOrganizationBySlug(pub.slug);
const getOrganizationsResponse = await OrganizationRepo.getOrganizationBySlug(org.slug);

expect(getOrganizationsResponse.slug).toEqual(pub.slug);
expect(getOrganizationsResponse.slug).toEqual(org.slug);
});
});

describe('getShoutouts tests:', () => {
test('getShoutouts - Random number of flyers with 2 shoutouts, 1 pub', async () => {
const pub = await OrganizationRepo.getOrganizationBySlug(
test('getShoutouts - Random number of flyers with 2 shoutouts, 1 org', async () => {
const org = await OrganizationRepo.getOrganizationBySlug(
(await OrganizationFactory.getRandomOrganization()).slug,
);
const numFlyers = _.random(1, 20);
const numShoutouts = numFlyers * 2;
const flyers = await FlyerFactory.createSpecific(numFlyers, {
organizationSlug: pub.slug,
organization: pub,
organizationSlugs: [org.slug],
organizations: [org],
shoutouts: 2,
});
await FlyerModel.insertMany(flyers);
const getOrganizationsResponse = await OrganizationRepo.getShoutouts(pub);
const getOrganizationsResponse = await OrganizationRepo.getShoutouts(org);

expect(getOrganizationsResponse).toEqual(numShoutouts);
});
Expand Down