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: create a chart from a run #344

Merged
merged 1 commit into from
Jun 14, 2024
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
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
/* eslint-disable */
import * as _m0 from "protobufjs/minimal";
import { Empty } from "../../../google/protobuf/empty";
import { Struct } from "../../../google/protobuf/struct";
import { ColumnDescription } from "./column_description";
import { ConnectionConfig } from "./connection_config";
import { Project } from "./project";
Expand Down Expand Up @@ -304,6 +305,15 @@ export interface ReturnSQLForInjectedModelResponse {
sql: string;
}

export interface CreateModelChartFileRequest {
modelName: string;
config: { [key: string]: any } | undefined;
}

export interface CreateModelChartFileResponse {
chartFile: string;
}

function createBaseGetProjectConfigRequest(): GetProjectConfigRequest {
return { projectRoot: "" };
}
Expand Down Expand Up @@ -3710,6 +3720,137 @@ export const ReturnSQLForInjectedModelResponse = {
},
};

function createBaseCreateModelChartFileRequest(): CreateModelChartFileRequest {
return { modelName: "", config: undefined };
}

export const CreateModelChartFileRequest = {
encode(message: CreateModelChartFileRequest, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer {
if (message.modelName !== "") {
writer.uint32(18).string(message.modelName);
}
if (message.config !== undefined) {
Struct.encode(Struct.wrap(message.config), writer.uint32(26).fork()).ldelim();
}
return writer;
},

decode(input: _m0.Reader | Uint8Array, length?: number): CreateModelChartFileRequest {
const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input);
let end = length === undefined ? reader.len : reader.pos + length;
const message = createBaseCreateModelChartFileRequest();
while (reader.pos < end) {
const tag = reader.uint32();
switch (tag >>> 3) {
case 2:
if (tag !== 18) {
break;
}

message.modelName = reader.string();
continue;
case 3:
if (tag !== 26) {
break;
}

message.config = Struct.unwrap(Struct.decode(reader, reader.uint32()));
continue;
}
if ((tag & 7) === 4 || tag === 0) {
break;
}
reader.skipType(tag & 7);
}
return message;
},

fromJSON(object: any): CreateModelChartFileRequest {
return {
modelName: isSet(object.modelName) ? gt.String(object.modelName) : "",
config: isObject(object.config) ? object.config : undefined,
};
},

toJSON(message: CreateModelChartFileRequest): unknown {
const obj: any = {};
if (message.modelName !== "") {
obj.modelName = message.modelName;
}
if (message.config !== undefined) {
obj.config = message.config;
}
return obj;
},

create<I extends Exact<DeepPartial<CreateModelChartFileRequest>, I>>(base?: I): CreateModelChartFileRequest {
return CreateModelChartFileRequest.fromPartial(base ?? ({} as any));
},
fromPartial<I extends Exact<DeepPartial<CreateModelChartFileRequest>, I>>(object: I): CreateModelChartFileRequest {
const message = createBaseCreateModelChartFileRequest();
message.modelName = object.modelName ?? "";
message.config = object.config ?? undefined;
return message;
},
};

function createBaseCreateModelChartFileResponse(): CreateModelChartFileResponse {
return { chartFile: "" };
}

export const CreateModelChartFileResponse = {
encode(message: CreateModelChartFileResponse, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer {
if (message.chartFile !== "") {
writer.uint32(10).string(message.chartFile);
}
return writer;
},

decode(input: _m0.Reader | Uint8Array, length?: number): CreateModelChartFileResponse {
const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input);
let end = length === undefined ? reader.len : reader.pos + length;
const message = createBaseCreateModelChartFileResponse();
while (reader.pos < end) {
const tag = reader.uint32();
switch (tag >>> 3) {
case 1:
if (tag !== 10) {
break;
}

message.chartFile = reader.string();
continue;
}
if ((tag & 7) === 4 || tag === 0) {
break;
}
reader.skipType(tag & 7);
}
return message;
},

fromJSON(object: any): CreateModelChartFileResponse {
return { chartFile: isSet(object.chartFile) ? gt.String(object.chartFile) : "" };
},

toJSON(message: CreateModelChartFileResponse): unknown {
const obj: any = {};
if (message.chartFile !== "") {
obj.chartFile = message.chartFile;
}
return obj;
},

create<I extends Exact<DeepPartial<CreateModelChartFileResponse>, I>>(base?: I): CreateModelChartFileResponse {
return CreateModelChartFileResponse.fromPartial(base ?? ({} as any));
},
fromPartial<I extends Exact<DeepPartial<CreateModelChartFileResponse>, I>>(object: I): CreateModelChartFileResponse {
const message = createBaseCreateModelChartFileResponse();
message.chartFile = object.chartFile ?? "";
return message;
},
};

