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

Modularization #458

Merged
merged 97 commits into from
Jun 27, 2024
Merged

Modularization #458

merged 97 commits into from
Jun 27, 2024

Conversation

Rapha0511
Copy link
Contributor

Description

The goal of the modularization is to let you choose precisly what action you want to achieve, and import only the code you need, without unrelated methods.

The main change is the introduction of a new Fetcher class, dedicated to fetching.

This allows:

  • The VAST Parser to be used for parsing only, without importing unnecessary methods related to fetching.
  • The VAST Client to be used for everything that needs to performe fetching AND parsing.

Here are the changes that have been made and that need to be taken in consideration when using the vast-client modularized.

New Private Fetcher Class:

This class includes:

  • A fetchVAST method used to fetch an XML document
  • All methods related to filtering the url before fetching.

All those methods were previously implemented in the VAST Parser

VAST Parser:

All the methods related to fetching have been moved to the new private class (Fetcher), dedicated to it.
The VAST Parser now contains only methods related to parsing.

Now, when using the parseVAST method from the VAST Parser, you will only be able to parse the first VAST encountered.
It is no longer possible to follow a wrappers chain using the parseVAST method ( if you want to do so, you should use the parseVAST method from the VAST Client).

Exemples:

  • When parsing directly an inline with the parseVAST method.

The inline vast will be parsed as before.

  • When parsing a Wrapper with the parseVAST method.

Capture1
In this exemple, only the wrapper1.xml will be parsed.

No fetching will be performed, wrapper2.xml and inline.xml will be ignored because we use the VASTParser alone.

VAST Client:

  • The get method is no longer using the getAndParseVAST method from the VAST Parser that was use to fetch and parse VAST document.
    Instead, it uses the fetchVAST method from the Fetcher and then parse the document with the parse method from VAST Parser.
  • All the methods related to filtering the url before fetching are now accessible using the VAST Client.
  • A parseVAST method has been added in order to directly parse an XML document and follow a wrappers chain if a wrapper is given.

VAST Tracker:

The VAST Tracker remains unchanged, you can still use it as before

Testing:

  • Every methods related to the new Fetcher class is tested in the fetcher.spec.js file, dedicated to it.
  • All tests related to Mocha have been migrated to Jest.
  • Mocha is no longer used in the project.

Bundling:

  • The IIFE format is no longer generated because it is now possible to import ES modules format by specifying the type="module" of your main file.

  • UMD format is no longer generated because it is obsolete, it is preferable to use ES modules

Additional informations:

  • To get the full benefit of those changes, projects using the vast-client must include three shaking.

Issue

The vast-client is divided in three components:

  • The VAST Client to fetch and parse VAST document.
  • The VAST Parser to directly parse VAST document.
  • The VAST Tracker to track the execution of an ad.

The VAST Parser in strongly dependant to the fetching, where is shouldn't.

When using the vast-client, you should be able to choose if you want to perform fetching and parsing or parsing only .

The VAST Parser should be used only if parsing is required.
The VAST Client must be used if fetching and parsing are required

All methods related to fetching should be separated to the VAST Parser since it is not part of the parsing.

Type

  • Breaking change
  • Enhancement
  • Fix
  • Documentation
  • Tooling

Copy link
Contributor

@ZacharieTFR ZacharieTFR left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Huge work ! Thanks ✨
I will do a separate review for the documentation, and a second one with fresh eyes as it contains lots of changes ! 👌

