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

Isaac/merge main release #92

Merged
merged 24 commits into from
May 3, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
24 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
6a0c1cc
Merge branch 'main' into isaac/merge-main-release
isaachan100 Apr 28, 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
7461cea
Merge branch 'main' into isaac/merge-main-release
isaachan100 May 3, 2023
69909e6
Merge branch 'release' of github.com:cuappdev/volume-backend into isa…
isaachan100 May 3, 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