-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1559 from Enterprise-CMCS/develop
merge sub ld
- Loading branch information
Showing
142 changed files
with
2,758 additions
and
243 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
import { getUser } from "../../app-api/getUser"; | ||
|
||
const handler = async (event) => { | ||
console.log("JWT claims before modification:", JSON.stringify(event)); | ||
try{ | ||
const userEmail = event.request.userAttributes.email; | ||
const user = await getUser(userEmail); | ||
const roles = []; | ||
for (const role of user.roleList) { | ||
roles.push(role.role) | ||
} | ||
event.response = event.response || {}; | ||
event.response.claimsOverrideDetails = event.response.claimsOverrideDetails || {}; | ||
event.response.claimsOverrideDetails.claimsToAddOrOverride = event.response.claimsOverrideDetails.claimsToAddOrOverride || {}; | ||
|
||
// Example of adding roles dynamically from DynamoDB to the JWT claims | ||
event.response.claimsOverrideDetails.claimsToAddOrOverride['custom:user_roles'] = JSON.stringify(roles); // Add user roles | ||
} catch(e) { | ||
console.log("error updating id token claims", e) | ||
} | ||
console.log("JWT claims after modification:", JSON.stringify(event)); | ||
return event; | ||
}; | ||
|
||
export { handler }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
import AWS from "aws-sdk"; | ||
const cognito = new AWS.CognitoIdentityServiceProvider(); | ||
import { getUser } from "../../app-api/getUser"; | ||
|
||
async function updateUserAttribute(userPoolId, username, roles) { | ||
const params = { | ||
UserPoolId: userPoolId, | ||
Username: username, | ||
UserAttributes: [ | ||
{ | ||
Name: 'custom:user_roles', | ||
Value: JSON.stringify(roles) | ||
} | ||
] | ||
}; | ||
|
||
await cognito.adminUpdateUserAttributes(params).promise(); | ||
} | ||
|
||
async function processCognitoUsers() { | ||
const userPoolId = process.env.USER_POOL_ID; | ||
console.log("user pool id: ", userPoolId) | ||
let paginationToken = null; | ||
let counter = 0; | ||
let hasRolesCounter = 0; | ||
let noRolesCounter =0; | ||
do { | ||
const params = { | ||
UserPoolId: userPoolId, | ||
AttributesToGet: ['email'], | ||
PaginationToken: paginationToken | ||
}; | ||
|
||
const listUsersResponse = await cognito.listUsers(params).promise(); | ||
console.log(listUsersResponse.Users.length + " users found") | ||
|
||
for (const user of listUsersResponse.Users) { | ||
const emailAttribute = user.Attributes.find(attr => attr.Name === 'email'); | ||
if (emailAttribute) { | ||
const userEmail = emailAttribute.Value; | ||
|
||
try { | ||
const externalUser = await getUser(userEmail); | ||
let roles = [""]; | ||
let roleList; | ||
try{ | ||
roleList = externalUser.roleList; | ||
}catch(error) { | ||
noRolesCounter ++ | ||
console.log(userEmail + " has no roles"); | ||
} | ||
if (roleList && roleList.length > 0 && roleList[0] != null) { | ||
roles = externalUser.roleList.map(role => role.role); | ||
hasRolesCounter ++; | ||
} else { | ||
console.log("user parsing error for user" + userEmail) | ||
} | ||
await updateUserAttribute(userPoolId, user.Username, roles); | ||
} catch (error) { | ||
console.error(`Error processing user ${userEmail}:`, error); | ||
} | ||
} | ||
counter++; | ||
} | ||
|
||
paginationToken = listUsersResponse.PaginationToken; | ||
} while (paginationToken); | ||
console.log(counter+ "users modified, "+ hasRolesCounter + "users had roles and "+ noRolesCounter + " users had no roles") | ||
} | ||
|
||
|
||
export const main = async () => { | ||
await processCognitoUsers().catch(console.error); | ||
console.log("function complete") | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
{ | ||
"_comment": "Dates are passed in format YYYY-MM-DD or keywords 'today' or 'x days from now'", | ||
"publicationDate": "2024-01-01", | ||
"expiryDate": "7 days from now", | ||
"header": "New Feature Release", | ||
"body": "We have released a new feature. Check it out now!", | ||
"buttonText": "Learn More", | ||
"buttonLink": "https://example.com/feature", | ||
"notificationType": "user" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,150 @@ | ||
import { DynamoDB } from "aws-sdk"; | ||
import { v4 as uuidv4 } from "uuid"; // Import UUID library | ||
import { addDays } from "date-fns"; // Import a date manipulation library like date-fns for handling date parsing | ||
|
||
const dynamoDb = new DynamoDB.DocumentClient( | ||
process.env.IS_OFFLINE | ||
? { | ||
endpoint: "http://localhost:8000", | ||
} | ||
: {} | ||
); | ||
const oneMacTableName = process.env.oneMacTableName || "defaultTableName"; | ||
|
||
// Define types for event and notification record | ||
interface EventInput { | ||
publicationDate?: string; | ||
expiryDate?: string; | ||
header: string; | ||
body: string; | ||
buttonText?: string; | ||
buttonLink?: string; | ||
notificationType: "user" | "system"; // User-friendly notification type input | ||
} | ||
|
||
interface NotificationRecord { | ||
pk: string; | ||
sk: string; | ||
GSI1pk: string; | ||
GSI1sk: string; | ||
publicationDate: string; | ||
expiryDate: string; | ||
header: string; | ||
body: string; | ||
buttonText?: string | null; | ||
buttonLink?: string | null; | ||
createdAt: string; | ||
} | ||
|
||
// Function to translate notificationType to GSI1pk value | ||
function mapNotificationType(type: "user" | "system"): string { | ||
return type === "user" ? "USER_NOTIFICATION" : "SYSTEM"; | ||
} | ||
|
||
// Function to handle flexible date inputs | ||
function parseDate(input?: string): string { | ||
// If no input is provided, default to the current date | ||
if (!input) { | ||
return new Date().toISOString(); | ||
} | ||
|
||
if (input === "today") { | ||
return new Date().toISOString(); | ||
} | ||
|
||
if (input.includes("days from now")) { | ||
const days = parseInt(input.split(" ")[0], 10); | ||
return addDays(new Date(), days).toISOString(); | ||
} | ||
|
||
// Assuming user passes YYYY-MM-DD format for easier input | ||
if (/^\d{4}-\d{2}-\d{2}$/.test(input)) { | ||
return new Date(`${input}T00:00:00Z`).toISOString(); | ||
} | ||
|
||
// Default to current date if input is invalid | ||
return new Date().toISOString(); | ||
} | ||
|
||
function validateEvent(event: EventInput): void { | ||
const missingParams: string[] = []; | ||
|
||
if (!event.header) { | ||
missingParams.push("header"); | ||
} | ||
if (!event.body) { | ||
missingParams.push("body"); | ||
} | ||
if (!event.notificationType) { | ||
missingParams.push("notificationType"); | ||
} | ||
if (missingParams.length > 0) { | ||
throw new Error(`Missing event parameters: ${missingParams.join(", ")}`); | ||
} | ||
|
||
console.log("Event passed validation"); | ||
} | ||
|
||
function generateNotificationId(): string { | ||
return uuidv4(); | ||
} | ||
|
||
function formatNotificationRecord(event: EventInput): NotificationRecord { | ||
const notificationId = generateNotificationId(); // Generate a UUID for the notification ID | ||
const pk = "SYSTEM"; // System-wide notification | ||
const sk = `NOTIFICATION#${notificationId}`; // Unique notification identifier | ||
|
||
// Default values for publicationDate and expiryDate with more user-friendly inputs | ||
const publicationDate = parseDate(event.publicationDate); // Default to current date | ||
const expiryDate = event.expiryDate | ||
? parseDate(event.expiryDate) | ||
: "9999-12-31T23:59:59Z"; // Default to far future date | ||
|
||
// Use the translated notificationType | ||
const GSI1pk = mapNotificationType(event.notificationType); | ||
const GSI1sk = `${publicationDate}#${expiryDate}`; // Sort key for GSI based on dates | ||
const createdAt = new Date().toISOString(); // Current timestamp | ||
|
||
return { | ||
pk, | ||
sk, | ||
GSI1pk, | ||
GSI1sk, | ||
publicationDate, | ||
expiryDate, | ||
header: event.header, | ||
body: event.body, | ||
buttonText: event.buttonText || null, // Optional field | ||
buttonLink: event.buttonLink || null, // Optional field | ||
createdAt, | ||
}; | ||
} | ||
|
||
async function insertNotification(record: NotificationRecord): Promise<void> { | ||
const params: DynamoDB.DocumentClient.PutItemInput = { | ||
TableName: oneMacTableName, | ||
Item: record, | ||
}; | ||
|
||
console.log("Inserting notification", params); | ||
|
||
await dynamoDb.put(params).promise(); | ||
} | ||
|
||
export const main = async (event: EventInput) => { | ||
console.log("insertNotification.main", event); | ||
|
||
validateEvent(event); | ||
|
||
const notificationRecord = formatNotificationRecord(event); | ||
|
||
await insertNotification(notificationRecord); | ||
|
||
return { | ||
statusCode: 200, | ||
body: JSON.stringify({ | ||
message: "Notification inserted successfully", | ||
record: notificationRecord, // Return the full inserted record | ||
}), | ||
}; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.