Skip to content

Commit

Permalink
chore: comments
Browse files Browse the repository at this point in the history
  • Loading branch information
Keyrxng committed Nov 26, 2024
1 parent 588377a commit 1f61f21
Show file tree
Hide file tree
Showing 9 changed files with 67 additions and 5 deletions.
2 changes: 1 addition & 1 deletion .cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,5 @@
"dictionaries": ["typescript", "node", "software-terms"],
"import": ["@cspell/dict-typescript/cspell-ext.json", "@cspell/dict-node/cspell-ext.json", "@cspell/dict-software-terms"],
"ignoreRegExpList": ["[0-9a-fA-F]{6}"],
"ignoreWords": ["ubiquibot", "Supabase", "supabase", "SUPABASE", "sonarjs", "mischeck"]
"ignoreWords": ["ubiquibot", "Supabase", "supabase", "SUPABASE", "sonarjs", "mischeck", "Typebox"]
}
7 changes: 7 additions & 0 deletions static/scripts/config-parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@ import { Octokit } from "@octokit/rest";
import { toastNotification } from "../utils/toaster";
import { CONFIG_FULL_PATH, CONFIG_ORG_REPO } from "@ubiquity-os/plugin-sdk/constants";

/**
* Responsible for fetching, parsing, and updating the user's installed plugin configurations.
*
* - `configRepoExistenceCheck` checks if the user has a config repo and creates one if not
* - `repoFileExistenceCheck` checks if the user has a config file and creates one if not
* - `fetchUserInstalledConfig` fetches the user's installed config from the config repo
*/
export class ConfigParser {
repoConfig: string | null = null;
repoConfigSha: string | null = null;
Expand Down
10 changes: 9 additions & 1 deletion static/scripts/fetch-manifest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,15 @@ import { Manifest, ManifestPreDecode } from "../types/plugins";
import { DEV_CONFIG_FULL_PATH, CONFIG_FULL_PATH, CONFIG_ORG_REPO } from "@ubiquity-os/plugin-sdk/constants";
import { getOfficialPluginConfig } from "../utils/storage";

/**
* Responsible for:
* - Mainly UbiquityOS Marketplace data fetching (config-parser fetches user configs)
* - Fetching the manifest.json files from the marketplace
* - Fetching the README.md files from the marketplace
* - Fetching the official plugin config from the orgs
* - Capturing the worker and action urls from the official plugin config (will be taken from the manifest directly soon)
* - Storing the fetched data in localStorage
*/
export class ManifestFetcher {
private _orgs: string[];
private _octokit: Octokit | null;
Expand Down Expand Up @@ -41,7 +50,6 @@ export class ManifestFetcher {
}

checkManifestCache(): Record<string, ManifestPreDecode> {
// check if the manifest is already in the cache
const manifestCache = localStorage.getItem("manifestCache");
if (manifestCache) {
return JSON.parse(manifestCache);
Expand Down
4 changes: 4 additions & 0 deletions static/scripts/render-manifest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ import { ExtendedHtmlElement } from "../types/github";
import { controlButtons } from "./rendering/control-buttons";
import { createBackButton } from "./rendering/navigation";

/**
* More of a controller than a renderer, this is responsible for rendering the manifest GUI
* and managing the state of the GUI with the help of the rendering functions.
*/
export class ManifestRenderer {
private _manifestGui: HTMLElement;
private _manifestGuiBody: ExtendedHtmlElement;
Expand Down
21 changes: 20 additions & 1 deletion static/scripts/rendering/config-editor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,22 @@ import MarkdownIt from "markdown-it";
import { getManifestCache } from "../../utils/storage";
const md = new MarkdownIt();

/**
* Displays the plugin configuration editor.
*
* - `pluginManifest` should never be null or there was a problem fetching from the marketplace
* - `plugin` should only be passed in if you intend on replacing the default configuration with their installed configuration
*
* Allows for:
* - Adding a single plugin configuration
* - Removing a single plugin configuration
* - Resetting the plugin configuration to the schema default
* - Building multiple plugins like a "shopping cart" and they all get pushed at once in the background
*
* Compromises:
* - Typebox Unions get JSON.stringify'd and displayed as one string meaning `text-conversation-rewards` has a monster config for HTML tags
* - Plugin config objects are split like `plugin.config.key` and `plugin.config.key2` and `plugin.config.key3` and so on
*/
export function renderConfigEditor(renderer: ManifestRenderer, pluginManifest: Manifest | null, plugin?: Plugin["uses"][0]["with"]): void {
renderer.currentStep = "configEditor";
renderer.backButton.style.display = "block";
Expand All @@ -16,6 +32,7 @@ export function renderConfigEditor(renderer: ManifestRenderer, pluginManifest: M
processProperties(renderer, pluginManifest, pluginManifest?.configuration.properties || {}, null);
const configInputs = document.querySelectorAll<HTMLInputElement | HTMLTextAreaElement>(".config-input");

// If plugin is passed in, we want to inject those values into the inputs
if (plugin) {
configInputs.forEach((input) => {
const key = input.getAttribute("data-config-key");
Expand Down Expand Up @@ -56,6 +73,7 @@ export function renderConfigEditor(renderer: ManifestRenderer, pluginManifest: M
}

const parsedConfig = renderer.configParser.parseConfig(renderer.configParser.repoConfig || localStorage.getItem("config"));
// for when `resetToDefault` is called and no plugin gets passed in, we still want to show the remove button
const isInstalled = parsedConfig.plugins?.find((p) => p.uses[0].plugin.includes(normalizePluginName(pluginManifest?.name || "")));

loadListeners({
Expand All @@ -75,7 +93,7 @@ export function renderConfigEditor(renderer: ManifestRenderer, pluginManifest: M
remove.classList.add("disabled");
}

resetToDefaultButton.hidden = !!plugin;
resetToDefaultButton.hidden = !!(plugin || isInstalled);
const manifestCache = getManifestCache();
const pluginUrls = Object.keys(manifestCache);
const pluginUrl = pluginUrls.find((url) => {
Expand Down Expand Up @@ -130,6 +148,7 @@ async function loadListeners({
handleResetToDefault(renderer, pluginManifest);
}

// ensure the listeners are removed before adding new ones
await (async () => {
getTrackedEventListeners(remove, "click")?.forEach((listener) => {
removeTrackedEventListener(remove, "click", listener);
Expand Down
13 changes: 12 additions & 1 deletion static/scripts/rendering/input-parsing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,20 @@ import AJV, { AnySchemaObject } from "ajv";
import { createInputRow } from "../../utils/element-helpers";
import { ManifestRenderer } from "../render-manifest";
import { Manifest } from "../../types/plugins";

// Without the raw Typebox Schema it was difficult to use Typebox which is why I've used AJV to validate the configuration.
const ajv = new AJV({ allErrors: true, coerceTypes: true, strict: true });

/**
* This creates the input rows for the configuration editor for any given plugin.
*/
export function processProperties(
renderer: ManifestRenderer,
manifest: Manifest | null | undefined,
props: Record<string, Manifest["configuration"]>,
prefix: string | null = null
) {
const required = manifest?.configuration?.required || [];

Object.keys(props).forEach((key) => {
const fullKey = prefix ? `${prefix}.${key}` : key;
const prop = props[key];
Expand All @@ -35,6 +39,13 @@ export function processProperties(
});
}

/**
* This parse the inputs from the configuration editor and returns the configuration object.
* It also returns an array of missing required fields if any.
*
* It should become a priority to establish API like usage of `null` and `undefined` in our schemas so it's
* easier and less buggy when using the installer.
*/
export function parseConfigInputs(
configInputs: NodeListOf<HTMLInputElement | HTMLTextAreaElement>,
manifest: Manifest
Expand Down
3 changes: 3 additions & 0 deletions static/scripts/rendering/org-select.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ import { controlButtons } from "./control-buttons";
import { renderPluginSelector } from "./plugin-select";
import { closeAllSelect, updateGuiTitle } from "./utils";

/**
* Renders the orgs for the authenticated user to select from.
*/
export function renderOrgPicker(renderer: ManifestRenderer, orgs: string[]) {
renderer.currentStep = "orgPicker";
controlButtons({ hide: true });
Expand Down
4 changes: 4 additions & 0 deletions static/scripts/rendering/plugin-select.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ import { renderConfigEditor } from "./config-editor";
import { controlButtons } from "./control-buttons";
import { closeAllSelect, updateGuiTitle } from "./utils";

/**
* Renders a dropdown of plugins taken from the marketplace with an installed indicator.
* The user can select a plugin and it will render the configuration editor for that plugin.
*/
export function renderPluginSelector(renderer: ManifestRenderer): void {
renderer.currentStep = "pluginSelector";
renderer.backButton.style.display = "block";
Expand Down
8 changes: 7 additions & 1 deletion static/scripts/rendering/write-add-remove.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,13 @@ import { getOfficialPluginConfig } from "../../utils/storage";
import { renderConfigEditor } from "./config-editor";
import { normalizePluginName } from "./utils";

/**
* Writes the new configuration to the config file. This does not push the config to GitHub
* only updates the local config. The actual push event is handled via a toast notification.
*
* - Acts as a "save" button for the configuration editor
* - Adds or removes a plugin configuration from the config file
*/
export function writeNewConfig(renderer: ManifestRenderer, option: "add" | "remove") {
const selectedManifest = localStorage.getItem("selectedPluginManifest");
if (!selectedManifest) {
Expand Down Expand Up @@ -38,7 +45,6 @@ export function writeNewConfig(renderer: ManifestRenderer, option: "add" | "remo
}

renderer.configParser.loadConfig();
// this relies on the manifest matching the repo name
const normalizedPluginName = normalizePluginName(pluginManifest.name);
const officialPluginConfig: Record<string, { actionUrl?: string; workerUrl?: string }> = getOfficialPluginConfig();
const pluginUrl = Object.keys(officialPluginConfig).find((url) => {
Expand Down

0 comments on commit 1f61f21

Please sign in to comment.