Skip to content

Commit

Permalink
Added field config for label option to mark requiredness and config d…
Browse files Browse the repository at this point in the history
…efault value
  • Loading branch information
retrodaredevil committed Apr 1, 2024
1 parent 25b090d commit 3adb660
Show file tree
Hide file tree
Showing 4 changed files with 126 additions and 49 deletions.
21 changes: 14 additions & 7 deletions pkg/plugin/parsing/parsing.go
Original file line number Diff line number Diff line change
Expand Up @@ -150,13 +150,20 @@ func getLabelsFromFlatData(flatData *jsonnode.Object, parsingOption querymodel.P
case querymodel.FIELD:
fieldValue := flatData.Get(labelOption.Value)
if fieldValue == nil {
return nil, errors.New(fmt.Sprintf("Label option: %s could not be satisfied as key %s does not exist", labelOption.Name, labelOption.Value))
}
switch typedFieldValue := fieldValue.(type) {
case jsonnode.String:
labels[labelOption.Name] = typedFieldValue.String()
default:
return nil, errors.New(fmt.Sprintf("Label option: %s could not be satisfied as key %s is not a string. It's type is %v", labelOption.Name, labelOption.Value, reflect.TypeOf(typedFieldValue)))
fieldConfig := labelOption.FieldConfig
if fieldConfig != nil && fieldConfig.Required {
return nil, errors.New(fmt.Sprintf("Label option: %s could not be satisfied as key %s does not exist", labelOption.Name, labelOption.Value))
} else if fieldConfig != nil && fieldConfig.DefaultValue != nil {
labels[labelOption.Name] = *fieldConfig.DefaultValue
}
// else omit
} else {
switch typedFieldValue := fieldValue.(type) {
case jsonnode.String:
labels[labelOption.Name] = typedFieldValue.String()
default:
return nil, errors.New(fmt.Sprintf("Label option: %s could not be satisfied as key %s is not a string. It's type is %v", labelOption.Name, labelOption.Value, reflect.TypeOf(typedFieldValue)))
}
}
}
}
Expand Down
11 changes: 8 additions & 3 deletions pkg/plugin/querymodel/querymodel.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,14 @@ const (
)

type LabelOption struct {
Name string `json:"name"`
Type LabelOptionType `json:"type"`
Value string `json:"value"`
Name string `json:"name"`
Type LabelOptionType `json:"type"`
Value string `json:"value"`
FieldConfig *LabelOptionFieldConfig `json:"fieldConfig"`
}
type LabelOptionFieldConfig struct {
Required bool `json:"required"`
DefaultValue *string `json:"defaultValue"`
}

