Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Test newsletter solution #2839 #2861

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,9 @@
"@sanity/client": "5.4.2",
"@sanity/image-url": "^1.0.2",
"@sanity/webhook": "^2.0.0",
"@types/axios": "^0.14.4",
"algoliasearch": "^4.16.0",
"axios": "^1.7.9",
"date-fns": "^2.29.3",
"date-fns-tz": "^2.0.0",
"easy-soap-request": "^5.4.0",
Expand Down
15 changes: 12 additions & 3 deletions web/pages/api/news-letter-distribution.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { distribute } from './subscription'
import { languages } from '../../languages'
import { NewsDistributionParameters } from '../../types/index'
import { NextApiRequest, NextApiResponse } from 'next'
import { isValidSignature, SIGNATURE_HEADER_NAME } from '@sanity/webhook'
import getRawBody from 'raw-body'
Expand All @@ -17,6 +16,16 @@ export const config = {
},
}

export type NewsDistributionParameters = {
segmentId?: number
timeStamp: string
title: string
ingress: string
link: string
newsType: string
languageCode: string
}

const logRequest = (req: NextApiRequest, title: string) => {
console.log('\n')
console.log(title)
Expand All @@ -36,7 +45,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
logRequest(req, 'Unauthorized request: Newsletter Distribution Endpoint')
return res.status(401).json({ success: false, msg: 'Unauthorized!' })
}

