Hide type models for Ruby in non-canary mode

This commit is contained in:
Koen Vlaswinkel
2024-02-23 10:31:28 +01:00
parent 6031d9b496
commit f57d4418c7
17 changed files with 144 additions and 7 deletions

View File

@@ -19,6 +19,10 @@ import type { AccessPathSuggestionRow } from "../suggestions";
type GenerateMethodDefinition<T> = (method: T) => DataTuple[];
type ReadModeledMethod = (row: DataTuple[]) => ModeledMethod;
type IsHiddenContext = {
method: MethodDefinition;
isCanary: boolean;
};
export type ModelsAsDataLanguagePredicate<T> = {
extensiblePredicate: string;
@@ -30,6 +34,14 @@ export type ModelsAsDataLanguagePredicate<T> = {
supportedEndpointTypes?: EndpointType[];
generateMethodDefinition: GenerateMethodDefinition<T>;
readModeledMethod: ReadModeledMethod;
/**
* Controls whether this predicate is hidden for a certain method. This only applies to the UI.
* If not specified, the predicate is visible for all methods.
*
* @param method The method to check if the predicate is hidden for.
*/
isHidden?: (context: IsHiddenContext) => boolean;
};
type ParseGenerationResults = (

View File

@@ -169,6 +169,8 @@ export const ruby: ModelsAsDataLanguage = {
methodParameters: "",
};
},
// Hide for all non-canary users
isHidden: ({ isCanary }) => !isCanary,
},
},
modelGeneration: {

View File

@@ -11,6 +11,7 @@ import type { ModelingStore } from "../modeling-store";
import { AbstractWebviewViewProvider } from "../../common/vscode/abstract-webview-view-provider";
import { assertNever } from "../../common/helpers-pure";
import type { ModelConfigListener } from "../../config";
import { isCanary } from "../../config";
import type { DatabaseItem } from "../../databases/local-databases";
import type { ModelingEvents } from "../modeling-events";
import type { QueryLanguage } from "../../common/query-language";
@@ -46,6 +47,7 @@ export class MethodModelingViewProvider extends AbstractWebviewViewProvider<
t: "setMethodModelingPanelViewState",
viewState: {
language: this.language,
isCanary: isCanary(),
},
});
}

View File

@@ -40,6 +40,7 @@ import type { Method } from "./method";
import type { ModeledMethod } from "./modeled-method";
import type { ExtensionPack } from "./shared/extension-pack";
import type { ModelConfigListener } from "../config";
import { isCanary } from "../config";
import { Mode } from "./shared/mode";
import { loadModeledMethods, saveModeledMethods } from "./modeled-method-fs";
import { pickExtensionPack } from "./extension-pack-picker";
@@ -450,6 +451,7 @@ export class ModelEditorView extends AbstractWebview<
mode: this.modelingStore.getMode(this.databaseItem),
showModeSwitchButton,
sourceArchiveAvailable,
isCanary: isCanary(),
},
});
}

View File

@@ -11,8 +11,10 @@ export interface ModelEditorViewState {
mode: Mode;
showModeSwitchButton: boolean;
sourceArchiveAvailable: boolean;
isCanary: boolean;
}
export interface MethodModelingPanelViewState {
language: QueryLanguage | undefined;
isCanary: boolean;
}

View File

@@ -34,6 +34,7 @@ const Template: StoryFn<typeof MethodModelingInputsComponent> = (args) => {
language={QueryLanguage.Java}
modeledMethod={m}
onChange={onChange}
isCanary={true}
/>
);
};

View File

