Skip to content

Commit fdd9efc

Browse files
Auto-update feature branch with changes from the main branch
2 parents 7df5f73 + 9d41592 commit fdd9efc

Some content is hidden

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

64 files changed

+2887
-840
lines changed

.github/workflows/deploy-prod.yml

Lines changed: 84 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ on:
88
- 'v*'
99

1010
jobs:
11-
test:
11+
test-unit:
1212
permissions:
1313
contents: read
1414
runs-on: ubuntu-latest
@@ -70,7 +70,7 @@ jobs:
7070
id: get_version
7171
run: echo "VITE_BUILD_HASH=${GITHUB_REF#refs/tags/v}" >> "$GITHUB_ENV"
7272

73-
- name: Run build
73+
- name: Run Prod build
7474
run: make build
7575
env:
7676
HUSKY: "0"
@@ -84,10 +84,89 @@ jobs:
8484
include-hidden-files: true
8585
name: build-prod
8686
path: |
87-
.aws-sam/
8887
dist/
8988
dist_ui/
9089
90+
- name: Clean and run QA build
91+
run: sudo rm -rf dist/ && sudo rm -rf dist_ui/ && make build
92+
env:
93+
HUSKY: "0"
94+
VITE_RUN_ENVIRONMENT: dev
95+
RunEnvironment: dev
96+
97+
- name: Upload Build files
98+
uses: actions/upload-artifact@v4
99+
with:
100+
include-hidden-files: true
101+
name: build-qa
102+
path: |
103+
dist/
104+
dist_ui/
105+
106+
test-e2e:
107+
runs-on: ubuntu-latest
108+
timeout-minutes: 30
109+
name: Run E2E testing
110+
concurrency:
111+
group: ${{ github.event.repository.name }}-dev-env
112+
cancel-in-progress: false
113+
permissions:
114+
id-token: write
115+
contents: read
116+
needs:
117+
- build
118+
environment: "AWS QA"
119+
steps:
120+
- uses: actions/checkout@v5
121+
env:
122+
HUSKY: "0"
123+
124+
- name: Set up Node for testing
125+
uses: actions/setup-node@v5
126+
with:
127+
node-version: 22.x
128+
cache: "yarn"
129+
130+
- name: Setup Terraform
131+
uses: hashicorp/setup-terraform@v3
132+
with:
133+
terraform_version: 1.12.2
134+
135+
- name: Restore Yarn Cache
136+
uses: actions/cache@v4
137+
with:
138+
path: node_modules
139+
key: yarn-modules-${{ runner.arch }}-${{ runner.os }}-${{ hashFiles('**/yarn.lock') }}-dev
140+
restore-keys: |
141+
yarn-modules-${{ runner.arch }}-${{ runner.os }}-
142+
143+
- name: Download Build files
144+
uses: actions/download-artifact@v5
145+
with:
146+
name: build-qa
147+
148+
- uses: aws-actions/configure-aws-credentials@v5
149+
with:
150+
role-to-assume: arn:aws:iam::427040638965:role/GitHubActionsRole
151+
role-session-name: Core_QA_Deployment_${{ github.run_id }}
152+
aws-region: us-east-2
153+
154+
- name: Publish to AWS
155+
run: make deploy_qa
156+
env:
157+
HUSKY: "0"
158+
VITE_RUN_ENVIRONMENT: dev
159+
160+
- name: Run health check
161+
run: make dev_health_check
162+
163+
- name: Run E2E testing
164+
run: make test_e2e
165+
env:
166+
JWT_KEY: ${{ secrets.JWT_KEY }}
167+
PLAYWRIGHT_USERNAME: ${{ secrets.PLAYWRIGHT_USERNAME }}
168+
PLAYWRIGHT_PASSWORD: ${{ secrets.PLAYWRIGHT_PASSWORD }}
169+
91170
deploy-prod:
92171
runs-on: ubuntu-latest
93172
timeout-minutes: 30
@@ -99,8 +178,8 @@ jobs:
99178
id-token: write
100179
contents: read
101180
needs:
102-
- test
103-
- build
181+
- test-unit
182+
- test-e2e
104183
environment: "AWS PROD"
105184
steps:
106185
- name: Set up Node for testing

.github/workflows/deploy-qa.yml

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,6 @@ jobs:
8080
include-hidden-files: true
8181
name: build
8282
path: |
83-
.aws-sam/
8483
dist/
8584
dist_ui/
8685
@@ -127,7 +126,6 @@ jobs:
127126
with:
128127
name: build
129128

130-
131129
- uses: aws-actions/configure-aws-credentials@v5
132130
with:
133131
role-to-assume: arn:aws:iam::427040638965:role/GitHubActionsRole
@@ -143,9 +141,7 @@ jobs:
143141
- name: Run health check
144142
run: make dev_health_check
145143

