import { withFilter } from 'graphql-subscriptions'; import { IFileStat, IFileService } from '@files-stack/core'; import { isEmpty } from 'lodash-es'; import { PubsubIdentifier, IFileOperation as FileOperation, FileChangeType, getParentResource, FileContentStatus, IFileOperationOutput, IFileSystemProviderRegistrationEvent, IFileSystemProviderActivationEvent, HotExitConfiguration, SaveReason, LoadReason, IResolvers, ISubscriptionfileDataArgs, } from '@files-stack/core'; import { IResolverOptions } from '@common-stack/server-core'; import { URI } from '@vscode-alt/monaco-editor/esm/vs/base/common/uri'; import { processError } from '../../core/process-error'; import { localize } from '@vscode-alt/monaco-editor/esm/vs/nls'; import { Schemas } from '@vscode-alt/monaco-editor/esm/vs/base/common/network'; export const resolver: (options: IResolverOptions) => IResolvers = (options: IResolverOptions) => ({ FileContentStatus: FileContentStatus, HotExitConfiguration: HotExitConfiguration, // SaveReason, // LoadReason, Query: { async resolveFile(root, args, ctx) { options.logger.debug('resolveFile (%s)', args.resource, args.options); try { const rest = await ctx.fileService.resolve(args.resource, args.options) as any; return rest; } catch (err) { processError(err) } }, resolveWorkspaceRoot: async function (root, args, ctx) { options.logger.debug('resolveWorkspaceRoot (%s)', args.workspace, args.workspaceFolders, args.options); try { const workspaceFilePath = args.workspace.uri.fsPath; const workspaceRoot = URI.file(workspaceFilePath.substring(0, workspaceFilePath.lastIndexOf('/'))); const rest = await ctx.fileService.resolve(workspaceRoot, args.options) as any; const children = await Promise.all(args.workspaceFolders.map(async (folder) => { try { return await ctx.fileService.resolve(folder.uri, args.options); } catch (e) { const resource = folder.uri.scheme === Schemas.file ? folder.uri.fsPath : folder.uri.toString(true); const err = localize('fileNotFoundError', 'File not found ({0})', resource); if (e.message.includes(err)) { ctx.notificationService.error(err); } return null; } })); return { ...rest, resource: URI.file(`${rest.resource.fsPath}(workspace)`), children: children.filter(child => child), isDirectory: true, hasChildren: true, name: `${args.workspace.name || 'untitled'}(workspace)`, title: `${args.workspace.name || 'untitled'}(workspace)`, }; } catch (err) { processError(err); } }, existsFile(root, args, ctx) { try { return ctx.fileService.exists(args.resource); } catch (err) { processError(err) } }, resolveContent(root, args, ctx) { try { return ctx.fileService.readFile(args.resource, args.options); } catch (err) { processError(err) } }, resolveContents(root, args, ctx) { try { return ctx.fileService.resolveAll(args.toResolve) as any; } catch (err) { processError(err) } }, }, Mutation: { loadFile(root, args, ctx) { try { return ctx.fileService.resolve(args.resource, args.options) as any; } catch (err) { processError(err) } }, loadFileContent(root, args, ctx) { try { return ctx.fileService.readFile(args.resource, args.options); } catch (err) { processError(err) } }, updateContent(root, args, ctx) { options.logger.debug('updateContent (%s)', args.resource.toString()); try { return ctx.fileService.writeFile(args.resource, args.value, args.options); } catch (err) { processError(err) } }, async moveFile(root, args, ctx) { options.logger.debug('move file (%s)', args.source.toString(), args.target.toString()); try { // ctx.fileService.unwatchFileChanges(decodeURIComponent(args.source)); return await ctx.fileService.move(args.source, args.target, args.overwrite); } catch (err) { processError(err) } }, copyFile(root, args, ctx) { options.logger.debug('copy file (%s)', args.source.toString(), args.target.toString()); try { return ctx.fileService.copy(args.source, args.target, args.overwrite); } catch (err) { processError(err) } }, canHandleResource(root, args, ctx) { try { return ctx.fileService.canHandleResource(args.resource); } catch (err) { processError(err) } }, createFile(root, args, ctx) { options.logger.debug('create file (%s)', args.resource.toString()); try { return ctx.fileService.createFile(args.resource, args.bufferOrReadable, args.options); } catch (err) { processError(err) } }, createFolder(root, args, ctx) { options.logger.debug('create folder (%s)', args.resource.toString()); try { return ctx.fileService.createFolder(args.resource); } catch (err) { processError(err) } }, rename(root, args, ctx) { options.logger.debug('rename (%s, %s)', args.resource.toString(), args.target); try { return ctx.fileService.move(args.resource, args.target, args.overwrite); } catch (err) { processError(err) } }, del(root, args, ctx) { options.logger.debug('delete (%j)', args.resource.toString()); try { ctx.fileService.del(args.resource, { useTrash: args.options.useTrash, recursive: args.options.recursive }); return true; } catch (err) { processError(err) } }, watchFileChanges(root, args, ctx) { try { ctx.fileService.watch(args.resource, args.options); return true; } catch (err) { processError(err) } }, unwatchFileChanges(root, args, ctx) { try { if (ctx.fileService.unwatch) { ctx.fileService.unwatch(args.resource, args.session); } return true; } catch (err) { processError(err) } }, }, Subscription: { fileData: { subscribe: withFilter(() => options.pubsub.asyncIterator(`${PubsubIdentifier.FILE_UPDATED}/${options.subscriptionID}`), async (payload: { fileData: IFileStat[] }, variables: ISubscriptionfileDataArgs) => { options.logger.debug('subscribe fileData: (%j), variables: (%j)', payload, variables); const variableResource = URI.revive(variables.resource); if (payload.fileData && payload.fileData.length && !isEmpty(payload.fileData[0])) { // only matched resource path should need to receive the data const paths = payload.fileData.filter(data => { const test = data.resource.toString().indexOf(variableResource.toString()); return test === 0; }); if (paths.length > 0) { return true; } return false; } }), }, fileOperation: { subscribe: withFilter(() => options.pubsub.asyncIterator(`${PubsubIdentifier.FILE_OPERATION}/${options.subscriptionID}`), (payload: { fileOperation: IFileOperationOutput }, variables) => { const inputResource = URI.revive(variables.resource); const operation = payload.fileOperation.operation; const resource = payload.fileOperation.resource; options.logger.debug('subscribe fileOperation operation: %j, resource: %j, variables: (%j)', operation, resource, variables); switch (operation) { case FileOperation.CREATE: case FileOperation.DELETE: case FileOperation.MOVE: case FileOperation.COPY: return resource.toString().indexOf(inputResource.toString()) === 0; default: return false; } }), }, onWillActivateFileSystemProvider: { subscribe: withFilter(() => options.pubsub.asyncIterator(`${PubsubIdentifier.onWillActivateFileSystemProvider}/${options.subscriptionID}`), (payload: { event: IFileSystemProviderActivationEvent }, variables) => { return true; } ) }, onDidChangeFileSystemProviderRegistrations: { subscribe: withFilter(() => options.pubsub.asyncIterator(`${PubsubIdentifier.onDidChangeFileSystemProviderRegistrations}/${options.subscriptionID}`), (payload: { event: IFileSystemProviderRegistrationEvent }, variables) => { return true; } ) } }, });