/**
* RustWithoutDatabaseService is the service that is used and where the database is not used and so not passed in as a
* parameter in.
Expand All @@ -3728,6 +3869,8 @@ export interface RustWithoutDatabaseService {
* ancillary files for set up like .gitignore, github actions, and some folders.
*/
GenerateProjectFiles(request: GenerateProjectFilesRequest): Promise<GenerateProjectFilesResponse>;
/** CreateModelChartFile returns the yaml file for the chart for the given model with the given chart settings */
CreateModelChartFile(request: CreateModelChartFileRequest): Promise<CreateModelChartFileResponse>;
}

export const RustWithoutDatabaseServiceServiceName = "quary.service.v1.RustWithoutDatabaseService";
Expand All @@ -3742,6 +3885,7 @@ export class RustWithoutDatabaseServiceClientImpl implements RustWithoutDatabase
this.IsPathEmpty = this.IsPathEmpty.bind(this);
this.StringifyProjectFile = this.StringifyProjectFile.bind(this);
this.GenerateProjectFiles = this.GenerateProjectFiles.bind(this);
this.CreateModelChartFile = this.CreateModelChartFile.bind(this);
}
GetProjectConfig(request: GetProjectConfigRequest): Promise<GetProjectConfigResponse> {
const data = GetProjectConfigRequest.encode(request).finish();
Expand Down Expand Up @@ -3772,6 +3916,12 @@ export class RustWithoutDatabaseServiceClientImpl implements RustWithoutDatabase
const promise = this.rpc.request(this.service, "GenerateProjectFiles", data);
return promise.then((data) => GenerateProjectFilesResponse.decode(_m0.Reader.create(data)));
}

CreateModelChartFile(request: CreateModelChartFileRequest): Promise<CreateModelChartFileResponse> {
const data = CreateModelChartFileRequest.encode(request).finish();
const promise = this.rpc.request(this.service, "CreateModelChartFile", data);
return promise.then((data) => CreateModelChartFileResponse.decode(_m0.Reader.create(data)));
}
}