146-
- name: Run post-deploy testing (Live and E2E)
147-
run: make test_post_deploy -j 2
144+
- name: Run live integration testing
145+
run: make test_live_integration
148146
env:
149-
PLAYWRIGHT_USERNAME: ${{ secrets.PLAYWRIGHT_USERNAME }}
150-
PLAYWRIGHT_PASSWORD: ${{ secrets.PLAYWRIGHT_PASSWORD }}
151147
JWT_KEY: ${{ secrets.JWT_KEY }}

Makefile

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ current_active_region = "us-east-2"
66
src_directory_root = src/
77
dist_ui_directory_root = dist_ui/
88
integration_test_directory_root = tests/live_integration/
9-
npm_install_params = --omit=dev --target_arch=arm64 --target_platform=linux --target_libc=glibc --cpu arm64 --os linux --arch=arm64
9+
yarn_install_params = --production --frozen-lockfile
10+
yarn_env = npm_config_arch=arm64 npm_config_platform=linux npm_config_libc=glibc
1011
GIT_HASH := $(shell git rev-parse --short HEAD)
1112

1213
.PHONY: clean
@@ -32,18 +33,19 @@ build: src/
3233
cp -r src/api/resources/ dist/api/resources
3334
rm -rf dist/lambda/sqs
3435
docker run --rm -v "$(shell pwd)/dist/lambda":/var/task public.ecr.aws/sam/build-nodejs22.x:latest \
35-
sh -c "npm install $(npm_install_params) && \
36+
sh -c "npm i -g yarn && $(yarn_env) yarn $(yarn_install_params) && \
3637
rm -rf node_modules/aws-crt/dist/bin/{darwin*,linux-x64*,linux-arm64-musl} && \
3738
rm -rf node_modules/argon2/prebuilds/{darwin*,freebsd*,linux-arm,linux-x64*,win32-x64*} && \
3839
rm -rf node_modules/argon2/prebuilds/linux-arm64/argon2.armv8.musl.node"
3940

4041
docker run --rm -v "$(shell pwd)/dist/sqsConsumer":/var/task public.ecr.aws/sam/build-nodejs22.x:latest \
41-
sh -c "npm install $(npm_install_params) && \
42+
sh -c "npm i -g yarn && $(yarn_env) yarn $(yarn_install_params) && \
4243
rm -rf node_modules/aws-crt/dist/bin/{darwin*,linux-x64*,linux-arm64-musl} && \
4344
rm -rf node_modules/argon2/prebuilds/{darwin*,freebsd*,linux-arm,linux-x64*,win32-x64*} && \
4445
rm -rf node_modules/argon2/prebuilds/linux-arm64/argon2.armv8.musl.node"
4546

4647
local:
48+
mkdir -p dist_devel/
4749
VITE_BUILD_HASH=$(GIT_HASH) yarn run dev
4850

4951
deploy_prod:
@@ -85,8 +87,6 @@ test_e2e: install
8587
yarn playwright install
8688
yarn test:e2e
8789

88-
test_post_deploy: test_live_integration test_e2e
89-
9090
dev_health_check:
9191
curl -f https://core.aws.qa.acmuiuc.org/api/v1/healthz && curl -f https://core.aws.qa.acmuiuc.org/
9292

package.json

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,7 @@
1616
"build": "concurrently --names 'api,ui,archival,linkryEdge' 'yarn workspace infra-core-api run build' 'yarn workspace infra-core-ui run build' 'yarn workspace infra-core-archival run build' 'yarn workspace infra-core-linkry-edge run build'",
1717
"postbuild": "node src/api/createLambdaPackage.js && yarn lockfile-manage",
1818
"dev": "cross-env DISABLE_AUDIT_LOG=true concurrently --names 'api,ui' 'yarn workspace infra-core-api run dev' 'yarn workspace infra-core-ui run dev'",
19-
"lockfile-manage": "synp --with-workspace --source-file yarn.lock",
20-
"postlockfile-manage": "cp package-lock.json dist/lambda/ && cp package-lock.json dist/sqsConsumer/ && cp src/api/package.lambda.json dist/lambda/package.json && cp src/api/package.lambda.json dist/sqsConsumer/package.json && rm package-lock.json",
19+
"lockfile-manage": "cp src/api/package.lambda.json dist/lambda/package.json && cp src/api/package.lambda.json dist/sqsConsumer/package.json && cp yarn.lock dist/lambda/ && cp yarn.lock dist/sqsConsumer/",
2120
"prettier": "yarn workspaces run prettier && prettier --check tests/**/*.ts",
2221
"prettier:write": "yarn workspaces run prettier:write && prettier --write tests/**/*.ts",
2322
"lint": "yarn workspaces run lint",
@@ -83,7 +82,6 @@
8382
"stylelint": "^16.23.1",
8483
"stylelint-config-standard-scss": "^15.0.1",
8584
"supertest": "^7.1.4",
86-
"synp": "^1.9.14",
8785
"tsx": "^4.20.4",
8886
"typescript": "^5.9.2",
8987
"typescript-eslint": "^8.40.0",
@@ -95,4 +93,4 @@
9593
"pdfjs-dist": "^4.8.69",
9694
"form-data": "^4.0.4"
9795
}
98-
}
96+
}

