1
0
Fork 0

add ability to change key/value view syntax

This commit is contained in:
ChaotiCryptidz 2022-01-19 16:53:37 +00:00
parent 153c5725aa
commit d82698d550
9 changed files with 247 additions and 124 deletions

View file

@ -65,6 +65,16 @@ export class Settings {
this.alertChange("theme"); this.alertChange("theme");
} }
get kvAlwaysCodeView(): boolean {
const value = this.storage.getItem("kvAlwaysCodeView") || false;
return value == "true";
}
set kvAlwaysCodeView(value: boolean) {
this.storage.setItem("kvAlwaysCodeView", String(value));
this.alertChange("kvAlwaysCodeView");
}
get kvEditorDefaultLanguage(): string { get kvEditorDefaultLanguage(): string {
return this.storage.getItem("kvEditorDefaultLanguage") || "yaml"; return this.storage.getItem("kvEditorDefaultLanguage") || "yaml";
} }
@ -84,4 +94,24 @@ export class Settings {
this.storage.setItem("kvEditorIndent", String(value)); this.storage.setItem("kvEditorIndent", String(value));
this.alertChange("kvEditorIndent"); this.alertChange("kvEditorIndent");
} }
get kvViewDefaultLanguage(): string {
return this.storage.getItem("kvViewDefaultLanguage") || "yaml";
}
set kvViewDefaultLanguage(value: string) {
this.storage.setItem("kvViewDefaultLanguage", value);
this.alertChange("kvViewDefaultLanguage");
}
get kvViewIndent(): number {
const value = this.storage.getItem("kvViewIndent");
if (value) return parseInt(value);
return 2;
}
set kvViewIndent(value: number) {
this.storage.setItem("kvViewIndent", String(value));
this.alertChange("kvViewIndent");
}
} }

View file

