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

[DOCS]Improve videos page with tagging capability #9882

Merged
merged 3 commits into from
Oct 19, 2023
Merged
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
51 changes: 50 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,56 @@ Take a look at this blog for reference - (Apache Hudi vs Delta Lake vs Apache Ic
For example if the article is https://www.uber.com/blog/cost-efficiency-big-data/ we would use `uber` as the tag here.
Another example - for https://robinhood.engineering/author-balaji-varadarajan-e3f496815ebf we would use
`robinhood` as the tag. For blogs directly contributed to hudi repo, we can use `apache hudi` as the tag.


## Video Guides

When adding a new video guide, please follow these guidelines.

1. Every video guide should have the `title`, `last_modified_at`, `authors`, `image`, `navigate`, `tags` in the metadata
of the video guide. For example the front matter for a video guide should look like below.
```
---
title: "Video guide title"
last_modified_at: <Time in this format - 2023-10-13T16:54:38.964863-07:00>
authors:
- name: FirstName LastName
- name: FirstName LastName
category: blog
image: /assets/images/video_blogs/<image_file>
navigate: "<youtube/any other video link>"
tags:
- guide
- hands-on
- csv
- aws glue
- apache hudi
---
```
2. The video guide should be named as a `<yyyy-mm-dd>-<Video Guide Title>.md` file where date part of the file name
represents date published.
3. The image must be uploaded in the path /assets/images/video_blogs/<image_file-name> and should be of standard size
1200 * 600. Its easy to use the same name as the video guide for the image as well - `<yyyy-mm-dd>-<Video Guide Title>.png`
If there is no thumbnail or cover image stick to the default image - `/assets/images/hudi-video-page-default.png`
4. The navigate field represents the actual link to video.
5. The tags should be representative of Hudi component/feature focussed in the guide. The tags can refer to services or
techniques used in the guide. Stick to 5 - 7 tags at the max.
1. tag 1
- code-walkthrough
- guide (represents hands-on labs, labs)
2. tag 2
Can be the use case or functionality achieved. Example - de-duplication. This can be skipped if the guide is in
general talking about ingestion using different sources and sinks.
3. tag 3
- Represent individual Hudi features/components - clustering, compaction, ingestion, meta-sync etc.
4. tag 4
- List of technologies used in the guide. This should be an inclusive list. Qualify names fully here. For example
prefer to "amazon athena" instead of "athena". This helps in discoverability of the guides.
5. tag N
- [beginner, intermediate, advanced]. Use this tag if its clear on what level this guide targets. Else feel
free to skip this tag.
6. Ensure that tags are consistent. When adding new tags refer [Blog Tags](https://hudi.apache.org/blog/tags) and
[Video Guide Tags](https://hudi.apache.org/videos/tags) to check if there is a tag already and prefer to use that.

## Maintainer

Apache Hudi Community
20 changes: 20 additions & 0 deletions website/docusaurus.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,12 @@ module.exports = {
},
},
plugins: [
['@docusaurus/plugin-content-blog', {
id: 'video-blog',
path: 'videoBlog',
routeBasePath: 'videos',
blogSidebarCount: 0,
}],
[
'@docusaurus/plugin-content-docs',
{
Expand Down Expand Up @@ -446,6 +452,20 @@ module.exports = {
defaultMode: 'light',
disableSwitch: true,
},
blog: {
path: 'blog', // Path to the existing blog folder
routeBasePath: 'blog', // Route for the existing blog
include: ['*.md', '*.mdx'], // File types to include for the existing blog

// Add the new blog for videos
videoBlog: {
path: 'video-blog', // Path to the video blog folder
routeBasePath: 'videos', // Route for the video blog
include: ['*.md', '*.mdx'], // File types to include for the video blog
videoBlogRoute: '/videos'
// Add any other specific settings for the video blog
},
},
},
presets: [
[
Expand Down
299 changes: 0 additions & 299 deletions website/src/pages/videos.md

This file was deleted.

35 changes: 29 additions & 6 deletions website/src/theme/BlogPostItem/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import EditThisPage from '@theme/EditThisPage';
import styles from './styles.module.css';
import Tag from '@theme/Tag';
import AuthorName from "@site/src/components/AuthorName";
import { useLocation } from 'react-router-dom';
import classNames from "classnames";

function useReadingTimePlural() {
const {selectMessage} = usePluralForm();
Expand All @@ -40,6 +42,7 @@ function useReadingTimePlural() {

function BlogPostItem(props) {
const readingTimePlural = useReadingTimePlural();
const location = useLocation();
const { withBaseUrl } = useBaseUrlUtils();

const {
Expand All @@ -64,6 +67,12 @@ function BlogPostItem(props) {
const image = assets.image ?? frontMatter.image ?? '/assets/images/hudi-logo-medium.png';
const tagsExists = tags.length > 0;

const manageVideoOpen = (videoLink) => {
if(videoLink) {
window.open(videoLink, '_blank', 'noopener noreferrer');
}
}

const tagsList = () => {
return (
<>
Expand Down Expand Up @@ -119,28 +128,42 @@ function BlogPostItem(props) {
<div>
{!isBlogPostPage && image && (
<div className="col blogThumbnail" itemProp="blogThumbnail">
<Link itemProp="url" to={permalink}>
{
location.pathname === '/blog' ? <Link itemProp="url" to={permalink}>
<img
src={withBaseUrl(image, {
absolute: true,
})}
className="blog-image"
/>
</Link>
</Link> :
<img onClick={() => manageVideoOpen(frontMatter?.navigate)}
src={withBaseUrl(image, {
absolute: true,
})}
className={classNames(styles.videoImage, 'blog-image')}
/>
}

</div>
)}
<TitleHeading className={styles.blogPostTitle} itemProp="headline">
{isBlogPostPage ? (
<TitleHeading className={styles.blogPostPageTitle} itemProp="headline">
<TitleHeading className={styles.blogPostPageTitle} itemProp="headline">
{title}
</TitleHeading>
) : (
location.pathname === '/blog' ?
<Link itemProp="url" to={permalink}>
<TitleHeading className={styles.blogPostTitle} itemProp="headline">
{title}
</TitleHeading>
</Link>

:
<TitleHeading onClick={() => manageVideoOpen(frontMatter?.navigate)}
className={styles.blogPostTitle} itemProp="headline">
{title}
</TitleHeading>
)}
</TitleHeading>
<div className={clsx(styles.blogInfo, "margin-top--sm margin-bottom--sm")}>
Expand Down Expand Up @@ -179,14 +202,14 @@ function BlogPostItem(props) {
)}

{(tagsExists || truncated) && isBlogPostPage && editUrl && (
<footer
<footer0
className={clsx('row docusaurus-mt-lg', {
[styles.blogPostDetailsFull]: isBlogPostPage,
})}>
<div className="col margin-top--sm">
<EditThisPage editUrl={editUrl}/>
</div>
</footer>
</footer0>
)}
</article>
);
Expand Down
5 changes: 5 additions & 0 deletions website/src/theme/BlogPostItem/styles.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@

.blogPostTitle {
font-size: 1rem;
color: #0db1f9;
}

.blogPostTitle:hover, .videoImage {
cursor: pointer;
}

.blogPostPageTitle {
Expand Down
132 changes: 132 additions & 0 deletions website/src/theme/LayoutHead/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import React from 'react';
import Head from '@docusaurus/Head';
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
import useBaseUrl from '@docusaurus/useBaseUrl';
import SearchMetadata from '@theme/SearchMetadata';
import Seo from '@theme/Seo';
import {
DEFAULT_SEARCH_TAG,
useTitleFormatter,
useAlternatePageUtils,
useThemeConfig,
} from '@docusaurus/theme-common';
import {useLocation} from '@docusaurus/router'; // Useful for SEO
// See https://developers.google.com/search/docs/advanced/crawling/localized-versions
// See https://github.com/facebook/docusaurus/issues/3317

function AlternateLangHeaders() {
const {
i18n: {defaultLocale, locales},
} = useDocusaurusContext();
const alternatePageUtils = useAlternatePageUtils(); // Note: it is fine to use both "x-default" and "en" to target the same url
// See https://www.searchviu.com/en/multiple-hreflang-tags-one-url/

return (
<Head>
{locales.map((locale) => (
<link
key={locale}
rel="alternate"
href={alternatePageUtils.createUrl({
locale,
fullyQualified: true,
})}
hrefLang={locale}
/>
))}
<link
rel="alternate"
href={alternatePageUtils.createUrl({
locale: defaultLocale,
fullyQualified: true,
})}
hrefLang="x-default"
/>
</Head>
);
} // Default canonical url inferred from current page location pathname

function useDefaultCanonicalUrl() {
const {
siteConfig: {url: siteUrl},
} = useDocusaurusContext();
const {pathname} = useLocation();
return siteUrl + useBaseUrl(pathname);
}

function CanonicalUrlHeaders({permalink}) {
const {
siteConfig: {url: siteUrl},
} = useDocusaurusContext();
const defaultCanonicalUrl = useDefaultCanonicalUrl();
const canonicalUrl = permalink
? `${siteUrl}${permalink}`
: defaultCanonicalUrl;
return (
<Head>
<meta property="og:url" content={canonicalUrl} />
<link rel="canonical" href={canonicalUrl} />
</Head>
);
}

export default function LayoutHead(props) {
const {
siteConfig: {favicon},
i18n: {currentLocale, localeConfigs},
} = useDocusaurusContext();
const {metadata, image: defaultImage} = useThemeConfig();
const {title, description, image, keywords, searchMetadata} = props;
const faviconUrl = useBaseUrl(favicon);
const pageTitle = useTitleFormatter(title); // See https://github.com/facebook/docusaurus/issues/3317#issuecomment-754661855
// const htmlLang = currentLocale.split('-')[0];

const htmlLang = currentLocale; // should we allow the user to override htmlLang with localeConfig?

const htmlDir = localeConfigs[currentLocale].direction;
return (
<>
<Head>
<html lang={htmlLang} dir={htmlDir} />
{favicon && <link rel="icon" href={faviconUrl} />}
<title>{pageTitle}</title>
<meta property="og:title" content={pageTitle} />
<meta name="twitter:card" content="summary_large_image" />
</Head>

{/* image can override the default image */}
{defaultImage && <Seo image={defaultImage} />}
{image && <Seo image={image} />}

<Seo description={description} keywords={keywords} />

<CanonicalUrlHeaders />

<AlternateLangHeaders />

<SearchMetadata
tag={DEFAULT_SEARCH_TAG}
locale={currentLocale}
{...searchMetadata}
/>

<Head // it's important to have an additional <Head> element here,
// as it allows react-helmet to override values set in previous <Head>
// ie we can override default metadata such as "twitter:card"
// In same Head, the same meta would appear twice instead of overriding
// See react-helmet doc
>
{/* Yes, "metadatum" is the grammatically correct term */}
{metadata.map((metadatum, i) => (
<meta key={`metadata_${i}`} {...metadatum} />
))}
</Head>
</>
);
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
title: "Insert | Update | Delete On Datalake (S3) with Apache Hudi and glue Pyspark"
last_modified_at: 2023-10-13T16:54:38.964863-07:00
authors:
- name: Soumil Shah
category: blog
image: /assets/images/hudi-video-page-default.png
navigate: "https://youtu.be/94DPKkzDm-8"
tags:
- guide
- apache hudi
---
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
title: "Build a Spark pipeline to analyze streaming data using AWS Glue, Apache Hudi, S3 and Athena"
last_modified_at: 2023-10-13T16:54:38.964863-07:00
authors:
- name: Soumil Shah
category: blog
image: /assets/images/hudi-video-page-default.png
navigate: "https://youtu.be/uJI6B4MPmoM"
tags:
- guide
- apache hudi
---
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
title: "Different table types in Apache Hudi | MOR and COW | Deep Dive | By Sivabalan Narayanan"
last_modified_at: 2023-10-13T16:54:38.964863-07:00
authors:
- name: Soumil Shah
category: blog
image: /assets/images/hudi-video-page-default.png
navigate: "https://youtu.be/vyEvlt57L-s"
tags:
- guide
- apache hudi
---
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
title: "Simple 5 Steps Guide to get started with Apache Hudi and Glue 4.0 and query the data using Athena"
last_modified_at: 2023-10-13T16:54:38.964863-07:00
authors:
- name: Soumil Shah
category: blog
image: /assets/images/hudi-video-page-default.png
navigate: "https://youtu.be/z9rFyNOE82o"
tags:
- guide
- apache hudi
---
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
title: "Build Datalakes on S3 with Apache HUDI in a easy way for Beginners with hands on labs | Glue"
last_modified_at: 2023-10-13T16:54:38.964863-07:00
authors:
- name: Soumil Shah
category: blog
image: /assets/images/hudi-video-page-default.png
navigate: "https://youtu.be/5zF4jc_3rFs"
tags:
- guide
- apache hudi
---
Loading