@@ -50,6 +50,7 @@ const UnsavedTag = ({ modelingStatus }: { modelingStatus: ModelingStatus }) => (
export type MethodModelingProps = {
language: QueryLanguage;
isCanary: boolean;
modelingStatus: ModelingStatus;
method: Method;
modeledMethods: ModeledMethod[];
@@ -60,6 +61,7 @@ export type MethodModelingProps = {
export const MethodModeling = ({
language,
isCanary,
modelingStatus,
modeledMethods,
method,
@@ -80,6 +82,7 @@ export const MethodModeling = ({
</DependencyContainer>
<MultipleModeledMethodsPanel
language={language}
isCanary={isCanary}
method={method}
modeledMethods={modeledMethods}
isModelingInProgress={isModelingInProgress}

View File

@@ -24,6 +24,7 @@ const Name = styled.span`
export type MethodModelingInputsProps = {
language: QueryLanguage;
isCanary: boolean;
method: Method;
modeledMethod: ModeledMethod | undefined;
modelPending: boolean;
@@ -33,6 +34,7 @@ export type MethodModelingInputsProps = {
export const MethodModelingInputs = ({
language,
isCanary,
method,
modeledMethod,
modelPending,
@@ -55,7 +57,7 @@ export const MethodModelingInputs = ({
{isModelingInProgress ? (
<InProgressDropdown />
) : (
<ModelTypeDropdown {...inputProps} />
<ModelTypeDropdown isCanary={isCanary} {...inputProps} />
)}
</Input>
</Container>

View File

@@ -116,6 +116,7 @@ export function MethodModelingView({
return (
<MethodModeling
language={viewState?.language}
isCanary={viewState?.isCanary ?? false}
modelingStatus={modelingStatus}
method={method}
modeledMethods={modeledMethods}

View File

@@ -19,6 +19,7 @@ import type { ModelingStatus } from "../../model-editor/shared/modeling-status";
export type MultipleModeledMethodsPanelProps = {
language: QueryLanguage;
isCanary: boolean;
method: Method;
modeledMethods: ModeledMethod[];
modelingStatus: ModelingStatus;
@@ -61,6 +62,7 @@ const ModificationActions = styled.div`
export const MultipleModeledMethodsPanel = ({
language,
isCanary,
method,
modeledMethods,
modelingStatus,
@@ -157,6 +159,7 @@ export const MultipleModeledMethodsPanel = ({
{modeledMethods.length > 0 ? (
<MethodModelingInputs
language={language}
isCanary={isCanary}
method={method}
modeledMethod={modeledMethods[selectedIndex]}
modelPending={isModelPending(
@@ -170,6 +173,7 @@ export const MultipleModeledMethodsPanel = ({
) : (
<MethodModelingInputs
language={language}
isCanary={isCanary}
method={method}
modeledMethod={undefined}
modelPending={isModelPending(

View File

@@ -18,6 +18,7 @@ describe(MethodModeling.name, () => {
render({
language: QueryLanguage.Java,
isCanary: false,
modelingStatus: "saved",
method,
modeledMethods: [modeledMethod],

View File

@@ -19,6 +19,7 @@ describe(MethodModelingInputs.name, () => {
const modeledMethod = createSinkModeledMethod();
const modelPending = false;
const isModelingInProgress = false;
const isCanary = false;
const onChange = jest.fn();
it("renders the method modeling inputs", () => {
@@ -28,6 +29,7 @@ describe(MethodModelingInputs.name, () => {
modeledMethod,
modelPending,
isModelingInProgress,
isCanary,
onChange,
});
@@ -55,6 +57,7 @@ describe(MethodModelingInputs.name, () => {
modeledMethod,
modelPending,
isModelingInProgress,
isCanary,
onChange,
});
@@ -78,6 +81,7 @@ describe(MethodModelingInputs.name, () => {
modeledMethod,
modelPending,
isModelingInProgress,
isCanary,
onChange,
});
@@ -93,6 +97,7 @@ describe(MethodModelingInputs.name, () => {
modeledMethod={updatedModeledMethod}
modelPending={modelPending}
isModelingInProgress={isModelingInProgress}
isCanary={isCanary}
onChange={onChange}
/>,
);
@@ -123,6 +128,7 @@ describe(MethodModelingInputs.name, () => {
modeledMethod,
modelPending,
isModelingInProgress: true,
isCanary,
onChange,
});

View File

@@ -16,6 +16,7 @@ describe(MultipleModeledMethodsPanel.name, () => {
reactRender(<MultipleModeledMethodsPanel {...props} />);
const language = QueryLanguage.Java;
const isCanary = false;
const method = createMethod();
const isModelingInProgress = false;
const isProcessedByAutoModel = false;
@@ -28,6 +29,7 @@ describe(MultipleModeledMethodsPanel.name, () => {
it("renders the method modeling inputs once", () => {
render({
language,
isCanary,
method,
modeledMethods,
modelingStatus,
@@ -47,6 +49,7 @@ describe(MultipleModeledMethodsPanel.name, () => {
it("disables all pagination", () => {
render({
language,
isCanary,
method,
modeledMethods,
modelingStatus,
@@ -70,6 +73,7 @@ describe(MultipleModeledMethodsPanel.name, () => {
it("cannot add or delete modeling", () => {
render({
language,
isCanary,
method,
modeledMethods,
modelingStatus,
@@ -102,6 +106,7 @@ describe(MultipleModeledMethodsPanel.name, () => {
it("renders the method modeling inputs once", () => {
render({
language,
isCanary,
method,
modeledMethods,
modelingStatus,
@@ -121,6 +126,7 @@ describe(MultipleModeledMethodsPanel.name, () => {
it("disables all pagination", () => {
render({
language,
isCanary,
method,
modeledMethods,
modelingStatus,
@@ -143,6 +149,7 @@ describe(MultipleModeledMethodsPanel.name, () => {
it("cannot delete modeling", () => {
render({
language,
isCanary,
method,
modeledMethods,
modelingStatus,
@@ -161,6 +168,7 @@ describe(MultipleModeledMethodsPanel.name, () => {
it("can add modeling", async () => {
render({
language,
isCanary,
method,
modeledMethods,
modelingStatus,
@@ -188,6 +196,7 @@ describe(MultipleModeledMethodsPanel.name, () => {
it("changes selection to the newly added modeling", async () => {
const { rerender } = render({
language,
isCanary,
method,
modeledMethods,
modelingStatus,
@@ -201,6 +210,7 @@ describe(MultipleModeledMethodsPanel.name, () => {
rerender(
<MultipleModeledMethodsPanel
language={language}
isCanary={isCanary}
method={method}
modeledMethods={
onChange.mock.calls[onChange.mock.calls.length - 1][1]
@@ -229,6 +239,7 @@ describe(MultipleModeledMethodsPanel.name, () => {
it("renders the method modeling inputs once", () => {
render({
language,
isCanary,
method,
modeledMethods,
isModelingInProgress,
@@ -248,6 +259,7 @@ describe(MultipleModeledMethodsPanel.name, () => {
it("renders the pagination", () => {
render({
language,
isCanary,
method,
modeledMethods,
isModelingInProgress,
@@ -264,6 +276,7 @@ describe(MultipleModeledMethodsPanel.name, () => {
it("disables the correct pagination", async () => {
render({
language,
isCanary,
method,
modeledMethods,
isModelingInProgress,
@@ -285,6 +298,7 @@ describe(MultipleModeledMethodsPanel.name, () => {
it("can use the pagination", async () => {
render({
language,
isCanary,
method,
modeledMethods,
isModelingInProgress,
@@ -323,6 +337,7 @@ describe(MultipleModeledMethodsPanel.name, () => {
it("correctly updates selected pagination index when the number of models decreases", async () => {
const { rerender } = render({
language,
isCanary,
method,
modeledMethods,
isModelingInProgress,
@@ -336,6 +351,7 @@ describe(MultipleModeledMethodsPanel.name, () => {
rerender(
<MultipleModeledMethodsPanel
language={language}
isCanary={isCanary}
method={method}
modeledMethods={[modeledMethods[1]]}
isModelingInProgress={isModelingInProgress}
@@ -356,6 +372,7 @@ describe(MultipleModeledMethodsPanel.name, () => {
it("does not show errors", () => {
render({
language,
isCanary,
method,
modeledMethods,
isModelingInProgress,
@@ -370,6 +387,7 @@ describe(MultipleModeledMethodsPanel.name, () => {
it("can update the first modeling", async () => {
render({
language,
isCanary,
method,
modeledMethods,
isModelingInProgress,
@@ -404,6 +422,7 @@ describe(MultipleModeledMethodsPanel.name, () => {
it("can update the second modeling", async () => {
render({
language,
isCanary,
method,
modeledMethods,
isModelingInProgress,
@@ -440,6 +459,7 @@ describe(MultipleModeledMethodsPanel.name, () => {
it("can delete modeling", async () => {
render({
language,
isCanary,
method,
modeledMethods,
isModelingInProgress,
@@ -459,6 +479,7 @@ describe(MultipleModeledMethodsPanel.name, () => {
it("can add modeling", async () => {
render({
language,
isCanary,
method,
modeledMethods,
isModelingInProgress,
@@ -486,6 +507,7 @@ describe(MultipleModeledMethodsPanel.name, () => {
it("shows an error when adding a neutral modeling", async () => {
const { rerender } = render({
language,
isCanary,
method,
modeledMethods,
isModelingInProgress,
@@ -499,6 +521,7 @@ describe(MultipleModeledMethodsPanel.name, () => {
rerender(
<MultipleModeledMethodsPanel
language={language}
isCanary={isCanary}
method={method}
modeledMethods={
onChange.mock.calls[onChange.mock.calls.length - 1][1]
@@ -521,6 +544,7 @@ describe(MultipleModeledMethodsPanel.name, () => {
rerender(
<MultipleModeledMethodsPanel
language={language}
isCanary={isCanary}
method={method}
modeledMethods={
onChange.mock.calls[onChange.mock.calls.length - 1][1]
@@ -541,6 +565,7 @@ describe(MultipleModeledMethodsPanel.name, () => {
rerender(
<MultipleModeledMethodsPanel
language={language}
isCanary={isCanary}
method={method}
modeledMethods={
onChange.mock.calls[onChange.mock.calls.length - 1][1]
@@ -561,6 +586,7 @@ describe(MultipleModeledMethodsPanel.name, () => {
it("changes selection to the newly added modeling", async () => {
const { rerender } = render({
language,
isCanary,
method,
modeledMethods,
isModelingInProgress,
@@ -576,6 +602,7 @@ describe(MultipleModeledMethodsPanel.name, () => {
rerender(
<MultipleModeledMethodsPanel
language={language}
isCanary={isCanary}
method={method}
modeledMethods={
onChange.mock.calls[onChange.mock.calls.length - 1][1]
@@ -613,6 +640,7 @@ describe(MultipleModeledMethodsPanel.name, () => {
it("can use the pagination", async () => {
render({
language,
isCanary,
method,
modeledMethods,
isModelingInProgress,
@@ -705,6 +733,7 @@ describe(MultipleModeledMethodsPanel.name, () => {
it("preserves selection when a modeling other than the selected modeling is removed", async () => {
const { rerender } = render({
language,
isCanary,
method,
modeledMethods,
isModelingInProgress,
@@ -718,6 +747,7 @@ describe(MultipleModeledMethodsPanel.name, () => {
rerender(
<MultipleModeledMethodsPanel
language={language}
isCanary={isCanary}
method={method}
modeledMethods={modeledMethods.slice(0, 2)}
isModelingInProgress={isModelingInProgress}
@@ -733,6 +763,7 @@ describe(MultipleModeledMethodsPanel.name, () => {
it("reduces selection when the selected modeling is removed", async () => {
const { rerender } = render({
language,
isCanary,
method,
modeledMethods,
isModelingInProgress,
@@ -748,6 +779,7 @@ describe(MultipleModeledMethodsPanel.name, () => {
rerender(
<MultipleModeledMethodsPanel
language={language}
isCanary={isCanary}
method={method}
modeledMethods={modeledMethods.slice(0, 2)}
isModelingInProgress={isModelingInProgress}
@@ -777,6 +809,7 @@ describe(MultipleModeledMethodsPanel.name, () => {
it("can add modeling", () => {
render({
language,
isCanary,
method,
modeledMethods,
isModelingInProgress,
@@ -793,6 +826,7 @@ describe(MultipleModeledMethodsPanel.name, () => {
it("can delete first modeling", async () => {
render({
language,
isCanary,
method,
modeledMethods,
isModelingInProgress,
@@ -812,6 +846,7 @@ describe(MultipleModeledMethodsPanel.name, () => {
it("can delete second modeling", async () => {
render({
language,
isCanary,
method,
modeledMethods,
isModelingInProgress,
@@ -832,6 +867,7 @@ describe(MultipleModeledMethodsPanel.name, () => {
it("can add modeling after deleting second modeling", async () => {
const { rerender } = render({
language,
isCanary,
method,
modeledMethods,
isModelingInProgress,
@@ -851,6 +887,7 @@ describe(MultipleModeledMethodsPanel.name, () => {
rerender(
<MultipleModeledMethodsPanel
language={language}
isCanary={isCanary}
method={method}
modeledMethods={modeledMethods.slice(0, 1)}
isModelingInProgress={isModelingInProgress}
@@ -891,6 +928,7 @@ describe(MultipleModeledMethodsPanel.name, () => {
it("shows errors", () => {
render({
language,
isCanary,
method,
modeledMethods,
isModelingInProgress,
@@ -905,6 +943,7 @@ describe(MultipleModeledMethodsPanel.name, () => {
it("shows the correct error message", async () => {
render({
language,
isCanary,
method,
modeledMethods,
isModelingInProgress,

View File

@@ -37,6 +37,7 @@ import { createEmptyModeledMethod } from "../../model-editor/modeled-method-empt
import type { AccessPathOption } from "../../model-editor/suggestions";
import { ModelInputSuggestBox } from "./ModelInputSuggestBox";
import { ModelOutputSuggestBox } from "./ModelOutputSuggestBox";
import { getModelsAsDataLanguage } from "../../model-editor/languages";
const ApiOrMethodRow = styled.div`
min-height: calc(var(--input-height) * 1px);
@@ -192,9 +193,37 @@ const ModelableMethodRow = forwardRef<HTMLElement | undefined, MethodRowProps>(
[method],
);
const modelingStatus = getModelingStatus(modeledMethods, methodIsUnsaved);
// Only show modeled methods that are non-hidden. These are also the ones that are
// used for determining the modeling status.
const shownModeledMethods = useMemo(() => {
const modelsAsDataLanguage = getModelsAsDataLanguage(viewState.language);
const addModelButtonDisabled = !canAddNewModeledMethod(modeledMethods);
return modeledMethodsToDisplay(
modeledMethods.filter((modeledMethod) => {
if (modeledMethod.type === "none") {
return true;
}
const predicate = modelsAsDataLanguage.predicates[modeledMethod.type];
if (!predicate) {
return true;
}
return !predicate.isHidden?.({
method,
isCanary: viewState.isCanary,
});
}),
method,
);
}, [method, modeledMethods, viewState]);
const modelingStatus = getModelingStatus(
shownModeledMethods,
methodIsUnsaved,
);
const addModelButtonDisabled = !canAddNewModeledMethod(shownModeledMethods);
return (
<DataGridRow
@@ -206,7 +235,7 @@ const ModelableMethodRow = forwardRef<HTMLElement | undefined, MethodRowProps>(
}}
>
<DataGridCell
gridRow={`span ${modeledMethods.length + validationErrors.length}`}
gridRow={`span ${shownModeledMethods.length + validationErrors.length}`}
ref={ref}
>
<ApiOrMethodRow>
@@ -257,7 +286,7 @@ const ModelableMethodRow = forwardRef<HTMLElement | undefined, MethodRowProps>(
)}
{!props.modelingInProgress && (
<>
{modeledMethods.map((modeledMethod, index) => {
{shownModeledMethods.map((modeledMethod, index) => {
const modelPending = isModelPending(
modeledMethod,
modelingStatus,
@@ -269,6 +298,7 @@ const ModelableMethodRow = forwardRef<HTMLElement | undefined, MethodRowProps>(
<DataGridCell>
<ModelTypeDropdown
language={viewState.language}
isCanary={viewState.isCanary}
method={method}
modeledMethod={modeledMethod}
modelPending={modelPending}

View File

@@ -16,6 +16,7 @@ import { InputDropdown } from "./InputDropdown";
type Props = {
language: QueryLanguage;
isCanary: boolean;
method: Method;
modeledMethod: ModeledMethod | undefined;
modelPending: boolean;
@@ -34,6 +35,7 @@ type Option = { value: ModeledMethodType; label: string };
export const ModelTypeDropdown = ({
language,
isCanary,
method,
modeledMethod,
modelPending,
@@ -55,6 +57,10 @@ export const ModelTypeDropdown = ({
return null;
}
if (predicate.isHidden && predicate.isHidden({ method, isCanary })) {
return null;
}
return {
value: type,
label: typeLabels[type],
@@ -64,7 +70,7 @@ export const ModelTypeDropdown = ({
];
return baseOptions;
}, [language, method.endpointType]);
}, [language, isCanary, method]);
const handleChange = useCallback(
(e: ChangeEvent<HTMLSelectElement>) => {

View File

@@ -23,6 +23,7 @@ describe(ModelTypeDropdown.name, () => {
modelPending={false}
onChange={onChange}
method={method}
isCanary={false}
/>,
);
@@ -34,7 +35,7 @@ describe(ModelTypeDropdown.name, () => {
);
});
it("allows changing the type to 'Type' for Ruby", async () => {
it("allows changing the type to 'Type' for Ruby in canary mode", async () => {
const method = createMethod();
const modeledMethod = createNoneModeledMethod();
@@ -45,6 +46,7 @@ describe(ModelTypeDropdown.name, () => {
modelPending={false}
onChange={onChange}
method={method}
isCanary={true}
/>,
);
@@ -56,6 +58,26 @@ describe(ModelTypeDropdown.name, () => {
);
});
it("does not allow changing the type to 'Type' for Ruby in non-canary mode", async () => {
const method = createMethod();
const modeledMethod = createNoneModeledMethod();
render(
<ModelTypeDropdown
language={QueryLanguage.Ruby}
modeledMethod={modeledMethod}
modelPending={false}
onChange={onChange}
method={method}
isCanary={false}
/>,
);
expect(
screen.queryByRole("option", { name: "Type" }),
).not.toBeInTheDocument();
});
it("does not allow changing the type to 'Type' for Java", async () => {
const method = createMethod();
const modeledMethod = createNoneModeledMethod();
@@ -67,6 +89,7 @@ describe(ModelTypeDropdown.name, () => {
modelPending={false}
onChange={onChange}
method={method}
isCanary={false}
/>,
);

View File

@@ -15,6 +15,7 @@ export function createMockModelEditorViewState(
showModeSwitchButton: true,
extensionPack: createMockExtensionPack(),
sourceArchiveAvailable: true,
isCanary: false,
...data,
};
}