playwright.config.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,11 @@ import { defineConfig, devices } from '@playwright/test';
33
export default defineConfig({
44
testDir: './tests/e2e/',
55
/* Run tests in files in parallel */
6-
fullyParallel: false,
6+
fullyParallel: true,
77
/* Fail the build on CI if you accidentally left test.only in the source code. */
88
forbidOnly: !!process.env.CI,
99
/* Retry on CI only */
1010
retries: process.env.CI ? 2 : 0,
11-
/* Opt out of parallel tests on CI. */
12-
workers: process.env.CI ? 1 : undefined,
1311
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
1412
reporter: process.env.CI ? 'github' : 'html',
1513
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */

src/api/functions/linkry.ts

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import {
55
} from "@aws-sdk/client-dynamodb";
66
import { unmarshall } from "@aws-sdk/util-dynamodb";
77
import { LinkryGroupUUIDToGroupNameMap } from "common/config.js";
8-
import { LinkRecord } from "common/types/linkry.js";
8+
import { LinkRecord, OrgLinkRecord } from "common/types/linkry.js";
99
import { FastifyRequest } from "fastify";
1010

1111
export async function fetchLinkEntry(
@@ -72,6 +72,40 @@ export async function fetchOwnerRecords(
7272
});
7373
}
7474

75+
export async function fetchOrgRecords(
76+
orgId: string,
77+
tableName: string,
78+
dynamoClient: DynamoDBClient,
79+
) {
80+
const fetchAllOwnerRecords = new QueryCommand({
81+
TableName: tableName,
82+
IndexName: "AccessIndex",
83+
KeyConditionExpression: "#access = :accessVal",
84+
ExpressionAttributeNames: {
85+
"#access": "access",
86+
},
87+
ExpressionAttributeValues: {
88+
":accessVal": { S: `OWNER#${orgId}` },
89+
},
90+
ScanIndexForward: false,
91+
});
92+
93+
const result = await dynamoClient.send(fetchAllOwnerRecords);
94+
95+
// Process the results
96+
return (result.Items || []).map((item) => {
97+
const unmarshalledItem = unmarshall(item);
98+
99+
// Strip '#' from access field
100+
if (unmarshalledItem.access) {
101+
unmarshalledItem.access =
102+
unmarshalledItem.access.split("#")[1] || unmarshalledItem.access;
103+
}
104+
105+
return unmarshalledItem as OrgLinkRecord;
106+
});
107+
}
108+
75109
export function extractUniqueSlugs(records: LinkRecord[]) {
76110
return Array.from(
77111
new Set(records.filter((item) => item.slug).map((item) => item.slug)),

src/api/functions/organizations.ts

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import { Modules } from "common/modules.js";
2525
import { retryDynamoTransactionWithBackoff } from "api/utils.js";
2626
import { Redis, ValidLoggers } from "api/types.js";
2727
import { createLock, IoredisAdapter, type SimpleLock } from "redlock-universal";
28+
import { batchGetUserInfo } from "./uin.js";
2829

2930
export interface GetOrgInfoInputs {
3031
id: string;
@@ -54,7 +55,7 @@ export async function getOrgInfo({
5455
ConsistentRead: true,
5556
});
5657
let response = { leads: [] } as {
57-
leads: { name: string; username: string; title: string | undefined }[];
58+
leads: { name?: string; username: string; title: string | undefined }[];
5859
};
5960
try {
6061
const responseMarshall = await dynamoClient.send(query);
@@ -98,18 +99,39 @@ export async function getOrgInfo({
9899
.map(
99100
(x) =>
100101
({
101-
name: x.name,
102102
username: x.username,
103103
title: x.title,
104104
nonVotingMember: x.nonVotingMember || false,
105105
}) as {
106-
name: string;
107106
username: string;
108107
title: string | undefined;
109108
nonVotingMember: boolean;
110109
},
111110
);
112-
response = { ...response, leads: unmarshalledLeads };
111+
112+
// Resolve usernames to names
113+
const emails = unmarshalledLeads.map((lead) => lead.username);
114+
const userInfo = await batchGetUserInfo({
115+
emails,
116+
dynamoClient,
117+
logger,
118+
});
119+
120+
// Add names to leads
121+
const leadsWithNames = unmarshalledLeads.map((lead) => {
122+
const info = userInfo[lead.username];
123+
const name =
124+
info?.firstName || info?.lastName
125+
? [info.firstName, info.lastName].filter(Boolean).join(" ")
126+
: undefined;
127+
128+
return {
129+
...lead,
130+
name,
131+
};
132+
});
133+
134+
response = { ...response, leads: leadsWithNames };
113135
}
114136
} catch (e) {
115137
if (e instanceof BaseError) {

0 commit comments

Comments
 (0)