const { publicRuntimeConfig } = getConfig()
const data = JSON.parse(body)
const locale = languages.find((lang) => lang.name == data.languageCode)?.locale || 'en'
Expand Down Expand Up @@ -83,7 +92,7 @@ async function distributeWithRetry(
const date = getDateWithMs()

try {
const isSuccessful = await distribute(newsDistributionParameters)
const isSuccessful = await distribute()
if (!isSuccessful) throw new Error('Distribution was unsuccessful.')
res = {
success: true,
Expand Down
2 changes: 2 additions & 0 deletions web/pages/api/rss/groq.global.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export type LatestNewsType = {
publishDateTime: string
hero: ImageWithCaptionData
ingress: PortableTextBlock
subscriptionType: string
}

export const latestNews = /* groq */ `
Expand All @@ -20,6 +21,7 @@ export const latestNews = /* groq */ `
"slug": slug.current,
title,
"hero": heroImage,
subscriptionType,
"publishDateTime": ${publishDateTimeQuery},
${ingressForNewsQuery},
}
Expand Down
1 change: 1 addition & 0 deletions web/pages/api/rss/index.global.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ const generateRssFeed = async (lang: 'no' | 'en') => {
<guid>https://equinor.com${lang === 'no' ? '/no' : ''}${article.slug}</guid>
<pubDate>${new Date(article.publishDateTime).toUTCString()}</pubDate>
<description><![CDATA[<img src="${bannerImageUrl}"${imageAlt}/><br/>${descriptionHtml}]]></description>
${article.subscriptionType ? `<category>${article.subscriptionType}</category>` : ''}
</item>`
})

Expand Down
195 changes: 90 additions & 105 deletions web/pages/api/subscription.ts
Original file line number Diff line number Diff line change
@@ -1,120 +1,105 @@
import soapRequest from 'easy-soap-request'
import * as xml2js from 'xml2js'
import { LoginResult, SubscribeFormParameters, NewsDistributionParameters } from '../../types/index'
//import { appInsights } from '../../common'
import axios from 'axios'

const subscriptionUrl = process.env.BRANDMASTER_EMAIL_SUBSCRIPTION_URL || ''
export const authenticationUrl = process.env.BRANDMASTER_EMAIL_AUTHENTICATION_URL || ''
const clientSecret = process.env.BRANDMASTER_EMAIL_CLIENT_SECRET
const password = process.env.BRANDMASTER_EMAIL_PASSWORD
const apnId = process.env.BRANDMASTER_EMAIL_APN_ID
const otyId = process.env.BRANDMASTER_EMAIL_OTY_ID
const ptlId = process.env.BRANDMASTER_EMAIL_PTL_ID

const sampleHeaders = {
'Content-Type': 'text/xml;charset=UTF-8',
export type SubscribeFormParameters = {
firstName: string
email: string
crudeOilAssays?: boolean
generalNews?: boolean
magazineStories?: boolean
stockMarketAnnouncements?: boolean
languageCode: string
}
const xml = `<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"><s:Body><Authentication___Login xmlns="http://tempuri.org/"><clientSecret>${clientSecret}</clientSecret><userName>SUBSCRIPTIONAPI</userName><password>${password}</password><comId>34</comId><ptlId>${ptlId}</ptlId><otyId>${otyId}</otyId><laeId>1</laeId><apnId>${apnId}</apnId></Authentication___Login></s:Body></s:Envelope>`
const authenticate = async () => {
const { response } = await soapRequest({
url: authenticationUrl,
headers: sampleHeaders,
xml: xml,
timeout: 5000,
})
const { body } = response
let apiSecret = ''
let instId = ''
xml2js.parseString(body, function (err, result) {
if (err != null) console.error('Error while authenticating from Brandmaster : ----------------\n' + err)
if (parsedError(result, 'could not get apiSecret and instId ') != undefined) return

const soapBody = result['SOAP-ENV:Envelope']['SOAP-ENV:Body']['0']
const loginResult = soapBody['v1:Authentication___LoginResponse']['0']['v1:Result']['0']
apiSecret = loginResult['v1:apiSecret']['0']
instId = loginResult['v1:instId']['0']
})
return { apiSecret, instId }
const MAKE_SUBSCRIBER_API_BASE_URL = process.env.MAKE_SUBSCRIBER_API_BASE_URL
const MAKE_NEWSLETTER_API_BASE_URL = process.env.MAKE_NEWSLETTER_API_BASE_URL
const MAKE_API_KEY = process.env.MAKE_API_KEY || ''
const SUBSCRIBER_LIST_ID = process.env.MAKE_SUBSCRIBER_LIST_ID
const MAKE_API_USER = process.env.MAKE_API_USERID || ''
const MAKE_NEWSLETTER_ID = process.env.MAKE_NEWSLETTER_ID

export type NewsDistributionParameters = {
newsletterId: number
senderId: number
segmentId?: number
timeStamp: string
title: string
ingress: string
link: string
newsType: string
languageCode: string
}

const createSignUpRequest = async (loginResult: LoginResult, formParameters: SubscribeFormParameters) => {
const additionalParameters = `
{
"stock_market": "${formParameters.stockMarketAnnouncements ? 'Y' : 'N'}",
"company_news": "${formParameters.generalNews ? 'Y' : 'N'}",
"crude_oil_assays": "${formParameters.crudeOilAssays ? 'Y' : 'N'}",
"magazine": "${formParameters.magazineStories ? 'Y' : 'N'}",
"type": "Investor",
"lang": "${formParameters.languageCode}"
}`
const subscriberApi = axios.create({
baseURL: MAKE_SUBSCRIBER_API_BASE_URL,
headers: {
'Content-Type': 'application/json',
Authorization: `Basic ${Buffer.from(`${MAKE_API_USER}:${MAKE_API_KEY}`).toString('base64')}`,
},
})

const envelope = `<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"><s:Body><Subscription___SignUp xmlns="http://tempuri.org/"><clientSecret>${clientSecret}</clientSecret><apiSecret>${loginResult.apiSecret}</apiSecret><instId>${loginResult.instId}</instId><firstName>${formParameters.firstName}</firstName><email>${formParameters.email}</email><additionalParams>${additionalParameters}</additionalParams></Subscription___SignUp></s:Body></s:Envelope>`
const { response } = await soapRequest({
url: subscriptionUrl,
headers: sampleHeaders,
xml: envelope,
timeout: 5000,
})
xml2js.parseString(response.body, function (err, result) {
if (err != null) {
console.log('Error while creating signup request to Brandmaster : ----------------\n' + err)
response.statusCode = 500
}
if (parsedError(result, 'could not create sign up request ') != undefined) {
response.statusCode = 500
return
}
})
return response.statusCode == 200
}
/**
* Subscribe a user using subscriber_list_id and tags
*/
export const signUp = async (formParameters: SubscribeFormParameters) => {
try {
const requestedTags: string[] = []
if (formParameters.stockMarketAnnouncements) requestedTags.push('Stock')
if (formParameters.generalNews) requestedTags.push('Company')
if (formParameters.crudeOilAssays) requestedTags.push('Crude')
if (formParameters.magazineStories) requestedTags.push('Magazine')

const createDistributeRequest = async (loginResult: LoginResult, parameters: NewsDistributionParameters) => {
const envelope = `<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"><s:Body><Subscription___Distribute xmlns="http://tempuri.org/"><clientSecret>${clientSecret}</clientSecret><apiSecret>${loginResult.apiSecret}</apiSecret><instId>${loginResult.instId}</instId><timeStamp>${parameters.timeStamp}</timeStamp><Title><![CDATA[${parameters.title}]]></Title><Ingress><![CDATA[${parameters.ingress}]]></Ingress><newsURL><![CDATA[${parameters.link}]]></newsURL><newsType><![CDATA[${parameters.newsType}]]></newsType><language><![CDATA[${parameters.languageCode}]]></language><additionalParams/></Subscription___Distribute></s:Body></s:Envelope>`
const { response } = await soapRequest({
url: subscriptionUrl,
headers: sampleHeaders,
xml: envelope,
timeout: 5000,
})
xml2js.parseString(response.body, function (err, result) {
if (err != null) {
console.error('Error while creating distribute request to Brandmaster : ----------------\n' + err)
response.statusCode = 400
}
const error = parsedError(
result,
'could not distribute newsletter ' + parameters.link + ' published at ' + parameters.timeStamp,
)
if (error != undefined) {
// should trigger mail...
console.log('Newsletter distribution failure', response.body.toString())
// @TODO Move to Dynatrace
// appInsights.trackEvent({name:"Newsletter distribution failure"},{message:error})
response.statusCode = 400
const requestBody = {
email: formParameters.email,
tags: requestedTags,
}
})

return response.statusCode == 200
}
console.log('📤 Sending subscription request:', {
url: `/subscribers?subscriber_list_id=${SUBSCRIBER_LIST_ID}`,
headers: subscriberApi.defaults.headers,
body: requestBody,
})

export const signUp = async (formParameters: SubscribeFormParameters) => {
const loginResult = await authenticate()
if (loginResult.apiSecret != '' && loginResult.instId != '') return createSignUpRequest(loginResult, formParameters)
else return false
}
const response = await subscriberApi.post(`/subscribers?subscriber_list_id=${SUBSCRIBER_LIST_ID}`, requestBody)

export const distribute = async (parameters: NewsDistributionParameters) => {
const loginResult = await authenticate()
if (loginResult.apiSecret != '' && loginResult.instId != '') {
return createDistributeRequest(loginResult, parameters)
} else return false
return response.status === 200
} catch (error: any) {
console.error('❌ Error in signUp:', {
message: error.message,
responseData: error.response?.data,
responseStatus: error.response?.status,
requestHeaders: error.config?.headers,
})
return false
}
}

const parsedError = (result: any, prefix: string) => {
const soapBody = result['SOAP-ENV:Envelope']['SOAP-ENV:Body']['0']
if (soapBody['SOAP-ENV:Fault'] != undefined) {
const error = soapBody['SOAP-ENV:Fault']['0']['faultstring']
console.error(Date() + ' : Newsletter Failure Error: ' + prefix + '\n' + error)
return error
const newsletterApi = axios.create({
baseURL: MAKE_NEWSLETTER_API_BASE_URL,
headers: {
'Content-Type': 'application/json',
Authorization: `Basic ${Buffer.from(`${MAKE_API_USER}:${MAKE_API_KEY}`).toString('base64')}`,
},
})

/**
* Distribute a newsletter
*/
export const distribute = async () => {
try {
const url = `${MAKE_NEWSLETTER_API_BASE_URL}/recurring_actions/${MAKE_NEWSLETTER_ID}/trigger`
const requestBody = {
sender_id: MAKE_API_USER,
}
const response = await newsletterApi.post(url, requestBody)
return response.status === 200
} catch (error: any) {
console.error('❌ Error in distribute:', {
message: error.message,
responseData: error.response?.data,
responseStatus: error.response?.status,
requestHeaders: error.config?.headers,
})

return false
}
}
23 changes: 20 additions & 3 deletions web/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading