Skip to content

Commit 2915b4d

Browse files
authored
Merge pull request #2303 from MoveOnOrg/stage-main-13.1.0
Spoke 13.1.0 Release
2 parents 19c5eea + ffa6386 commit 2915b4d

File tree

43 files changed

+668
-168
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+668
-168
lines changed

.env.example

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,4 +52,14 @@ TEXTER_SIDEBOXES=celebration-gif,default-dynamicassignment,default-releasecontac
5252
OWNER_CONFIGURABLE=ALL
5353
NGP_VAN_API_KEY=
5454
NGP_VAN_APP_NAME=
55-
NGP_VAN_WEBHOOK_BASE_URL=
55+
NGP_VAN_WEBHOOK_BASE_URL=
56+
NGP_VAN_API_KEY_ENCRYPTED=
57+
NGP_VAN_API_BASE_URL=
58+
NGP_VAN_CACHE_TTL=
59+
NGP_VAN_EXPORT_JOB_TYPE_ID=
60+
NGP_VAN_MAXIMUM_LIST_SIZE=
61+
NGP_VAN_CAUTIOUS_CELL_PHONE_SELECTION=
62+
ACTION_HANDLERS=
63+
MESSAGE_HANDLERS=
64+
CONTACT_LOADERS=
65+
SERVICE_MANAGERS=

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ Spoke is an open source text-distribution tool for organizations to mobilize sup
77

88
Spoke was created by Saikat Chakrabarti and Sheena Pakanati, and is now maintained by MoveOn.org.
99