package.json Outdated
@@ -27,12 +33,11 @@
"scripts": {
"performance": "node performance/performance_test.js",
"prebuild": "rm -rf dist_old && mkdir dist_old && cp -a dist/. dist_old/",
"build": "rollup -c",
"build": "rollup -c --watch",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we should have a --watch here, otherwise this will keep the process open and wait for any changes. Maybe having a separate command will be more convenient.

rollup.config.js Outdated
const minifiedConfig = Object.assign({}, config);
minifiedConfig.output = Object.assign({}, config.output);
minifiedConfig.plugins = Object.assign([], config.plugins);
// Node configuration
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can remove this comment and l.54 as they don't add additional informations

rollup.config.js Outdated
outputFile.substr(0, extensionIndex) +
'.min' +
outputFile.substr(extensionIndex);
function createNodeConfig(filePath, minifiedOutput, notMinifiedOutput) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why using a function instead of a const here?

fetcher.URLTemplateFilters = [(url) => url.replace('foo', 'bar')];
const expectedUrl = 'www.bar.foo';
fetcher
.fetchVAST(url, {}, 4, vastParser.emit.bind(vastParser))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of using a real instance of vastParser you can mock it using a spy function as it's only used for the emit here and you don't want to test the whole chain but only how it was called.


it('should updates the estimated bitrate', () => {
jest.spyOn(Bitrate, 'updateEstimatedBitrate');
return fetcher
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return fetcher
fetcher

spyTrack = jest.spyOn(vastTracker, 'trackURLs');
vastTracker.click();
});
it('should have sent clickthrough event with clickThrough url', () => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
it('should have sent clickthrough event with clickThrough url', () => {
it('should have emit clickthrough event with clickThrough url', () => {

});
});

describe('#clickthroughs', () => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
describe('#clickthroughs', () => {
describe('#Linear', () => {

const fallbackClickThroughURL = 'http://example.com/fallback-clickthrough',
clickThroughURL = 'http://example.com/clickthrough';

describe('#VAST clichthrough with no fallback provided', () => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
describe('#VAST clichthrough with no fallback provided', () => {
describe('#VAST clickthrough with no fallback provided', () => {

Comment on lines 759 to 771
beforeEach(() => {
const creative = createCreativeLinear();
creative.videoClickThroughURLTemplate = clickThroughURL;
vastTracker = new VASTTracker(vastClient, {}, creative);
spyEmit = jest.spyOn(vastTracker, 'emit');
vastTracker.click();
});
it('should have sent clickthrough event with VAST clickthrough url', () => {
expect(spyEmit).toHaveBeenCalledWith(
'clickthrough',
'http://example.com/clickthrough'
);
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can be merged together as there is no other assertion in the suite. Same below

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we should regroup all spec files in the same folder WDYT ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the folder hierarchy looks good like that as we have only specs in this forlder and their resources in samples


const xml = new DOMParser().parseFromString('<VAST></VAST>', 'text/xml');
const xml = new DOMParser().parseFromString(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can getNodesFromXml from utils be used here ?

Comment on lines 155 to 156
//initParsingStatus always will be called before parseVastXml
VastParser.initParsingStatus();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is it required ?

Comment on lines 301 to 302
.spyOn(VastParser, 'parse')
.mockImplementation(() => Promise.resolve([linearAd]));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
.spyOn(VastParser, 'parse')
.mockImplementation(() => Promise.resolve([linearAd]));
.spyOn(VastParser, 'parse')
.mockReturnValue(Promise.resolve([linearAd]);

Comment on lines 317 to 318
res = response;
expect(res).toEqual({
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can only use response here

Comment on lines 348 to 351
expect(Bitrate.updateEstimatedBitrate).toHaveBeenCalledWith(
undefined,
undefined
);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should never happen IMO

});
});

it('will successfully fetch the next wrapper url if it is provided', () => {
const adWithWrapper = { ...ad, nextWrapperURL: wrapperBVastUrl };
jest.spyOn(VastParser, 'fetchVAST');

jest.spyOn(fetcher, 'fetchVAST').mockImplementation(() => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use mockReturnValue instead as you don't add logic

README.md Outdated
@@ -47,9 +47,25 @@ vastClient.get('https://www.examplevast.com/vast.xml')

In addition to fetching and parsing a VAST resource, **VASTClient** provides options to filter a sequence of calls based on count and time of execution, together with the possibility to track URLs using **VASTTracker**.

If you need to directy parse a VAST XML and follow a wrappers chain, you can use the **VASTClient** just like so :
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's better to have it next to the get method IMO

Suggested change
If you need to directy parse a VAST XML and follow a wrappers chain, you can use the **VASTClient** just like so :
If you need to directly parse a VAST XML and also follow any wrappers chain, you can use the `parseVAST` method from the **VASTClient** :

README.md Outdated
### VASTParser

To directly parse a VAST XML you can use the **VASTParser**:
The **VASTParser** will not proceed to any fetching, the final response will only contain the first VAST encountered.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
The **VASTParser** will not proceed to any fetching, the final response will only contain the first VAST encountered.
The **VASTParser** will make no fetching, the final response will only contain the first VAST encountered.

README.md Outdated

You can add the script directly to your page and access the library's components through the `VAST` object.
If you want to use this file, add it to your project and make sure that your main file is in type module as below.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should be as explicit as possible, because when you write add it to your project it doesn't explain how to actually add it.

@@ -99,6 +101,74 @@ Instance of a class which implements the `Storage` interface. Should be set up o

## Public Methods 💚 <a name="methods"></a>

### addURLTemplateFilter(filter)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you move thoses methods at the end ? We should keep the most important one at the top

*/
```

### removeURLTemplateFilter()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As it has been replaced with removeLastURLTemplateFilter you need to update it aswell

src/fetcher.js Outdated
Comment on lines 72 to 73
* @emits VASTParser#VAST-resolving
* @emits VASTParser#VAST-resolved
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should keep thoses informations, aswell as Returns a Promise which resolves,rejects according to the result of the request.

ZacharieTFR and others added 2 commits April 25, 2024 11:26
Upgraded fetching method, added error logging, and code Improvment
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Switched back to .then instead of async/await to avoid "no-async-promise-executor" error.
According to ESLint documentation, it is not considered good practice to use an async function as the executor function within a new Promise.

see https://eslint.org/docs/latest/rules/no-async-promise-executor

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we want to use async/await, why not using async/await on fetchVAST? async fetchVast

@Rapha0511 Rapha0511 merged commit 37d5ac4 into master Jun 27, 2024
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

4 participants