forked from ampproject/amphtml
-
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.
✨Create new extension - AMP-smartlinks (ampproject#20967)
* creating new amp-smartlinks extension * BAM-2584 AMP-Smartlinks (ampproject#3) * adding example page and amp-smartlinks to bundles.config * creating amp-smartlinks scaffolding * BAM-2584 adding hardcoded POC implementation of amp-smartlinks * adding new constants file and linkmate file/class * adding linkmate call and association working with link-rewrite service * add amp config call and update workflow to use those values * setup initial amp-smartlinks and linkmate workflow * adding new config variables for link attribute and selector * add tests for amp-smartlinks * add test for linkmate.js * add more thorough anchorList check in runSmartlinks * adding code comments, new constants structure and better options validation * update amp-smartlinks to have helpful information * updating global vars and cleaning up main files * clean up tests and add more explicit type assertions * clean up jsdoc tags * BAM-2585 Whitelist import, fix type errors, and replace user.assert with userAssert (ampproject#4) * BAM-2585 whitelist navigation import and fix type errors * replace user.assert with userAssert * BAM-2585 fix validator and type check (ampproject#5) * more validator fixes (ampproject#6) * BAM-2585 Fix validator, copyright, and whitespace (ampproject#7) * alphabetize validator, fix whitespace, and add valid tag * update year in copyright statement * BAM-2585 move `link-rewriter` to import statements and updating types (ampproject#8) * BAM-2585 remove link-rewriter and switch to importing from skimlinks extension * clean up promise chain and more descriptive API comments * update xhr to pull from ampdoc.win and add types to constants.js * fix type in buildPageImpressionPayload_ * update validator with new empty value check (ampproject#9) * update validator for exclusive-links * switch page-impression API request to customEventReporter (ampproject#11) * BAM-2585 fix jsdoc in linkmate-options and unnecessary param in page_impression request (ampproject#12) * fix jsdoc for linkmate params and make runLinkmate more readable (ampproject#13) * add try/catch on amp_config fetch and updated constants.js * remove bad type in constants.js and add check for existing shop-links (ampproject#14) * add check for auction_id in mapLinks and add jsdoc for SMARTLINKS_REWRITER_ID * fix type notation in constants.js and linkmate-options.js * fix indentation in example file * add note to README describing link-rewriter priority queue behavior (ampproject#15) * add exception to compile.js for amp-skimlinks * update validator to use empty value as indicator linkmate param * fix validator and linkmate-options to use new config style (ampproject#16) * updating tests for linkmate-options.js * remove redundant userAssert in linkmate-options * update tests to reflect config changes * update tests to send accurate config params * update readme to refelct config changes and more accurate function names in linkmate-options
- Loading branch information
1 parent
c268e40
commit 2959313
Showing
17 changed files
with
1,503 additions
and
0 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
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,47 @@ | ||
<!-- | ||
Copyright 2019 The AMP HTML Authors. All Rights Reserved. | ||
Licensed under the Apache License, Version 2.0 (the "License"); | ||
you may not use this file except in compliance with the License. | ||
You may obtain a copy of the License at | ||
http://www.apache.org/licenses/LICENSE-2.0 | ||
Unless required by applicable law or agreed to in writing, software | ||
distributed under the License is distributed on an "AS-IS" BASIS, | ||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
See the License for the specific language governing permissions and | ||
limitations under the license. | ||
--> | ||
<!-- | ||
Test Description: | ||
Valid amp-smartlinks tag | ||
--> | ||
<!doctype html> | ||
<html ⚡> | ||
<head> | ||
<meta charset="utf-8"> | ||
<link rel="canonical" href="./regular-html-version.html"> | ||
<meta name="viewport" content="width=device-width,minimum-scale=1"> | ||
<meta name="amp-link-rewriter-priorities" content="amp-smartlinks"> | ||
<style amp-boilerplate>body{-webkit-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-moz-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-ms-animation:-amp-start 8s steps(1,end) 0s 1 normal both;animation:-amp-start 8s steps(1,end) 0s 1 normal both}@-webkit-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-moz-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-ms-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-o-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}</style><noscript><style amp-boilerplate>body{-webkit-animation:none;-moz-animation:none;-ms-animation:none;animation:none}</style></noscript> | ||
<script async custom-element="amp-smartlinks" src="https://cdn.ampproject.org/v0/amp-smartlinks-0.1.js"></script> | ||
<script async src="https://cdn.ampproject.org/v0.js"></script> | ||
</head> | ||
<body> | ||
<title>AMP Smartlinks</title> | ||
<h1>Hello from Narrativ</h1> | ||
<amp-smartlinks | ||
layout="nodisplay" | ||
nrtv-account-name="amppublisher" | ||
linkmate | ||
link-selector="a"> | ||
</amp-smartlinks> | ||
|
||
<div class="linkmate-eligible-links"> | ||
<a href="https://www.exampleretailer.com/prod1234">Example Retailer</a> | ||
<a href="https://www.exampleretailer.com/prod1234">Duplicate Example Retailer</a> | ||
</div> | ||
|
||
<div class="linkmate-ineligible-links"> | ||
<a href="https://www.badretailer.com/worstProductEver4321">Bad Retailer</a> | ||
</div> | ||
</body> | ||
</html> |
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,4 @@ | ||
- PhilWinchester | ||
- pbecotte | ||
- c-nichols | ||
- zhouyx |
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,220 @@ | ||
/** | ||
* Copyright 2019 The AMP HTML Authors. All Rights Reserved. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS-IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
import {CommonSignals} from '../../../src/common-signals'; | ||
import {CustomEventReporterBuilder} from '../../../src/extension-analytics.js'; | ||
import {Services} from '../../../src/services'; | ||
import {dict} from '../../../src/utils/object'; | ||
import {getData} from './../../../src/event-helper'; | ||
|
||
import {ENDPOINTS} from './constants'; | ||
import {LinkRewriterManager} from | ||
'../../amp-skimlinks/0.1/link-rewriter/link-rewriter-manager'; | ||
import {Linkmate} from './linkmate'; | ||
import {getConfigOptions} from './linkmate-options'; | ||
|
||
const TAG = 'amp-smartlinks'; | ||
|
||
|
||
export class AmpSmartlinks extends AMP.BaseElement { | ||
/** | ||
* @param {!AmpElement} element | ||
*/ | ||
constructor(element) { | ||
super(element); | ||
|
||
/** @private {?../../../src/service/xhr-impl.Xhr} */ | ||
this.xhr_ = null; | ||
|
||
/** @private {?../../../src/service/ampdoc-impl.AmpDoc} */ | ||
this.ampDoc_ = null; | ||
|
||
/** @private {?../../amp-skimlinks/0.1/link-rewriter/link-rewriter-manager.LinkRewriterManager} */ | ||
this.linkRewriterService_ = null; | ||
|
||
/** @private {?../../amp-skimlinks/0.1/link-rewriter/link-rewriter.LinkRewriter} */ | ||
this.smartLinkRewriter_ = null; | ||
|
||
/** | ||
* This will store config attributes from the extension options and an API | ||
* request. The attributes from options are: | ||
* exclusiveLinks, linkAttribute, linkSelector, linkmateEnabled, nrtvSlug | ||
* The attributes from the API are: | ||
* linkmateExpected, publisherID | ||
* @private {?Object} */ | ||
this.linkmateOptions_ = null; | ||
|
||
/** @private {?./linkmate.Linkmate} */ | ||
this.linkmate_ = null; | ||
|
||
/** @private {?string} */ | ||
this.referrer_ = null; | ||
} | ||
|
||
/** @override */ | ||
buildCallback() { | ||
this.ampDoc_ = this.getAmpDoc(); | ||
this.xhr_ = Services.xhrFor(this.ampDoc_.win); | ||
const viewer = Services.viewerForDoc(this.ampDoc_); | ||
|
||
this.linkmateOptions_ = getConfigOptions(this.element); | ||
this.linkRewriterService_ = new LinkRewriterManager(this.ampDoc_); | ||
|
||
return this.ampDoc_.whenBodyAvailable() | ||
.then(() => viewer.getReferrerUrl()) | ||
.then(referrer => { | ||
this.referrer_ = referrer; | ||
viewer.whenFirstVisible().then(() => { | ||
this.runSmartlinks_(); | ||
}); | ||
}); | ||
} | ||
|
||
/** | ||
* Wait for the config promise to resolve and then proceed to functionality | ||
* @private | ||
*/ | ||
runSmartlinks_() { | ||
this.getLinkmateOptions_().then(config => { | ||
this.linkmateOptions_.linkmateExpected = config['linkmate_enabled']; | ||
this.linkmateOptions_.publisherID = config['publisher_id']; | ||
|
||
this.postPageImpression_(); | ||
this.linkmate_ = new Linkmate( | ||
/** @type {!../../../src/service/ampdoc-impl.AmpDoc} */ | ||
(this.ampDoc_), | ||
/** @type {!../../../src/service/xhr-impl.Xhr} */ | ||
(this.xhr_), | ||
/** @type {!Object} */ | ||
(this.linkmateOptions_) | ||
); | ||
this.smartLinkRewriter_ = this.initLinkRewriter_(); | ||
|
||
// If the config specified linkmate to run and our API is expecting | ||
// linkmate to run | ||
if (this.linkmateOptions_.linkmateEnabled && | ||
this.linkmateOptions_.linkmateExpected) { | ||
this.smartLinkRewriter_.getAnchorReplacementList(); | ||
} | ||
}); | ||
} | ||
|
||
/** | ||
* API call to retrieve the Narrativ config for this extension. | ||
* API response will be a list containing nested json values. For the purpose | ||
* of this extension there will only ever be one value in the list: | ||
* {amp_config: {linkmate_enabled: <!boolean>, publisher_id: <!number>}} | ||
* @return {?Promise<!JsonObject>} | ||
* @private | ||
*/ | ||
getLinkmateOptions_() { | ||
const fetchUrl = ENDPOINTS.NRTV_CONFIG_ENDPOINT.replace( | ||
'.nrtv_slug.', this.linkmateOptions_.nrtvSlug | ||
); | ||
|
||
try { | ||
return this.xhr_.fetchJson(fetchUrl, { | ||
method: 'GET', | ||
ampCors: false, | ||
}) | ||
.then(res => res.json()) | ||
.then(res => { | ||
return getData(res)[0]['amp_config']; | ||
}); | ||
} catch (err) { | ||
return null; | ||
} | ||
} | ||
|
||
|
||
/** | ||
* API call to indicate a page load event happened | ||
* @private | ||
*/ | ||
postPageImpression_() { | ||
// When using layout='nodisplay' manually trigger CustomEventReporterBuilder | ||
this.signals().signal(CommonSignals.LOAD_START); | ||
const payload = this.buildPageImpressionPayload_(); | ||
|
||
const builder = new CustomEventReporterBuilder(this.element); | ||
|
||
builder.track('page-impression', ENDPOINTS.PAGE_IMPRESSION_ENDPOINT); | ||
|
||
builder.setTransportConfig(dict({ | ||
'beacon': true, | ||
'image': false, | ||
'xhrpost': true, | ||
'useBody': true, | ||
})); | ||
|
||
builder.setExtraUrlParams(payload); | ||
const reporter = builder.build(); | ||
|
||
reporter.trigger('page-impression'); | ||
} | ||
|
||
/** | ||
* Initialize and register a Narrativ LinkRewriter instance | ||
* @return {!../../amp-skimlinks/0.1/link-rewriter/link-rewriter.LinkRewriter} | ||
* @private | ||
*/ | ||
initLinkRewriter_() { | ||
const options = {linkSelector: this.linkmateOptions_.linkSelector}; | ||
|
||
return this.linkRewriterService_.registerLinkRewriter( | ||
TAG, | ||
anchorList => { | ||
return this.linkmate_.runLinkmate(anchorList); | ||
}, | ||
options | ||
); | ||
} | ||
|
||
/** | ||
* Build the payload for our page load event. | ||
* @return {!JsonObject} | ||
* @private | ||
*/ | ||
buildPageImpressionPayload_() { | ||
return /** @type {!JsonObject} */ (dict({ | ||
'events': [{'is_amp': true}], | ||
'organization_id': this.linkmateOptions_.publisherID, | ||
'organization_type': 'publisher', | ||
'user': { | ||
'page_session_uuid': this.generateUUID_(), | ||
'source_url': this.ampDoc_.getUrl(), | ||
'previous_url': this.referrer_, | ||
'user_agent': this.ampDoc_.win.navigator.userAgent, | ||
}, | ||
})); | ||
} | ||
|
||
/** | ||
* Generate a unique UUID for this session. | ||
* @return {string} | ||
* @private | ||
*/ | ||
generateUUID_() { | ||
return ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, c => | ||
(c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4) | ||
.toString(16) | ||
); | ||
} | ||
} | ||
|
||
AMP.extension('amp-smartlinks', '0.1', AMP => { | ||
AMP.registerElement('amp-smartlinks', AmpSmartlinks); | ||
}); |
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,26 @@ | ||
/** | ||
* Copyright 2019 The AMP HTML Authors. All Rights Reserved. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS-IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
const BASE_API_URL = 'https://api.narrativ.com/api'; | ||
/** @const @enum {string} */ | ||
export const ENDPOINTS = { | ||
PAGE_IMPRESSION_ENDPOINT: | ||
`${BASE_API_URL}/v1/events/impressions/page_impression/`, | ||
NRTV_CONFIG_ENDPOINT: | ||
`${BASE_API_URL}/v0/publishers/.nrtv_slug./amp_config/`, | ||
LINKMATE_ENDPOINT: | ||
`${BASE_API_URL}/v1/publishers/.pub_id./linkmate/smart_links/`, | ||
}; |
Oops, something went wrong.