Skip to content

Commit

Permalink
Merge remote-tracking branch 'lperson/lp_resolve_tags_server' into st…
Browse files Browse the repository at this point in the history
…age-main-81fast
  • Loading branch information
schuyler1d committed Aug 18, 2020
2 parents 150c93d + df2773d commit dbce65a
Show file tree
Hide file tree
Showing 8 changed files with 339 additions and 31 deletions.
16 changes: 13 additions & 3 deletions __test__/server/api/updateContactTags.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -97,8 +97,15 @@ describe("mutations.updateContactTags", () => {
});

it("saves the tags", async () => {
const contactTags = dbExpectedTags.map(tag => ({
id: tag.id,
value: null
}));

contactTags[1].value = "Everyone votes!";

const result = await wrappedMutations.updateContactTags(
dbExpectedTags,
contactTags,
contacts[0].id
);

Expand All @@ -115,7 +122,7 @@ describe("mutations.updateContactTags", () => {
}),
expect.objectContaining({
tag_id: tags[1].id,
value: null,
value: "Everyone votes!",
campaign_contact_id: contacts[0].id
}),
expect.objectContaining({
Expand All @@ -141,7 +148,10 @@ describe("mutations.updateContactTags", () => {
jest.spyOn(console, "error");

const result = await wrappedMutations.updateContactTags(
dbExpectedTags,
dbExpectedTags.map(tag => ({
id: tag.id,
value: tag.value
})),
999999 // this will cause cacheableData.campaignContact.load to throw an exception
);

Expand Down
244 changes: 244 additions & 0 deletions __test__/server/models/cacheable_queries/tag-campaign-contact.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,244 @@
import {
createStartedCampaign,
setupTest,
cleanupTest
} from "../../../test_helpers";
import { r, cacheableData } from "../../../../src/server/models";
import * as TagCampaignContact from "../../../../src/server/models/cacheable_queries/tag-campaign-contact";

describe("cacheable_queries.tagCampaignContactCache", () => {
let initData;
let tags;
let organization;
let contacts;
let contactCacheEnabled;
let numberOfExpectedCalls;

const makeLoadToCacheCallsExpectation = (
numberOfCalls,
campaignContactId
) => {
return Array(numberOfCalls).fill([campaignContactId]);
};

beforeEach(async () => {
await setupTest();
initData = await createStartedCampaign();

({
testOrganization: {
data: { createOrganization: organization }
},
testContacts: contacts
} = initData);

tags = [
{
id: 1,
name: "HELP!",
description: "Need help",
organization_id: organization.id
},
{
id: 2,
name: "SUPPRESSION",
description: "Reporting voter suppression",
organization_id: organization.id
},
{
id: 3,
name: "VOLUNTEER",
description: "Commits to volunteer",
organization_id: organization.id
},
{
id: 4,
name: "MEGADONOR",
description: "Sending a wire transfer",
organization_id: organization.id
}
];

await r.knex("tag").insert(tags);

jest.spyOn(TagCampaignContact, "loadToCache");

contactCacheEnabled =
process.env.REDIS_CONTACT_CACHE || global.REDIS_CONTACT_CACHE;
numberOfExpectedCalls = r.redis && contactCacheEnabled ? 0 : 1;
});

afterEach(async () => {
jest.restoreAllMocks();
await cleanupTest();
if (r.redis) {
r.redis.flushdb();
}
});

describe(".save", () => {
beforeEach(async () => {});

it("saves the tags", async () => {
await cacheableData.tagCampaignContact.save(contacts[0].id, [
{
id: 2,
value: "Everyone votes!"
},
{
id: 1
}
]);

expect(TagCampaignContact.loadToCache.mock.calls).toEqual([
[contacts[0].id]
]);

const tagCampaignContacts = await r.knex("tag_campaign_contact").select();

expect(tagCampaignContacts).toEqual(
expect.arrayContaining([
expect.objectContaining({ tag_id: 1, value: null }),
expect.objectContaining({ tag_id: 2, value: "Everyone votes!" })
])
);
});
});

describe(".query", () => {
let expectedQueryReturn;

beforeEach(async () => {
await cacheableData.tagCampaignContact.save(contacts[0].id, [
{
id: 2,
value: "Everyone votes!"
},
{
id: 1
}
]);

expectedQueryReturn = expect.arrayContaining([
{
id: 1,
value: null
},
{
id: 2,
value: "Everyone votes!"
}
]);

TagCampaignContact.loadToCache.mockClear();
});

it("returns the tags for the campaign contact", async () => {
let contactTags = await cacheableData.tagCampaignContact.query({
campaignContactId: contacts[0].id,
minimalObj: true
});

expect(TagCampaignContact.loadToCache.mock.calls).toEqual(
makeLoadToCacheCallsExpectation(numberOfExpectedCalls, contacts[0].id)
);

expect(contactTags).toHaveLength(2);
expect(contactTags).toEqual(expectedQueryReturn);

contactTags = await cacheableData.tagCampaignContact.query({
campaignContactId: contacts[0].id,
minimalObj: true
});

expect(TagCampaignContact.loadToCache.mock.calls).toEqual(
makeLoadToCacheCallsExpectation(
numberOfExpectedCalls * 2,
contacts[0].id
)
);
});

describe("when a tag's value is updated", () => {
beforeEach(async () => {
expectedQueryReturn = expect.arrayContaining([
{
id: 1,
value: "4th of July, Asbury Park"
},
{
id: 2,
value: "Everyone votes!"
}
]);
});

it("updates the value and doesn't save another instance of the tag", async () => {
await cacheableData.tagCampaignContact.save(contacts[0].id, [
{
id: 1,
value: "4th of July, Asbury Park"
}
]);

const tagCampaignContacts = await r
.knex("tag_campaign_contact")
.select();

expect(tagCampaignContacts).toEqual(
expect.arrayContaining([
expect.objectContaining({
tag_id: 1,
value: "4th of July, Asbury Park"
}),
expect.objectContaining({ tag_id: 2, value: "Everyone votes!" })
])
);

const contactTags = await cacheableData.tagCampaignContact.query({
campaignContactId: contacts[0].id,
minimalObj: true
});
expect(contactTags).toHaveLength(2);
expect(contactTags).toEqual(expectedQueryReturn);
});
});

describe("when the contact has no tags", () => {
it("", async () => {
const contactTags = await cacheableData.tagCampaignContact.query({
campaignContactId: 9999999,
minimalObj: true
});

expect(contactTags).toEqual([]);
});
});

describe("when one of the tags is resolved", () => {
beforeEach(async () => {
await cacheableData.tagCampaignContact.save(contacts[0].id, [
{
id: 3,
value: "RESOLVED"
}
]);

TagCampaignContact.loadToCache.mockClear();
});

it("doesn't return it", async () => {
const contactTags = await cacheableData.tagCampaignContact.query({
campaignContactId: contacts[0].id,
minimalObj: true
});

expect(TagCampaignContact.loadToCache.mock.calls).toEqual(
makeLoadToCacheCallsExpectation(numberOfExpectedCalls, contacts[0].id)
);
expect(contactTags).toHaveLength(2);
expect(contactTags).toEqual(expectedQueryReturn);
});
});
});
});
10 changes: 9 additions & 1 deletion src/api/schema.js
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,11 @@ const rootSchema = gql`
organizationId: String
}
input ContactTagInput {
id: String
value: String
}
type FoundContact {
found: Boolean
assignment: Assignment
Expand Down Expand Up @@ -303,7 +308,10 @@ const rootSchema = gql`
interactionStepIds: [String]
campaignContactId: String!
): CampaignContact
updateContactTags(tags: [TagInput], campaignContactId: String!): String
updateContactTags(
tags: [ContactTagInput]
campaignContactId: String!
): String
updateQuestionResponses(
questionResponses: [QuestionResponseInput]
campaignContactId: String!
Expand Down
2 changes: 1 addition & 1 deletion src/containers/AssignmentTexterContact.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -536,7 +536,7 @@ const mutations = {
updateContactTags: ownProps => (tags, campaignContactId) => ({
mutation: gql`
mutation updateContactTags(
$tags: [TagInput]
$tags: [ContactTagInput]
$campaignContactId: String!
) {
updateContactTags(tags: $tags, campaignContactId: $campaignContactId)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ export class TexterSidebox extends React.Component {
// TODO: include old tags as well, not just new tags
const tags = Object.keys(newTags)
.filter(tid => newTags[tid])
.map(tid => ({ id: tid, name: newTags[tid] }));
.map(tid => ({ id: tid }));
self.props
.onUpdateTags(tags)
.then(() => {
Expand Down
5 changes: 4 additions & 1 deletion src/server/api/campaign-contact.js
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,10 @@ export const resolvers = {
return campaignContact.tags;
}

return cacheableData.tagCampaignContact.query(campaignContact.id, true);
return cacheableData.tagCampaignContact.query({
campaignContactId: campaignContact.id,
minimalObj: true
});
},
optOut: async (campaignContact, _, { loaders }) => {
let isOptedOut = null;
Expand Down
2 changes: 1 addition & 1 deletion src/server/models/cacheable_queries/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import message from "./message";
import optOut from "./opt-out";
import organization from "./organization";
import questionResponse from "./question-response";
import tagCampaignContact from "./tag-campaign-contact";
import { tagCampaignContactCache as tagCampaignContact } from "./tag-campaign-contact";
import user from "./user";

const cacheableData = {
Expand Down
Loading

0 comments on commit dbce65a

Please sign in to comment.