Skip to content

Commit

Permalink
Merge pull request #2499 from StateVoicesNational/stage-main-14.1.2
Browse files Browse the repository at this point in the history
Stage main 14.1.2
  • Loading branch information
mau11 authored Oct 23, 2024
2 parents 7ce0e22 + 149d1ae commit 715ab46
Show file tree
Hide file tree
Showing 20 changed files with 2,768 additions and 3,526 deletions.
4 changes: 2 additions & 2 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ version: 2
updates:
- package-ecosystem: "npm"
directory: "/"
target-branch: "stage-main-14.1"
target-branch: "stage-main-14.2"
schedule:
interval: "weekly"
day: "monday"
time: "12:00"
rebase-strategy: auto
open-pull-requests-limit: 10
labels:
- "A-dependencies"
- "A-dependencies"
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ Want to know more?

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.

<a href="https://heroku.com/deploy?template=https://github.com/StateVoicesNational/Spoke/tree/v14.1.1">
<a href="https://heroku.com/deploy?template=https://github.com/StateVoicesNational/Spoke/tree/v14.1.2">

<img src="https://www.herokucdn.com/deploy/button.svg" alt="Deploy">
</a>
Expand Down
2 changes: 1 addition & 1 deletion app.json
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@
}
},
"addons": [
"heroku-postgresql:basic",
"heroku-postgresql:essential-0",
{
"plan": "heroku-redis:mini",
"options": {
Expand Down
16 changes: 13 additions & 3 deletions docs/HOWTO_USE_POSTGRESQL.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,19 @@
To use Postgresql, follow these steps:

1. Either install docker (recommended) or postgresql on your machine:
* If you installed docker run the database using: `docker compose up`
* If you installed postgres locally, create the spoke dev database: `psql -c "create database spokedev;"`
* Then create a spoke user to connect to the database with `createuser -P spoke` with password "spoke" (to match the credentials in the .env.example file)
* If you installed docker run the database using: `docker-compose up`
* If you installed postgres locally (or if you already have a local installation of postgres), create the spoke dev database: `psql -c "create database spokedev;"`
* Then create a spoke user to connect to the database with `psql -d spokedev -c "create user spoke with password 'spoke';"` (to match the credentials in the .env.example file)
* Grant permissions to the new Spoke user:
* `psql -d spokedev -c "GRANT ALL PRIVILEGES ON DATABASE spokedev TO spoke;"`
* `psql -d spokedev -c "GRANT ALL PRIVILEGES ON schema public TO spoke;"`
* Create a test database by running the following commands:
```
psql -d spokedev
CREATE DATABASE spoke_test;
CREATE USER spoke_test WITH PASSWORD 'spoke_test';
GRANT ALL PRIVILEGES ON DATABASE spoke_test TO spoke_test;
```
1. In `.env` set `DB_TYPE=pg`. (Otherwise, you will use sqlite.)
2. Set `DB_PORT=5432`, which is the default port for Postgres.
Expand Down
1 change: 1 addition & 0 deletions docs/REFERENCE-environment_variables.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
| DEV_APP_PORT | Port for development Webpack server. Required for development. |
| DOWNTIME | When enabled it will redirect users to a /downtime page. If set to a string, it will show the string as a message to users on the downtime page. Use this to take the system down for maintenance. It will NOT stop graphql requests and will NOT stop users that are already in the app. |
| DOWNTIME_NO_DB | On AWS Lambda this blocks the site from loading the app at all and swaps out a system that redirects users to /downtime. This is useful for DB maintenance. For non-Lambda environments, just run the src/server/downtime app instead of src/server/index default app |
| DOWNTIME_NO_INITIAL | When enabled, texters will see a message telling them that sending of initial messages is paused, but they will still be able to reply to any incoming responses. |
| DOWNTIME_TEXTER | Setting DOWNTIME_TEXTER to a text message (without quotes, please) will give the message as a text to texters when they arrive on the site, but the admin pages will still be accessible. This could be useful if you want to stop new texters from landing on the site and texting, while you debug things. |
| DST_REFERENCE_TIMEZONE | Timezone to use to determine whether DST is in effect. If it's DST in this timezone, we assume it's DST everywhere. _Default_: "US/Eastern". (The default will work for any campaign in the US. For example, if the campaign is in Australia, use "Australia/Sydney" or some other timezone in Australia. Note that DST is opposite in the northern and souther hemispheres.) |
| DYNAMICASSIGNMENT_BATCHES | Enables texter strategies for campaigns. Enabled by default are "finished-replies-tz", "vetted-texters", and "finished-replies". Learn more at [HOWTO-use-dynamicassignment-batches.md](https://github.com/StateVoicesNational/Spoke/blob/e217a971f0e1eed6f96d1494aee0f62ce715a771/docs/HOWTO-use-dynamicassignment-batches.md#dynamic-assignment-strategies). NOTE: "vetted-takeconversations" is experimental and does not work on its own, needing other three default strategies enabled.
Expand Down
22 changes: 22 additions & 0 deletions docs/RELEASE_NOTES.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,27 @@
# Release Notes

## 14.1.2

_October 2024_: Version 14.1.2

14.1.2 is a patch release.

### Bug Fixes

- [#2488](https://github.com/StateVoicesNational/Spoke/pull/2488) - Fix Redis and readonly DB certificate issues - @jeffm2001
- [#2490](https://github.com/StateVoicesNational/Spoke/pull/2490) - Fix Bandwidth sending - @jeffm2001
- [#2491](https://github.com/StateVoicesNational/Spoke/pull/2491) - Avoid error on opt-out - @jeffm2001
- [#2493](https://github.com/StateVoicesNational/Spoke/pull/2493) - Fix broken docker image build - @engelhartrueben
- [#2442](https://github.com/StateVoicesNational/Spoke/pull/2442) - Add instructions for local postgres - @sjwmoveon
- [#2478](https://github.com/StateVoicesNational/Spoke/pull/2478) - Allow pausing of initial messages - @jeffm2001
- [#2494](https://github.com/StateVoicesNational/Spoke/pull/2494) - Heroku Plan Update - @engelhartrueben
- [#2498](https://github.com/StateVoicesNational/Spoke/pull/2498) - Remove unused variables causing /phone-numbers blank screen error for Admins - @mau11
- [#2492](https://github.com/StateVoicesNational/Spoke/pull/2492) - Fix UserInputError GraphQL errors - @jeffm2001

### Appreciations

[Jeff Mann](https://github.com/jeffm2001), [Maureen Zitouni](https://github.com/mau11), [Ruby Engelhart](https://github.com/engelhartrueben), [Sophie Waldman](https://github.com/sjwmoveon)

## 14.1.1

_October 2024_: Version 14.1.1
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "spoke",
"version": "14.1.1",
"version": "14.1.2",
"description": "Spoke",
"main": "src/server",
"engines": {
Expand Down Expand Up @@ -119,6 +119,7 @@
"bandwidth-sdk": "^1.0.2",
"body-parser": "^1.15.2",
"browserify": "^17.0.0",
"buffer": "^6.0.3",
"color-difference": "^0.3.4",
"cookie-session": "^2.1.0",
"dataloader": "^2.2.2",
Expand Down
6 changes: 6 additions & 0 deletions src/components/AssignmentTexter/Controls.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -1173,6 +1173,12 @@ export class AssignmentTexterContactControls extends React.Component {

renderFirstMessage(enabledSideboxes) {
const { contact } = this.props;
if (window.DOWNTIME_NO_INITIAL) {
return [
this.renderToolbar(enabledSideboxes),
<div>Sending initial messages is currently paused. You can still reply to any incoming messages.</div>
];
}
return [
this.renderToolbar(enabledSideboxes),
<ContactToolbar
Expand Down
3 changes: 2 additions & 1 deletion src/components/IncomingMessageFilter.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -412,7 +412,8 @@ class IncomingMessageFilter extends Component {
placeholder="Error code number"
label="Error codes"
value={this.state.errorCode}
onChange={(_, errorCode) => {
onChange={event => {
const errorCode = event.target.value;
this.setState({ errorCode });
}}
onKeyPress={evt => {
Expand Down
4 changes: 2 additions & 2 deletions src/containers/AdminIncomingMessageList.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ export class AdminIncomingMessageList extends Component {
handleErrorCodeChange = async errorCode => {
const contactsFilter = {
...this.state.contactsFilter,
errorCode: errorCode ? errorCode.split(",") : null
errorCode: errorCode ? errorCode.split(",").map(Number) : null
};
await this.setState({
contactsFilter,
Expand Down Expand Up @@ -234,7 +234,7 @@ export class AdminIncomingMessageList extends Component {
handleReassignAllMatchingRequested = async newTexterUserId => {
await this.props.mutations.bulkReassignCampaignContacts(
this.props.params.organizationId,
newTexterUserId,
newTexterUserId.toString(),
this.state.campaignsFilter || {},
this.state.assignmentsFilter || {},
this.state.contactsFilter || {},
Expand Down
10 changes: 5 additions & 5 deletions src/containers/AdminPhoneNumberInventory.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@ const inlineStyles = {
cancelButton: {
marginTop: 15,
marginRight: 5
},
deleteButton: {
marginTop: 15
}
};

Expand Down Expand Up @@ -263,9 +266,6 @@ class AdminPhoneNumberInventory extends React.Component {
}

renderBuyNumbersForm() {
const service = this.props.data.organization.serviceVendor;
const serviceName = service.name;
const serviceConfig = service.config || "{}";
return (
<GSForm
schema={this.buyNumbersFormSchema()}
Expand Down Expand Up @@ -420,8 +420,8 @@ class AdminPhoneNumberInventory extends React.Component {
</Button>
<Button
variant="contained"
color="secondary"
variant="outlined"
color="primary"
style={inlineStyles.deleteButton}
onClick={this.handleDeletePhoneNumbersSubmit}
>
Delete {this.state.deleteNumbersCount} Numbers
Expand Down
2 changes: 1 addition & 1 deletion src/containers/TexterTodo.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,7 @@ const queries = {
ownProps.location.query.review === "1"
? {
messageStatus,
errorCode: ["0"],
errorCode: [0],
...(ownProps.params.reviewContactId && {
contactId: ownProps.params.reviewContactId
})
Expand Down
41 changes: 21 additions & 20 deletions src/extensions/service-vendors/bandwidth/messaging.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Configuration, MessagesApiFp } from "bandwidth-sdk";
import { Configuration, MessagesApi } from "bandwidth-sdk";
import { log } from "../../../lib";
import { getFormattedPhoneNumber } from "../../../lib/phone-format";
import { getConfig } from "../../../server/api/lib/config";
Expand Down Expand Up @@ -38,7 +38,7 @@ export async function getBandwidthController(organization, config) {
username: config.userName,
password: config.password
});
return new MessagesApiFp(client);
return new MessagesApi(client);
}

export async function sendMessage({
Expand Down Expand Up @@ -106,7 +106,6 @@ export async function sendMessage({
bandwidthMessage.media = [parsedMessage.mediaUrl];
}

let response;
if (/bandwidthapitest/.test(message.text)) {
let err;
const response = {
Expand All @@ -131,15 +130,24 @@ export async function sendMessage({
organization,
config
);
response = await messagingController.createMessage(
const { status, data } = await messagingController.createMessage(
config.accountId,
bandwidthMessage
);
console.log(
"bandwidth.sendMessage createMessage response",
response && response.statusCode,
response && response.result
status,
data
);
await postMessageSend({
status,
data,
message,
contact,
trx,
organization,
changes
});
} catch (err) {
console.log("bandwidth.sendMessage ERROR", err);
await postMessageSend({
Expand All @@ -152,22 +160,15 @@ export async function sendMessage({
});
return;
}
await postMessageSend({
response,
message,
contact,
trx,
organization,
changes
});
}

export async function postMessageSend({
message,
contact,
trx,
err,
response,
status,
data,
organization,
changes
}) {
Expand All @@ -181,10 +182,10 @@ export async function postMessageSend({
organization_id: organization.id,
service: "bandwidth"
};
if (response && response.statusCode === 202 && response.result) {
changesToSave.service_id = response.result.id;
if (status && status === 202 && data) {
changesToSave.service_id = data.id;
organizationContact.status_code = 1;
organizationContact.user_number = response.result.from;
organizationContact.user_number = data.from;
cacheableData.campaignContact.updateStatus(
contact,
undefined,
Expand All @@ -193,8 +194,8 @@ export async function postMessageSend({
} else {
// ERROR
changesToSave.send_status = "ERROR";
changesToSave.error_code = response.statusCode;
organizationContact.last_error_code = response.statusCode;
changesToSave.error_code = status;
organizationContact.last_error_code = status;
}
let updateQuery = r.knex("message").where("id", message.id);
if (trx) {
Expand Down
2 changes: 1 addition & 1 deletion src/lib/conversations.js
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ export const getConversationFiltersFromQuery = (query, organizationTags) => {
filters.contactsFilter.messageStatus = query.messageStatus;
}
if (query.errorCode) {
filters.contactsFilter.errorCode = query.errorCode.split(",");
filters.contactsFilter.errorCode = query.errorCode.split(",").map(Number);
}
if (query.tags) {
if (/^[a-z]/.test(query.tags)) {
Expand Down
2 changes: 1 addition & 1 deletion src/server/api/conversations.js
Original file line number Diff line number Diff line change
Expand Up @@ -483,7 +483,7 @@ export async function reassignConversations(

returnCampaignIdAssignmentIds.push({
campaignId,
assignmentId: assignmentId.toString()
assignmentId: assignmentId?.toString()
});
}
}
Expand Down
4 changes: 4 additions & 0 deletions src/server/api/mutations/getOptOutMessage.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
import { getConfig } from "../lib/config";
import optOutMessageCache from "../../models/cacheable_queries/opt-out-message";
import zipStateCache from "../../models/cacheable_queries/zip";

export const getOptOutMessage = async (
_,
{ organizationId, zip, defaultMessage }
) => {
if (!getConfig("OPT_OUT_PER_STATE")) {
return defaultMessage;
}
try {
const queryResult = await optOutMessageCache.query({
organizationId: organizationId,
Expand Down
1 change: 1 addition & 0 deletions src/server/middleware/render-index.js
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ export default function renderIndex(html, css, assetMap) {
window.CAN_GOOGLE_IMPORT=${canGoogleImport}
window.DOWNTIME="${process.env.DOWNTIME || ""}"
window.DOWNTIME_TEXTER="${process.env.DOWNTIME_TEXTER || ""}"
window.DOWNTIME_NO_INITIAL="${process.env.DOWNTIME_NO_INITIAL || ""}"
window.EXPERIMENTAL_PER_CAMPAIGN_MESSAGING_LEGACY=${getConfig(
"EXPERIMENTAL_PER_CAMPAIGN_MESSAGING_LEGACY",
null,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ const organizationContactCache = {
if (r.redis && organizationContact) {
await r.redis
.MULTI()
.SET(cachekey, json.stringify(organizationcontact))
.SET(cacheKey, JSON.stringify(organizationContact))
.EXPIRE(cacheKey, 43200) // 12 hours
.exec();
}
Expand Down
16 changes: 12 additions & 4 deletions src/server/models/thinky.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import bluebird from "bluebird";
import knex from "knex";
import config from "../knex-connect";

const { parse: pgDbUrlParser } = require("pg-connection-string");

// Instantiate the rethink-knex-adapter using the config defined in
// /src/server/knex.js.
const knexConn = knex(config);
Expand All @@ -14,11 +16,18 @@ if (
) {
const roConfig = {
...config,
connection: process.env.READONLY_DATABASE_URL || {
connection: {
...config.connection,
host: process.env.DB_READONLY_HOST
}
};

if (process.env.READONLY_DATABASE_URL) {
roConfig.connection = pgDbUrlParser(process.env.READONLY_DATABASE_URL);
const useSSL = process.env.DB_USE_SSL === "1" || process.env.DB_USE_SSL.toLowerCase() === "true";
roConfig.connection.ssl = useSSL ? { rejectUnauthorized: false } : false;
}

thinkyConn.r.knexReadOnly = knex(roConfig);
} else {
thinkyConn.r.knexReadOnly = thinkyConn.r.knex;
Expand Down Expand Up @@ -51,10 +60,9 @@ if (redisUrl) {
if (/rediss/.test(redisSettings.url)) {
// secure redis protocol for Redis 6.0+
// https://devcenter.heroku.com/articles/securing-heroku-redis#using-node-js
redisSettings.tls = {
redisSettings.socket = {
tls: true,
rejectUnauthorized: false,
requestCert: true,
agent: false
};
}
if (process.env.REDIS_JSON) {
Expand Down
Loading

0 comments on commit 715ab46

Please sign in to comment.