Skip to content

Commit

Permalink
Merge branch 'master' into feat/trip-planner-improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
thecristen committed Feb 14, 2024
2 parents 227af7a + cd5e120 commit dbd7bcb
Show file tree
Hide file tree
Showing 54 changed files with 644 additions and 275 deletions.
6 changes: 3 additions & 3 deletions .github/workflows/algolia-update.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,19 +19,19 @@ jobs:
if: github.repository_owner == 'mbta'
steps:
- uses: actions/checkout@v4
- uses: actions/cache/restore@v3
- uses: actions/cache/restore@v4
with:
path: ~/.asdf
key: ci-asdf-cache-${{ hashFiles('.tool-versions') }}
restore-keys: ci-asdf-cache-
- uses: mbta/actions/reshim-asdf@v2
- uses: actions/cache/restore@v3
- uses: actions/cache/restore@v4
with:
path: deps
key: ci-mix-cache-${{ hashFiles('mix.lock') }}
restore-keys: ci-mix-cache-
# the running application runs Webpack to render React stuff :(, so need NPM too
- uses: actions/cache/restore@v3
- uses: actions/cache/restore@v4
with:
path: assets/node_modules
key: ci-nodejs-cache-${{ hashFiles('assets/package-lock.json') }}
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/create-asana-attachment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ on:

jobs:
call-workflow:
uses: mbta/workflows/.github/workflows/asana.yml@1c8f002ddb10ea4b353bc3a485661d2d8b0f102e
uses: mbta/workflows/.github/workflows/asana.yml@ba1de661b594f5513124ea8a717f3ba5bb551a7a
with:
complete-on-merge: false
review-section: "Code/Design Review"
Expand Down
15 changes: 0 additions & 15 deletions .github/workflows/deploy-monitor.yml

This file was deleted.

22 changes: 22 additions & 0 deletions .github/workflows/push-monitor.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
name: Push Monitor

on:
workflow_dispatch:

jobs:
push:
name: push-monitor
runs-on: ubuntu-latest
permissions:
id-token: write
contents: read
steps:
- name: Checkout
id: checkout
uses: actions/checkout@v4
- name: Build, tag, and push image to Amazon ECR
uses: mbta/actions/build-push-ecr@v2
with:
docker-repo: "${{ secrets.TID_AWS_ACCOUNT_ID }}.dkr.ecr.us-east-1.amazonaws.com/dotcom-monitor"
dockerfile-path: "-f ./deploy/monitor/Dockerfile ."
role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
2 changes: 1 addition & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ jobs:
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/setup-all
- uses: actions/cache/restore@v3
- uses: actions/cache/restore@v4
with:
path: |
_build
Expand Down
4 changes: 2 additions & 2 deletions .tool-versions
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
nodejs 18.17.1
erlang 24.3.4.14
elixir 1.16.0-otp-24
erlang 26.2.1
elixir 1.16.1-otp-26
73 changes: 71 additions & 2 deletions assets/ts/components/Alerts.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React, { ReactElement, useState } from "react";
import { format, parseISO } from "date-fns";
import { Alert as AlertType, Lifecycle } from "../__v3api";
import { handleReactEnterKeyPress } from "../helpers/keyboard-events-react";
import { caret } from "../helpers/icon";
Expand Down Expand Up @@ -123,6 +124,70 @@ const caretIcon = (
return caret("c-expandable-block__header-caret--black", expanded);
};

