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

feat: use meta.json to request smarter & fix wrong initialization timings of features #717

Merged
merged 6 commits into from
Aug 23, 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
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
"dom-loaded": "^3.0.0",
"echarts": "^5.3.0",
"element-ready": "^6.2.1",
"github-url-detection": "^6.1.0",
"github-url-detection": "^8.1.0",
"jquery": "^3.6.0",
"lodash-es": "^4.17.21",
"office-ui-fabric-react": "^7.183.0",
Expand Down
78 changes: 78 additions & 0 deletions src/api/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,81 @@ export const getMetricByName = async (
}
}
};

/**
* Common interface for both repo meta and user meta
* e.g. https://oss.x-lab.info/open_digger/github/X-lab2017/open-digger/meta.json (repo meta file)
* e.g. https://oss.x-lab.info/open_digger/github/tyn1998/meta.json (user meta file)
* @param name repo name or user name
*/
export interface CommonMeta {
type: 'user' | 'repo';
updatedAt: number; // time stamp
labels: unknown[]; // TODO: define the type
}

export interface RepoMeta extends CommonMeta {}

export interface UserMeta extends CommonMeta {
repos: unknown[];
}

class MetaStore {
private static instance: MetaStore;
private responseCache: Map<string, Promise<Response>>;
private constructor() {
this.responseCache = new Map<string, Promise<Response>>();
}

public static getInstance(): MetaStore {
if (!MetaStore.instance) {
MetaStore.instance = new MetaStore();
}
return MetaStore.instance;
}

/**
* Fetch the meta file and cache the response
* @param name repo name or user name
*/
private fetchMeta(name: string) {
const url = `${OSS_XLAB_ENDPOINT}/open_digger/github/${name}/meta.json`;
const promise = fetch(url);
this.responseCache.set(name, promise);
}

/**
* Check if the meta file exists
* @param name repo name or user name
* @returns true if the meta file exists, false otherwise
*/
public async has(name: string) {
if (!this.responseCache.has(name)) {
this.fetchMeta(name);
}
const response = await this.responseCache.get(name)!;
if (!response.ok) {
return false;
} else {
return true;
}
}

/**
* Get the parsed meta file if it exists
* @param name repo name or user name
* @returns the parsed meta file if it exists, undefined otherwise
*/
public async get(name: string): Promise<CommonMeta | undefined> {
if (await this.has(name)) {
const meta: CommonMeta = await this.responseCache
.get(name)!
// clone the response to avoid the response being used up
// https://stackoverflow.com/a/54115314/10369621
.then((res) => res.clone().json());
return meta;
}
}
}

