# Write a Middleware for Youtube Playlist

This article explains the steps to implement a Middleware for a Youtube Playlist Widget Provider.

Given the following HTML page, we need to integrate the Youtube Playlist into the Marfeel version of the website.

<html>
  <body>
    <h1>Learning more about Middleware</h1>

    <p>
      This tutorial series will teach you everything you'll need to know about Middleware.
    </p>

    <div class="video_playlist">
      <iframe src="https://www.youtube.com/watch?v=2pZmKW9-I_k">
        <a class="video_play" data-video-url="https://www.youtube.com/watch?v=iTZ1-85I77c">
          <img src="https://i.ytimg.com/vi/2pZmKW9-I_k/hqdefault.jpg?sqp=-oaymwEYCKgBEF5IVfKriqkDCwgBFQAAiEIYAXAB&rs=AOn4CLAAHeRygFd0XyAEzHXgh3lu9fj00Q" />
          <h3>Middleware - Part 1</h3>
        </a>

        <a class="video_play" data-video-url="https://www.youtube.com/watch?v=0DzDqtcxnz0">
          <img src="https://i.ytimg.com/vi/2pZmKW9-I_k/hqdefault.jpg?sqp=-oaymwEYCKgBEF5IVfKriqkDCwgBFQAAiEIYAXAB&rs=AOn4CLAAHeRygFd0XyAEzHXgh3lu9fj00Q" />
          <h3>Middleware - Part 2</h3>
        </a>

        <a class="video_play" data-video-url="https://www.youtube.com/watch?v=0DzDqtcxnz0">
          <img src="https://i.ytimg.com/vi/2pZmKW9-I_k/hqdefault.jpg?sqp=-oaymwEYCKgBEF5IVfKriqkDCwgBFQAAiEIYAXAB&rs=AOn4CLAAHeRygFd0XyAEzHXgh3lu9fj00Q" />
          <h3>Middleware - Part 3</h3>
        </a>
      </iframe>
    </div>

  </body>
</html>

# Youtube Playlist Widget Provider

At Marfeel, there is already a Widget Provider for Youtube Playlist Widget Provider (opens new window) available. If we take a look at its schema, we'll see that it has two required properties, videoSrc and videos.

videos has its schema, finding that the expected format is an array of objects containing at least, an id property.

TIP

Schemas are auto-generated and available within the package. Find it within the node_modules folder of the repository you are working on.

The path will be similar to node_modules/@marfeel/widget/schema/index.json.

To send the correct values for these parameters you'll implement a Middleware that uses the OnExtraction hook, the Middleware will retrieve the data from the tenant's page and pass it to the Widget Provider.

The Middleware implementation will receive the tenant's HTML as the document parameter.

# Implementation guide

First, locate where the data you need it is and implement a logic that finds the desired element from the document and processes it to retrieve the information required:

const wrapper = document.querySelector('.video_playlist');

const iframe = wrapper.querySelector('iframe');

const videoSrc = iframe.getAttribute('src');
const videos = [...wrapper.querySelectorAll('a.video_play')]
    .map((element) => {
        const id = element.dataset.videoUrl.replace('https://www.youtube.com/watch?v=', '');
        const imgSrc = element.querySelector('img')?.getAttribute('src');
        const title = element.querySelector('h3')?.textContent;

        return {
            id,
            imgSrc,
            title
        };
    });

In this example we use document.querySelector('.video_playlist') (opens new window) to find the target element. Its src attribute corresponds to the videoSrc parameter.

Then, we find the video elements a.video_play and iterate them with a map function (opens new window) where we use the replace string manipulation method (opens new window) to retrieve the desired id. Each video image source is extracted from the img element and the title from the h3 of each video element.

Once all required values are retrieved, you need to return an object that matches the signature of YoutubePlaylistProps (opens new window). In this case, an object with the videoSrc and an array of videos and their parameters.

// on-extraction.ts

import { YoutubePlaylistProps } from '@marfeel/widget-providers-youtube-playlist';
import { onExtractionFunction } from '@marfeel/middlewares-types';

export const onExtraction: onExtractionFunction<YoutubePlaylistProps> = async ({ document }): Promise<YoutubePlaylistProps | undefined> => {
    const wrapper = document.querySelector('.video_playlist');

    if (!wrapper) {
        return;
    }

    const iframe = wrapper.querySelector('iframe');

    if (!iframe) {
        return;
    }

    const videoSrc = iframe.getAttribute('src');
    const videos = [...wrapper.querySelectorAll('a.video_play')]
        .map((element) => {
            const id = element.dataset.videoUrl.replace('https://www.youtube.com/watch?v=', '');
            const imgSrc = element.querySelector('img')?.getAttribute('src');
            const title = element.querySelector('h3')?.textContent;

            return {
                id,
                imgSrc,
                title
            };
        });

    return {
        videoSrc,
        videos
    };
};

# Create the test

All providers require an associated test, and all Middleware tests are very alike.

Read more on how to create tests for Middleware.