10-
The latest version is [13.0.1](https://github.com/MoveOnOrg/Spoke/tree/13.0.1) (see [release notes](https://github.com/MoveOnOrg/Spoke/blob/main/docs/RELEASE_NOTES.md#v1301))
10+
The latest version is [13.1.0](https://github.com/MoveOnOrg/Spoke/tree/13.1.0) (see [release notes](https://github.com/MoveOnOrg/Spoke/blob/main/docs/RELEASE_NOTES.md#v1310))
1111

1212

1313
## Setting up Spoke
@@ -24,7 +24,7 @@ Want to know more?
2424
### Quick Start with Heroku
2525
This version of Spoke suitable for testing and, potentially, for small campaigns. This won't cost any money and will not support production(aka large-scale) usage. It's a great way to practice deploying Spoke or see it in action.
2626

27-
<a href="https://heroku.com/deploy?template=https://github.com/MoveOnOrg/Spoke/tree/13.0.1">
27+
<a href="https://heroku.com/deploy?template=https://github.com/MoveOnOrg/Spoke/tree/13.1.0">
2828

2929
<img src="https://www.herokucdn.com/deploy/button.svg" alt="Deploy">
3030
</a>

__test__/components/AssignmentSummary.test.js

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ function getAssignment({ isDynamic = false, counts = {} }) {
2222
id: "1",
2323
title: "New Campaign",
2424
description: "asdf",
25+
organization: {
26+
allowSendAll: window.ALLOW_SEND_ALL
27+
},
2528
useDynamicAssignment: isDynamic,
2629
hasUnassignedContacts: false,
2730
introHtml: "yoyo",
@@ -79,7 +82,7 @@ describe("AssignmentSummary text", function t() {
7982
);
8083
});
8184

82-
describe("AssignmentSummary actions inUSA and NOT AllowSendAll", () => {
85+
describe("AssignmentSummary actions when NOT AllowSendAll", () => {
8386
function create(
8487
unmessaged,
8588
unreplied,
@@ -88,7 +91,6 @@ describe("AssignmentSummary actions inUSA and NOT AllowSendAll", () => {
8891
skipped,
8992
isDynamic
9093
) {
91-
window.NOT_IN_USA = 0;
9294
window.ALLOW_SEND_ALL = false;
9395
return mount(
9496
<ApolloProvider client={ApolloClientSingleton}>
@@ -185,7 +187,7 @@ describe("AssignmentSummary actions inUSA and NOT AllowSendAll", () => {
185187
});
186188
});
187189

188-
describe("AssignmentSummary NOT inUSA and AllowSendAll", () => {
190+
describe("AssignmentSummary when AllowSendAll", () => {
189191
function create(
190192
unmessaged,
191193
unreplied,
@@ -194,7 +196,6 @@ describe("AssignmentSummary NOT inUSA and AllowSendAll", () => {
194196
skipped,
195197
isDynamic
196198
) {
197-
window.NOT_IN_USA = 1;
198199
window.ALLOW_SEND_ALL = true;
199200
return mount(
200201
<AssignmentSummary
@@ -223,14 +224,14 @@ describe("AssignmentSummary NOT inUSA and AllowSendAll", () => {
223224
).toBe("Send messages");
224225
});
225226

226-
it('renders "Send messages" with unreplied', () => {
227+
it('renders "Respond" with unreplied', () => {
227228
const actions = create(0, 1, 0, 0, 0, false);
228229
expect(
229230
actions
230231
.find(Button)
231232
.at(0)
232233
.text()
233-
).toBe("Send messages");
234+
).toBe("Respond");
234235
});
235236
});
236237

@@ -328,17 +329,21 @@ describe("contacts filters", () => {
328329
})}
329330
/>
330331
);
331-
const sendMessages = mockRender.mock.calls[0][0];
332+
const respondMessages = mockRender.mock.calls[0][0];
333+
expect(respondMessages.title).toBe("Respond");
334+
expect(respondMessages.contactsFilter).toBe("reply");
335+
336+
const sendMessages = mockRender.mock.calls[1][0];
332337
expect(sendMessages.title).toBe("Past Messages");
333338
expect(sendMessages.contactsFilter).toBe("stale");
334339

335-
const skippedMessages = mockRender.mock.calls[1][0];
340+
const skippedMessages = mockRender.mock.calls[2][0];
336341
expect(skippedMessages.title).toBe("Skipped Messages");
337342
expect(skippedMessages.contactsFilter).toBe("skipped");
338343

339-
const sendFirstTexts = mockRender.mock.calls[2][0];
344+
const sendFirstTexts = mockRender.mock.calls[3][0];
340345
expect(sendFirstTexts.title).toBe("Send messages");
341-
expect(sendFirstTexts.contactsFilter).toBe("all");
346+
expect(sendFirstTexts.contactsFilter).toBe("text");
342347
});
343348
});
344349

__test__/extensions/contact-loaders/ngpvan/ngpvan.test.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -469,6 +469,7 @@ describe("ngpvan", () => {
469469
["NGP_VAN_API_BASE_URL", organization],
470470
["NGP_VAN_TIMEOUT", organization],
471471
["NGP_VAN_APP_NAME", organization],
472+
["NGP_VAN_API_KEY_ENCRYPTED", organization, {}],
472473
["NGP_VAN_API_KEY", organization],
473474
["NGP_VAN_DATABASE_MODE", organization],
474475
["NGP_VAN_EXPORT_JOB_TYPE_ID", organization],

__test__/extensions/contact-loaders/ngpvan/util.test.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,12 +120,13 @@ describe("ngpvan/util", () => {
120120
}
121121

122122
try {
123-
auth = Van.getAuth(organization);
123+
auth = await Van.getAuth(organization);
124124
} catch (caughtException) {
125125
error = caughtException;
126126
} finally {
127127
expect(config.getConfig.mock.calls).toEqual([
128128
["NGP_VAN_APP_NAME", organization],
129+
["NGP_VAN_API_KEY_ENCRYPTED", organization, {}],
129130
["NGP_VAN_API_KEY", organization],
130131
["NGP_VAN_DATABASE_MODE", organization]
131132
]);

__test__/extensions/service-vendors/twilio.test.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -849,7 +849,6 @@ describe("config functions", () => {
849849
["TWILIO_ACCOUNT_SID", organization, expectedConfigOpts]
850850
]);
851851
expect(configFunctions.getConfig.mock.calls).toEqual([
852-
["TWILIO_AUTH_TOKEN_ENCRYPTED", organization, expectedConfigOpts],
853852
["TWILIO_AUTH_TOKEN_ENCRYPTED", organization, expectedConfigOpts],
854853
["TWILIO_ACCOUNT_SID", organization, expectedConfigOpts],
855854
["TWILIO_ACCOUNT_SID", organization, expectedConfigOpts],

__test__/server/api/campaign/campaign.test.js

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -863,7 +863,8 @@ describe("Bulk Send", () => {
863863
) => {
864864
process.env.ALLOW_SEND_ALL = params.allowSendAll;
865865
process.env.ALLOW_SEND_ALL_ENABLED = params.allowSendAllEnabled;
866-
process.env.BULK_SEND_CHUNK_SIZE = params.bulkSendChunkSize;
866+
process.env.BULK_SEND_BATCH_SIZE =
867+
params.bulkSendBatchSize || params.bulkSendChunkSize;
867868

868869
testCampaign.use_dynamic_assignment = true;
869870
await createScript(testAdminUser, testCampaign);
@@ -1014,6 +1015,20 @@ describe("Bulk Send", () => {
10141015
};
10151016
await testBulkSend(params, 0, expectErrorBulkSending);
10161017
});
1018+
1019+
it("should send initial texts to as many contacts as are in the batch size if batch size is smaller than the chunk size", async () => {
1020+
const params = {
1021+
allowSendAll: true,
1022+
allowSendAllEnabled: true,
1023+
bulkSendBatchSize: Math.round(NUMBER_OF_CONTACTS / 4),
1024+
bulkSendChunkSize: NUMBER_OF_CONTACTS
1025+
};
1026+
await testBulkSend(
1027+
params,
1028+
params.bulkSendBatchSize,
1029+
expectSuccessBulkSending(params.bulkSendBatchSize)
1030+
);
1031+
});
10171032
});
10181033

10191034
describe("campaigns query", () => {

__test__/server/api/people.test.js

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
1+
/**
2+
* @jest-environment jsdom
3+
*/
14
/* eslint-disable no-unused-expressions, consistent-return */
25
import { r } from "../../../src/server/models/";
36
import { getUsersGql } from "../../../src/containers/PeopleList";
47
import { GraphQLError } from "graphql/error";
8+
import { resolvers } from "../../../src/server/api/schema";
9+
import { validate as uuidValidate } from 'uuid';
510

611
import {
712
setupTest,
@@ -479,4 +484,49 @@ describe("people", () => {
479484
]);
480485
});
481486
});
487+
488+
describe("reset password", () => {
489+
/**
490+
* Run the resetUserPassword mutation
491+
* @param {number} organizationId
492+
* @param {number} texterId
493+
* @param {number} userId
494+
* @returns Promise
495+
*/
496+
function resetUserPassword(admin, organizationId, texterId) {
497+
return resolvers.RootMutation.resetUserPassword(null, {
498+
organizationId: organizationId,
499+
userId: texterId
500+
}, {
501+
loaders: {
502+
organization: {
503+
load: async id => {
504+
return (await r.knex("organization").where({ id }))[0];
505+
}
506+
}
507+
},
508+
user: admin
509+
});
510+
}
511+
512+
it("reset local password", () => {
513+
resetUserPassword(testAdminUsers[0], organizationId, testTexterUsers[0].id).then(uuid => {
514+
// Non-Auth0 password reset will return verion 4 UUID
515+
expect(uuidValidate(uuid)).toBeTruthy();
516+
});
517+
});
518+
519+
it("reset Auth0 password", () => {
520+
// Remove PASSPORT_STRATEGY env var. PASSPORT_STRATEGY will default to "auth0" if there's nothing explicitly set
521+
delete window.PASSPORT_STRATEGY;
522+
523+
resetUserPassword(testAdminUsers[0], organizationId, testTexterUsers[0].id).catch(e => {
524+
// Auth0 password reset will attempt to make HTTP request, which will fail in Jest test
525+
const match = e.message.match(/Error: Request id (.*) failed; all 2 retries exhausted/);
526+
527+
expect(match).toHaveLength(2);
528+
expect(uuidValidate(match[1])).toBeTruthy();
529+
});
530+
});
531+
});
482532
});

app.json

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -44,19 +44,18 @@
4444
},
4545

4646
"DEFAULT_SERVICE": {
47-
"description": "specifies using twilio api ",
48-
"required": true,
49-
"value": "twilio"
47+
"description": "type bandwidth or twilio",
48+
"required": true
5049
},
5150

5251
"TWILIO_ACCOUNT_SID": {
5352
"description": "for twilio integration and connected to twilio account",
54-
"required": true
53+
"required": false
5554
},
5655

5756
"TWILIO_AUTH_TOKEN": {
5857
"description": "for twilio integration and connected to twilio account",
59-
"required": true
58+
"required": false
6059
},
6160

6261
"TWILIO_MULTI_ORG": {
@@ -184,9 +183,9 @@
184183
}
185184
},
186185
"addons": [
187-
"heroku-postgresql:hobby-dev",
186+
"heroku-postgresql:basic",
188187
{
189-
"plan": "heroku-redis:hobby-dev",
188+
"plan": "heroku-redis:mini",
190189
"options": {
191190
"maxmemory-policy": "volatile-lru"
192191
}

docs/HOWTO-configure-auth0.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ callback(null, user, context);
5151
<script src="https://cdn.auth0.com/js/base64.js"></script>
5252
<script src="https://cdn.auth0.com/js/es5-shim.min.js"></script>
5353
<![endif]-->
54-
<script src="https://cdn.auth0.com/js/lock/11.28/lock.min.js"></script>
54+
<script src="https://cdn.auth0.com/js/lock/11.34.2/lock.min.js"></script>
5555
<script>
5656
// Decode utf8 characters properly
5757
var config = JSON.parse(decodeURIComponent(escape(window.atob('@@config@@'))));

0 commit comments

Comments
 (0)