Skip to content
Closed
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
25 changes: 25 additions & 0 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
### Title

Describes the **What**, not the ~~How~~ or the ~~Why~~.

### Description

- Links to ticket(s) with the title.
- Describes the **How** since the _Why_ should be in the ticket spec/technical analysis.
- Classes, methods, files… the reviewers should pay more attention to.


### Checklist for authors and reviewers

<!-- Move to the next section the non-applicable items -->

- [ ] The title of the PR uses business wording, not technical jargon, for the changelog readers to understand it
- [ ] The PR implements the changes asked in the referenced task / issue
- [ ] The tests are relevant, and cover the corner/error cases, not only the happy path
- [ ] You understand the impact of this PR on existing code/features
- [ ] The changes include adequate logging and Datadog traces
- [ ] Documentation is updated (API, developer documentation, ADR, Notion...)

### Non applicable

<!-- Move here non applicable items of the checklist, if any -->
45 changes: 45 additions & 0 deletions .github/release-drafter.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
name-template: 'v$RESOLVED_VERSION'
tag-template: 'v$RESOLVED_VERSION'

template: |
## Changes

$CHANGES

### Contributors

$CONTRIBUTORS

categories:
- title: '🚀 New Features'
labels:
- 'type: feature'
- title: '🐛 Bug Fixes'
labels:
- 'type: bugfix'
- 'type: hotfix'

exclude-labels:
- 'skip-changelog'
- 'release'
- 'type: chore'
- 'type: ci'
- 'type: refactor'
- 'type: test'
- 'type: docs'
- 'type: security'
- 'type: devx'
change-template: '- $TITLE (#$NUMBER)'
change-title-escapes: '\<*_&#@`'
version-resolver:
major:
labels:
- 'major'
minor:
labels:
- 'minor'
- 'type: feature'
patch:
labels:
- 'patch'
default: patch
7 changes: 7 additions & 0 deletions .github/release_templates.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# ${type} ${version}

${type} started by ${user}


> **Note**
> Merge this Pull Request by write /release in PR comment
110 changes: 110 additions & 0 deletions .github/workflows/release-publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
# This workflow is triggered when a pull request is merged and the label 'release' is present.
# It fetches the last draft release, updates it to a non-draft release and sends a Slack message with the release notes.
name: Publish Release

on:
pull_request:
types:
- closed

jobs:

release:
if: github.event.pull_request.merged == true && contains(github.event.pull_request.labels.*.name, 'release')
runs-on: ubuntu-24.04

steps:

- uses: actions/checkout@v4

- name: Install taskfile.dev
uses: arduino/setup-task@v2
with:
version: 3.x
repo-token: ${{ github.token }}

- name: Fetch last draft release
id: fetch-release-draft
shell: bash
run: |

# Call Github releases API and filter draft releases
DRAFT_RELEASE=$(curl \
-H "Accept: application/vnd.github+json" \
-H "Authorization: Bearer ${{ github.token }}" \
https://api.github.com/repos/${{ github.repository }}/releases | \
jq 'map(select(.draft == true))' \
)

# Fail if 0 or more than 1 draft release is found
if [[ $(echo $DRAFT_RELEASE | jq 'length') -ne 1 ]]
then
echo "No draft release found or more than one draft release found"
exit 1
fi

DRAFT_RELEASE=$(echo $DRAFT_RELEASE | jq first)

# Retrieve name, id and body of the draft release
# We need to remove the quotes from the JSON output
NAME=$(echo $DRAFT_RELEASE | jq '.name' | sed 's/"//g')
ID=$(echo $DRAFT_RELEASE | jq '.id')
BODY=$(echo $DRAFT_RELEASE | jq '.body' | sed 's/"//g')

# Add URLs to GitHub pull requests
PULL_REQUEST_URL_START=${{ github.server_url }}/${{ github.repository }}/pull/
ESCAPED_PULL_REQUEST_URL_START=$(printf '%s\n' "$PULL_REQUEST_URL_START" | sed -e 's/[\/&]/\\&/g')
BODY=$(echo -e "$BODY" | sed -E "s/#([0-9]+)/[#\1](${ESCAPED_PULL_REQUEST_URL_START}\1)/g")

# Add URLs to GitHub profiles
PROFILE_URL_START=${{ github.server_url }}/
ESCAPED_PROFILE_URL_START=$(printf '%s\n' "$PROFILE_URL_START" | sed -e 's/[\/&]/\\&/g')
BODY=$(echo -e "$BODY" | sed -E "s/@([[:alnum:]-]+)/[@\1](${ESCAPED_PROFILE_URL_START}\1)/g")

# Write the output variables
echo "name=$NAME" >> $GITHUB_OUTPUT
echo "id=$ID" >> $GITHUB_OUTPUT
echo "body<<EOF" >> $GITHUB_OUTPUT
echo -e "$BODY" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT

- name: Publish Github release
uses: actions/github-script@v7
with:
# target_commitish is set to refs/heads/develop by release-drafter as we need to retrieve pull requests merged into develop
# We need to override it to refs/heads/main to point to the last commit of main branch instead of develop branch
script: |
const { owner, repo } = context.repo;
await github.rest.repos.updateRelease({
owner,
repo,
release_id: "${{ steps.fetch-release-draft.outputs.id }}",
draft: false,
make_latest: true,
tag_name: "${{ steps.fetch-release-draft.outputs.name }}",
target_commitish: "refs/heads/main"
});

- name: Format release notes for Slack
uses: LoveToKnow/slackify-markdown-action@v1.1.1
id: slack-markdown-release-notes
with:
text: |
New release of ${{ github.repository }}, **[${{ steps.fetch-release-draft.outputs.name }}](https://github.com/${{ github.repository }}/releases/tag/${{ steps.fetch-release-draft.outputs.name }})**:

${{ steps.fetch-release-draft.outputs.body }}

cc <@france.berut> <@khadija.cherif>

- name: Send changelog to Slack
uses: slackapi/slack-github-action@v1.27.0
with:
channel-id: CR9C57YM6
slack-message: ${{ steps.slack-markdown-release-notes.outputs.text }}
payload: |
{
"username": "${{ github.event.sender.login }}",
"icon_url": "${{ github.event.sender.avatar_url }}"
}
env:
SLACK_BOT_TOKEN: ${{ secrets.SLACK_RELEASE_CHANGELOG_BOT_TOKEN }}
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
.DS_Store
coverage/*
.vscode/system-objecttype-extensions.d.ts
30 changes: 30 additions & 0 deletions @types/reachFiveSettings.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/// <reference path="../../node_modules/sfcc-dts/@types/sfcc/index.d.ts" />

type SitePreferences = dw.system.SitePreferences;

interface ReachFiveSettings extends SitePreferences {
isReachFiveEnabled: boolean;
isReachFiveTransitionActive: boolean;
reachFiveTransitionCookieDuration: number;
isReachFiveSessionForcedAuth: boolean;
isReach5ThemeActive: boolean;
reach5Domain: string;
reach5ApiKey: string;
reach5ClientSecret: string;
reachFiveProviderId: string;
isReachFiveFastRegister: boolean;
isReachFiveLoginAllowed: boolean;
reach5ManagementApiKey: string;
reach5ManagementClientSecret: string;
reach5ManagementScope: string;
reach5ProfileFieldsJSON: Record<string, unknown>;
reach5UiSdkUrl: string;
reach5CoreSdkUrl: string;
reach5SupportedLanguageCodes: string[];
reach5DefaulLanguageCode: string;
reachFiveCheckCredentials: string;
isReachFiveEmailAsLogin: boolean;
isReachFiveReturnProviderToken: boolean;
}

export = ReachFiveSettings;
8 changes: 8 additions & 0 deletions cartridges/int_reachfive/cartridge/models/ReachFiveModel.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,14 @@ var ReachFiveModel = ({
credentials.setAuthenticationProviderID(this.reachFiveProviderId);
credentials.setExternalID(externalID);

try {
// we add email for this new external customer profile
var externalCustomerProfile = newCustomer.getExternalProfile(this.reachFiveProviderId, externalID);
externalCustomerProfile.setEmail(externalProfile.email);
} catch (e) {
LOGGER.debug('Error on added email to external profile {0}', e);
}

LOGGER.info('Customer created with credentials and an external profile {0} with the external ID {1}', this.reachFiveProviderId, externalID);
}
else
Expand Down
29 changes: 6 additions & 23 deletions cartridges/int_reachfive/cartridge/models/reachfiveSettings.js
Original file line number Diff line number Diff line change
@@ -1,33 +1,16 @@
'use strict';

/**
* @typedef {import('@types/reachFiveSettings')} ReachFiveSettings
*/

var currentSite = require('dw/system/Site').getCurrent();
var LOGGER = require('dw/system/Logger').getLogger('loginReachFive');

/**
* @constructor
* @classdesc Reachfive site preferences
* @property {boolean} isReachFiveEnabled - is Reachfive enabled
* @property {boolean} isReachFiveTransitionActive - is Reach Five Transition Active
* @property {number} reachFiveTransitionCookieDuration - Reach Five Transition Cookie Duration
* @property {boolean} isReachFiveSessionForcedAuth - Session Forced Authentication
* @property {boolean} isReach5ThemeActive - Active or not default reach five theme
* @property {string} reach5Domain - Reach Five Domain
* @property {string} reach5ApiKey - Define the API KEY for the Identity API
* @property {string} reach5ClientSecret - Define the Client Secret for the Identity API
* @property {string} reachFiveProviderId - Reach Five provider ID
* @property {boolean} isReachFiveFastRegister - is Reach Five Fast Register
* @property {boolean} isReachFiveLoginAllowed - is Reach Five Login Allowed
* @property {string} reach5ManagementApiKey - Define the API KEY for the Management API
* @property {string} reach5ManagementClientSecret - Define the Client Secret for the Management API
* @property {string} reach5ManagementScope - Reach Five Management scope
* @property {json} reach5ProfileFieldsJSON - ReachFive profile fields JSON
* @property {string} reach5UiSdkUrl - Web UI SDK Url
* @property {string} reach5CoreSdkUrl - Web Core SDK Url
* @property {Array} reach5SupportedLanguageCodes - Supported ReachFive LanguageCodes
* @property {string} reach5DefaulLanguageCode - Default ReachFive LanguageCode
* @property {string} reachFiveCheckCredentials - Check credentials method
* @property {boolean} isReachFiveEmailAsLogin - Create profile with login as an email
* @property {boolean} isReachFiveReturnProviderToken - Retrieve the provider token in the SFCC session
* @implements {ReachFiveSettings}
*/
function Settings() {
Object.defineProperties(this, {
Expand Down Expand Up @@ -106,7 +89,7 @@ function Settings() {
reachFiveCheckCredentials: {
get: function () {
var prefEnum = currentSite.getCustomPreferenceValue('reachFiveCheckCredentials');
return prefEnum.getValue() || '';
return prefEnum ? prefEnum.getValue() : '';
}
}
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,8 @@ var LOGGER = require('dw/system/Logger').getLogger('loginReachFive');
var StringUtils = require('dw/util/StringUtils');
var URLUtils = require('dw/web/URLUtils');

var reachFiveService = require('*/cartridge/scripts/interfaces/reachFiveInterface');
var ReachfiveSessionModel = require('*/cartridge/models/reachfiveSession');



/**
* @function
* @description Return reach five cookie name
Expand Down Expand Up @@ -266,7 +263,17 @@ function getReachFiveManagementScope() {
* @return {string} Reach Five JSON which contains fields to synchronize and mapping between SFCC and ReachFive profile fields
* */
function getReachFiveProfileFieldsJSON() {
return getReachFivePreferences('reach5ProfileFieldsJSON');
try {
var profileFields = getReachFivePreferences('reach5ProfileFieldsJSON');
if (!profileFields) {
LOGGER.error('Error - "reach5ProfileFieldsJSON" Site Preference is missing');
return new Status(Status.ERROR);
}
return JSON.parse(profileFields);
} catch (e) {
LOGGER.error('Error while parsing site preference "reach5ProfileFieldsJSON": {0}', e);
}
return null;
}

/**
Expand Down Expand Up @@ -453,54 +460,6 @@ function createLoginRedirectUrl(tkn, stateTarget) {
return url;
}

/**
* @function
* @description Checks/updates the current tokens from the session for a 5 minute horizon
* @param {boolean} [updateFlag] is update access token required
* @return {Object} result
* */
function verifySessionAccessTkn(updateFlag) {

var status = {
success: false,
msg: Resource.msg('reachfive.access_tkn.expired', 'reachfive', null)
};

var updateToken = true;
if (typeof updateFlag !== 'undefined') {
updateToken = updateFlag;
}

var reachfiveSession = new ReachfiveSessionModel();

if (reachfiveSession.isAccessToken5MinLimit()) {
status.success = true;
} else if (updateToken) {
if (reachfiveSession.refresh_token) {
var tokenObj = reachFiveService.retrieveAccessTokenWithRefresh(reachfiveSession.refresh_token);

if (tokenObj.ok) {
status.success = true;
reachfiveSession.initialize(tokenObj.object);
} else {
LOGGER.error('Error. Unable to update access_token with refresh_token, error: {0}', tokenObj.errorMessage);
status.msg = Resource.msg('reachfive.server.error', 'reachfive', null);
}
} else {
LOGGER.error('Error. access_token has expired and can not be updated. Check reachfive client preferences scope for "offline_access".');
}
}

return status;
}








/**
* Export modules
* */
Expand Down Expand Up @@ -536,5 +495,4 @@ module.exports.setReachFiveLoginCookie = setReachFiveLoginCookie;
module.exports.getReachFiveUserCustomObjectType = getReachFiveUserCustomObjectType;
module.exports.getStateObjBase64 = getStateObjBase64;
module.exports.createLoginRedirectUrl = createLoginRedirectUrl;
module.exports.verifySessionAccessTkn = verifySessionAccessTkn;
module.exports.isReachFiveEnableKakaoTalkNameSplit = isReachFiveEnableKakaoTalkNameSplit;
9 changes: 9 additions & 0 deletions cartridges/int_reachfive/cartridge/scripts/helpers/utils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
'use strict';

function mergeObjects(obj1, obj2) {
return Object.assign({}, obj1, obj2);
}

module.exports = {
mergeObjects: mergeObjects
};
Loading