Compare commits

...

16 commits

Author SHA1 Message Date
Alex Weininger 8b286657b0
Update bug.yaml 2021-08-18 17:41:48 -07:00
Alex Weininger 6a2fbb956b
Update bug.yaml 2021-08-18 17:41:22 -07:00
Alex Weininger f9d42ad703
Update bug.yaml 2021-08-18 17:40:59 -07:00
Alex Weininger e76dbc9e89
Create bug.yaml 2021-08-18 17:25:27 -07:00
alexweininger 883893aaab Fixup 2021-06-17 16:55:43 -07:00
Alex Weininger 47c405a9f5
Add setting to format json strings when viewing a document (#22)
* Add setting to format json strings when viewing a document

* Update changelog
2021-06-17 16:49:08 -07:00
alexweininger 2e77f93759 Fixup changelog 2021-06-03 07:21:04 -07:00
alexweininger 98a132fdef Update changelog 2021-06-03 07:14:15 -07:00
Alex Weininger 704bd72420
Able to set all collection properties when creating (#21)
* Able to set all collection properties when creating

* Update CHANGELOG.md
2021-06-03 07:12:21 -07:00
alexweininger 60a510b646 Update Readme 2021-05-31 01:50:55 -05:00
alexweininger 6bcbf57143 Change readme image sizes 2021-05-31 01:49:43 -05:00
alexweininger ac466375e5 Update readme with functions feature images 2021-05-31 01:45:03 -05:00
alexweininger bdff542ef9 update CI 2021-05-31 01:29:41 -05:00
alexweininger 38273dac92 Bump version, update changelog 2021-05-31 01:28:46 -05:00
Alex Weininger 185e1a98f4
Create function tags (#19)
* Basic functions create tag and create execution feature

* Detailed function support, create tag still broken

* create tag and pick folder

* edit changelog and bump extension version

* fix linting

* function tag creation

* Create function tags

* Fix linting
2021-05-31 01:24:51 -05:00
Alex Weininger 9da0e1ff61
Fix error when deleting user (#18) 2021-05-31 01:16:29 -05:00
46 changed files with 786 additions and 188 deletions

48
.github/ISSUE_TEMPLATE/bug.yaml vendored Normal file
View file

@ -0,0 +1,48 @@
name: Bug Report
description: File a bug report
title: "[Bug] "
labels: [bug]
body:
- type: markdown
attributes:
value: |
Thanks for taking the time to fill out this bug report!
- type: textarea
id: repro
attributes:
label: Reproduction steps
description: "How do you trigger this bug? Please walk us through it step by step."
value: |
1.
2.
3.
...
render: bash
validations:
required: true
- type: dropdown
id: version
attributes:
label: Version
description: What version of VS Code are you using?
options:
- Stable (Default)
- Insiders
validations:
required: true
- type: dropdown
id: os
attributes:
label: What operating system are you seeing the problem on?
multiple: true
options:
- All
- Windows
- macOS
- Linux
- type: textarea
id: logs
attributes:
label: Relevant log output
description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks.
render: shell

View file

@ -26,7 +26,6 @@ jobs:
with: with:
node-version: ${{ matrix.node-version }} node-version: ${{ matrix.node-version }}
- run: npm ci - run: npm ci
- run: npm run package
- run: npx vsce package - run: npx vsce package
- uses: actions/upload-artifact@v2 - uses: actions/upload-artifact@v2

View file

@ -6,6 +6,21 @@ Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how
## [Unreleased] ## [Unreleased]
## [0.1.3] - 2021-6-17
## Added
- New feature! JSON strings inside documents will now be automatically formatted as JSON when viewing documents. You can turn this feature off via the `appwrite.formatJsonStrings` setting.
## [0.1.2] - 2021-6-03
## Added
- Ability to set the `list`, `default` and `array` properties when creating a new collection. | [Issue #20](https://github.com/streamlux/vscode-appwrite/issues/20) | [PR #21](https://github.com/streamlux/vscode-appwrite/pull/21) | Thanks [@Maatteogekko](https://github.com/Maatteogekko)!
## [0.1.1] - 2021-5-31
## Added
- You can now easily create function tags from multiple places in the extension. [PR #19](https://github.com/streamlux/vscode-appwrite/pull/19)
## Fixed
- Fixed an error when deleting a user. [Issue #17](https://github.com/streamlux/vscode-appwrite/issues/17) [PR #18](https://github.com/streamlux/vscode-appwrite/pull/18) Thanks [@aadarshadhakalg](https://github.com/aadarshadhakalg)!
## [0.1.0] - 2021-5-29 ## [0.1.0] - 2021-5-29
## Functions! ## Functions!

View file

@ -18,7 +18,15 @@ From [appwrite.io](https://appwrite.io)
### Connect to multiple Appwrite projects ### Connect to multiple Appwrite projects
![Mutliple projects](media/features/projects/projectsView1.gif) <img src="media/features/projects/projectsView1.gif" height="600">
### Creating function tags with ease!
![Functions feature](media/features/functions/CreateTag.gif)
### Create and manage Appwrite cloud functions, upload tags, and view function output
<img src="media/features/functions/functionsOverview.png" height="500">
### View database documents right inside VS Code. ### View database documents right inside VS Code.

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 470 KiB

2
package-lock.json generated
View file

@ -1,6 +1,6 @@
{ {
"name": "vscode-appwrite", "name": "vscode-appwrite",
"version": "0.1.0", "version": "0.0.9",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {

View file

@ -2,7 +2,7 @@
"name": "vscode-appwrite", "name": "vscode-appwrite",
"displayName": "Appwrite", "displayName": "Appwrite",
"description": "Manage your Appwrite resources right from VS Code!", "description": "Manage your Appwrite resources right from VS Code!",
"version": "0.1.0", "version": "0.1.3",
"engines": { "engines": {
"vscode": "^1.55.0" "vscode": "^1.55.0"
}, },
@ -40,151 +40,187 @@
{ {
"command": "vscode-appwrite.Connect", "command": "vscode-appwrite.Connect",
"title": "Connect to Appwrite", "title": "Connect to Appwrite",
"category": "" "category": "Appwrite"
}, },
{ {
"command": "vscode-appwrite.CreateUser", "command": "vscode-appwrite.CreateUser",
"title": "Create user", "title": "Create user",
"icon": "$(add)" "icon": "$(add)",
"category": "Appwrite"
}, },
{ {
"command": "vscode-appwrite.DeleteUser", "command": "vscode-appwrite.DeleteUser",
"title": "Delete user", "title": "Delete user",
"icon": "$(trash)" "icon": "$(trash)",
"category": "Appwrite"
}, },
{ {
"command": "vscode-appwrite.GetUserLogs", "command": "vscode-appwrite.GetUserLogs",
"title": "Get user logs", "title": "Get user logs",
"icon": "$(output)" "icon": "$(output)",
"category": "Appwrite"
}, },
{ {
"command": "vscode-appwrite.openUserInConsole", "command": "vscode-appwrite.openUserInConsole",
"title": "View user in Appwrite console", "title": "View user in Appwrite console",
"icon": "$(link-external)" "icon": "$(link-external)",
"category": "Appwrite"
}, },
{ {
"command": "vscode-appwrite.viewUserPrefs", "command": "vscode-appwrite.viewUserPrefs",
"title": "View user preferences", "title": "View user preferences",
"icon": "$(json)" "icon": "$(json)",
"category": "Appwrite"
}, },
{ {
"command": "vscode-appwrite.refreshEntry", "command": "vscode-appwrite.refreshEntry",
"title": "Refresh", "title": "Refresh",
"icon": "$(refresh)" "icon": "$(refresh)",
"category": "Appwrite"
}, },
{ {
"command": "vscode-appwrite.copyUserId", "command": "vscode-appwrite.copyUserId",
"title": "Copy user ID", "title": "Copy user ID",
"icon": "$(clippy)" "icon": "$(clippy)",
"category": "Appwrite"
}, },
{ {
"command": "vscode-appwrite.copyUserEmail", "command": "vscode-appwrite.copyUserEmail",
"title": "Copy user email", "title": "Copy user email",
"icon": "$(clippy)" "icon": "$(clippy)",
"category": "Appwrite"
}, },
{ {
"command": "vscode-appwrite.refreshUsersList", "command": "vscode-appwrite.refreshUsersList",
"title": "Refresh users list", "title": "Refresh users list",
"icon": "$(refresh)" "icon": "$(refresh)",
"category": "Appwrite"
}, },
{ {
"command": "vscode-appwrite.OpenUsersDocumentation", "command": "vscode-appwrite.OpenUsersDocumentation",
"title": "Open documentation", "title": "Open documentation",
"icon": "$(book)" "icon": "$(book)",
"category": "Appwrite"
}, },
{ {
"command": "vscode-appwrite.OpenDatabaseDocumentation", "command": "vscode-appwrite.OpenDatabaseDocumentation",
"title": "Open documentation", "title": "Open documentation",
"icon": "$(book)" "icon": "$(book)",
"category": "Appwrite"
}, },
{ {
"command": "vscode-appwrite.viewDocumentAsJson", "command": "vscode-appwrite.viewDocumentAsJson",
"title": "View as JSON" "title": "View as JSON",
"category": "Appwrite"
}, },
{ {
"command": "vscode-appwrite.viewCollectionAsJson", "command": "vscode-appwrite.viewCollectionAsJson",
"title": "View collection as JSON" "title": "View collection as JSON",
"category": "Appwrite"
}, },
{ {
"command": "vscode-appwrite.refreshCollection", "command": "vscode-appwrite.refreshCollection",
"title": "Refresh", "title": "Refresh",
"icon": "$(refresh)" "icon": "$(refresh)",
"category": "Appwrite"
}, },
{ {
"command": "vscode-appwrite.createRule", "command": "vscode-appwrite.createRule",
"title": "Create collection rule", "title": "Create collection rule",
"icon": "$(add)" "icon": "$(add)",
"category": "Appwrite"
}, },
{ {
"command": "vscode-appwrite.removeRule", "command": "vscode-appwrite.removeRule",
"title": "Remove collection rule" "title": "Remove collection rule",
"category": "Appwrite"
}, },
{ {
"command": "vscode-appwrite.deleteDocument", "command": "vscode-appwrite.deleteDocument",
"title": "Delete document" "title": "Delete document",
"category": "Appwrite"
}, },
{ {
"command": "vscode-appwrite.deleteCollection", "command": "vscode-appwrite.deleteCollection",
"title": "Delete collection" "title": "Delete collection",
"category": "Appwrite"
}, },
{ {
"command": "vscode-appwrite.refreshCollectionsList", "command": "vscode-appwrite.refreshCollectionsList",
"title": "Refresh", "title": "Refresh",
"icon": "$(refresh)" "icon": "$(refresh)",
"category": "Appwrite"
}, },
{ {
"command": "vscode-appwrite.createCollection", "command": "vscode-appwrite.createCollection",
"title": "Create collection", "title": "Create collection",
"icon": "$(add)" "icon": "$(add)",
"category": "Appwrite"
}, },
{ {
"command": "vscode-appwrite.createPermission", "command": "vscode-appwrite.createPermission",
"title": "Create permission", "title": "Create permission",
"icon": "$(add)" "icon": "$(add)",
"category": "Appwrite"
}, },
{ {
"command": "vscode-appwrite.deletePermission", "command": "vscode-appwrite.deletePermission",
"title": "Delete permission", "title": "Delete permission",
"icon": "$(trash)" "icon": "$(trash)",
"category": "Appwrite"
}, },
{ {
"command": "vscode-appwrite.editPermission", "command": "vscode-appwrite.editPermission",
"title": "Edit permission", "title": "Edit permission",
"icon": "$(edit)" "icon": "$(edit)",
"category": "Appwrite"
}, },
{ {
"command": "vscode-appwrite.refreshHealth", "command": "vscode-appwrite.refreshHealth",
"title": "Refresh health", "title": "Refresh health",
"icon": "$(refresh)" "icon": "$(refresh)",
"category": "Appwrite"
}, },
{ {
"command": "vscode-appwrite.openHealthDocumentation", "command": "vscode-appwrite.openHealthDocumentation",
"title": "Open health documentation", "title": "Open health documentation",
"icon": "$(book)" "icon": "$(book)",
"category": "Appwrite"
}, },
{ {
"command": "vscode-appwrite.refreshStorage", "command": "vscode-appwrite.refreshStorage",
"title": "Refresh storage", "title": "Refresh storage",
"icon": "$(refresh)" "icon": "$(refresh)",
"category": "Appwrite"
}, },
{ {
"command": "vscode-appwrite.openStorageDocumentation", "command": "vscode-appwrite.openStorageDocumentation",
"title": "Open storage documentation", "title": "Open storage documentation",
"icon": "$(book)" "icon": "$(book)",
"category": "Appwrite"
}, },
{ {
"command": "vscode-appwrite.addProject", "command": "vscode-appwrite.addProject",
"title": "Add Appwrite project", "title": "Add Appwrite project",
"icon": "$(plus)" "icon": "$(plus)",
"category": "Appwrite"
}, },
{ {
"command": "vscode-appwrite.setActiveProject", "command": "vscode-appwrite.setActiveProject",
"title": "Set as active" "title": "Set as active",
"category": "Appwrite"
}, },
{ {
"command": "vscode-appwrite.refreshProjects", "command": "vscode-appwrite.refreshProjects",
"title": "Refresh projects", "title": "Refresh projects",
"icon": "$(refresh)" "icon": "$(refresh)",
"category": "Appwrite"
},
{
"command": "vscode-appwrite.refreshFunctions",
"title": "Refresh functions",
"icon": "$(refresh)",
"category": "Appwrite"
}, },
{ {
"command": "vscode-appwrite.refreshFunctions", "command": "vscode-appwrite.refreshFunctions",
@ -194,91 +230,115 @@
{ {
"command": "vscode-appwrite.removeProject", "command": "vscode-appwrite.removeProject",
"title": "Remove project", "title": "Remove project",
"icon": "$(trash)" "icon": "$(trash)",
"category": "Appwrite"
}, },
{ {
"command": "vscode-appwrite.CreateTag", "command": "vscode-appwrite.CreateTag",
"title": "Create function tag", "title": "Create function tag",
"icon": "$(cloud-upload)" "icon": "$(cloud-upload)",
"shortTitle": "Create function tag",
"category": "Appwrite"
}, },
{ {
"command": "vscode-appwrite.deleteTag", "command": "vscode-appwrite.deleteTag",
"title": "Delete tag", "title": "Delete tag",
"icon": "$(trash)" "icon": "$(trash)",
"category": "Appwrite"
}, },
{ {
"command": "vscode-appwrite.CreateExecution", "command": "vscode-appwrite.CreateExecution",
"title": "Execute" "title": "Execute",
"category": "Appwrite"
}, },
{ {
"command": "vscode-appwrite.activateTag", "command": "vscode-appwrite.activateTag",
"title": "Activate" "title": "Activate",
"category": "Appwrite"
}, },
{ {
"command": "vscode-appwrite.editValue", "command": "vscode-appwrite.editValue",
"title": "Edit", "title": "Edit",
"icon": "$(edit)" "icon": "$(edit)",
"category": "Appwrite"
}, },
{ {
"command": "vscode-appwrite.deleteFunction", "command": "vscode-appwrite.deleteFunction",
"title": "Delete", "title": "Delete",
"icon": "$(trash)" "icon": "$(trash)",
"category": "Appwrite"
}, },
{ {
"command": "vscode-appwrite.openFunctionsDocumentation", "command": "vscode-appwrite.openFunctionsDocumentation",
"title": "Open functions documentation", "title": "Open functions documentation",
"icon": "$(book)" "icon": "$(book)",
"category": "Appwrite"
}, },
{ {
"command": "vscode-appwrite.createFunction", "command": "vscode-appwrite.createFunction",
"title": "Create function", "title": "Create function",
"icon": "$(add)" "icon": "$(add)",
"category": "Appwrite"
}, },
{ {
"command": "vscode-appwrite.createFunctionVar", "command": "vscode-appwrite.createFunctionVar",
"title": "Create variable", "title": "Create variable",
"icon": "$(add)" "icon": "$(add)",
"category": "Appwrite"
}, },
{ {
"command": "vscode-appwrite.deleteFunctionVar", "command": "vscode-appwrite.deleteFunctionVar",
"title": "Delete variable", "title": "Delete variable",
"icon": "$(trash)" "icon": "$(trash)",
"category": "Appwrite"
}, },
{ {
"command": "vscode-appwrite.viewExecutionOutput", "command": "vscode-appwrite.viewExecutionOutput",
"title": "View execution stdout", "title": "View execution stdout",
"enablement": "viewItem =~ /^((execution|execution_outputOnly))$/" "enablement": "viewItem =~ /^((execution|execution_outputOnly))$/",
"category": "Appwrite"
}, },
{ {
"command": "vscode-appwrite.viewExecutionErrors", "command": "vscode-appwrite.viewExecutionErrors",
"title": "View execution stderr", "title": "View execution stderr",
"enablement": "viewItem =~ /^((execution|execution_errorOnly))$/" "enablement": "viewItem =~ /^((execution|execution_errorOnly))$/",
"category": "Appwrite"
}, },
{ {
"command": "vscode-appwrite.copyExecutionOutput", "command": "vscode-appwrite.copyExecutionOutput",
"title": "Copy execution stdout", "title": "Copy execution stdout",
"enablement": "viewItem =~ /^((execution|execution_outputOnly))$/" "enablement": "viewItem =~ /^((execution|execution_outputOnly))$/",
"category": "Appwrite"
}, },
{ {
"command": "vscode-appwrite.copyExecutionErrors", "command": "vscode-appwrite.copyExecutionErrors",
"title": "Copy execution stderr", "title": "Copy execution stderr",
"enablement": "viewItem =~ /^((execution|execution_errorOnly))$/" "enablement": "viewItem =~ /^((execution|execution_errorOnly))$/",
"category": "Appwrite"
}, },
{ {
"command": "vscode-appwrite.openExecutionsInBrowser", "command": "vscode-appwrite.openExecutionsInBrowser",
"title": "View executions in browser", "title": "View executions in browser",
"enablement": "viewItem =~ /^(executions)$/", "enablement": "viewItem =~ /^(executions)$/",
"icon": "$(link-external)" "icon": "$(link-external)",
"category": "Appwrite"
}, },
{ {
"command": "vscode-appwrite.openFunctionTagsInBrowser", "command": "vscode-appwrite.openFunctionTagsInBrowser",
"title": "Open function tags in browser", "title": "Open function tags in browser",
"icon": "$(link-external)" "icon": "$(link-external)",
"category": "Appwrite"
}, },
{ {
"command": "vscode-appwrite.openFunctionSettingsInBrowser", "command": "vscode-appwrite.openFunctionSettingsInBrowser",
"title": "Open function settings in browser", "title": "Open function settings in browser",
"icon": "$(link-external)" "icon": "$(link-external)",
"category": "Appwrite"
},
{
"command": "vscode-appwrite.viewMore",
"title": "View more",
"category": "Appwrite"
} }
], ],
"views": { "views": {
@ -570,6 +630,9 @@
} }
], ],
"commandPalette": [ "commandPalette": [
{
"command": "vscode-appwrite.CreateTag"
},
{ {
"command": "vscode-appwrite.Connect" "command": "vscode-appwrite.Connect"
}, },
@ -663,6 +726,11 @@
"type": "string", "type": "string",
"default": "", "default": "",
"markdownDescription": "Project id of the active project, see [docs](https://github.com/streamlux/vscode-appwrite/) for more information." "markdownDescription": "Project id of the active project, see [docs](https://github.com/streamlux/vscode-appwrite/) for more information."
},
"appwrite.formatJsonStrings": {
"type": "boolean",
"default": true,
"markdownDescription": "Format JSON strings when viewing documents"
} }
} }
} }

10
src/appwrite.d.ts vendored
View file

@ -276,7 +276,7 @@ export type CollectionsList = {
collections: Collection[]; collections: Collection[];
}; };
export type CreatedRule = Omit<Rule, "$id" | "$collection" | "default" | "list">; export type CreatedRule = Omit<Rule, "$id" | "$collection">;
export type Rule = { export type Rule = {
$id: string; $id: string;
@ -284,10 +284,10 @@ export type Rule = {
type: string; type: string;
key: string; key: string;
label: string; label: string;
default: string; default?: any;
array: boolean;
required: boolean; required: boolean;
list: string[]; array: boolean;
list?: string[];
}; };
export type Permissions = { export type Permissions = {
@ -305,7 +305,7 @@ export type Client = {
setSelfSigned: (value: boolean) => void; setSelfSigned: (value: boolean) => void;
}; };
export type UsersClient = { export type UsersClient = {
deleteUser: (id: string) => Promise<any>; delete: (id: string) => Promise<any>;
deleteSessions: (id: string) => Promise<any>; deleteSessions: (id: string) => Promise<any>;
create: (email: string, password: string, name?: string) => Promise<any>; create: (email: string, password: string, name?: string) => Promise<any>;
getLogs: (id: string) => Promise<Log[]>; getLogs: (id: string) => Promise<Log[]>;

View file

@ -1,10 +1,10 @@
import { Client, Execution, ExecutionList, FunctionsClient, TagList, Vars } from "../appwrite"; import { Client, Execution, ExecutionList, FunctionsClient, FunctionsList, Tag, TagList, Vars } from "../appwrite";
import { AppwriteSDK } from '../constants'; import { AppwriteSDK } from '../constants';
import AppwriteCall from '../utils/AppwriteCall'; import AppwriteCall from '../utils/AppwriteCall';
import { ReadStream } from 'node:fs'; import { ReadStream } from 'node:fs';
export class Functions { export class Functions {
private readonly functions: FunctionsClient; public readonly functions: FunctionsClient;
constructor(client: Client) { constructor(client: Client) {
this.functions = new AppwriteSDK.Functions(client); this.functions = new AppwriteSDK.Functions(client);
@ -13,7 +13,7 @@ export class Functions {
public async create(name: string, execute: string[], env: string, vars?: Vars, events?: string[], schedule?: string, timeout?: number): Promise<any> { public async create(name: string, execute: string[], env: string, vars?: Vars, events?: string[], schedule?: string, timeout?: number): Promise<any> {
return await AppwriteCall(this.functions.create(name, execute, env, vars, events, schedule, timeout)); return await AppwriteCall(this.functions.create(name, execute, env, vars, events, schedule, timeout));
} }
public async list(search?: string, offset?: number, limit?: number, orderType?: 'ASC' | 'DESC'): Promise<any> { public async list(search?: string, offset?: number, limit?: number, orderType?: 'ASC' | 'DESC'): Promise<FunctionsList | undefined> {
return await AppwriteCall(this.functions.list(search, offset, limit, orderType)); return await AppwriteCall(this.functions.list(search, offset, limit, orderType));
} }
public async get(functionId: string): Promise<any> { public async get(functionId: string): Promise<any> {
@ -28,7 +28,7 @@ export class Functions {
public async delete(functionId: string): Promise<void> { public async delete(functionId: string): Promise<void> {
return await AppwriteCall(this.functions.delete(functionId)); return await AppwriteCall(this.functions.delete(functionId));
} }
public async createTag(functionId: string, command: string, code: ReadStream): Promise<any> { public async createTag(functionId: string, command: string, code: ReadStream): Promise<Tag | undefined> {
return await AppwriteCall(this.functions.createTag(functionId, command, code)); return await AppwriteCall(this.functions.createTag(functionId, command, code));
} }
public async listTags(id: string, search?: string, limit?: number, offset?: number, orderType?: 'ASC' | 'DESC'): Promise<TagList | undefined> { public async listTags(id: string, search?: string, limit?: number, offset?: number, orderType?: 'ASC' | 'DESC'): Promise<TagList | undefined> {
@ -40,11 +40,11 @@ export class Functions {
public async deleteTag(functionId: string, tagId: string): Promise<void> { public async deleteTag(functionId: string, tagId: string): Promise<void> {
return await AppwriteCall(this.functions.deleteTag(functionId, tagId)); return await AppwriteCall(this.functions.deleteTag(functionId, tagId));
} }
public async createExecution(functionId: string, data?: string): Promise<any> { public async createExecution(functionId: string, data?: string): Promise<Execution | undefined> {
return await AppwriteCall(this.functions.createExecution(functionId, data)); return await AppwriteCall(this.functions.createExecution(functionId, data));
} }
public async listExecutions(functionId: string, search?: string, limit?: number, offset?: number, orderType?: 'ASC' | 'DESC'): Promise<ExecutionList | undefined> { public async listExecutions(functionId: string, search?: string, limit?: number, offset?: number, orderType?: 'ASC' | 'DESC'): Promise<ExecutionList | undefined> {
return await AppwriteCall(this.functions.listExecutions(functionId, search, offset, limit, orderType)); return await AppwriteCall(this.functions.listExecutions(functionId, search, limit, offset, orderType));
} }
public async getExecution(functionId: string, executionId: string): Promise<Execution | undefined> { public async getExecution(functionId: string, executionId: string): Promise<Execution | undefined> {
return await AppwriteCall(this.functions.getExecution(functionId, executionId)); return await AppwriteCall(this.functions.getExecution(functionId, executionId));

View file

@ -16,7 +16,7 @@ export class Users {
} }
public async delete(userId: string): Promise<void> { public async delete(userId: string): Promise<void> {
await AppwriteCall(this.users.deleteUser(userId), () => { await AppwriteCall(this.users.delete(userId), () => {
window.showInformationMessage(`Deleted user with id: ${userId}.`); window.showInformationMessage(`Deleted user with id: ${userId}.`);
}); });
} }

View file

@ -1,4 +1,4 @@
import { EditableTreeItem } from '../../tree/common/SimpleEditableTreeItem'; import { EditableTreeItem } from '../../tree/common/editable/SimpleEditableTreeItem';
export async function editValue(treeItem: EditableTreeItem): Promise<void> { export async function editValue(treeItem: EditableTreeItem): Promise<void> {
if (treeItem === undefined) { if (treeItem === undefined) {

View file

@ -0,0 +1,5 @@
import { AppwriteTreeItemBase } from '../../ui/AppwriteTreeItemBase';
export async function viewMore(treeItem: AppwriteTreeItemBase<any>): Promise<void> {
await treeItem.viewMore();
}

View file

@ -10,15 +10,13 @@ export async function createRule(rulesTreeItem: RulesTreeItem): Promise<void> {
return; return;
} }
const ruleContext = await createRuleWizard();
const collection = rulesTreeItem.parent.collection; const collection = rulesTreeItem.parent.collection;
const ruleContext = await createRuleWizard(collection);
if (ruleContext) { if (ruleContext) {
const newRule: CreatedRule = { const newRule: CreatedRule = {
...ruleContext, ...ruleContext,
type: ruleContext.type, type: ruleContext.type,
required: true,
array: false,
}; };
databaseClient.createRule(collection, newRule); databaseClient.createRule(collection, newRule);

View file

@ -1,8 +1,30 @@
import { workspace } from "vscode";
import { DocumentTreeItem } from "../../tree/database/DocumentTreeItem"; import { DocumentTreeItem } from "../../tree/database/DocumentTreeItem";
import { openReadOnlyJson } from "../../ui/openReadonlyContent"; import { openReadOnlyJson } from "../../ui/openReadonlyContent";
function parseJSONString(str: string): { valid: boolean; value: any } {
try {
return { value: JSON.parse(str), valid: true };
} catch (e) {
return { value: str, valid: false };
}
}
export async function viewDocumentAsJson(documentTreeItem: DocumentTreeItem): Promise<void> { export async function viewDocumentAsJson(documentTreeItem: DocumentTreeItem): Promise<void> {
const documentId = documentTreeItem.document["$id"]; const document = documentTreeItem.document;
const documentId = document["$id"];
const formatJsonStrings = workspace.getConfiguration("appwrite").get<Boolean>("formatJsonStrings");
if (formatJsonStrings) {
Object.entries(document).forEach(([key, value]) => {
if (typeof value === "string") {
const result = parseJSONString(value);
document[key] = result.value;
}
});
}
await openReadOnlyJson( await openReadOnlyJson(
{ {
label: documentId, label: documentId,

View file

@ -1,7 +1,8 @@
import { Tag } from '../../appwrite';
import { functionsClient } from '../../client'; import { functionsClient } from '../../client';
import { TagTreeItem } from '../../tree/functions/tags/TagTreeItem'; import { TagTreeItem } from '../../tree/functions/tags/TagTreeItem';
export async function activateTag(tagItem: TagTreeItem): Promise<void> { export async function activateTag(tagItem: TagTreeItem | Tag): Promise<void> {
const tag = tagItem.tag; const tag = tagItem instanceof TagTreeItem ? tagItem.tag : tagItem;
await functionsClient?.updateTag(tag.functionId, tag.$id); await functionsClient?.updateTag(tag.functionId, tag.$id);
} }

View file

@ -1,10 +1,57 @@
import { window } from 'vscode';
import { Execution } from '../../appwrite';
import { functionsClient } from '../../client'; import { functionsClient } from '../../client';
import { ext } from '../../extensionVariables'; import { ext } from '../../extensionVariables';
import { FunctionTreeItem } from '../../tree/functions/FunctionTreeItem'; import { FunctionTreeItem } from '../../tree/functions/FunctionTreeItem';
import { sleep } from '../../utils/sleep';
import { viewExecutionErrors } from './viewExecutionErrors';
import { viewExecutionOutput } from './viewExecutionOutput';
export async function createExecution(functionTreeItem: FunctionTreeItem): Promise<void> { export async function createExecution(functionTreeItem: FunctionTreeItem): Promise<void> {
const func = functionTreeItem.func; const func = functionTreeItem.func;
ext.outputChannel.appendLog(`Creating execution for function ${func.name}`); await executeFunction(func.$id);
}
await functionsClient?.createExecution(func.$id);
export async function executeFunction(functionId: string): Promise<void> {
ext.outputChannel.appendLog(`Creating execution for function with ID: ${functionId}`);
let execution = await functionsClient?.createExecution(functionId);
ext.outputChannel.appendLog(JSON.stringify(execution, null, 2));
await ext.tree?.functions?.refresh();
if (execution === undefined) {
return;
}
execution = await waitForExecution(execution);
ext.tree?.functions?.refresh();
if (execution === undefined) {
return;
}
const failed = execution.status === "failed";
const item = !failed ? "View output" : "View errors";
const action = await window.showInformationMessage(`Execution ${failed ? "failed" : "completed"} in ${execution.time.toFixed(2)}s.`, item);
if (action === item) {
if (item === "View output") {
await viewExecutionOutput(execution);
return;
}
await viewExecutionErrors(execution);
return;
}
}
async function waitForExecution(execution: Execution | undefined): Promise<Execution | undefined> {
if (execution === undefined) {
return;
}
if (execution.status === "processing" || execution.status === "waiting") {
await sleep(5000);
ext.outputChannel.appendLog("Execution still ...");
return await waitForExecution(await functionsClient?.getExecution(execution.functionId, execution.$id));
}
return execution;
} }

View file

@ -1,41 +1,164 @@
import { ProgressLocation, Uri, window } from "vscode"; import { ProgressLocation, QuickPickItem, Uri, window, workspace } from "vscode";
import { functionsClient, storageClient } from "../../client"; import { functionsClient } from "../../client";
import { getTarReadStream } from "../../utils/tar"; import { getTarReadStream } from "../../utils/tar";
import { ext } from "../../extensionVariables"; import { ext } from "../../extensionVariables";
import * as fs from "fs"; import * as fs from "fs";
import { TagsTreeItem } from "../../tree/functions/tags/TagsTreeItem"; import { TagsTreeItem } from "../../tree/functions/tags/TagsTreeItem";
import { selectWorkspaceFolder } from "../../utils/workspace"; import { selectWorkspaceFolder } from "../../utils/workspace";
export async function createTag(item: TagsTreeItem | Uri): Promise<void> { import { ProgressMessage } from "../../utils/types";
import { Tag } from "../../appwrite";
import { activateTag } from "./activateTag";
export async function createTag(item?: TagsTreeItem | Uri): Promise<void> {
if (item instanceof Uri) { if (item instanceof Uri) {
window.withProgress({ location: ProgressLocation.Notification, title: "Creating tag..." }, async (_progress, _token) => { const functions = await functionsClient?.list();
await createTagFromUri(item); if (functions === undefined) {
}); return;
}
const pick = await window.showQuickPick(
functions.functions.map<QuickPickItem>(
(func): QuickPickItem => ({ label: func.name, description: func.env, detail: func.$id })
),
{ placeHolder: "Select a function to create tag" }
);
if (pick === undefined || pick.detail === undefined) {
return;
}
const tags = await functionsClient?.listTags(pick.detail);
let value;
if (tags && tags.tags.length > 0) {
value = tags.tags[tags.tags.length - 1].command;
}
const command = await window.showInputBox({ value, prompt: "Command to run your code" });
if (command === undefined) {
return;
}
const tag = await window.withProgress(
{ location: ProgressLocation.Notification, title: "Creating tag..." },
async (progress, _token) => {
if (pick.detail === undefined) {
return;
}
return await createTagFromUri(pick.detail, command, item, progress);
}
);
if (tag) {
await tagNotification(tag);
}
return; return;
} }
if (item instanceof TagsTreeItem) { if (item instanceof TagsTreeItem) {
const func = item.parent.func;
const folder = await selectWorkspaceFolder("Select folder of your function code."); const folder = await selectWorkspaceFolder("Select folder of your function code.");
console.log(folder); if (folder === undefined || folder === "") {
window.withProgress({ location: ProgressLocation.Notification, title: "Creating tag..." }, async (_progress, _token) => { return;
await createTagFromUri(Uri.parse(folder)); }
}); const tags = await functionsClient?.listTags(func.$id);
let value;
if (tags && tags.tags.length > 0) {
value = tags.tags[tags.tags.length - 1].command;
}
const command = await window.showInputBox({ value, prompt: "Command to run your code" });
if (command === undefined) {
return;
}
const tag = await window.withProgress(
{ location: ProgressLocation.Notification, title: "Creating tag..." },
async (progress, _token) => {
return await createTagFromUri(func.$id, command, Uri.parse(folder), progress);
}
);
if (tag) {
await tagNotification(tag);
return;
}
}
if (item === undefined) {
const functions = await functionsClient?.list();
if (functions === undefined) {
return;
}
const pick = await window.showQuickPick(
functions.functions.map<QuickPickItem>(
(func): QuickPickItem => ({ label: func.name, description: func.env, detail: func.$id })
),
{ placeHolder: "Select a function to create tag" }
);
if (pick === undefined || pick.detail === undefined) {
return;
}
const funcId = pick.detail;
const folder = await selectWorkspaceFolder("Select folder of your function code.");
const tags = await functionsClient?.listTags(funcId);
let value;
if (tags && tags.tags.length > 0) {
value = tags.tags[tags.tags.length - 1].command;
}
const command = await window.showInputBox({ value, prompt: "Command to run your code" });
if (command === undefined) {
return;
}
const tag = await window.withProgress(
{ location: ProgressLocation.Notification, title: "Creating tag..." },
async (progress, _token) => {
return await createTagFromUri(funcId, command, Uri.parse(folder), progress);
}
);
if (tag) {
await tagNotification(tag);
return;
}
} }
} }
async function createTagFromUri(uri: Uri): Promise<void> { async function createTagFromUri(functionId: string, command: string, uri: Uri, progress: ProgressMessage): Promise<Tag | undefined> {
const tarFilePath = await getTarReadStream(uri); progress.report({ message: "Creating tarball", increment: 10 });
if (functionsClient === undefined) { if (functionsClient === undefined) {
return; return;
} }
if (tarFilePath === undefined) { let tarFilePath;
ext.outputChannel.appendLog("Error creating tar file."); try {
tarFilePath = await getTarReadStream(uri);
} catch (e) {
window.showErrorMessage("Error creating tar file.\n" + e);
return; return;
} }
if (tarFilePath === undefined) {
window.showErrorMessage("Failed to create tar file.");
ext.outputChannel.appendLog("Failed to create tar file.");
return;
}
// somehow makes the upload work
await workspace.fs.readFile(Uri.file(tarFilePath));
progress.report({ message: "Uploading tag", increment: 60 });
try { try {
await functionsClient.createTag("60b1836a8e5d9", "python ./hello.py", fs.createReadStream(tarFilePath)); return await functionsClient.createTag(functionId, command, fs.createReadStream(tarFilePath));
await storageClient?.createFile(fs.createReadStream(tarFilePath));
} catch (e) { } catch (e) {
ext.outputChannel.appendLog("Creating tag error: " + e); ext.outputChannel.appendLog("Creating tag error: " + e);
} }
} }
async function tagNotification(tag: Tag): Promise<void> {
ext.tree?.functions?.refresh();
if (tag) {
const action = await window.showInformationMessage(
`Successfully created tag with size ${tag.size}B.`,
"Activate tag",
"View in console"
);
if (action === "Activate tag") {
await activateTag(tag);
}
if (action === "View in console") {
//
}
return;
}
}

View file

@ -1,11 +1,16 @@
import { Execution } from '../../appwrite';
import { ExecutionTreeItem } from "../../tree/functions/executions/ExecutionTreeItem"; import { ExecutionTreeItem } from "../../tree/functions/executions/ExecutionTreeItem";
import { openReadOnlyContent } from "../../ui/openReadonlyContent"; import { openReadOnlyContent } from "../../ui/openReadonlyContent";
export async function viewExecutionErrors(executionItem: ExecutionTreeItem): Promise<void> { export async function viewExecutionErrors(executionItem: ExecutionTreeItem | Execution): Promise<void> {
if (executionItem === undefined) { if (executionItem === undefined) {
return; return;
} }
const execution = executionItem.execution; let execution = executionItem as Execution;
await openReadOnlyContent({ label: `${executionItem.parent.parent.func.name} execution stderr`, fullId: `${execution.$id}-errors.txt` }, execution.stderr, '.txt');
if (executionItem instanceof ExecutionTreeItem) {
execution = executionItem.execution;
}
await openReadOnlyContent({ label: `Execution stderr`, fullId: `${execution.$id}-errors.txt` }, execution.stderr, '.txt');
} }

View file

@ -1,11 +1,19 @@
import { Execution } from '../../appwrite';
import { ExecutionTreeItem } from "../../tree/functions/executions/ExecutionTreeItem"; import { ExecutionTreeItem } from "../../tree/functions/executions/ExecutionTreeItem";
import { openReadOnlyContent } from "../../ui/openReadonlyContent"; import { openReadOnlyContent } from "../../ui/openReadonlyContent";
export async function viewExecutionOutput(executionItem: ExecutionTreeItem): Promise<void> { export async function viewExecutionOutput(executionItem: ExecutionTreeItem | Execution): Promise<void> {
if (executionItem === undefined) { if (executionItem === undefined) {
return; return;
} }
const execution = executionItem.execution;
await openReadOnlyContent({ label: `${executionItem.parent.parent.func.name} execution stdout`, fullId: `${execution.$id}-output.txt` }, execution.stdout, '.txt'); let execution = executionItem as Execution;
if (executionItem instanceof ExecutionTreeItem) {
execution = executionItem.execution;
}
console.log(execution.dateCreated);
await openReadOnlyContent({ label: `Execution stdout`, fullId: `${execution.$id}-output.txt` }, execution.stdout, '.txt');
} }

View file

@ -42,6 +42,8 @@ import { openExecutionsInBrowser } from './functions/openExecutionsInBrowser';
import { openFunctionSettingsInBrowser } from './functions/openFunctionSettingsInBrowser'; import { openFunctionSettingsInBrowser } from './functions/openFunctionSettingsInBrowser';
import { openFunctionTagsInBrowser } from './functions/openFunctionTagsInBrowser'; import { openFunctionTagsInBrowser } from './functions/openFunctionTagsInBrowser';
import { viewMore } from './common/viewMore';
class CommandRegistrar { class CommandRegistrar {
constructor(private readonly context: ExtensionContext) {} constructor(private readonly context: ExtensionContext) {}
@ -74,6 +76,7 @@ export function registerCommands(context: ExtensionContext): void {
/** Common **/ /** Common **/
registerCommand("editValue", editValue); registerCommand("editValue", editValue);
registerCommand("viewMore", viewMore);
/** General **/ /** General **/
registerCommand("Connect", connectAppwrite, "all"); registerCommand("Connect", connectAppwrite, "all");

View file

@ -17,7 +17,7 @@ export type AppwriteTree = {
}; };
export type Ext = { export type Ext = {
context?: ExtensionContext; context: ExtensionContext;
outputChannel: AppwriteOutputChannel; outputChannel: AppwriteOutputChannel;
tree?: AppwriteTree; tree?: AppwriteTree;
}; };

View file

@ -0,0 +1,13 @@
import { TreeItem } from "vscode";
export abstract class EditableTreeItemBase<T> extends TreeItem {
public abstract setValue(value: T): Promise<void>;
constructor(contextValuePrefix: string, public readonly value: T, description?: string) {
super(typeof value === "string" ? value : "No label");
this.contextValue = `editable_${contextValuePrefix}`;
this.description = description ?? contextValuePrefix;
}
public abstract prompt(): Promise<void>;
}

View file

@ -0,0 +1,38 @@
import { QuickPickItem, QuickPickOptions, window } from "vscode";
import { EditableTreeItemBase } from "./EditableTreeItemBase";
export abstract class EnumEditableTreeItemBase extends EditableTreeItemBase<string[]> {
public abstract options: string[] | QuickPickItem[];
public quickPickOptions: QuickPickOptions;
constructor(contextValuePrefix: string, public readonly value: string[], description?: string) {
super(contextValuePrefix, value, description);
this.quickPickOptions = {};
}
public async prompt(): Promise<void> {
const value = await window.showQuickPick(
this.options.map<QuickPickItem>((option: QuickPickItem | string): QuickPickItem => {
if (typeof option === "string") {
return { label: option, picked: this.value.includes(option) };
}
const picked = this.value.includes(option.label);
return { ...option, picked, alwaysShow: picked };
}).sort((a, b) => {
if (a.picked) {
return -1;
}
if (b.picked) {
return 1;
}
return 0;
}),
{ ...this.quickPickOptions, canPickMany: true }
);
if (value !== undefined) {
this.setValue(value.map((item) => item.label));
}
}
}

View file

@ -0,0 +1,18 @@
import { TreeItem, window } from "vscode";
export class EditableTreeItem extends TreeItem {
public readonly setValue: (value: string) => Promise<void>;
constructor(label: string, contextValuePrefix: string, public readonly value: string, setValue: (value: string) => Promise<void>) {
super(label);
this.setValue = setValue;
this.contextValue = `editable_${contextValuePrefix}`;
}
public async prompt(): Promise<void> {
const value = await window.showInputBox({ value: this.value });
if (value !== undefined) {
this.setValue(value);
}
}
}

View file

@ -0,0 +1,22 @@
import { InputBoxOptions, window } from "vscode";
import { EditableTreeItemBase } from "./EditableTreeItemBase";
export abstract class StringEditableTreeItemBase extends EditableTreeItemBase<string> {
public abstract setValue(value: string): Promise<void>;
public inputBoxOptions: InputBoxOptions;
constructor(contextValuePrefix: string, public readonly value: string, description?: string) {
super(contextValuePrefix, value, description);
this.inputBoxOptions = {
prompt: description,
};
}
public async prompt(): Promise<void> {
const value = await window.showInputBox({ value: this.value, ...this.inputBoxOptions });
if (value !== undefined) {
this.setValue(value);
}
}
}

View file

@ -4,8 +4,8 @@ import AppwriteCall from "../../utils/AppwriteCall";
import { Collection, CollectionsList } from "../../appwrite"; import { Collection, CollectionsList } from "../../appwrite";
import { CollectionTreeItem } from "./CollectionTreeItem"; import { CollectionTreeItem } from "./CollectionTreeItem";
import { AppwriteSDK } from "../../constants"; import { AppwriteSDK } from "../../constants";
import { AppwriteTreeItemBase } from "../../ui/AppwriteTreeItemBase";
import { ext } from '../../extensionVariables'; import { ext } from '../../extensionVariables';
import { AppwriteTreeItemBase } from '../../ui/AppwriteTreeItemBase';
export class DatabaseTreeItemProvider implements vscode.TreeDataProvider<vscode.TreeItem> { export class DatabaseTreeItemProvider implements vscode.TreeDataProvider<vscode.TreeItem> {
private _onDidChangeTreeData: vscode.EventEmitter<vscode.TreeItem | undefined | void> = new vscode.EventEmitter< private _onDidChangeTreeData: vscode.EventEmitter<vscode.TreeItem | undefined | void> = new vscode.EventEmitter<
@ -34,7 +34,7 @@ export class DatabaseTreeItemProvider implements vscode.TreeDataProvider<vscode.
} }
if (parent instanceof AppwriteTreeItemBase) { if (parent instanceof AppwriteTreeItemBase) {
return parent.getChildren?.() ?? []; return await parent.getChildren?.() ?? [];
} }
const databaseSdk = new AppwriteSDK.Database(client); const databaseSdk = new AppwriteSDK.Database(client);

View file

@ -44,7 +44,7 @@ export class FunctionsTreeItemProvider implements vscode.TreeDataProvider<vscode
} }
if (parent instanceof AppwriteTreeItemBase) { if (parent instanceof AppwriteTreeItemBase) {
return parent.getChildren?.() ?? []; return await parent.getChildren?.() ?? [];
} }
return []; return [];

View file

@ -1,7 +1,5 @@
import { MarkdownString, ThemeColor, ThemeIcon, TreeItem } from "vscode"; import { MarkdownString, ThemeColor, ThemeIcon, TreeItem } from "vscode";
import { Execution, ExecutionStatus } from "../../../appwrite"; import { Execution, ExecutionStatus } from "../../../appwrite";
import { functionsClient } from "../../../client";
import { ext } from "../../../extensionVariables";
import { msToDate } from "../../../utils/date"; import { msToDate } from "../../../utils/date";
import { ExecutionsTreeItem } from "./ExecutionsTreeItem"; import { ExecutionsTreeItem } from "./ExecutionsTreeItem";
@ -12,17 +10,11 @@ const executionStatusIcons: Record<ExecutionStatus, ThemeIcon> = {
failed: new ThemeIcon("circle-filled", new ThemeColor("testing.iconFailed")), failed: new ThemeIcon("circle-filled", new ThemeColor("testing.iconFailed")),
}; };
function sleep(ms: number) {
return new Promise((resolve) => {
setTimeout(resolve, ms);
});
}
export class ExecutionTreeItem extends TreeItem { export class ExecutionTreeItem extends TreeItem {
public isAutoRefreshing: boolean = false; public isAutoRefreshing: boolean = false;
private refreshCount: number = 0; private refreshCount: number = 0;
constructor(public readonly parent: ExecutionsTreeItem, public readonly execution: Execution) { constructor(public readonly parent: ExecutionsTreeItem, public execution: Execution) {
super(execution.$id); super(execution.$id);
this.label = this.getLabel(execution); this.label = this.getLabel(execution);
this.iconPath = executionStatusIcons[execution.status]; this.iconPath = executionStatusIcons[execution.status];
@ -31,40 +23,45 @@ export class ExecutionTreeItem extends TreeItem {
this.description = execution.trigger; this.description = execution.trigger;
this.contextValue = this.getContextValue(execution); this.contextValue = this.getContextValue(execution);
this.isAutoRefreshing = execution.status === "processing" || execution.status === "waiting"; this.isAutoRefreshing = execution.status === "processing" || execution.status === "waiting";
this.autoRefresh(); // if (this.isAutoRefreshing) {
// this.autoRefresh();
// }
} }
async autoRefresh(): Promise<void> { // async autoRefresh(): Promise<void> {
if (!this.isAutoRefreshing) { // if (!this.isAutoRefreshing && this.refreshCount < 5) {
return; // return;
} // }
this.refreshCount++; // this.refreshCount++;
ext.outputChannel.appendLog("Refreshing execution."); // ext.outputChannel.appendLog("Refreshing execution.");
const execution = await functionsClient?.getExecution(this.parent.parent.func.$id, this.execution.$id); // const execution = await functionsClient?.getExecution(this.parent.parent.func.$id, this.execution.$id);
if (!execution) { // if (!execution) {
ext.outputChannel.appendLog("Execution is undefined"); // ext.outputChannel.appendLog("Execution is undefined");
this.isAutoRefreshing = false; // this.isAutoRefreshing = false;
return; // return;
} // }
// this.execution = execution;
this.contextValue = this.getContextValue(execution); // this.contextValue = this.getContextValue(execution);
this.iconPath = executionStatusIcons[execution.status]; // this.iconPath = executionStatusIcons[execution.status];
this.label = this.getLabel(execution); // this.label = this.getLabel(execution);
this.isAutoRefreshing = execution.status === "processing" || execution.status === "waiting"; // this.isAutoRefreshing = execution.status === "processing" || execution.status === "waiting";
// ext.tree?.functions?.refreshChild(this);
ext.tree?.functions?.refreshChild(this); // await sleep(1000);
await sleep(1000); // this.autoRefresh();
this.autoRefresh(); // }
}
getLabel(execution: Execution): string { getLabel(execution: Execution): string {
if (execution.status === "completed" || execution.status === "failed") { if (execution.status === "completed" || execution.status === "failed") {
return `${this.getCreated(execution)} (${execution.time.toPrecision(2)}s)`; return `${this.getCreated(execution)} (${this.getExecutionTime(execution)}s)`;
} }
return `${this.getCreated(execution)} (${execution.status})`; return `${this.getCreated(execution)} (${execution.status})`;
} }
getExecutionTime(execution: Execution): string {
return execution.time.toPrecision(2);
}
getContextValue(execution: Execution): string { getContextValue(execution: Execution): string {
if (execution.status === "completed" || execution.status === "failed") { if (execution.status === "completed" || execution.status === "failed") {
if (execution.stderr === "" && execution.stdout === "") { if (execution.stderr === "" && execution.stdout === "") {

View file

@ -1,21 +1,47 @@
import { ThemeIcon, TreeItem, TreeItemCollapsibleState } from "vscode"; import { ThemeIcon, TreeItem, TreeItemCollapsibleState } from "vscode";
import { Execution, ExecutionList } from '../../../appwrite'; import { Execution, ExecutionList } from "../../../appwrite";
import { functionsClient } from "../../../client"; import { functionsClient } from "../../../client";
import { AppwriteTreeItemBase } from '../../../ui/AppwriteTreeItemBase'; import { ExecutionTreeItem } from "./ExecutionTreeItem";
import { ExecutionTreeItem } from './ExecutionTreeItem'; import { FunctionTreeItem } from "../FunctionTreeItem";
import { FunctionTreeItem } from '../FunctionTreeItem'; import { ext } from "../../../extensionVariables";
import { AppwriteTreeItemBase } from "../../../ui/AppwriteTreeItemBase";
export class ExecutionsTreeItem extends AppwriteTreeItemBase<FunctionTreeItem> { export class ExecutionsTreeItem extends AppwriteTreeItemBase<FunctionTreeItem> {
constructor(public readonly parent: FunctionTreeItem) { constructor(public readonly parent: FunctionTreeItem) {
super(parent, "Executions"); super(parent, "Executions");
} }
private executionsToShow = 10;
public async getChildren(): Promise<TreeItem[]> { public async getChildren(): Promise<TreeItem[]> {
if (!functionsClient) { if (!functionsClient) {
return []; return [];
} }
const executions: ExecutionList | undefined = await functionsClient.listExecutions(this.parent.func.$id, undefined, undefined, undefined, 'DESC'); const executions: ExecutionList | undefined = await functionsClient.listExecutions(
const children = executions?.executions.map((execution: Execution) => new ExecutionTreeItem(this, execution)) ?? [new TreeItem('No exeuctions.')]; this.parent.func.$id,
undefined,
this.executionsToShow,
undefined,
"DESC"
);
const children = executions?.executions.map((execution: Execution) => new ExecutionTreeItem(this, execution)) ?? [
new TreeItem("No executions."),
];
if (children.length === 0) {
children.push(new TreeItem("No executions."));
}
ext.outputChannel.appendLog(`Found ${executions?.sum} executions`);
if (executions?.sum ?? (0 > this.executionsToShow && this.executionsToShow !== 100)) {
const viewMoreItem: TreeItem = {
command: {
command: "vscode-appwrite.viewMore",
arguments: [this],
title: "View more",
},
label: "View more...",
};
children.push(viewMoreItem);
}
return children; return children;
} }
@ -24,4 +50,12 @@ export class ExecutionsTreeItem extends AppwriteTreeItemBase<FunctionTreeItem> {
contextValue = "executions"; contextValue = "executions";
iconPath = new ThemeIcon("history"); iconPath = new ThemeIcon("history");
async viewMore(): Promise<void> {
this.executionsToShow += 10;
if (this.executionsToShow > 100) {
this.executionsToShow = 100;
}
ext.tree?.functions?.refreshChild(this);
}
} }

View file

@ -3,7 +3,7 @@ import { Function } from "../../../appwrite";
import { functionsClient } from "../../../client"; import { functionsClient } from "../../../client";
import { appwriteSystemEvents } from "../../../constants"; import { appwriteSystemEvents } from "../../../constants";
import { ext } from "../../../extensionVariables"; import { ext } from "../../../extensionVariables";
import { EnumEditableTreeItemBase } from "../../common/EnumEditableTreeItem"; import { EnumEditableTreeItemBase } from "../../common/editable/EnumEditableTreeItem";
import { FunctionSettingsTreeItem } from "./FunctionSettingsTreeItem"; import { FunctionSettingsTreeItem } from "./FunctionSettingsTreeItem";
export class EventsTreeItem extends EnumEditableTreeItemBase { export class EventsTreeItem extends EnumEditableTreeItemBase {

View file

@ -1,7 +1,7 @@
import { ThemeIcon, TreeItem, TreeItemCollapsibleState } from "vscode"; import { ThemeIcon, TreeItem, TreeItemCollapsibleState } from "vscode";
import { Function } from "../../../appwrite"; import { Function } from "../../../appwrite";
import { functionsClient } from "../../../client"; import { functionsClient } from "../../../client";
import { AppwriteTreeItemBase } from "../../../ui/AppwriteTreeItemBase"; import { AppwriteTreeItemBase } from '../../../ui/AppwriteTreeItemBase';
import { ChildTreeItem } from "../../ChildTreeItem"; import { ChildTreeItem } from "../../ChildTreeItem";
import { FunctionTreeItem } from "../FunctionTreeItem"; import { FunctionTreeItem } from "../FunctionTreeItem";
import { EventsTreeItem } from "./EventsTreeItem"; import { EventsTreeItem } from "./EventsTreeItem";

View file

@ -2,7 +2,7 @@ import { InputBoxOptions, MarkdownString } from "vscode";
import { Function } from "../../../appwrite"; import { Function } from "../../../appwrite";
import { functionsClient } from "../../../client"; import { functionsClient } from "../../../client";
import { ext } from "../../../extensionVariables"; import { ext } from "../../../extensionVariables";
import { StringEditableTreeItemBase } from '../../common/StringEditableTreeItem'; import { StringEditableTreeItemBase } from '../../common/editable/StringEditableTreeItem';
import { FunctionSettingsTreeItem } from "./FunctionSettingsTreeItem"; import { FunctionSettingsTreeItem } from "./FunctionSettingsTreeItem";
const tooltip = "Function name"; const tooltip = "Function name";

View file

@ -5,7 +5,7 @@ import { ext } from "../../../extensionVariables";
import cron from "cron-validate"; import cron from "cron-validate";
import { FunctionSettingsTreeItem } from "./FunctionSettingsTreeItem"; import { FunctionSettingsTreeItem } from "./FunctionSettingsTreeItem";
import cronstrue from "cronstrue"; import cronstrue from "cronstrue";
import { StringEditableTreeItemBase } from '../../common/StringEditableTreeItem'; import { StringEditableTreeItemBase } from '../../common/editable/StringEditableTreeItem';
export class ScheduleTreeItem extends StringEditableTreeItemBase { export class ScheduleTreeItem extends StringEditableTreeItemBase {
private readonly func: Function; private readonly func: Function;

View file

@ -2,7 +2,7 @@ import { InputBoxOptions, MarkdownString } from "vscode";
import { Function } from "../../../appwrite"; import { Function } from "../../../appwrite";
import { functionsClient } from "../../../client"; import { functionsClient } from "../../../client";
import { ext } from "../../../extensionVariables"; import { ext } from "../../../extensionVariables";
import { StringEditableTreeItemBase } from "../../common/StringEditableTreeItem"; import { StringEditableTreeItemBase } from "../../common/editable/StringEditableTreeItem";
function isNumeric(str: string) { function isNumeric(str: string) {
console.log("here"); console.log("here");

View file

@ -2,7 +2,7 @@ import { InputBoxOptions, MarkdownString, window } from "vscode";
import { Function } from "../../../appwrite"; import { Function } from "../../../appwrite";
import { functionsClient } from "../../../client"; import { functionsClient } from "../../../client";
import { ext } from "../../../extensionVariables"; import { ext } from "../../../extensionVariables";
import { StringEditableTreeItemBase } from "../../common/StringEditableTreeItem"; import { StringEditableTreeItemBase } from "../../common/editable/StringEditableTreeItem";
import { VarsTreeItem } from "./VarsTreeItem"; import { VarsTreeItem } from "./VarsTreeItem";
const tooltip = "Environment var"; const tooltip = "Environment var";

View file

@ -1,6 +1,6 @@
import { TreeItem, TreeItemCollapsibleState } from "vscode"; import { TreeItem, TreeItemCollapsibleState } from "vscode";
import { Vars } from "../../../appwrite"; import { Vars } from "../../../appwrite";
import { AppwriteTreeItemBase } from "../../../ui/AppwriteTreeItemBase"; import { AppwriteTreeItemBase } from '../../../ui/AppwriteTreeItemBase';
import { FunctionSettingsTreeItem } from "./FunctionSettingsTreeItem"; import { FunctionSettingsTreeItem } from "./FunctionSettingsTreeItem";
import { VarTreeItem } from "./VarTreeItem"; import { VarTreeItem } from "./VarTreeItem";

View file

@ -10,7 +10,7 @@ export class TagTreeItem extends TreeItem {
const active = func.tag === tag.$id; const active = func.tag === tag.$id;
this.label = `${msToDate(tag.dateCreated)}${active ? ' (Active)' : ''}`; this.label = `${msToDate(tag.dateCreated)}${active ? ' (Active)' : ''}`;
this.description = tag.$id; this.description = tag.$id;
this.iconPath = new ThemeIcon(active ? 'circle-filled' : 'circle-outline'); this.iconPath = new ThemeIcon(active ? 'circle-large-filled' : 'circle-large-outline');
this.contextValue = `tag${active ? '_active' : ''}`; this.contextValue = `tag${active ? '_active' : ''}`;
this.tooltip = new MarkdownString(`ID: ${tag.$id} \nCreated: ${msToDate(tag.dateCreated)} \nCommand: ${tag.command} \nSize: ${tag.size}B`); this.tooltip = new MarkdownString(`ID: ${tag.$id} \nCreated: ${msToDate(tag.dateCreated)} \nCommand: ${tag.command} \nSize: ${tag.size}B`);
} }

View file

@ -14,7 +14,22 @@ export class TagsTreeItem extends AppwriteTreeItemBase<FunctionTreeItem> {
return []; return [];
} }
const tags = await functionsClient.listTags(this.parent.func.$id); const tags = await functionsClient.listTags(this.parent.func.$id);
return tags?.tags.sort((a, b) => b.dateCreated - a.dateCreated).map((tag) => new TagTreeItem(this, tag)) ?? [new TreeItem('No tags.')]; const children = tags?.tags.sort((a, b) => b.dateCreated - a.dateCreated).map((tag) => new TagTreeItem(this, tag)) ?? [new TreeItem('No tags.')];
if (children.length === 0) {
const noTagsItem: TreeItem = {
command: {
command: "vscode-appwrite.CreateTag",
title: "Create tag",
arguments: [this],
tooltip: "Create a tag"
},
label: "Create a tag",
iconPath: new ThemeIcon("cloud-upload"),
};
children.push(noTagsItem);
}
return children;
} }
collapsibleState = TreeItemCollapsibleState.Collapsed; collapsibleState = TreeItemCollapsibleState.Collapsed;

View file

@ -7,4 +7,7 @@ export abstract class AppwriteTreeItemBase<Parent = void> extends TreeItem {
abstract getChildren?(): Promise<TreeItem[]>; abstract getChildren?(): Promise<TreeItem[]>;
viewMore(): Promise<void> {
return Promise.resolve();
}
} }

View file

@ -1,42 +1,19 @@
import { QuickPickItem, window } from "vscode"; import { QuickPickItem, window } from "vscode";
import { Collection, CollectionsList } from "../appwrite";
import { client } from "../client";
import { AppwriteSDK } from "../constants";
import AppwriteCall from "../utils/AppwriteCall";
export type CreateRuleWizardContext = { export type CreateRuleWizardContext = {
label: string; label: string;
key: string; key: string;
type: keyof typeof ruleTypes; type: keyof typeof ruleTypes;
default: any;
required: boolean;
array: boolean;
list?: string[];
}; };
export async function createRuleWizard(): Promise<CreateRuleWizardContext | undefined> {
const label = await window.showInputBox({
placeHolder: "Label",
prompt: "Attribute internal display name",
});
if (label === undefined) {
return;
}
const key = await window.showInputBox({
placeHolder: "Key",
prompt: "Attribute key name. Used as the document JSON key in the Database API.",
});
if (key === undefined) {
return;
}
const ruleTypeItems: QuickPickItem[] = Object.entries(ruleTypes).map(([label, description]) => ({
label,
description,
}));
const type = await window.showQuickPick(ruleTypeItems);
if (type === undefined) {
return;
}
if (label && key && type) {
return { label, key, type: (type.label as unknown) as keyof typeof ruleTypes };
}
return undefined;
}
const ruleTypes = { const ruleTypes = {
text: "Any string value.", text: "Any string value.",
numeric: "Any integer or float value.", numeric: "Any integer or float value.",
@ -45,6 +22,119 @@ const ruleTypes = {
url: "Any valid URL.", url: "Any valid URL.",
email: "Any valid email address.", email: "Any valid email address.",
ip: "Any valid IP v4 or v6 address.", ip: "Any valid IP v4 or v6 address.",
document: document: "Accept a valid child document from specified collection(s).",
"Accept a valid child document. When using this type you are also required to pass the 'list' parameter with an array of the collections UID values of the document types you want to accept.",
}; };
type RuleType = keyof typeof ruleTypes;
export async function createRuleWizard(collection: Collection): Promise<CreateRuleWizardContext | undefined> {
const label = await window.showInputBox({
placeHolder: "Attribute label",
prompt: "Attribute internal display name",
validateInput: (value) => {
if (value === "") {
return "Label cannot be empty.";
}
},
});
if (label === undefined) {
return;
}
const key = await window.showInputBox({
placeHolder: "Attribute key name",
prompt: "Attribute key name. Used as the document JSON key in the Database API.",
ignoreFocusOut: true,
validateInput: (value) => {
if (value === "") {
return "Key name cannot be empty.";
}
},
});
if (key === undefined) {
return;
}
const ruleTypeItems: QuickPickItem[] = Object.entries(ruleTypes).map(([label, description]) => ({
label,
detail: description,
}));
const typeItem = await window.showQuickPick(ruleTypeItems, { placeHolder: "Rule value type." });
const type: RuleType | undefined = (typeItem?.label as RuleType) ?? undefined;
if (typeItem === undefined || type === undefined) {
return;
}
let list: string[] | undefined = undefined;
if (type === "document") {
const databaseSdk = new AppwriteSDK.Database(client);
const collectionsList = await AppwriteCall<CollectionsList, CollectionsList>(databaseSdk.listCollections());
if (collectionsList === undefined) {
window.showErrorMessage("Could not get collections list.");
return;
}
if (collectionsList) {
const collections = collectionsList.collections.filter((c) => c.$id !== collection.$id);
const qpItems: QuickPickItem[] = collections.map((collection) => ({
label: collection.name,
description: collection.$id,
}));
const listInput = await window.showQuickPick(qpItems, {
canPickMany: true,
placeHolder: "Collections which contain valid child documents for this document attribute.",
ignoreFocusOut: true,
});
list = listInput?.map((item) => item.description as string) ?? [];
}
}
if (label === "document" && list === undefined) {
return;
}
const array = await window.showQuickPick(["Primitive", "Array"], {
placeHolder: "Decide if this rule is a primitive or an array of values.",
ignoreFocusOut: true,
});
if (array === undefined) {
return;
}
const required = await window.showQuickPick(["Required", "Optional"], {
placeHolder: "Decide if this rule value is required in order to pass document validation.",
ignoreFocusOut: true,
});
if (required === undefined) {
return;
}
const defaultValue = await window.showInputBox({
placeHolder: "Default value (press Enter to skip)",
prompt: "Default value for this rule type. Make sure that the default value is able to pass validation in order to avoid errors when skipping optional values.",
ignoreFocusOut: true,
});
if (defaultValue === undefined) {
return;
}
if (label && key && type) {
return {
label,
key,
type,
default: defaultValue,
array: array === "Array",
required: required === "Required",
list,
};
}
return undefined;
}

View file

@ -1,5 +1,9 @@
import dayjs = require('dayjs'); import dayjs = require("dayjs");
import utc = require("dayjs/plugin/utc");
import timezone = require("dayjs/plugin/timezone"); // dependent on utc plugin
dayjs.extend(utc);
dayjs.extend(timezone);
export function msToDate(ms: number): string { export function msToDate(ms: number): string {
return dayjs(ms).format("LTS"); return dayjs(ms * 1000).tz(dayjs.tz.guess()).format("LTS");
} }

5
src/utils/sleep.ts Normal file
View file

@ -0,0 +1,5 @@
export function sleep(ms: number): Promise<void> {
return new Promise((resolve) => {
setTimeout(resolve, ms);
});
}

View file

@ -2,8 +2,8 @@ import tar = require("tar");
import { Uri, window, workspace } from "vscode"; import { Uri, window, workspace } from "vscode";
import { ext } from "../extensionVariables"; import { ext } from "../extensionVariables";
import * as path from "path"; import * as path from "path";
import * as os from "os";
import * as fs from "fs"; import * as fs from "fs";
import { sleep } from './sleep';
export async function getTarReadStream(folder: Uri): Promise<string | undefined> { export async function getTarReadStream(folder: Uri): Promise<string | undefined> {
try { try {
@ -15,11 +15,14 @@ export async function getTarReadStream(folder: Uri): Promise<string | undefined>
window.showErrorMessage("No workspace open."); window.showErrorMessage("No workspace open.");
return; return;
} }
ext.outputChannel.appendLog(`Creating '${tarName}' in '${workspace.workspaceFolders?.[0].uri.fsPath}'...`); ext.outputChannel.appendLog(`Creating '${tarName}' in '${workspace.workspaceFolders?.[0].uri.fsPath}'...`);
const tarFilePath = path.join(ext.context?.globalStorageUri.fsPath ?? '', tarName);
await workspace.fs.createDirectory(ext.context.globalStorageUri);
const tarFilePath = path.join(os.tmpdir(), tarName); tar.create({ gzip: true, cwd: cwd, mode: 1777 }, [path.relative(cwd, folder.fsPath)]).pipe(fs.createWriteStream(tarFilePath));
tar.create({ gzip: true, cwd: cwd }, [path.relative(cwd, folder.fsPath)]).pipe(fs.createWriteStream(tarFilePath)); await sleep(500);
ext.outputChannel.appendLog(`Created ${tarFilePath}`); ext.outputChannel.appendLog(`Created ${tarFilePath}`);

6
src/utils/types.d.ts vendored Normal file
View file

@ -0,0 +1,6 @@
import { Progress } from 'vscode';
export type ProgressMessage = Progress<{
message?: string | undefined;
increment?: number | undefined;
}>;