/**
Expand Down Expand Up @@ -4034,6 +4184,10 @@ type KeysOfUnion<T> = T extends T ? keyof T : never;
export type Exact<P, I extends P> = P extends Builtin ? P
: P & { [K in keyof P]: Exact<P[K], I[K]> } & { [K in Exclude<keyof I, KeysOfUnion<P>>]: never };

function isObject(value: any): boolean {
return typeof value === "object" && value !== null;
}

function isSet(value: any): boolean {
return value !== null && value !== undefined;
}
5 changes: 5 additions & 0 deletions js/packages/quary-extension-bus/src/callBacks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
import { ConnectionConfig } from '@quary/proto/quary/service/v1/connection_config'
import { QueryResult } from '@quary/proto/quary/service/v1/query_result'
import { ChartFile } from '@quary/proto/quary/service/v1/chart_file'
import { JSONStruct } from './jsonValue'

export type Callbacks = {
useGlobalStateNotSet: null
Expand Down Expand Up @@ -40,6 +41,10 @@ export type Callbacks = {
executeSQLViewRunQuery: { limit: number | undefined }
executeSQLViewExportCSV: { data: QueryResult }
executeSQLViewCopyToClipboard: { data: QueryResult }
executeSQLViewCreateChart: {
model: string
chartSettings: JSONStruct
}
importSources: {
sources: ProjectFileSource[]
folderPath: string
Expand Down
1 change: 1 addition & 0 deletions js/packages/quary-extension-bus/src/globalViewState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ export type SqlDocumentationResultsView =
}
| {
type: 'run'
modelName: string
results: QueryResult
}
| {
Expand Down
2 changes: 2 additions & 0 deletions js/packages/quary-extension-bus/src/jsonValue.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
export type JSONStruct = { [x: string]: JSONValue }

export type JSONValue =
| string
| number
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ type Story = StoryObj<typeof ExecuteSQLView>

export const Main: Story = {
args: {
modelName: 'model_a',
results: {
type: 'run',
results: sampleQueryResult,
Expand Down
60 changes: 54 additions & 6 deletions js/packages/quary-extension-ui/src/views/ExecuteSQL.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
import React, { useState } from 'react'
import { PlayIcon, ClipboardDocumentListIcon } from '@heroicons/react/20/solid'
import React, { useRef, useState } from 'react'
import {
PlayIcon,
ClipboardDocumentListIcon,
ChartBarSquareIcon,
} from '@heroicons/react/20/solid'
import { DownloadIcon } from '@radix-ui/react-icons'
import { useCallBackFrontEnd } from '@shared/callBacks'
import { SqlDocumentationResultsView } from '@shared/globalViewState'
import { codeToString } from '@shared/result.ts'
import { JSONStruct, JSONValue } from '@shared/jsonValue.ts'
import {
Tooltip,
TooltipContent,
Expand Down Expand Up @@ -38,20 +43,31 @@ export const ExecuteSQLView: React.FC<Props> = ({ results, limit }) => {
executeSQLViewRunQuery,
executeSQLViewExportCSV,
executeSQLViewCopyToClipboard,
executeSQLViewCreateChart,
} = useCallBackFrontEnd(
[
'executeSQLViewRunQuery',
'executeSQLViewExportCSV',
'executeSQLViewCopyToClipboard',
'executeSQLViewCreateChart',
],
vscode.postMessage,
)
const loading = results.type === 'loading'

const chartState = useRef<JSONStruct>({})
const createChart = async () => {
await executeSQLViewCreateChart({
chartSettings: chartState.current,
model: results.type === 'run' ? results.modelName : '',
})
}

return (
<>
<div className="pt-5">
<TableToolbar
createChart={createChart}
loading={loading}
stagedLimit={stagedLimit}
setStagedLimit={setStagedLimit}
Expand All @@ -78,8 +94,11 @@ export const ExecuteSQLView: React.FC<Props> = ({ results, limit }) => {
}}
/>
<Separator className="my-4" />

<Results results={results} limit={limit} />
<Results
results={results}
limit={limit}
chartSettingsRef={chartState}
/>
</div>
<Separator className="my-4" />
</>
Expand All @@ -93,7 +112,9 @@ const TableToolbar: React.FC<{
reload: (message: { limit: number | undefined }) => void
exportCSV: () => void
copyToClipboard: () => void
createChart: () => void
}> = ({
createChart,
loading,
stagedLimit,
setStagedLimit,
Expand Down Expand Up @@ -140,6 +161,18 @@ const TableToolbar: React.FC<{
</TooltipProvider>
</div>
<div className="flex gap-2">
<TooltipProvider>
<Tooltip>
<TooltipTrigger>
<Button variant="outline" disabled={loading} onClick={createChart}>
<ChartBarSquareIcon className="h-5 w-5" />
</Button>
</TooltipTrigger>
<TooltipContent>
<p>Create Chart</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
<TooltipProvider>
<Tooltip>
<TooltipTrigger>
Expand Down Expand Up @@ -176,7 +209,17 @@ const TableToolbar: React.FC<{
</div>
)

const Results: React.FC<Props> = ({ results, limit }) => {
interface ResultsProps {
results: SqlDocumentationResultsView
limit: number | undefined
chartSettingsRef: React.MutableRefObject<JSONValue>
}

const Results: React.FC<ResultsProps> = ({
results,
limit,
chartSettingsRef,
}) => {
switch (results.type) {
case 'error': {
return (
Expand All @@ -201,7 +244,12 @@ const Results: React.FC<Props> = ({ results, limit }) => {
<DataTable result={results.results} limit={limit} />
</TabsContent>
<TabsContent value="perspective">
<Perspective results={results.results} />
<Perspective
results={results.results}
updateConfigListener={(chartDefinition) => {
chartSettingsRef.current = chartDefinition
}}
/>
</TabsContent>
</Tabs>
)
Expand Down
7 changes: 2 additions & 5 deletions js/packages/quary-extension/src/web/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -524,12 +524,12 @@ export const returnCommands = (
)
if (!asset) {
return Err({
code: ErrorCodes.INTERNAL,
code: ErrorCodes.INVALID_ARGUMENT,
message: `Active file is not a model: ${JSON.stringify(activeFileName)}`,
})
}

const preInitServices = await getPreInitServices(extensionContext)
const preInitServices = getPreInitServices(extensionContext)
return executeSQLOnModel(
asset.name,
services,
Expand Down Expand Up @@ -594,15 +594,12 @@ export const returnCommandsWithLogs = (
function extractModelNameFromFilePath(filePath: string): Result<string> {
const pathSegments = filePath.split(/[/\\]/)
const fileName = pathSegments.pop()

if (!fileName) {
return Err({
code: ErrorCodes.INTERNAL,
message: `No file name found in the path: ${filePath}`,
})
}

const modelName = fileName.replace('.sql', '').replace('.snapshot', '')

return Ok(modelName)
}
Loading
Loading