@ -81,10 +81,13 @@ module.exports = {
settings_general_page_direction_ltr: "Left to Right", settings_general_page_direction_ltr: "Left to Right",
settings_general_page_direction_rtl: "Right to Left", settings_general_page_direction_rtl: "Right to Left",
// Key/Value Editor Settings // Key/Value Settings
settings_kveditor_title: "Key/Value Editor", settings_kv_title: "Key/Value",
settings_kveditor_default_language: "Default Data Interchange Format", settings_kv_default_view_language: "Default View Syntax",
settings_kveditor_default_indent: "Default Indent", settings_kv_default_view_indent: "Default View Indent",
settings_kv_default_editor_language: "Default Editor Syntax",
settings_kv_default_editor_indent: "Default Editor Indent",
settings_kv_always_view_in_code_mode: "Always view in code mode",
// Set Vault URL Page // Set Vault URL Page
set_vault_url_title: "Set Vault URL", set_vault_url_title: "Set Vault URL",
@ -163,12 +166,13 @@ module.exports = {
kv_secret_delete_all_btn: "Delete All Versions", kv_secret_delete_all_btn: "Delete All Versions",
kv_secret_delete_version_btn: "Delete Version {{ version }}", kv_secret_delete_version_btn: "Delete Version {{ version }}",
kv_secret_versions_btn: "Versions", kv_secret_versions_btn: "Versions",
kv_secret_syntax: "Syntax",
// Key Value Secret Editor Page // Key Value Secret Editor Page
kv_sec_edit_title: "K/V Edit", kv_sec_edit_title: "K/V Edit",
kv_sec_edit_syntax: "Syntax", kv_sec_edit_syntax: "Syntax",
kv_sec_edit_loading: "Loading Editor..", kv_sec_edit_loading: "Loading Editor..",
kv_sec_edit_invalid_json_err: "Invalid JSON", kv_sec_edit_invalid_data_err: "Invalid Data",
kv_sec_edit_suffix: " (edit)", kv_sec_edit_suffix: " (edit)",
// Key Value Secret Versions Page // Key Value Secret Versions Page

View file

@ -4,48 +4,15 @@ import { Component, JSX, createRef } from "preact";
import { DefaultPageProps } from "../../../../types/DefaultPageProps"; import { DefaultPageProps } from "../../../../types/DefaultPageProps";
import { InputWithTitle } from "../../../elements/InputWithTitle"; import { InputWithTitle } from "../../../elements/InputWithTitle";
import { SecretTitleElement } from "../SecretTitleElement"; import { SecretTitleElement } from "../SecretTitleElement";
import {
SupportedLanguages,
dumpData,
parseData,
toPrismCode,
} from "../../../../utils/dataInterchange";
import { setErrorText } from "../../../../pageUtils"; import { setErrorText } from "../../../../pageUtils";
import { sortedObjectMap } from "../../../../utils"; import { sortedObjectMap } from "../../../../utils";
import JSON5 from "json5";
import i18next from "i18next"; import i18next from "i18next";
import yaml from "js-yaml";
export const SupportedEditorLanguages = [
{ name: "json", readable: "JSON" },
{ name: "json5", readable: "JSON5" },
{ name: "yaml", readable: "YAML" },
];
function parseData(data: string, syntax = "json"): Record<string, unknown> {
if (syntax == "json") {
return JSON.parse(data) as Record<string, unknown>;
} else if (syntax == "json5") {
return JSON5.parse(data);
} else if (syntax == "yaml") {
return yaml.load(data) as Record<string, unknown>;
}
}
function dumpData(data: Record<string, unknown>, space = 4, syntax = "json"): string {
if (syntax == "json") {
return JSON.stringify(data, null, space);
} else if (syntax == "json5") {
return JSON5.stringify(data, null, space);
} else if (syntax == "yaml") {
return yaml.dump(data, { indent: space });
}
}
export function validateData(str: string, syntax = "json"): boolean {
try {
parseData(str, syntax);
} catch (e) {
const err = e as Error;
setErrorText(err.message);
return false;
}
return true;
}
export type KVEditProps = DefaultPageProps & { export type KVEditProps = DefaultPageProps & {
baseMount: string; baseMount: string;
@ -78,8 +45,10 @@ export class KVEditor extends Component<KVEditProps, KVEditState> {
if (!this.state.dataLoaded) return; if (!this.state.dataLoaded) return;
const editorContent = this.state.code; const editorContent = this.state.code;
if (!validateData(editorContent, this.state.syntax)) { try {
setErrorText(i18next.t("kv_sec_edit_invalid_json_err")); parseData(editorContent, this.state.syntax);
} catch {
setErrorText(i18next.t("kv_sec_edit_invalid_data_err"));
return; return;
} }
@ -133,16 +102,6 @@ export class KVEditor extends Component<KVEditProps, KVEditState> {
return <p>{i18next.t("content_loading")}</p>; return <p>{i18next.t("content_loading")}</p>;
} }
let codeEditorLanguage: string;
if (this.state.syntax == "json") {
codeEditorLanguage = "json";
} else if (this.state.syntax == "json5") {
codeEditorLanguage = "json5";
} else if (this.state.syntax == "yaml") {
codeEditorLanguage = "yaml";
}
return ( return (
<div> <div>
<InputWithTitle title={i18next.t("kv_sec_edit_syntax")}> <InputWithTitle title={i18next.t("kv_sec_edit_syntax")}>
@ -153,7 +112,7 @@ export class KVEditor extends Component<KVEditProps, KVEditState> {
this.setState({ syntax: this.syntaxSelectRef.current.value }); this.setState({ syntax: this.syntaxSelectRef.current.value });
}} }}
> >
{SupportedEditorLanguages.map((lang) => { {SupportedLanguages.map((lang) => {
return ( return (
<option <option
label={lang.readable} label={lang.readable}
@ -166,7 +125,7 @@ export class KVEditor extends Component<KVEditProps, KVEditState> {
</InputWithTitle> </InputWithTitle>
<p class="uk-text-danger" id="errorText" /> <p class="uk-text-danger" id="errorText" />
<CodeEditor <CodeEditor
language={codeEditorLanguage} language={toPrismCode(this.state.syntax)}
tabSize={this.props.settings.kvEditorIndent} tabSize={this.props.settings.kvEditorIndent}
code={this.getStringKVData(this.state.kvData)} code={this.getStringKVData(this.state.kvData)}
onUpdate={(code) => this.onCodeUpdate(code)} onUpdate={(code) => this.onCodeUpdate(code)}

View file

@ -1,31 +1,63 @@
import { Button } from "../../../elements/Button"; import { Button } from "../../../elements/Button";
import { CodeBlock } from "../../../elements/CodeBlock"; import { CodeBlock } from "../../../elements/CodeBlock";
import { Component, JSX } from "preact"; import { Component, JSX, createRef } from "preact";
import { CopyableInputBox } from "../../../elements/CopyableInputBox"; import { CopyableInputBox } from "../../../elements/CopyableInputBox";
import { DefaultPageProps } from "../../../../types/DefaultPageProps"; import { DefaultPageProps } from "../../../../types/DefaultPageProps";
import { DoesNotExistError } from "../../../../types/internalErrors"; import { DoesNotExistError } from "../../../../types/internalErrors";
import { Grid, GridSizes } from "../../../elements/Grid"; import { Grid, GridSizes } from "../../../elements/Grid";
import { InputWithTitle } from "../../../elements/InputWithTitle";
import { SecretTitleElement } from "../SecretTitleElement"; import { SecretTitleElement } from "../SecretTitleElement";
import { SupportedLanguages, dumpData, toPrismCode } from "../../../../utils/dataInterchange";
import { kvDeleteURL, kvEditURL, kvVersionsURL } from "../../pageLinks"; import { kvDeleteURL, kvEditURL, kvVersionsURL } from "../../pageLinks";
import { sortedObjectMap } from "../../../../utils"; import { sortedObjectMap } from "../../../../utils";
import i18next from "i18next"; import i18next from "i18next";
export type KVSecretViewProps = { export type KVSecretViewProps = DefaultPageProps & {
kvData: Record<string, unknown>; kvData: Record<string, unknown>;
}; };
export class KVSecretVew extends Component<KVSecretViewProps, unknown> { export class KVSecretVew extends Component<KVSecretViewProps, { syntax: string }> {
syntaxSelectRef = createRef<HTMLSelectElement>();
render(): JSX.Element { render(): JSX.Element {
const secretsMap = sortedObjectMap(this.props.kvData); const secretsMap = sortedObjectMap(this.props.kvData);
let isMultiLevelJSON = false; let isMultiLevel = false;
for (const value of secretsMap.values()) { for (const value of secretsMap.values()) {
if (typeof value == "object") isMultiLevelJSON = true; if (typeof value == "object") isMultiLevel = true;
} }
if (isMultiLevelJSON) { if (isMultiLevel || this.props.settings.kvAlwaysCodeView) {
const jsonText = JSON.stringify(Object.fromEntries(secretsMap), null, 4); const syntax = this.state.syntax || this.props.settings.kvViewDefaultLanguage;
return <CodeBlock language="json" code={jsonText} />; const secretData = Object.fromEntries(secretsMap);
const codeData = dumpData(secretData, this.props.settings.kvViewIndent, syntax);
return (
<>
<InputWithTitle title={i18next.t("kv_secret_syntax")}>
<select
ref={this.syntaxSelectRef}
class="uk-select uk-form-width-medium"
onChange={() => {
this.setState({ syntax: this.syntaxSelectRef.current.value });
}}
>
{SupportedLanguages.map((lang) => {
return (
<option
label={lang.readable}
value={lang.name}
selected={this.props.settings.kvEditorDefaultLanguage == lang.name}
/>
);
})}
</select>
</InputWithTitle>
<CodeBlock
language={toPrismCode(this.props.settings.kvViewDefaultLanguage)}
code={codeData}
/>
</>
);
} else { } else {
return ( return (
<> <>
@ -161,7 +193,7 @@ export class KeyValueView extends Component<DefaultPageProps, KeyValueViewState>
)} )}
</p> </p>
{!this.state.isDeleted && <KVSecretVew kvData={this.state.secretInfo} />} {!this.state.isDeleted && <KVSecretVew {...this.props} kvData={this.state.secretInfo} />}
{this.state.isDeleted && ( {this.state.isDeleted && (
<> <>

View file

@ -1,54 +0,0 @@
import { Component, createRef } from "preact";
import { DefaultPageProps } from "../../../types/DefaultPageProps";
import { InputWithTitle } from "../../elements/InputWithTitle";
import { SupportedEditorLanguages } from "../Secrets/KeyValue/KeyValueEdit";
import { settingsSavedNotification } from "./Settings";
import i18next from "i18next";
export class KeyValueEditorSettings extends Component<DefaultPageProps> {
syntaxSelectRef = createRef<HTMLSelectElement>();
indentInputRef = createRef<HTMLInputElement>();
render() {
return (
<div>
<h4>{i18next.t("settings_kveditor_title")}</h4>
<InputWithTitle title={i18next.t("settings_kveditor_default_language")}>
<select
ref={this.syntaxSelectRef}
class="uk-select uk-form-width-medium"
onChange={() => {
this.props.settings.kvEditorDefaultLanguage = this.syntaxSelectRef.current.value;
settingsSavedNotification();
}}
>
{SupportedEditorLanguages.map((lang) => {
return (
<option
label={lang.readable}
value={lang.name}
selected={this.props.settings.kvEditorDefaultLanguage == lang.name}
/>
);
})}
</select>
</InputWithTitle>
<InputWithTitle title={i18next.t("settings_kveditor_default_indent")}>
<input
ref={this.indentInputRef}
class="uk-input uk-form-width-medium"
type="number"
value={this.props.settings.kvEditorIndent}
onChange={() => {
const value = this.indentInputRef.current?.value;
const indent = parseInt(value);
this.props.settings.kvEditorIndent = indent;
settingsSavedNotification();
}}
/>
</InputWithTitle>
</div>
);
}
}

View file

@ -0,0 +1,114 @@
import { Component, createRef } from "preact";
import { DefaultPageProps } from "../../../types/DefaultPageProps";
import { InputWithTitle } from "../../elements/InputWithTitle";
import { SupportedLanguages } from "../../../utils/dataInterchange";
import { settingsSavedNotification } from "./Settings";
import i18next from "i18next";
export class KeyValueSettings extends Component<DefaultPageProps> {
editorSyntaxSelectRef = createRef<HTMLSelectElement>();
editorIndentInputRef = createRef<HTMLInputElement>();
viewSyntaxSelectRef = createRef<HTMLSelectElement>();
viewIndentInputRef = createRef<HTMLInputElement>();
codeModeToggleRef = createRef<HTMLInputElement>();
render() {
return (
<div>
<h4>{i18next.t("settings_kv_title")}</h4>
{/* KV View Language */}
<InputWithTitle title={i18next.t("settings_kv_default_view_language")}>
<select
ref={this.viewSyntaxSelectRef}
class="uk-select uk-form-width-medium"
onChange={() => {
this.props.settings.kvViewDefaultLanguage = this.viewSyntaxSelectRef.current.value;
settingsSavedNotification();
}}
>
{SupportedLanguages.map((lang) => {
return (
<option
label={lang.readable}
value={lang.name}
selected={this.props.settings.kvViewDefaultLanguage == lang.name}
/>
);
})}
</select>
</InputWithTitle>
{/* KV View Indent */}
<InputWithTitle title={i18next.t("settings_kv_default_view_indent")}>
<input
ref={this.viewIndentInputRef}
class="uk-input uk-form-width-medium"
type="number"
value={this.props.settings.kvEditorIndent}
onChange={() => {
const value = this.viewIndentInputRef.current.value;
const indent = parseInt(value);
this.props.settings.kvViewIndent = indent;
settingsSavedNotification();
}}
/>
</InputWithTitle>
{/* KV Editor Language */}
<InputWithTitle title={i18next.t("settings_kv_default_editor_language")}>
<select
ref={this.editorSyntaxSelectRef}
class="uk-select uk-form-width-medium"
onChange={() => {
this.props.settings.kvEditorDefaultLanguage =
this.editorSyntaxSelectRef.current.value;
settingsSavedNotification();
}}
>
{SupportedLanguages.map((lang) => {
return (
<option
label={lang.readable}
value={lang.name}
selected={this.props.settings.kvEditorDefaultLanguage == lang.name}
/>
);
})}
</select>
</InputWithTitle>
{/* KV Editor Indent */}
<InputWithTitle title={i18next.t("settings_kv_default_editor_indent")}>
<input
ref={this.editorIndentInputRef}
class="uk-input uk-form-width-medium"
type="number"
value={this.props.settings.kvEditorIndent}
onChange={() => {
const value = this.editorIndentInputRef.current.value;
const indent = parseInt(value);
this.props.settings.kvEditorIndent = indent;
settingsSavedNotification();
}}
/>
</InputWithTitle>
{/* Always view in code mode */}
<InputWithTitle title={i18next.t("settings_kv_always_view_in_code_mode")}>
<input
ref={this.codeModeToggleRef}
class="uk-checkbox"
type="checkbox"
checked={this.props.settings.kvAlwaysCodeView}
onChange={() => {
const value = this.codeModeToggleRef.current.checked;
this.props.settings.kvAlwaysCodeView = value;
settingsSavedNotification();
}}
/>
</InputWithTitle>
</div>
);
}
}

View file

@ -2,7 +2,7 @@ import { Component } from "preact";
import { DefaultPageProps } from "../../../types/DefaultPageProps"; import { DefaultPageProps } from "../../../types/DefaultPageProps";
import { GeneralSettings } from "./GeneralSettings"; import { GeneralSettings } from "./GeneralSettings";
import { Grid, GridSizes } from "../../elements/Grid"; import { Grid, GridSizes } from "../../elements/Grid";
import { KeyValueEditorSettings } from "./KeyValueEditorSettings"; import { KeyValueSettings } from "./KeyValueSettings";
import { PageTitle } from "../../elements/PageTitle"; import { PageTitle } from "../../elements/PageTitle";
import UIkit from "uikit"; import UIkit from "uikit";
import i18next from "i18next"; import i18next from "i18next";
@ -21,7 +21,7 @@ export class Settings extends Component<DefaultPageProps> {
<PageTitle title={i18next.t("settings_title")} /> <PageTitle title={i18next.t("settings_title")} />
<Grid size={GridSizes.NORMAL}> <Grid size={GridSizes.NORMAL}>
<GeneralSettings {...this.props} /> <GeneralSettings {...this.props} />
<KeyValueEditorSettings {...this.props} /> <KeyValueSettings {...this.props} />
</Grid> </Grid>
</> </>
); );

View file

@ -0,0 +1,38 @@
import JSON5 from "json5";
import yaml from "js-yaml";
export const SupportedLanguages = [
{ name: "json", readable: "JSON" },
{ name: "json5", readable: "JSON5" },
{ name: "yaml", readable: "YAML" },
];
export const LanguagePrismBlockMap: Record<string, string> = {
json: "json",
json5: "json5",
yaml: "yaml",
};
export function toPrismCode(syntax: string): string {
return LanguagePrismBlockMap[syntax];
}
export function parseData(data: string, syntax = "json"): Record<string, unknown> {
if (syntax == "json") {
return JSON.parse(data) as Record<string, unknown>;
} else if (syntax == "json5") {
return JSON5.parse(data);
} else if (syntax == "yaml") {
return yaml.load(data) as Record<string, unknown>;
}
}
export function dumpData(data: Record<string, unknown>, space = 4, syntax = "json"): string {
if (syntax == "json") {
return JSON.stringify(data, null, space);
} else if (syntax == "json5") {
return JSON5.stringify(data, null, space);
} else if (syntax == "yaml") {
return yaml.dump(data, { indent: space });
}
}