func (parsingOption *ParsingOption) GetTimeField(key string) *TimeField {
Expand Down
134 changes: 95 additions & 39 deletions src/components/QueryEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@ import {Button, IconButton, InlineField, Input, Select} from '@grafana/ui';
import {CoreApp, QueryEditorProps} from '@grafana/data';
import {DataSource} from '../datasource';
import {
DEFAULT_LABEL_OPTION_FIELD_CONFIG,
getQueryVariablesAsJsonString,
LabelOption,
LabelOptionType,
ParsingOption, TimeField,
ParsingOption,
TimeField,
WildGraphQLAnyQuery,
WildGraphQLDataSourceOptions
} from '../types';
Expand Down Expand Up @@ -444,47 +446,101 @@ function InnerQueryEditor({ query, onChange, onRunQuery, datasource }: Props) {
}
</div>
</>)}
{parsingOption.labelOptions?.map((labelOption, labelOptionIndex) => <>
<div className="gf-form-inline">
<InlineField
label={`Label: "${labelOption.name}"`}
tooltip={`Specify how the custom label "${labelOption.name}" should be populated. A type of "Constant" means that you may put whatever text you would like as the label. A type of "Field" means that the given field will be used as the label's value.`}
labelWidth={LABEL_WIDTH}
>
<Select
width={16}
options={[
{label: "Constant", value: LabelOptionType.CONSTANT},
{label: "Field", value: LabelOptionType.FIELD},
]}
value={labelOption.type}
onChange={(value) => {
const newType = value.value;
if (newType !== undefined) {
{parsingOption.labelOptions?.map((labelOption, labelOptionIndex) => {
// fieldConfig and fieldConfigSelection are undefined ONLY when labelOption.type is CONSTANT
const fieldConfig = labelOption.type === LabelOptionType.CONSTANT
? undefined
: (labelOption.fieldConfig ?? DEFAULT_LABEL_OPTION_FIELD_CONFIG);
const fieldConfigSelection = fieldConfig === undefined
? undefined
: fieldConfig.required
? "required"
: fieldConfig.defaultValue === undefined ? "omit" : "default";
return <>
<div className="gf-form-inline">
<InlineField
label={`Label: "${labelOption.name}"`}
tooltip={`Specify how the custom label "${labelOption.name}" should be populated. A type of "Constant" means that you may put whatever text you would like as the label. A type of "Field" means that the given field will be used as the label's value.`}
labelWidth={LABEL_WIDTH}
>
<Select
width={16}
options={[
{label: "Constant", value: LabelOptionType.CONSTANT},
{label: "Field", value: LabelOptionType.FIELD},
]}
value={labelOption.type}
onChange={(value) => {
const newType = value.value;
if (newType !== undefined) {
setLabelOption(parsingOptionIndex, labelOptionIndex, {
...labelOption,
type: newType,
});
}
}}
/>

</InlineField>
<InlineField label="Value" labelWidth={8}>
<Input
width={INPUT_WIDTH}
value={labelOption.value}
onChange={(event) => {
setLabelOption(parsingOptionIndex, labelOptionIndex, {
...labelOption,
type: newType,
});
}
}}
/>
value: event.currentTarget.value,
})
}}
/>
</InlineField>

</InlineField>
<InlineField label="Value" labelWidth={8}>
<Input
width={INPUT_WIDTH}
value={labelOption.value}
onChange={(event) => {
setLabelOption(parsingOptionIndex, labelOptionIndex, {
...labelOption,
value: event.currentTarget.value,
})
}}
/>
</InlineField>
<IconButton name={"minus"} onClick={() => deleteLabelOption(parsingOptionIndex, labelOptionIndex)}/>
</div>
</>)}
{fieldConfig &&
<InlineField label="If absent" labelWidth={10}>
<Select
width={16}
options={[
{label: "Error", value: "required"},
{label: "Omit", value: "omit"},
{label: "Use default", value: "default"},
]}
value={fieldConfigSelection!}
onChange={(value) => {
const newValue = value.value;
if (newValue !== undefined) {
setLabelOption(parsingOptionIndex, labelOptionIndex, {
...labelOption,
fieldConfig: {
required: newValue === "required",
defaultValue: newValue === "omit" ? undefined : (fieldConfig!.defaultValue ?? "")
}
});
}
}}
/>
</InlineField>
}
{fieldConfigSelection === "default" &&
<InlineField label="Default" labelWidth={10}>
<Input
width={INPUT_WIDTH}
value={fieldConfig!.defaultValue!}
onChange={(event) => {
setLabelOption(parsingOptionIndex, labelOptionIndex, {
...labelOption,
fieldConfig: {
required: false,
defaultValue: event.currentTarget.value
}
})
}}
/>
</InlineField>
}
<IconButton name={"minus"} onClick={() => deleteLabelOption(parsingOptionIndex, labelOptionIndex)}/>
</div>
</>;
})}
</>;
})}

Expand Down
9 changes: 9 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,20 @@ export interface LabelOption {
/** When {@link type} is {@link LabelOptionType.CONSTANT}, this represents a text value that is constant.
* When {@link type} is {@link LabelOptionType.FIELD}, this represents the path to a field relative to the data path */
value: string;
/** May be defined when {@link type} is {@link LabelOptionType.Field}. */
fieldConfig?: LabelOptionFieldConfig;
}
export enum LabelOptionType {
CONSTANT = "constant",
FIELD = "field",
}
export interface LabelOptionFieldConfig {
required: boolean;
defaultValue?: string;
}
export const DEFAULT_LABEL_OPTION_FIELD_CONFIG: LabelOptionFieldConfig = { // omit is default
required: false
}

export interface ParsingOption {
// TODO make sure an empty string is valid in the backend like this says
Expand Down

0 comments on commit 3adb660

Please sign in to comment.