Skip to content

Commit

Permalink
Adding TemplateValidator.
Browse files Browse the repository at this point in the history
  • Loading branch information
glevitzky committed Apr 12, 2018
1 parent 839ec0e commit c4df229
Show file tree
Hide file tree
Showing 7 changed files with 155 additions and 19 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ const TEMPLATE_CORS_CONFIG = {
credentials: 'omit',
};

export class AmpAdTemplates {
export class AmpAdTemplateHelper {

/**
* @param {!Window} win
Expand Down
3 changes: 1 addition & 2 deletions extensions/amp-a4a/0.1/cryptographic-validator.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,14 @@ import {
} from './amp-ad-type-defs';
import {SignatureVerifier, VerificationStatus} from './signature-verifier';
import {getAmpAdMetadata} from './amp-ad-utils';
import {getDefaultBootstrapBaseUrl} from '../../../src/3p-frame';
import {signingServerURLs} from '../../../ads/_a4a-config';
import {user} from '../../../src/log';
import {utf8Decode} from '../../../src/utils/bytes';

export const SIGNATURE_VERIFIER_PROPERTY_NAME =
'AMP_FAST_FETCH_SIGNATURE_VERIFIER_';

const TAG = 'amp-ad-render';
const TAG = 'amp-ad-cryptographic-validator';

export class CryptographicValidator extends Validator {
/** @param {!Window} win */
Expand Down
106 changes: 106 additions & 0 deletions extensions/amp-a4a/0.1/template-validator.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
/**
* Copyright 2018 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 {AmpAdTemplateHelper} from '../../amp-a4a/0.1/amp-ad-template-helper';
import {Services} from '../../../src/services';
import {Validator, ValidatorResult} from './amp-ad-type-defs';
import {pushIfNotExist} from '../../../src/utils/array';
import {tryParseJson} from '../../../src/json';
import {utf8Decode} from '../../../src/utils/bytes';

/** @const {string} */
export const AMP_TEMPLATED_CREATIVE_HEADER_NAME = 'AMP-template-amp-creative';

/**
* Validator for Template ads.
*/
export class TemplateValidator extends Validator {

constructor() {
super();

/** @private {?AmpAdTemplateHelper} */
this.ampAdTemplateHelper_ = null;
}

/**
* @param {string} templateString
* @param {!./amp-ad-type-defs.AmpTemplateCreativeDef} parsedResponseBody
* @return {!./amp-ad-type-defs.CreativeMetaDataDef}
* @private
*/
getAmpAdMetadata_(templateString, parsedResponseBody) {
// TODO(levitzky) The following minification is for demo purposes only. Once
// launched this will either be performed server-side, or will be replaced
// by more sophisticated logic.
const minifiedCreative = templateString.replace(
/<script async.+?<\/script>/g, '');
const metadata = /** @type {!./amp-ad-type-defs.CreativeMetaDataDef} */ ({
minifiedCreative,
customElementExtensions: [],
extensions: [],
});
if (parsedResponseBody.analytics) {
pushIfNotExist(metadata['customElementExtensions'], 'amp-analytics');
}
pushIfNotExist(metadata['customElementExtensions'], 'amp-mustache');
return metadata;
}

/**
* @param {!./amp-ad-type-defs.CreativeMetaDataDef} metadata
* @param {!Window} win
* @private
*/
processMetadata_(metadata, win) {
const extensions = Services.extensionsFor(win);
metadata.customElementExtensions.forEach(
extensionId => extensions./*OK*/preloadExtension(extensionId));
// TODO(levitzky) Add preload logic for fonts / images.
}

/** @override */
validate(context, unvalidatedBytes, headers) {
const creativeData = {};
const body = utf8Decode(/** @type {!ArrayBuffer} */ (unvalidatedBytes));
if (!headers ||
headers.get(AMP_TEMPLATED_CREATIVE_HEADER_NAME) !== 'amp-mustache') {
creativeData['creative'] = body;
return Promise.resolve(
/** @type {!./amp-ad-type-defs.ValidatorOutput} */ ({
creativeData,
adResponseType: 'template',
type: ValidatorResult.NON_AMP,
}));
}

const parsedResponseBody =
/** @type {!./amp-ad-type-defs.AmpTemplateCreativeDef} */ (
tryParseJson(body) || {});
this.ampAdTemplateHelper_ = this.ampAdTemplateHelper_ ||
new AmpAdTemplateHelper(context.win);
return this.ampAdTemplateHelper_
.fetch(parsedResponseBody.templateUrl)
.then(template => {
const creativeMetadata =
this.getAmpAdMetadata_(template, parsedResponseBody);
this.processMetadata_(creativeMetadata, context.win);
creativeData.templateData = parsedResponseBody;
creativeData.creativeMetadata = creativeMetadata;
return {creativeData, type: ValidatorResult.AMP};
});
}
}
19 changes: 10 additions & 9 deletions extensions/amp-a4a/0.1/test/test-amp-ad-templates.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,27 +14,27 @@
* limitations under the License.
*/

import {AmpAdTemplates} from '../amp-ad-templates';
import {AmpAdTemplateHelper} from '../amp-ad-template-helper';
import {AmpMustache} from '../../../amp-mustache/0.1/amp-mustache';
import {Xhr} from '../../../../src/service/xhr-impl';


describes.fakeWin('amp-ad-templates', {amp: true}, env => {
describes.fakeWin('AmpAdTemplateHelper', {amp: true}, env => {

const cdnUrl = 'https://adserver-com.cdn.ampproject.org/ad/s/' +
'adserver.com/amp_template_1';
const canonicalUrl = 'https://adserver.com/amp_template_1';

let win, doc;
let fetchTextMock;
let ampAdTemplates;
let ampAdTemplateHelper;

beforeEach(() => {
win = env.win;
win.AMP_MODE = {localDev: false};
doc = win.document;
fetchTextMock = sandbox.stub(Xhr.prototype, 'fetchText');
ampAdTemplates = new AmpAdTemplates(win);
ampAdTemplateHelper = new AmpAdTemplateHelper(win);
});

it('should return a promise resolving to a string template', () => {
Expand All @@ -52,16 +52,17 @@ describes.fakeWin('amp-ad-templates', {amp: true}, env => {
headers: {},
text: () => template,
}));
return ampAdTemplates.fetch(canonicalUrl)
return ampAdTemplateHelper.fetch(canonicalUrl)
.then(fetchedTemplate => expect(fetchedTemplate).to.equal(template));
});

it('should use CDN url if one is supplied', () => {
expect(ampAdTemplates.getTemplateProxyUrl_(cdnUrl)).to.equal(cdnUrl);
expect(ampAdTemplateHelper.getTemplateProxyUrl_(cdnUrl)).to.equal(cdnUrl);
});

it('should convert canonical to CDN', () => {
expect(ampAdTemplates.getTemplateProxyUrl_(canonicalUrl)).to.equal(cdnUrl);
expect(ampAdTemplateHelper.getTemplateProxyUrl_(canonicalUrl))
.to.equal(cdnUrl);
});

it('should render a template with correct values', () => {
Expand All @@ -74,7 +75,7 @@ describes.fakeWin('amp-ad-templates', {amp: true}, env => {
parentDiv./*OK*/innerHTML =
'<template type="amp-mustache"><p>{{foo}}</p></template>';
doc.body.appendChild(parentDiv);
return ampAdTemplates.render({foo: 'bar'}, parentDiv).then(result => {
return ampAdTemplateHelper.render({foo: 'bar'}, parentDiv).then(result => {
expect(result).to.not.be.null;
expect(result./*OK*/innerHTML).to.equal('bar');
});
Expand All @@ -93,7 +94,7 @@ describes.fakeWin('amp-ad-templates', {amp: true}, env => {
}, {
'type': 'googleanalytics',
}];
ampAdTemplates.insertAnalytics(parentDiv, analytics);
ampAdTemplateHelper.insertAnalytics(parentDiv, analytics);
expect(parentDiv.childNodes.length).to.equal(3);
expect(parentDiv.innerHTML).to.equal('<p>123</p>' +
'<amp-analytics config="remoteUrl">' +
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import {
CreativeMetaDataDef,
NO_CONTENT_RESPONSE,
} from '../../amp-a4a/0.1/amp-a4a';
import {AmpAdTemplates} from '../../amp-a4a/0.1/amp-ad-templates';
import {AmpAdTemplateHelper} from '../../amp-a4a/0.1/amp-ad-template-helper';
import {dev} from '../../../src/log';
import {getMode} from '../../../src/mode';
import {tryParseJson} from '../../../src/json';
Expand All @@ -37,8 +37,8 @@ export const AMP_TEMPLATED_CREATIVE_HEADER_NAME = 'AMP-template-amp-creative';
}} */
let AmpTemplateCreativeDef;

/** @private {?AmpAdTemplates} */
let ampAdTemplates;
/** @private {?AmpAdTemplateHelper} */
let ampAdTemplateHelper;

/**
* Fast Fetch implementation for AdZerk network that allows AMP creative
Expand Down Expand Up @@ -72,7 +72,8 @@ export class AmpAdNetworkAdzerkImpl extends AmpA4A {
/** @private {?AmpTemplateCreativeDef} */
this.ampCreativeJson_ = null;

ampAdTemplates = ampAdTemplates || new AmpAdTemplates(this.win);
ampAdTemplateHelper = ampAdTemplateHelper ||
new AmpAdTemplateHelper(this.win);
}

/**
Expand Down Expand Up @@ -114,7 +115,7 @@ export class AmpAdNetworkAdzerkImpl extends AmpA4A {
this.ampCreativeJson_ = /** @type {!AmpTemplateCreativeDef} */
(tryParseJson(body) || {});
// TODO(keithwrightbos): macro value validation? E.g. http invalid?
return ampAdTemplates
return ampAdTemplateHelper
.fetch(this.ampCreativeJson_.templateUrl)
.then(parsedTemplate => {
return utf8Encode(this.parseMetadataFromCreative(parsedTemplate));
Expand Down Expand Up @@ -169,12 +170,12 @@ export class AmpAdNetworkAdzerkImpl extends AmpA4A {
/** @override */
onCreativeRender(unusedMetadata) {
if (this.ampCreativeJson_ && this.ampCreativeJson_.data) {
ampAdTemplates.render(
ampAdTemplateHelper.render(
this.ampCreativeJson_.data,
this.iframe.contentWindow.document.body)
.then(renderedElement => {
if (this.ampCreativeJson_.analytics) {
ampAdTemplates.insertAnalytics(
ampAdTemplateHelper.insertAnalytics(
renderedElement, this.ampCreativeJson_.analytics);
}
this.iframe.contentWindow.document.body./*OK*/innerHTML =
Expand Down
13 changes: 13 additions & 0 deletions src/utils/array.js
Original file line number Diff line number Diff line change
Expand Up @@ -77,3 +77,16 @@ export function fromIterator(iterator) {
}
return array;
}

/**
* Adds item to array if it is not already present.
*
* @param {Array<T>} array
* @param {T} item
* @template T
*/
export function pushIfNotExist(array, item) {
if (array.indexOf(item) < 0) {
array.push(item);
}
}
16 changes: 16 additions & 0 deletions test/functional/utils/test-array.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
filterSplice,
findIndex,
fromIterator,
pushIfNotExist,
} from '../../../src/utils/array';

describe('filterSplice', function() {
Expand Down Expand Up @@ -98,3 +99,18 @@ describe('fromIterator', function() {
expect(fromIterator(iterator)).to.deep.equal([0, 2, 4]);
});
});

describe('pushIfNotExist', () => {
it('should push element', () => {
const array = [1, 2, 3, 4];
pushIfNotExist(array, 5);
expect(array).to.deep.equal([1, 2, 3, 4, 5]);
pushIfNotExist(array, 2.3);
expect(array).to.deep.equal([1, 2, 3, 4, 5, 2.3]);
});
it('should not push element', () => {
const array = [1, 2, 3, 4];
pushIfNotExist(array, 1);
expect(array).to.deep.equal([1, 2, 3, 4]);
});
});

0 comments on commit c4df229

Please sign in to comment.