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
This commit is contained in:
Alex Weininger 2021-05-31 01:24:51 -05:00 committed by GitHub
parent 9da0e1ff61
commit 185e1a98f4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
35 changed files with 557 additions and 141 deletions

View file

@ -6,6 +6,9 @@ Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how
## [Unreleased] ## [Unreleased]
## Added
- You can now easily create function tags from multiple places in the extension.
## [0.1.0] - 2021-5-29 ## [0.1.0] - 2021-5-29
## Functions! ## Functions!

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

@ -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"
}, },

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

@ -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

@ -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;
} }
} }
async function createTagFromUri(uri: Uri): Promise<void> { if (item === undefined) {
const tarFilePath = await getTarReadStream(uri); 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(functionId: string, command: string, uri: Uri, progress: ProgressMessage): Promise<Tag | undefined> {
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,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;
}>;