const htmlEscape = (unsafe: string): string => {
return unsafe
.replace(/&/g, "&")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;")
.replace(/'/g, "&#039;");
};

const htmlInsertFormatting = (str: string): string => {
return str
.replace(/^(.*:)\s/, "<strong>$1</strong>\n")
.replace(/\n(.*:)\s/, "<br /><strong>$1</strong>\n")
.replace(/\s*\n/g, "<br />");
};

// Strips off trailing periods of URLs (i guess the regex above matches the period if the URL is at the end of the sentence.)
const parseUrlAndSuffix = (url: string): [string, string] => {
if (url.endsWith(".")) {
return [url.substring(0, url.length - 1), "."];
}
return [url, ""];
};

const ensureScheme = (url: string): string => {
if (url.startsWith("http://") || url.startsWith("https://")) {
return url;
}
if (url.startsWith("mbta.com") || url.startsWith("MBTA.com")) {
return `https://${url}`;
}
return `http://${url}`;
};

const createUrl = (url: string): string => {
const [urlClean, suffix] = parseUrlAndSuffix(url);

const fullUrl = ensureScheme(urlClean);

// remove [http:// | https:// | www.] from URL:
const strippedUrl = fullUrl.replace(/(https?:\/\/)?(www\.)?/i, "");

// capitalize 'mbta' (special case):
const capitalStrippedUrl = strippedUrl.includes("mbta.com")
? strippedUrl.replace("mbta", "MBTA")
: strippedUrl;

return `<a target="_blank" href="${fullUrl}">${capitalStrippedUrl}</a>${suffix}`;
};

const replaceUrlsWithLinks = (desc: string): string => {
const urlRegex = /(https?:\/\/)?([\da-z.-]+)\.([a-z]{2,6})([/\w.-]*)*\/?/i;

return desc.replace(urlRegex, createUrl);
};

const formatAlertDescription = (description: string): string => {
const safeDescription = htmlEscape(description);

const updatedDesc = htmlInsertFormatting(safeDescription);

return replaceUrlsWithLinks(updatedDesc);
};

const alertDescription = (alert: AlertType): ReactElement<HTMLElement> => (
<div
className={`c-alert-item__bottom c-alert-item__buttom--${alert.priority}`}
Expand All @@ -136,10 +201,14 @@ const alertDescription = (alert: AlertType): ReactElement<HTMLElement> => (
<div
// eslint-disable-next-line react/no-danger
dangerouslySetInnerHTML={{
__html: alert.description
__html: formatAlertDescription(alert.description)
}}
/>
<div className="c-alert-item__updated">{alert.updated_at}</div>
{alert.updated_at && (
<div className="c-alert-item__updated">
Updated: {format(parseISO(alert.updated_at), "M/d/yyyy h:mm aa")}
</div>
)}
</div>
</div>
);
Expand Down
32 changes: 29 additions & 3 deletions assets/ts/components/__tests__/AlertsTest.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,13 @@ import Alerts, {
import { enzymeToJsonWithoutProps } from "../../app/helpers/testUtils";
import { Alert, InformedEntitySet } from "../../__v3api";
import { isAmenityAlert } from "../../models/alert";
import { fireEvent, render, screen } from "@testing-library/react";

/* eslint-disable camelcase */
const body = '<div id="react-root"></div>';

const highAlert: Alert = {
updated_at: "Updated: 4/11/2019 09:33A",
updated_at: "2019-04-11T09:33:00-05:00",
severity: 7,
priority: "high",
lifecycle: "new",
Expand All @@ -25,13 +26,13 @@ const highAlert: Alert = {
'Route 170 will be rerouted at certain times during the Marathon on Monday, April 15. More: <a href="https://mbta.com/marathon">mbta.com/marathon</a>',
effect: "detour",
description:
"<strong>Affected direction:</strong><br />Inbound<br />\r<br /><strong>Affected stops:</strong><br />Meridian St @ West Eagle St",
"Affected direction:\r\nInbound\r\n\r\nAffected stops:\r\nMeridian St @ West Eagle St",
url: "https://www.mbta.com",
banner: null
};

const lowAlert: Alert = {
updated_at: "Updated: 4/11/2019 09:33A",
updated_at: "2019-04-11T09:33:00-05:00",
severity: 7,
priority: "low",
lifecycle: "upcoming",
Expand All @@ -45,6 +46,23 @@ const lowAlert: Alert = {
banner: null
};

const alertUrlDesc: Alert = {
updated_at: "2019-04-11T09:33:00-05:00",
severity: 7,
priority: "high",
lifecycle: "new",
active_period: [],
informed_entity: {} as InformedEntitySet,
id: "304666",
header:
'Route 170 will be rerouted at certain times during the Marathon on Monday, April 15. More: <a href="https://mbta.com/marathon">mbta.com/marathon</a>',
effect: "detour",
description:
"<strong>Affected direction:</strong><br />Inbound<br />\r<br /><strong>Affected stops:</strong><br />Meridian St @ West Eagle St \r\n See more: www.mbta.com/help.",
url: null,
banner: null
};

test("handle click to expand and enter to collapse", () => {
document.body.innerHTML = body;

Expand Down Expand Up @@ -96,6 +114,14 @@ test("it includes the URL field when it exists", () => {
expect(wrapper.find("a")).toEqual({});
});

test("it sets the url in the description", () => {
render(<Alerts alerts={[alertUrlDesc]} />);

fireEvent.click(screen.getByText(/Route 170.*/));

expect(screen.getByText("MBTA.com/help")).toBeInTheDocument();
});

test("it has no dropdown when alert has no description", () => {
const noDescriptionAlert = { ...highAlert, description: "" };
const wrapper = mount(<Alerts alerts={[noDescriptionAlert]} />);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,8 @@ exports[`it renders 1`] = `
<div
className="c-alert-item__updated"
>
Updated: 4/11/2019 09:33A
Updated:
4/11/2019 9:33 AM
</div>
</div>
</div>
Expand Down
2 changes: 1 addition & 1 deletion assets/ts/schedule/components/AdditionalLineInfo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ const AdditionalLineInfo = ({
}: Props): ReactElement<HTMLElement> => (
<>
<ContentTeasers teasers={teasers} />
<PDFSchedules pdfs={pdfs} />
<PDFSchedules pdfs={pdfs} route={route} />
<Connections connections={connections} />
<Fares fares={fares} fareLink={fareLink} routeType={route.type} />
{/* Only show the hours for non subway lines */}
Expand Down
13 changes: 11 additions & 2 deletions assets/ts/schedule/components/PDFSchedules.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import ExpandableBlock from "../../components/ExpandableBlock";
import { SchedulePDF } from "./__schedule";
import renderSvg from "../../helpers/render-svg";
import pdfIcon from "../../../static/images/icon-pdf-default.svg";
import { Route } from "../../__v3api";
import { isACommuterRailRoute } from "../../models/route";

const link = (pdf: SchedulePDF): ReactElement<HTMLElement> => (
<a
Expand All @@ -20,12 +22,19 @@ const link = (pdf: SchedulePDF): ReactElement<HTMLElement> => (

interface Props {
pdfs: SchedulePDF[];
route: Route;
}

const PDFSchedules = ({ pdfs }: Props): ReactElement<HTMLElement> | null =>
const PDFSchedules = ({
pdfs,
route
}: Props): ReactElement<HTMLElement> | null =>
pdfs.length > 0 ? (
<ExpandableBlock
header={{ text: "PDF Schedules and Maps", iconSvgText: null }}
header={{
text: `PDF Schedules${isACommuterRailRoute(route) ? "" : " and Maps"}`,
iconSvgText: null
}}
initiallyExpanded
id="pdfs"
>
Expand Down
24 changes: 22 additions & 2 deletions assets/ts/schedule/components/__tests__/PDFSchedulesTest.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import React from "react";
import renderer from "react-test-renderer";
import { createReactRoot } from "../../../app/helpers/testUtils";
import PDFSchedules from "../PDFSchedules";
import { Route } from "../../../__v3api";
import { render, screen } from "@testing-library/react";

const pdfs = [
{
Expand All @@ -10,14 +12,32 @@ const pdfs = [
}
];

const crRoute = {
type: 2
} as Route;

const railRoute = {
type: 0
} as Route;

it("it renders", () => {
createReactRoot();
const tree = renderer.create(<PDFSchedules pdfs={pdfs} />).toJSON();
const tree = renderer
.create(<PDFSchedules pdfs={pdfs} route={railRoute} />)
.toJSON();
expect(tree).toMatchSnapshot();
});

it("it renders without pdfs", () => {
createReactRoot();
const tree = renderer.create(<PDFSchedules pdfs={[]} />).toJSON();
const tree = renderer
.create(<PDFSchedules pdfs={[]} route={railRoute} />)
.toJSON();
expect(tree).toMatchSnapshot();
});

it("Does not include the `and Maps` for CR routes", () => {
render(<PDFSchedules pdfs={pdfs} route={crRoute} />);
expect(screen.queryByText("PDF Schedules and Maps")).toBeNull();
expect(screen.getByText("PDF Schedules")).toBeInTheDocument();
});
2 changes: 1 addition & 1 deletion deploy/dotcom/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
###

# 1.) Get the Elixir dependencies within an Elixir container
FROM hexpm/elixir:1.16.0-erlang-24.3.4.14-debian-buster-20231009-slim as elixir-builder
FROM hexpm/elixir:1.16.1-erlang-26.2.1-debian-buster-20231009-slim as elixir-builder

ENV LANG="C.UTF-8" MIX_ENV="prod"

Expand Down
21 changes: 5 additions & 16 deletions deploy/monitor/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,12 @@ FROM node:21-bookworm

WORKDIR /home/runner

COPY . .
COPY package*.json .
COPY monitor /home/runner/monitor
COPY scenarios /home/runner/scenarios

RUN npm ci
RUN npm ci --ignore-scripts
RUN npx playwright install chromium
RUN npx playwright install-deps

RUN curl "https://download.splunk.com/products/universalforwarder/releases/9.1.3/linux/splunkforwarder-9.1.3-d95b3299fa65-Linux-armv8.tgz" -o "splunkforwarder-9.1.3-d95b3299fa65-Linux-armv8.tgz"
RUN tar xvfz splunkforwarder-9.1.3-d95b3299fa65-Linux-armv8.tgz -C /opt
RUN mv ./splunk/inputs.conf /opt/splunkforwarder/etc/system/local/inputs.conf
RUN mv ./splunk/user-seed.conf /opt/splunkforwarder/etc/system/local/user-seed.conf

RUN curl "https://awscli.amazonaws.com/awscli-exe-linux-aarch64.zip" -o "awscli.zip"
RUN unzip awscli.zip
RUN ./aws/install

ENV TARGET_URL=https://www.mbta.com

RUN chmod +x /home/runner/entrypoint

ENTRYPOINT ["/home/runner/entrypoint"]
CMD ["node", "monitor/all-scenarios.js"]
10 changes: 0 additions & 10 deletions deploy/monitor/entrypoint

This file was deleted.

4 changes: 0 additions & 4 deletions deploy/monitor/splunk/inputs.conf

This file was deleted.

Loading

0 comments on commit dbd7bcb

Please sign in to comment.