dev: cleanup filesystem utilities

This commit is contained in:
KernelDeimos
2024-12-09 14:42:14 -05:00
parent 3f5bc7163f
commit db00f0ae67
4 changed files with 3 additions and 348 deletions

View File

@@ -87,7 +87,7 @@ module.exports = class FSNodeContext {
this.fs = fs;
// Decorate all fetch methods with otel span
// TODO: language tool for traits; this is a trait
// TODO: Apply method decorators using a putility class feature
const fetch_methods = [
'fetchEntry',
'fetchPath',
@@ -271,8 +271,6 @@ module.exports = class FSNodeContext {
resourceService,
} = Context.get('services').values;
// await this.fs.resourceService
// .waitForResource(this.selector);
if ( fetch_entry_options.tracer == null ) {
fetch_entry_options.tracer = traceService.tracer;
}
@@ -288,11 +286,7 @@ module.exports = class FSNodeContext {
await new Promise (rslv => {
const detachables = new MultiDetachable();
let resolved = false;
const callback = (resolver) => {
// NOTE: commented out for now because it's too verbose
resolved = true;
detachables.detach();
rslv();
}
@@ -499,8 +493,7 @@ module.exports = class FSNodeContext {
[this.entry.id]
);
const versions_tidy = [];
for (let index = 0; index < versions.length; index++) {
const version = versions[index];
for ( const version of versions ) {
let username = version.user_id ? (await get_user({id: version.user_id})).username : null;
versions_tidy.push({
id: version.version_id,
@@ -551,14 +544,6 @@ module.exports = class FSNodeContext {
this.entry.is_empty = await is_empty(this.uid);
}
// TODO: this is currently not called anywhere; for now it
// will never be fetched since sharing is not a priority.
async fetchIsShared () {
if ( ! this.mysql_id ) return;
this.entry.is_shared = await is_shared_with_anyone(this.mysql_id);
}
async fetchAll(fsEntryFetcher, user, force) {
await this.fetchEntry({ thumbnail: true });
await this.fetchSubdomains(user);
@@ -610,7 +595,6 @@ module.exports = class FSNodeContext {
);
}
if ( ! this.path ) {
// console.log('PATH WAS NOT ON ENTRY', this);
await this.fetchPath();
}
if ( ! this.path ) {

View File

@@ -1,324 +0,0 @@
/*
* Copyright (C) 2024 Puter Technologies Inc.
*
* This file is part of Puter.
*
* Puter is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
const PerformanceMonitor = require('../monitor/PerformanceMonitor');
const FSNodeContext = require('./FSNodeContext');
const FSAccessContext = require('./FSAccessContext');
const { Context } = require('../util/context');
/**
* FSOperationContext represents a single operation on the filesystem.
*
* FSOperationContext is used to record events such as side-effects
* which occur during a high-level filesystem operation. It is also
* responsible for generating a client-safe result which describes
* the operation.
*/
module.exports = class FSOperationContext {
// TODO: rename this.fs to this.access
constructor (op_name, context, options) {
// migration: fs:create-service
// TODO: rename this.fs to this.access
// NOTE: the 2nd parameter of this constructor
// was called `fs` and was expected to be FSAccessContext.
// Now it should be a context object holding the services
// container. context.access is the FSAccessContext.
if ( context instanceof FSAccessContext ) {
this.fs = context;
} else if ( context ) {
this.context = context;
this.fs = context.access;
} else {
const x = Context.get();
this.fs = {};
this.fs.traceService = x.get('services').get('traceService');
}
this.name = op_name;
this.events = [];
this.parent_dirs_created = [];
this.created = [];
this.fields = {};
this.safeFields = {};
this.valueListeners_ = {};
this.valueFactories_ = {};
this.values_ = {};
this.rejections_ = {};
this.tasks_ = [];
this.currentCheckpoint_ = 'checkpoint not set';
if ( options.parent_operation ) {
this.parent = options.parent_operation;
}
this.donePromise = new Promise((resolve, reject) => {
this.doneResolve = resolve;
this.doneReject = reject;
});
// migration: arch:trace-service:move-outta-fs
if ( this.fs.traceService ) {
// Set 'span_' to current active span
const { context, trace } = require('@opentelemetry/api');
this.span_ = trace.getSpan(context.active());
}
this.monitor = PerformanceMonitor.createContext(`fs.${op_name}`);
}
checkpoint (label) {
this.currentCheckpoint_ = label;
}
async addTask (name, fn) {
const task = {
name,
operations: [],
promise: Promise.resolve(),
};
const taskContext = {
registerOperation: op => {
task.operations.push(op);
task.promise = task.promise.then(() => op.awaitDone());
}
};
const monitor = PerformanceMonitor.createContext('fs.rm');
monitor.label(`task:${name}`);
task.promise = task.promise.then(() => fn(taskContext));
this.tasks_.push(task);
let last_promise = null;
while ( task.promise !== last_promise ) {
last_promise = task.promise;
await task.promise;
}
// await task.promise;
monitor.stamp();
monitor.end();
}
get span () { return this.span_; }
recordParentDirCreated (fsNode) {
if ( ! fsNode ) {
throw new Error(
'falsy value to recordParentDirCreated',
fsNode,
);
}
this.parent_dirs_created.push(fsNode);
}
recordCreated (fsNode) {
this.created.push(fsNode);
}
set (field, value) {
this.fields[field] = value;
}
async set_now (field, value) {
this.fields[field] = value;
if ( value instanceof FSNodeContext ) {
this.safeFields[field] = await value.getSafeEntry();
}
}
get (field) {
return this.fields[field];
}
complete (options) {
options = options ?? {};
if ( this.parent ) {
for ( const fsNode of this.parent_dirs_created ) {
this.parent.recordParentDirCreated(fsNode);
}
for ( const fsNode of this.created ) {
this.parent.recordCreated(fsNode);
}
}
if ( this.tasks_.length > 0 ) {
// TODO: it's mutating input options, which is not ideal
if ( ! options.after ) options.after = [];
options.after.push(
this.tasks_.map(task => task.promise)
);
}
if ( options.after ) {
const thingsToWaitFor = options.after.map(item => {
if ( item.awaitDone ) return item.awaitDone;
return item;
});
(async () => {
await Promise.all(thingsToWaitFor);
this.doneResolve();
})();
return;
}
this.doneResolve();
}
onComplete(fn) {
this.donePromise.then(fn);
}
awaitDone () {
return this.donePromise;
}
provideValue (key, value) {
this.values_[key] = value;
let listeners = this.valueListeners_[key];
if ( ! listeners ) return;
delete this.valueListeners_[key];
for ( let listener of listeners ) {
if ( Array.isArray(listener) ) listener = listener[0];
listener(value);
}
}
rejectValue (key, err) {
this.rejections_[key] = err;
let listeners = this.valueListeners_[key];
if ( ! listeners ) return;
delete this.valueListeners_[key];
for ( let listener of listeners ) {
if ( ! Array.isArray(listener) ) continue;
if ( ! listener[1] ) continue;
listener = listener[1];
listener(err);
}
}
awaitValue (key) {
return new Promise ((rslv, rjct) => {
this.onValue(key, rslv, rjct);
});
}
onValue (key, fn, rjct) {
if ( this.values_[key] ) {
fn(this.values_[key]);
return;
}
if ( this.rejections_[key] ) {
if ( rjct ) {
rjct(this.rejections_[key]);
} else throw this.rejections_[key];
return;
}
if ( ! this.valueListeners_[key] ) {
this.valueListeners_[key] = [];
}
this.valueListeners_[key].push([fn, rjct]);
if ( this.valueFactories_[key] ) {
const fn = this.valueFactories_[key];
delete this.valueFactories_[key];
(async () => {
try {
const value = await fn();
this.provideValue(key, value);
} catch (e) {
this.rejectValue(key, e);
}
})();
}
}
async setFactory (key, factoryFn) {
if ( this.valueListeners_[key] ) {
let v;
try {
v = await factoryFn();
} catch (e) {
this.rejectValue(key, e);
}
this.provideValue(key, v);
return;
}
this.valueFactories_[key] = factoryFn;
}
/**
* Listen for another operation to complete, and then
* complete this operation. This is useful for operations
* which delegate to other operations.
*
* @param {FSOperationContext} other
* @returns {FSOperationContext} this
*/
completedBy (other) {
other.onComplete(() => {
this.complete();
});
return this;
}
/**
* Produces an object which describes the operation in a
* way that is intended to be sent to the client.
*
* @returns {Promise<Object>}
*/
async getClientSafeResult () {
const result = {};
for ( const field in this.fields ) {
if ( this.fields[field] instanceof FSNodeContext ) {
result[field] = this.safeFields[field] ??
await this.fields[field].getSafeEntry();
continue;
}
result[field] = this.fields[field];
}
result.parent_dirs_created = [];
for ( const fsNode of this.parent_dirs_created ) {
const fsNodeResult = await fsNode.getSafeEntry();
result.parent_dirs_created.push(fsNodeResult);
}
return result;
}
}

View File

@@ -47,12 +47,8 @@ class FilesystemService extends BaseService {
}
old_constructor (args) {
// super(args);
const { services } = args;
// this.services = services;
// services.registerService('resourceService', ResourceService);
services.registerService('sizeService', SizeService);
services.registerService('traceService', TraceService);
@@ -81,7 +77,7 @@ class FilesystemService extends BaseService {
// Decorate methods with otel span
// TODO: language tool for traits; this is a trait
// TODO: use putility class feature for method decorators
const span_methods = [
'write', 'mkdir', 'rm', 'mv', 'cp', 'read', 'stat',
'mkdir_2',

View File

@@ -17,7 +17,6 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
const APIError = require("../../api/APIError");
const { CodeModel } = require("../../codex/CodeModel");
const { Sequence } = require("../../codex/Sequence");
const { DB_WRITE } = require("../../services/database/consts");