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

move sql-editor component from grafana/experimental #104

Merged
merged 3 commits into from
Oct 24, 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
180 changes: 178 additions & 2 deletions cspell.config.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,180 @@
{
"ignorePaths": [".github/**", "node_modules/**", "dist/**", "yarn.lock"],
"words": ["cascader", "lefthook", "Datasource", "datasource", "gdev", "pickone", "arrowdown", "inlinesvg"]
"ignorePaths": [
".github/**",
"node_modules/**",
"dist/**",
"yarn.lock"
],
"words": [
"APPLOCK",
"arrowdown",
"ASSEMBLYPROPERTY",
"ASYMKEY",
"ASYMKEYPROPERTY",
"AUTOINCREMENT",
"cascader",
"CERTENCODED",
"CERTPRIVATEKEY",
"CERTPROPERTY",
"CHANGETABLE",
"CHARINDEX",
"COLLATIONPROPERTY",
"COLUMNPROPERTY",
"Conjs",
"CONNECTIONPROPERTY",
"CONTAINSTABLE",
"copyfiles",
"CUME",
"Customcolor",
"DATABASEPROPERTYEX",
"DATALENGTH",
"datasource",
"Datasource",
"DATEADD",
"DATEDIFF",
"DATEFIRST",
"DATEFROMPARTS",
"DATENAME",
"DATEPART",
"Datetime",
"DATETIMEFROMPARTS",
"DATETIMEOFFSETFROMPARTS",
"DBACCESS",
"DBCC",
"DBTS",
"DECRYPTBYASYMKEY",
"DECRYPTBYCERT",
"DECRYPTBYKEY",
"DECRYPTBYKEYAUTOASYMKEY",
"DECRYPTBYKEYAUTOCERT",
"DECRYPTBYPASSPHRASE",
"ENCRYPTBYASYMKEY",
"ENCRYPTBYCERT",
"ENCRYPTBYKEY",
"ENCRYPTBYPASSPHRASE",
"EOMONTH",
"ERRLVL",
"EVENTDATA",
"FILEGROUP",
"FILEGROUPPROPERTY",
"FILEPROPERTY",
"FILESTREAM",
"FILETABLEROOTPATH",
"FILLFACTOR",
"FORMATMESSAGE",
"FREETEXT",
"FREETEXTTABLE",
"FROMPARTS",
"FULLTEXTCATALOGPROPERTY",
"FULLTEXTSERVICEPROPERTY",
"gdev",
"GETANSINULL",
"GETFILENAMESPACEPATH",
"GETPATHLOCATOR",
"HASHBYTES",
"HOLDLOCK",
"IDENTITYCOL",
"INDEXKEY",
"INDEXPROPERTY",
"inlinesvg",
"ISNUMERIC",
"LANGID",
"lefthook",
"LOGINPROPERTY",
"LTRIM",
"mimetypes",
"mockdate",
"NESTLEVEL",
"NEWID",
"NEWSEQUENTIALID",
"NOCHECK",
"NONCLUSTERED",
"NOTNULL",
"NTILE",
"OBJECTPROPERTY",
"OBJECTPROPERTYEX",
"OBJECTSIGNED",
"OPENDATASOURCE",
"OPENQUERY",
"OPENROWSET",
"OPENXML",
"opers",
"pangea",
"PARSENAME",
"PATINDEX",
"pickone",
"prismjs",
"PROCID",
"PUBLISHINGSERVERNAME",
"PWDCOMPARE",
"PWDENCRYPT",
"QUOTENAME",
"quuz",
"RAISERROR",
"RAQB",
"READTEXT",
"REINDEX",
"REMSERVER",
"ROLEMEMBER",
"ROWCOUNT",
"ROWGUID",
"ROWGUIDCOL",
"Rowset",
"ROWVERSION",
"RTRIM",
"SACTION",
"SAVEPOINT",
"SECURITYAUDIT",
"SEMANTICKEYPHRASETABLE",
"SEMANTICSIMILARITYDETAILSTABLE",
"SEMANTICSIMILARITYTABLE",
"SERVERPROPERTY",
"SERVICENAME",
"SESSIONPROPERTY",
"SETUSER",
"SIGNBYASYMKEY",
"SIGNBYCERT",
"SMALLDATETIMEFROMPARTS",
"SNAME",
"SOUNDEX",
"SPID",
"SQLCA",
"SQLCODE",
"sqlds",
"sqleditor",
"SQLERROR",
"SQLSTATE",
"sqlutil",
"SQLWARNING",
"SRVROLEMEMBER",
"STDEV",
"STDEVP",
"SUSER",
"SWITCHOFFSET",
"SYMKEYPROPERTY",
"SYSDATETIME",
"SYSDATETIMEOFFSET",
"SYSUTCDATETIME",
"TABLESAMPLE",
"tempvar",
"TEXTPTR",
"TEXTSIZE",
"TEXTVALID",
"TIMEFROMPARTS",
"TIMETICKS",
"TODATETIMEOFFSET",
"TRANCOUNT",
"TSEQUAL",
"typecheck",
"TYPEPROPERTY",
"UNIXTIME",
"UNPIVOT",
"UPDATETEXT",
"VARP",
"VERIFYSIGNEDBYASYMKEY",
"VERIFYSIGNEDBYCERT",
"WAITFOR",
"WRITETEXT",
"XACT"
]
}
2 changes: 1 addition & 1 deletion src/components/QueryEditor/NumberInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ interface State {
* This is an Input field that will call `onChange` for blur and enter
*
* @internal this is not exported to the `@grafana/ui` library, it is used
* by options editor (number and slider), and direclty with in grafana core
* by options editor (number and slider), and directly with in grafana core
*/

export class NumberInput extends PureComponent<Props, State> {
Expand Down
20 changes: 10 additions & 10 deletions src/components/QueryEditor/query-editor-raw/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

- `SuggestionKind` - a descriptive string representing a type of a suggestion, i.e. `SelectKeyword`, `Tables`, `LogicalOperators` etc.
- `LinkedToken` - linked list element representing each individual token with a query. Allows traversing the query back and forth. Used by `StatementPositionResolver`(see below)
- `StatementPosition` - a desctiptive string representing cursor/token position within the query. Each statement position is defined together with `StatementPositionResolver` that, given some position context, returns a boolean value indicating whether or not we are in a given `StatementPosition` position.
- `StatementPosition` - a descriptive string representing cursor/token position within the query. Each statement position is defined together with `StatementPositionResolver` that, given some position context, returns a boolean value indicating whether or not we are in a given `StatementPosition` position.
```ts
export type StatementPositionResolver = (
currentToken: LinkedToken | null,
Expand All @@ -13,19 +13,19 @@
previousIsSlash: Boolean // To be removed as it's CloudWatch specific
) => Boolean;
```
- `SuggestionKind` and `StatementPosition` are glued together via suggestions kind registry (language specific!). This registry contains items of `SuggestionKindRegistyItem` type of the following interface:
- `SuggestionKind` and `StatementPosition` are glued together via suggestions kind registry (language specific!). This registry contains items of `SuggestionKindRegistryItem` type of the following interface:
```ts
export interface SuggestionKindRegistyItem extends RegistryItem {
export interface SuggestionKindRegistryItem extends RegistryItem {
id: StatementPosition;
kind: SuggestionKind[];
}
```
This item defines what kinds of suggestions should be provided in a given statement position
- Registries. There are couple of different registries used that drive the autocomplete mechanism.
- **Language specific**: functions registry, operators registry, suggestion kinds registries and statement position resolvers registires. Those registires contain SQL defaults as well as allow extension per language type.
- **Instance specific**: Registry of `SuggestionsRegistyItem` items that glue particular `SuggestionKind` with an async function that provides completion items for it.
- **Language specific**: functions registry, operators registry, suggestion kinds registries and statement position resolvers registries. Those registries contain SQL defaults as well as allow extension per language type.
- **Instance specific**: Registry of `SuggestionsRegistryItem` items that glue particular `SuggestionKind` with an async function that provides completion items for it.
```ts
export interface SuggestionsRegistyItem extends RegistryItem {
export interface SuggestionsRegistryItem extends RegistryItem {
id: SuggestionKind;
suggestions: (position: PositionContext, m: typeof monacoTypes) => Promise<CustomSuggestion[]>;
}
Expand All @@ -39,7 +39,7 @@ Goals
- [ ] Allow providing suggestions for standard-ish SQL syntax (THIS PR)
- [ ] Allow providing custom SQL dialects and suggestions for them (TODO - CloudWatch implementation sets a good base for how to provide custom dialect definition)

`SQLEditor` component builds on top of `CodeEditor` component, but we may want to base it on `ReactMonacoEditor` component instead to be less prone to `CodeEditor` API changes and have full controll over the Monaco API. For now the `CodeEditor` is good enough for a simplification.
`SQLEditor` component builds on top of `CodeEditor` component, but we may want to base it on `ReactMonacoEditor` component instead to be less prone to `CodeEditor` API changes and have full control over the Monaco API. For now the `CodeEditor` is good enough for a simplification.

`SQLEditor` API:

Expand Down Expand Up @@ -115,17 +115,17 @@ export interface SQLCompletionItemProvider

/**
* Allows providing a custom function for resolving db tables.
* It's up to the consumer to decide whether the columns are resolved via API calls or preloaded in the query editor(i.e. full db schema is preloades loaded).
* It's up to the consumer to decide whether the columns are resolved via API calls or preloaded in the query editor(i.e. full db schema is preloaded).
* @alpha
*/
tables?: {
resolve: () => Promise<TableDefinition[]>;
// Allows providing a custom function for calculating the table name from the query. If not specified a default implemnentation is used. I.e. BigQuery requires the table name to be fully qualified name: <project>.<dataset>.<table>
// Allows providing a custom function for calculating the table name from the query. If not specified a default implementation is used. I.e. BigQuery requires the table name to be fully qualified name: <project>.<dataset>.<table>
parseName?: (t: LinkedToken) => string;
};
/**
* Allows providing a custom function for resolving table.
* It's up to the consumer to decide whether the columns are resolved via API calls or preloaded in the query editor(i.e. full db schema is preloades loaded).
* It's up to the consumer to decide whether the columns are resolved via API calls or preloaded in the query editor(i.e. full db schema is preloaded).
* @alpha
*/
columns?: {
Expand Down
135 changes: 135 additions & 0 deletions src/components/SQLEditor/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
## SQLEditor

### Core concepts

- `SuggestionKind` - a descriptive string representing a type of a suggestion, i.e. `SelectKeyword`, `Tables`, `LogicalOperators` etc.
- `LinkedToken` - linked list element representing each individual token with a query. Allows traversing the query back and forth. Used by `StatementPositionResolver`(see below)
- `StatementPosition` - a descriptive string representing cursor/token position within the query. Each statement position is defined together with `StatementPositionResolver` that, given some position context, returns a boolean value indicating whether or not we are in a given `StatementPosition` position.
```ts
export type StatementPositionResolver = (
currentToken: LinkedToken | null,
previousKeyword: LinkedToken | null,
previousNonWhiteSpace: LinkedToken | null,
previousIsSlash: Boolean // To be removed as it's CloudWatch specific
) => Boolean;
```
- `SuggestionKind` and `StatementPosition` are glued together via suggestions kind registry (language specific!). This registry contains items of `SuggestionKindRegistryItem` type of the following interface:
```ts
export interface SuggestionKindRegistryItem extends RegistryItem {
id: StatementPosition;
kind: SuggestionKind[];
}
```
This item defines what kinds of suggestions should be provided in a given statement position
- Registries. There are couple of different registries used that drive the autocomplete mechanism.
- **Language specific**: functions registry, operators registry, suggestion kinds registries and statement position resolvers registries. Those registries contain SQL defaults as well as allow extension per language type.
- **Instance specific**: Registry of `SuggestionsRegistryItem` items that glue particular `SuggestionKind` with an async function that provides completion items for it.
```ts
export interface SuggestionsRegistryItem extends RegistryItem {
id: SuggestionKind;
suggestions: (position: PositionContext, m: typeof monacoTypes) => Promise<CustomSuggestion[]>;
}
```
Think about instance-specific registry as having i.e. mixed data source with multiple query editors for the same type of data source and you wish to provide only table suggestions that are valid for particular query row.

### SQLEditor component

Goals

- [ ] Allow providing suggestions for standard-ish SQL syntax (THIS PR)
- [ ] Allow providing custom SQL dialects and suggestions for them (TODO - CloudWatch implementation sets a good base for how to provide custom dialect definition)

`SQLEditor` component builds on top of `CodeEditor` component, but we may want to base it on `ReactMonacoEditor` component instead to be less prone to `CodeEditor` API changes and have full control over the Monaco API. For now the `CodeEditor` is good enough for a simplification.

`SQLEditor` API:

```ts
interface SQLEditorProps {
query: string;
onChange: (q: string) => void;
language?: LanguageDefinition;
}
```

The important part is the `LanguageDefinition` interface which provides way to customize the completion both on a language and instance level:

```ts
interface LanguageDefinition extends monacoTypes.languages.ILanguageExtensionPoint {
// TODO: Will allow providing a custom language definition.
loadLanguage?: (module: any) => Promise<void>;
// Provides API for customizing the autocomplete
completionProvider?: (m: Monaco) => SQLCompletionItemProvider;
}
```

The `completionProvider` function is the core of the autocomplete customization. `SQLEditor` comes with standard SQL completion items, but this function allows:

- providing dynamic suggestions: tables, columns
- providing custom `StatementPositionResolvers` that are specific for a given dialect or not implemented yet for standard SQL
- providing custom `SuggestionKind` and resolvers for this kind of suggestions.

```ts
export interface SQLCompletionItemProvider
extends Omit<monacoTypes.languages.CompletionItemProvider, 'provideCompletionItems'> {
/**
* Allows dialect specific functions to be added to the completion list.
* @alpha
*/
supportedFunctions?: () => Array<{
id: string;
name: string;
}>;

/**
* Allows dialect specific operators to be added to the completion list.
* @alpha
*/
supportedOperators?: () => Array<{
id: string;
operator: string;
type: OperatorType;
}>;

/**
* Allows adding macros that are available in the dialect datasource.
* @alpha
*/
supportedMacros?: () => Array<{
id: string;
name: string;
type: MacroType;
args: Array<string>;
}>;

/**
* Allows custom suggestion kinds to be defined and correlate them with <Custom>StatementPosition.
* @alpha
*/
customSuggestionKinds?: () => CustomSuggestionKind[];

/**
* Allows custom statement placement definition.
* @alpha
*/
customStatementPlacement?: () => CustomStatementPlacement[];

/**
* Allows providing a custom function for resolving db tables.
* It's up to the consumer to decide whether the columns are resolved via API calls or preloaded in the query editor(i.e. full db schema is preloaded).
* @alpha
*/
tables?: {
resolve: () => Promise<TableDefinition[]>;
// Allows providing a custom function for calculating the table name from the query. If not specified a default implementation is used. I.e. BigQuery requires the table name to be fully qualified name: <project>.<dataset>.<table>
parseName?: (t: LinkedToken) => string;
};
/**
* Allows providing a custom function for resolving table.
* It's up to the consumer to decide whether the columns are resolved via API calls or preloaded in the query editor(i.e. full db schema is preloaded).
* @alpha
*/
columns?: {
resolve: (table: string) => Promise<ColumnDefinition[]>;
};
}
```
Loading
Loading