Skip to content

Commit

Permalink
Update newsletter recovery to use new JSON API (#15787)
Browse files Browse the repository at this point in the history
  • Loading branch information
robhudson authored Jan 8, 2025
1 parent 11ef8d3 commit 85b5943
Show file tree
Hide file tree
Showing 6 changed files with 122 additions and 4 deletions.
7 changes: 7 additions & 0 deletions bedrock/newsletter/templates/newsletter/recovery.html
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,13 @@
</div>
{% endif %}

{# noscript warning to enable JavaScript #}
<noscript>
<div class="mzp-c-notification-bar mzp-t-error">
<p>{{ ftl('ui-please-turn-on-javascript') }}</p>
</div>
</noscript>

<div class="mzp-l-content mzp-t-content-sm">
<h1 class="mzp-u-title-sm">{{ ftl('newsletters-manage-your-newsletter') }}</h1>

Expand Down
6 changes: 5 additions & 1 deletion bedrock/newsletter/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,11 @@ def recovery(request):
form = EmailForm()
fxa_error = request.GET.get("fxa_error", "0") == "1"

context = {"form": form, "recovery_url": f"{settings.BASKET_URL}/news/recover/", "fxa_error": fxa_error}
context = {
"form": form,
"recovery_url": f"{settings.BASKET_URL}/api/v1/users/recover/",
"fxa_error": fxa_error,
}

# This view is shared between two different templates. For context see bug 1442129.
if "/newsletter/opt-out-confirmation/" in request.get_full_path():
Expand Down
71 changes: 71 additions & 0 deletions media/js/newsletter/form-utils.es6.js
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,61 @@ const FormUtils = {
xhr.send(params);
},

/**
* Perform an AJAX POST to Basket as JSON
* @param {String} email
* @param {Object} params (JSON object)
* @param {String} url (Basket API endpoint)
* @param {Function} successCallback
* @param {Function} errorCallback
*/
postJsonToBasket: (email, params, url, successCallback, errorCallback) => {
const xhr = new XMLHttpRequest();

// Emails used in automation for page-level integration tests
// should avoid hitting basket directly.
if (email === '[email protected]') {
successCallback();
return;
} else if (email === '[email protected]') {
errorCallback();
return;
}

xhr.onload = function (e) {
let response = e.target.response || e.target.responseText;

if (typeof response !== 'object') {
response = JSON.parse(response);
}

if (response) {
if (
response.status === 'ok' &&
e.target.status >= 200 &&
e.target.status < 300
) {
successCallback();
} else if (response.status === 'error' && response.desc) {
errorCallback(response.desc);
} else {
errorCallback();
}
} else {
errorCallback();
}
};

xhr.onerror = errorCallback;
xhr.open('POST', url, true);
xhr.setRequestHeader('Content-type', 'application/json');
xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
xhr.timeout = 5000;
xhr.ontimeout = errorCallback;
xhr.responseType = 'json';
xhr.send(JSON.stringify(params));
},

/**
* Removes Basket UUID token from page URL path and updates browser history.
* Note: this function will remove *everything* from the path *after* the token.
Expand Down Expand Up @@ -299,6 +354,22 @@ const FormUtils = {
return q.join('&');
},

/**
* Helper function to serialize form data for XHR request.
* @param {HTMLElement} form
* @returns {Object} JSON object
*/
serializeToJson: (form) => {
const json = {};
for (let i = 0; i < form.elements.length; i++) {
const elem = form.elements[i];
if (elem.name) {
json[elem.name] = elem.value;
}
}
return json;
},

/**
* Helper function that strips HTML tags from text form input.
* @param {String} text
Expand Down
4 changes: 2 additions & 2 deletions media/js/newsletter/recovery.es6.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ const RecoveryEmailForm = {
e.preventDefault();

const email = document.getElementById('id_email').value;
const params = 'email=' + encodeURIComponent(email);
const params = FormUtils.serializeToJson(_recoveryForm);
const url = _recoveryForm.getAttribute('action');

// Disable form fields until POST has completed.
Expand All @@ -76,7 +76,7 @@ const RecoveryEmailForm = {
return;
}

FormUtils.postToBasket(
FormUtils.postJsonToBasket(
email,
params,
url,
Expand Down
36 changes: 36 additions & 0 deletions tests/unit/spec/newsletter/form-utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,42 @@ describe('serialize', function () {
});
});

describe('serializeToJson', function () {
afterEach(function () {
const form = document.querySelector('.send-to-device-form');
form.parentNode.removeChild(form);
});

it('should return a JSON object as expected', function () {
const form = `<form class="send-to-device-form" action="https://basket.mozilla.org/news/subscribe/" method="post">
<div class="send-to-device-form-fields">
<div class="platform-container">
<input type="hidden" name="newsletters" value="download-firefox-mobile-reco">
<input type="hidden" name="source-url" value="https://www.mozilla.org/en-US/firefox/browsers/mobile/ios/">
</div>
<div class="mzp-c-field mzp-l-stretch">
<label class="mzp-c-field-label" for="s2d-hero-input">Enter your email</label>
<input id="s2d-hero-input" class="mzp-c-field-control send-to-device-input" name="email" type="text" required="" value="[email protected]">
</div>
<div class="mzp-c-button-container mzp-l-stretch">
<button type="submit" class="button mzp-c-button mzp-t-product ">Send</button>
</div>
</div>
</form>`;
document.body.insertAdjacentHTML('beforeend', form);
expect(
FormUtils.serializeToJson(
document.querySelector('.send-to-device-form')
)
).toEqual({
newsletters: 'download-firefox-mobile-reco',
'source-url':
'https://www.mozilla.org/en-US/firefox/browsers/mobile/ios/',
email: '[email protected]'
});
});
});

describe('stripHTML', function () {
it('should strip HTML tags from strings as expected', function () {
expect(
Expand Down
2 changes: 1 addition & 1 deletion tests/unit/spec/newsletter/recovery.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import RecoveryEmailForm from '../../../../media/js/newsletter/recovery.es6';

describe('RecoveryEmailForm', function () {
beforeEach(async function () {
const form = `<form method="post" action="https://basket.mozilla.org/news/recover/" id="newsletter-recovery-form" class="newsletter-recovery-form mzp-c-form">
const form = `<form method="post" action="https://basket.mozilla.org/api/v1/users/recover/" id="newsletter-recovery-form" class="newsletter-recovery-form mzp-c-form">
<header class="mzp-c-newsletter-header">
<p>Enter your email address and we’ll send you a link to your email preference center.</p>
</header>
Expand Down

0 comments on commit 85b5943

Please sign in to comment.