da40f70b75c0ee9e5cf44a5b546e1be0d2e7d4d7
configuration/findUsersWithSessionInMonths.sh
| ... | ... | @@ -0,0 +1,27 @@ |
| 1 | +#!/bin/bash |
|
| 2 | +# Usage: ${0} {MONGO-URI} {MONTHS} |
|
| 3 | +# Example: findUsersWithSessionInMonths.sh "mongodb://localhost/winddb?replicaSet=rs0" 12 |
|
| 4 | +echo ' |
|
| 5 | +var cutoff = new Date(); |
|
| 6 | +cutoff.setMonth(cutoff.getMonth() - '${2}'); |
|
| 7 | + |
|
| 8 | +var activeUsers = db.SESSIONS.distinct( |
|
| 9 | + "SESSION_ATTRIBUTES.SESSION_ATTRIBUTE_VALUE.SESSION_PRINCIPAL_REALM_VALUE", |
|
| 10 | + { SESSION_START_TIMESTAMP: { $gte: cutoff } } |
|
| 11 | +).flat(); |
|
| 12 | + |
|
| 13 | +db.USERS.find( |
|
| 14 | + { |
|
| 15 | + EMAIL_VALIDATED: true, |
|
| 16 | + EMAIL: { $exists: true, $ne: "" }, |
|
| 17 | + NAME: { $in: activeUsers }, |
|
| 18 | + $or: [ |
|
| 19 | + { DID_OPT_OUT_OF_FEATURE_AND_COMMUNITY_EMAILS: { $exists: false } }, |
|
| 20 | + { DID_OPT_OUT_OF_FEATURE_AND_COMMUNITY_EMAILS: false } |
|
| 21 | + ] |
|
| 22 | + }, |
|
| 23 | + { EMAIL: 1, FULLNAME: 1, NAME: 1, _id: 0 } |
|
| 24 | +).forEach(function(u) { |
|
| 25 | + var name = (u.FULLNAME && u.FULLNAME.trim()) || u.NAME || "user"; |
|
| 26 | + print(u.EMAIL + "," + name); |
|
| 27 | +})' | mongosh "${1}" |
configuration/sendEmails.md
| ... | ... | @@ -0,0 +1,89 @@ |
| 1 | +# sendEmails.sh — Personalized Email Sender |
|
| 2 | + |
|
| 3 | +Bash script for sending personalized HTML emails via SMTP (AWS WorkMail). |
|
| 4 | + |
|
| 5 | +## Prerequisites |
|
| 6 | + |
|
| 7 | +- `curl` with SMTP/TLS support |
|
| 8 | +- `base64` (coreutils) |
|
| 9 | +- SMTP credentials for the WorkMail account (`support@sapsailing.com`) |
|
| 10 | + |
|
| 11 | +## Files |
|
| 12 | + |
|
| 13 | +| File | Purpose | |
|
| 14 | +|---|---| |
|
| 15 | +| `sendEmails.sh` | The sending script | |
|
| 16 | +| `emailTemplate.html` | HTML email template with `{{NAME}}` placeholder | |
|
| 17 | +| `recipients.csv` | CSV file with recipient list | |
|
| 18 | + |
|
| 19 | +## CSV Format |
|
| 20 | + |
|
| 21 | +```csv |
|
| 22 | +email,name |
|
| 23 | +alice@example.com,Alice |
|
| 24 | +bob@example.com,Bob |
|
| 25 | +sailor42@example.com,sailor42 |
|
| 26 | +``` |
|
| 27 | + |
|
| 28 | +- **email** — recipient address |
|
| 29 | +- **name** — inserted into the salutation ("Dear {{NAME}},"). Use the user's first name if known, otherwise their username/nickname. |
|
| 30 | +- The header row is auto-detected and skipped if the first field starts with `email` (case-insensitive). |
|
| 31 | + |
|
| 32 | +## Usage |
|
| 33 | + |
|
| 34 | +```bash |
|
| 35 | +./sendEmails.sh [--dry-run] <recipients.csv> [template.html] |
|
| 36 | +``` |
|
| 37 | + |
|
| 38 | +| Argument | Required | Default | Description | |
|
| 39 | +|---|---|---|---| |
|
| 40 | +| `--dry-run` | no | — | Preview personalized emails without sending | |
|
| 41 | +| `recipients.csv` | yes | — | Path to the CSV file | |
|
| 42 | +| `template.html` | no | `/tmp/emailTemplate.html` | Path to the HTML template | |
|
| 43 | + |
|
| 44 | +## Environment Variables |
|
| 45 | + |
|
| 46 | +| Variable | Required | Description | |
|
| 47 | +|---|---|---| |
|
| 48 | +| `SMTP_USER` | yes (unless `--dry-run`) | SMTP username for WorkMail | |
|
| 49 | +| `SMTP_PASS` | yes (unless `--dry-run`) | SMTP password for WorkMail | |
|
| 50 | + |
|
| 51 | +## Examples |
|
| 52 | + |
|
| 53 | +Preview all emails without sending: |
|
| 54 | + |
|
| 55 | +```bash |
|
| 56 | +./sendEmails.sh --dry-run recipients.csv |
|
| 57 | +``` |
|
| 58 | + |
|
| 59 | +Send using a custom template: |
|
| 60 | + |
|
| 61 | +```bash |
|
| 62 | +export SMTP_USER="your-smtp-username" |
|
| 63 | +export SMTP_PASS="your-smtp-password" |
|
| 64 | +./sendEmails.sh recipients.csv myTemplate.html |
|
| 65 | +``` |
|
| 66 | + |
|
| 67 | +## Configuration |
|
| 68 | + |
|
| 69 | +These values are set at the top of the script and can be adjusted: |
|
| 70 | + |
|
| 71 | +| Variable | Default | Description | |
|
| 72 | +|---|---|---| |
|
| 73 | +| `FROM_ADDR` | `SAP Sailing Analytics <support@sapsailing.com>` | Sender display name and address | |
|
| 74 | +| `SUBJECT` | `Your SAP Sailing Analytics Account — Open Source Transition Update` | Email subject line | |
|
| 75 | +| `SMTP_URL` | `smtps://smtp.mail.eu-west-1.awsapps.com:465` | WorkMail SMTP endpoint | |
|
| 76 | + |
|
| 77 | +## How It Works |
|
| 78 | + |
|
| 79 | +1. Reads the CSV line by line, skipping the header row. |
|
| 80 | +2. For each recipient, replaces `{{NAME}}` in the HTML template with the name from the CSV. |
|
| 81 | +3. Constructs a MIME message with UTF-8 base64-encoded subject and body. |
|
| 82 | +4. Sends via `curl` over SMTPS to the WorkMail endpoint. |
|
| 83 | +5. Waits 1 second between sends to respect SES rate limits. |
|
| 84 | + |
|
| 85 | +## Recommended Workflow |
|
| 86 | + |
|
| 87 | +1. **Preview** — Run with `--dry-run` and review the output. |
|
| 88 | +2. **Test** — Send to your own address first (`echo "email,name" > test.csv && echo "you@example.com,YourName" >> test.csv`). |
|
| 89 | +3. **Send** — Run against the full recipient list. |
configuration/sendEmails.sh
| ... | ... | @@ -0,0 +1,118 @@ |
| 1 | +#!/usr/bin/env bash |
|
| 2 | +set -euo pipefail |
|
| 3 | + |
|
| 4 | +FROM_ADDR="SAP Sailing Analytics <support@sapsailing.com>" |
|
| 5 | +SUBJECT="SAP Sailing Analytics — Open Source Transition Update" |
|
| 6 | +SMTP_URL="smtps://smtp.mail.eu-west-1.awsapps.com:465" |
|
| 7 | + |
|
| 8 | +usage() { |
|
| 9 | + cat <<'EOF' |
|
| 10 | +Usage: sendEmails.sh [--dry-run] <recipients.csv> [template.html] |
|
| 11 | + |
|
| 12 | + recipients.csv CSV file with columns: email,name |
|
| 13 | + template.html HTML template with {{NAME}} placeholder |
|
| 14 | + (default: /tmp/emailTemplate.html) |
|
| 15 | + --dry-run Print personalized emails to stdout without sending |
|
| 16 | + |
|
| 17 | +Environment variables: |
|
| 18 | + SMTP_USER SMTP username (required unless --dry-run) |
|
| 19 | + SMTP_PASS SMTP password (required unless --dry-run) |
|
| 20 | + |
|
| 21 | +CSV format example: |
|
| 22 | + email,name |
|
| 23 | + alice@example.com,Alice |
|
| 24 | + bob@example.com,Bob |
|
| 25 | +EOF |
|
| 26 | + exit 1 |
|
| 27 | +} |
|
| 28 | + |
|
| 29 | +DRY_RUN=false |
|
| 30 | +if [[ "${1:-}" == "--dry-run" ]]; then |
|
| 31 | + DRY_RUN=true |
|
| 32 | + shift |
|
| 33 | +fi |
|
| 34 | + |
|
| 35 | +CSV="${1:-}" |
|
| 36 | +TEMPLATE="${2:-/tmp/emailTemplate.html}" |
|
| 37 | + |
|
| 38 | +[[ -z "$CSV" ]] && usage |
|
| 39 | +[[ ! -f "$CSV" ]] && { echo "Error: CSV file not found: $CSV"; exit 1; } |
|
| 40 | +[[ ! -f "$TEMPLATE" ]] && { echo "Error: Template file not found: $TEMPLATE"; exit 1; } |
|
| 41 | + |
|
| 42 | +if [[ "$DRY_RUN" == false ]]; then |
|
| 43 | + [[ -z "${SMTP_USER:-}" ]] && { echo "Error: SMTP_USER not set"; exit 1; } |
|
| 44 | + [[ -z "${SMTP_PASS:-}" ]] && { echo "Error: SMTP_PASS not set"; exit 1; } |
|
| 45 | +fi |
|
| 46 | + |
|
| 47 | +TEMPLATE_BODY=$(cat "$TEMPLATE") |
|
| 48 | + |
|
| 49 | +SENT=0 |
|
| 50 | +FAILED=0 |
|
| 51 | +SKIPPED_HEADER=false |
|
| 52 | + |
|
| 53 | +while IFS= read -r line || [[ -n "$line" ]]; do |
|
| 54 | + # Skip empty lines |
|
| 55 | + [[ -z "${line// }" ]] && continue |
|
| 56 | + |
|
| 57 | + # Skip header row |
|
| 58 | + if [[ "$SKIPPED_HEADER" == false ]]; then |
|
| 59 | + SKIPPED_HEADER=true |
|
| 60 | + if echo "$line" | grep -qi "^email"; then |
|
| 61 | + continue |
|
| 62 | + fi |
|
| 63 | + fi |
|
| 64 | + |
|
| 65 | + EMAIL=$(echo "$line" | cut -d',' -f1 | xargs) |
|
| 66 | + NAME=$(echo "$line" | cut -d',' -f2- | xargs) |
|
| 67 | + |
|
| 68 | + [[ -z "$EMAIL" ]] && continue |
|
| 69 | + |
|
| 70 | + BODY="${TEMPLATE_BODY//\{\{NAME\}\}/$NAME}" |
|
| 71 | + |
|
| 72 | + if [[ "$DRY_RUN" == true ]]; then |
|
| 73 | + echo "========================================" |
|
| 74 | + echo "To: $EMAIL" |
|
| 75 | + echo "From: $FROM_ADDR" |
|
| 76 | + echo "Subject: $SUBJECT" |
|
| 77 | + echo "----------------------------------------" |
|
| 78 | + echo "$BODY" |
|
| 79 | + echo "" |
|
| 80 | + SENT=$((SENT + 1)) |
|
| 81 | + continue |
|
| 82 | + fi |
|
| 83 | + |
|
| 84 | + MIME_MSG=$(cat <<MIME |
|
| 85 | +From: $FROM_ADDR |
|
| 86 | +To: $EMAIL |
|
| 87 | +Subject: =?UTF-8?B?$(echo -n "$SUBJECT" | base64 -w0)?= |
|
| 88 | +MIME-Version: 1.0 |
|
| 89 | +Content-Type: text/html; charset=UTF-8 |
|
| 90 | +Content-Transfer-Encoding: base64 |
|
| 91 | + |
|
| 92 | +$(echo -n "$BODY" | base64 -w76) |
|
| 93 | +MIME |
|
| 94 | + ) |
|
| 95 | + |
|
| 96 | + if echo "$MIME_MSG" | curl --silent --show-error \ |
|
| 97 | + --url "$SMTP_URL" \ |
|
| 98 | + --ssl-reqd \ |
|
| 99 | + --mail-from "support@sapsailing.com" \ |
|
| 100 | + --mail-rcpt "$EMAIL" \ |
|
| 101 | + --user "${SMTP_USER}:${SMTP_PASS}" \ |
|
| 102 | + --upload-file -; then |
|
| 103 | + echo "Sent to $EMAIL ($NAME)" |
|
| 104 | + SENT=$((SENT + 1)) |
|
| 105 | + else |
|
| 106 | + echo "FAILED: $EMAIL ($NAME)" >&2 |
|
| 107 | + FAILED=$((FAILED + 1)) |
|
| 108 | + fi |
|
| 109 | + |
|
| 110 | + sleep 1 |
|
| 111 | +done < "$CSV" |
|
| 112 | + |
|
| 113 | +echo "" |
|
| 114 | +if [[ "$DRY_RUN" == true ]]; then |
|
| 115 | + echo "Dry run complete. $SENT emails previewed." |
|
| 116 | +else |
|
| 117 | + echo "Done. Sent: $SENT, Failed: $FAILED" |
|
| 118 | +fi |
java/com.sap.sse.replication.interfaces/src/com/sap/sse/replication/FullyInitializedReplicableTracker.java
| ... | ... | @@ -130,7 +130,8 @@ public class FullyInitializedReplicableTracker<R extends Replicable<?, ?>> exten |
| 130 | 130 | * {@link #waitForService(long)}). If no such service object can be found before timing out, {@code null} |
| 131 | 131 | * is returned. Once a service object has been retrieved and a non-{@code null} {@link #replicationServiceTracker} |
| 132 | 132 | * has been provided at construction time, the {@link ReplicationService} is obtained from that tracker by |
| 133 | - * waiting for it at least {@code timeoutInMillis} milliseconds and then |
|
| 133 | + * waiting for it at least {@code timeoutInMillis} milliseconds and then is asked to wait for the replication |
|
| 134 | + * to be fully initialized, so in particular having received and incorporated the initial load. |
|
| 134 | 135 | * |
| 135 | 136 | * @param timeoutInMillis |
| 136 | 137 | * 0 means indefinite wait time |