export const metaStore = MetaStore.getInstance();
2 changes: 1 addition & 1 deletion src/feature-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ const getFeatureID = (url: string): FeatureId => {
/** Register a new feature */
const add = async (
id: FeatureId,
...loaders: FeatureLoader[]
...loaders: FeatureLoader[] // support multiple loaders for one feature, but currently only one is used
): Promise<void> => {
/* Feature filtering and running */
const options = await globalReady;
Expand Down
24 changes: 16 additions & 8 deletions src/helpers/generate-data-by-month.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
const generateDataByMonth = (originalData: any) => {
/**
* Months with value of 0 are not listed in data file for size optimization
* purpose, this function inserts those missing zeros.
* @param originalData
* @param updatedAt meta file last updated time
* @returns
*/
const generateDataByMonth = (originalData: any, updatedAt?: number) => {
if (originalData === null) {
return [];
}
Expand All @@ -19,15 +26,16 @@ const generateDataByMonth = (originalData: any) => {
else if (dateA > dateB) return 1;
else return 0;
});

// get the last month that has data
const lastDataAvailableMonth = updatedAt ? new Date(updatedAt) : new Date();
lastDataAvailableMonth.setDate(0);

const oldestMonth = orderedMonths[0];
const now = new Date();
if (now.getDate() === 1) {
// data for last month is not ready in the first day of the month (#595)
now.setDate(0); // a way to let month - 1
}
now.setDate(0); // see issue #632
const newestMonth =
now.getFullYear() + '-' + (now.getMonth() + 1).toString().padStart(2, '0');
lastDataAvailableMonth.getFullYear() +
'-' +
(lastDataAvailableMonth.getMonth() + 1).toString().padStart(2, '0');
// insert no-event months (assigned to 0) and generate final data
const arrayData: [string, number][] = [];
const start = new Date(oldestMonth);
Expand Down
9 changes: 9 additions & 0 deletions src/helpers/get-developer-info.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
import { metaStore } from '../api/common';

import $ from 'jquery';
import * as pageDetect from 'github-url-detection';

export function getDeveloperName() {
return $('.p-nickname.vcard-username.d-block').text().trim().split(' ')[0];
}

export async function isDeveloperWithMeta() {
return (
pageDetect.isUserProfile() && (await metaStore.has(getDeveloperName()))
);
}
28 changes: 28 additions & 0 deletions src/helpers/get-repo-info.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,33 @@
import { metaStore } from '../api/common';

import $ from 'jquery';
import * as pageDetect from 'github-url-detection';
import elementReady from 'element-ready';

export function getRepoName() {
return pageDetect.utils.getRepositoryInfo(window.location)!.nameWithOwner;
}

export function hasRepoContainerHeader() {
const headerElement = $('#repository-container-header');
return headerElement && !headerElement.attr('hidden');
}

export async function isRepoRoot() {
return pageDetect.isRepoRoot();
}

/**
* check if the repository is public
*/
export async function isPublicRepo() {
const selector = 'meta[name="octolytics-dimension-repository_public"]';
await elementReady(selector);
// <meta name="octolytics-dimension-repository_public" content="true/false">
const isPublic = $(selector).attr('content') === 'true';
return pageDetect.isRepo() && isPublic;
}
Comment on lines +10 to +29
Copy link
Member Author

Choose a reason for hiding this comment

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

This three checkers will work in both the old and the new UI.


export async function isPublicRepoWithMeta() {
return (await isPublicRepo()) && (await metaStore.has(getRepoName()));
}
5 changes: 3 additions & 2 deletions src/helpers/is-perceptor.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
const isPerceptor = (): boolean =>
window.location.search.includes('?redirect=perceptor');
const isPerceptor = (): boolean => {
return window.location.search.includes('?redirect=perceptor');
};

export default isPerceptor;
17 changes: 0 additions & 17 deletions src/helpers/is-public-repo.ts

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,25 +1,33 @@
import React from 'react';
import { render, Container } from 'react-dom';
import $ from 'jquery';
import * as pageDetect from 'github-url-detection';

import features from '../../../../feature-manager';
import { getDeveloperName } from '../../../../helpers/get-developer-info';
import {
getDeveloperName,
isDeveloperWithMeta,
} from '../../../../helpers/get-developer-info';
import { getActivity, getOpenrank } from '../../../../api/developer';
import { UserMeta, metaStore } from '../../../../api/common';
import View from './view';

const featureId = features.getFeatureID(import.meta.url);
let developerName: string;
let activity: any;
let openrank: any;
let meta: UserMeta;

const getData = async () => {
activity = await getActivity(developerName);
openrank = await getOpenrank(developerName);
meta = (await metaStore.get(developerName)) as UserMeta;
};

const renderTo = (container: Container) => {
render(<View activity={activity} openrank={openrank} />, container);
render(
<View activity={activity} openrank={openrank} meta={meta} />,
container
);
};

const init = async (): Promise<void> => {
Expand All @@ -45,10 +53,9 @@ const restore = async () => {
}
renderTo($(`#${featureId}`)[0]);
};

features.add(featureId, {
include: [pageDetect.isUserProfile],
awaitDomReady: true,
asLongAs: [isDeveloperWithMeta],
awaitDomReady: false,
init,
restore,
});
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,24 @@ import optionsStorage, {
defaults,
} from '../../../../options-storage';
import Bars from '../../../../components/Bars';
import { UserMeta } from '../../../../api/common';

const githubTheme = getGithubTheme();

const generateBarsData = (activity: any, openrank: any) => {
const generateBarsData = (activity: any, openrank: any, updatedAt: number) => {
return {
data1: generateDataByMonth(activity),
data2: generateDataByMonth(openrank),
data1: generateDataByMonth(activity, updatedAt),
data2: generateDataByMonth(openrank, updatedAt),
};
};

interface Props {
activity: any;
openrank: any;
meta: UserMeta;
}

const View = ({ activity, openrank }: Props): JSX.Element | null => {
const View = ({ activity, openrank, meta }: Props): JSX.Element | null => {
const [options, setOptions] = useState<HypercrxOptions>(defaults);

useEffect(() => {
Expand All @@ -34,7 +36,7 @@ const View = ({ activity, openrank }: Props): JSX.Element | null => {

if (!activity || !openrank) return null;

let barsData: any = generateBarsData(activity, openrank);
let barsData: any = generateBarsData(activity, openrank, meta.updatedAt);

return (
<div className="border-top color-border-secondary pt-3 mt-3">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ import * as pageDetect from 'github-url-detection';
import elementReady from 'element-ready';

import features from '../../../../feature-manager';
import { getDeveloperName } from '../../../../helpers/get-developer-info';
import {
getDeveloperName,
isDeveloperWithMeta,
} from '../../../../helpers/get-developer-info';
import { getDeveloperNetwork, getRepoNetwork } from '../../../../api/developer';
import View from './view';

Expand Down Expand Up @@ -58,7 +61,7 @@ const restore = async () => {
};

features.add(featureId, {
asLongAs: [pageDetect.isUserProfile],
asLongAs: [isDeveloperWithMeta],
awaitDomReady: false,
init,
restore,
Expand Down
3 changes: 1 addition & 2 deletions src/pages/ContentScripts/features/oss-gpt/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@ import $ from 'jquery';

import features from '../../../../feature-manager';
import getGithubTheme from '../../../../helpers/get-github-theme';
import isPublicRepo from '../../../../helpers/is-public-repo';
import { getRepoName } from '../../../../helpers/get-repo-info';
import { getRepoName, isPublicRepo } from '../../../../helpers/get-repo-info';
import View from './view';

interface DocsMetaItem {
Expand Down
3 changes: 2 additions & 1 deletion src/pages/ContentScripts/features/perceptor-layout/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { render, Container } from 'react-dom';

import features from '../../../../feature-manager';
import isPerceptor from '../../../../helpers/is-perceptor';
import { isPublicRepoWithMeta } from '../../../../helpers/get-repo-info';
import View from './view';

const featureId = features.getFeatureID(import.meta.url);
Expand Down Expand Up @@ -33,7 +34,7 @@ const init = async (): Promise<void> => {
};

features.add(featureId, {
asLongAs: [isPerceptor],
asLongAs: [isPerceptor, isPublicRepoWithMeta],
awaitDomReady: false,
init,
});
4 changes: 2 additions & 2 deletions src/pages/ContentScripts/features/perceptor-tab/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import elementReady from 'element-ready';
import iconSvgPath from './icon-svg-path';
import features from '../../../../feature-manager';
import isPerceptor from '../../../../helpers/is-perceptor';
import isPublicRepo from '../../../../helpers/is-public-repo';
import { isPublicRepoWithMeta } from '../../../../helpers/get-repo-info';
import sleep from '../../../../helpers/sleep';

const featureId = features.getFeatureID(import.meta.url);
Expand Down Expand Up @@ -95,7 +95,7 @@ const init = async (): Promise<void> => {
};

features.add(featureId, {
asLongAs: [isPublicRepo],
asLongAs: [isPublicRepoWithMeta],
awaitDomReady: false,
init,
});
Loading
Loading