From a6e438cfd5ba9302f9e42e0ba2c6a23b61c75496 Mon Sep 17 00:00:00 2001 From: jeff-zucker Date: Tue, 16 Feb 2021 16:44:37 -0800 Subject: [PATCH] readme & other changes --- README.md | 17 +--- core/.gitignore | 4 - core/src/handleRequest.js | 8 +- core/src/handleResponse.js | 10 ++- core/src/index.js | 6 +- core/src/performRequestedMethod.js | 15 ++-- dropbox/README.md | 10 ++- dropbox/examples/dropbox-read.js | 36 ++++++++ dropbox/package.json | 2 +- dropbox/src/index.js | 133 +++++++++++++++++++++++++---- file/README.md | 10 ++- file/package.json | 2 +- file/src/index.js | 6 +- mem/README.md | 13 ++- ssh/README.md | 10 ++- tests/all.js | 29 ++----- tests/shouldSucceed.js | 9 +- 17 files changed, 226 insertions(+), 94 deletions(-) create mode 100644 dropbox/examples/dropbox-read.js diff --git a/README.md b/README.md index 227e295..8d18840 100644 --- a/README.md +++ b/README.md @@ -6,17 +6,8 @@ Treat any storage backend as a Solid pod Solid-Rest translates Solid requests into backend requests and backend responses into Solid responses. This means that any storage system that has a Solid-Rest plugin may be treated as a pod. Currently there are plugins for file and dropbox which means that any app that uses Solid-Rest can address file:// and dropbox:// URIs the same way as a Solid pod https:// URI and expect the same responses with some exceptions : permissions are not handled by Solid .acls, they are based on the underlying file or cloud permissions; collaborative tools such as chat are not available. These backends can now be addressed with most Solid libraries (e.g. rdflib) and apps (e.g. the databrowser). -Plugins for ssh, in-memory storage, in-browser storage (indexedDB, localStorage, Native File API) are almost ready and will be released soon. +Plugins for ssh, in-memory storage, in-browser storage (indexedDB, localStorage, Native File API) are in development. -Although Solid-Rest can be used stand-alone, it is best used in conjunction with other libraries, especially [Solid-Node-Client](), a nodejs client for Solid. Solid-Node-Client comes preloaded with the Solid-Rest-File plugin and you may add the dropbox and other plugins as shown below: -```javascript - import { SolidNodeClient } from 'SolidNodeClient'; - import { SolidRestDropbox } from 'SolidRestDropbox'; - const client = new SolidNodeClient({ - handlers : { dropbox : new SolidRestDropbox() } - }); - await client.login( your_dropbox_credentials, {protocol:'dropbox'} ); - await client.login( your_pod_credentials, {protocol:'https'} ); - // you may now use client.fetch() as per the [Solid Rest Spec]() with - // dropbox://, file://, and authenticated https:// URIs. -``` +Although Solid-Rest can be used stand-alone, it is best used in conjunction with other libraries, especially [Solid-Node-Client](), a nodejs client for Solid. + Solid-Node-Client comes preloaded with the Solid-Rest-File plugin, so it will be transparently included in anything using that library. + \ No newline at end of file diff --git a/core/.gitignore b/core/.gitignore index 419f783..448e095 100644 --- a/core/.gitignore +++ b/core/.gitignore @@ -4,8 +4,4 @@ .#* drafts node_modules -dist -coverage -lib test-folder -log.txt \ No newline at end of file diff --git a/core/src/handleRequest.js b/core/src/handleRequest.js index ff26ec6..9b6cfb3 100644 --- a/core/src/handleRequest.js +++ b/core/src/handleRequest.js @@ -27,6 +27,8 @@ const methods = { PATCH: { requiresWrite: 1, requiresContentType: 1 + }, + LOGIN: { } }; export async function handleRequest(uri, originalRequest) { @@ -41,7 +43,7 @@ export async function handleRequest(uri, originalRequest) { if (request.method === 'POST' && !request.headers.link) return 400; const item = this.item = await this.getItem(uri, request); // // Errors we find by comparing the request & the itemRequested - + if(item.isContainer && !item.contentType) item.contentType="text/turtle"; if (item.folderFileConfusion) return 400; // can't have both /foo and /foo/ if (item.patchOnNonRdf) return 400; @@ -73,7 +75,7 @@ export async function handleRequest(uri, originalRequest) { if (request.method === 'PUT' || request.method === 'PATCH') { - if (item.isContainer) return 405; + // if (item.isContainer) return 405; let okDir = await this.perform('CREATE_INTERMEDIATE_CONTAINERS'); if (!okDir) return 500; } @@ -88,4 +90,4 @@ export async function handleRequest(uri, originalRequest) { // SUCCESS !!! return response; -} // ENDS \ No newline at end of file +} // ENDS diff --git a/core/src/handleResponse.js b/core/src/handleResponse.js index 0a205b4..f4e9f65 100644 --- a/core/src/handleResponse.js +++ b/core/src/handleResponse.js @@ -71,20 +71,26 @@ export async function handleResponse(response, originalRequest) { const body = finalResponse.body || this.response.body || ""; // Now we merge headers we created with response headers, prefering response + + Object.assign(headers, finalResponse.headers); headers.status = headers.status || this.response.headers.status || 500; - headers.statusText = headers.statusText || statusText[headers.status]; //console.log(headers) + headers.statusText = headers.statusText || statusText[headers.status]; // Now we create & return the Response object if (originalRequest.plainResponse) { // from a server that wants to munge return { + status:headers.status, + statusText:headers.statusText, body: body, headers: headers }; } headers = wrapHeaders ? { + status:headers.status, + statusText:headers.statusText, headers: headers } : headers; let responseObject; @@ -126,4 +132,4 @@ function createLinkHeader(item) { return; `<${fn}.meta>; rel="describedBy", <${fn}.acl>; rel="acl",` + `; rel="type"`; } -} // THE END! \ No newline at end of file +} // THE END! diff --git a/core/src/index.js b/core/src/index.js index 19af963..8dcf363 100644 --- a/core/src/index.js +++ b/core/src/index.js @@ -37,7 +37,9 @@ export default class SolidRest { } async login(options) { - return await this.perform('LOGIN', options); + options = {loginOptions:options,method:'login'}; + let response = await this.handleRequest('http://example.org/', options); + return await this.handleResponse(response, options ); } async itemExists(pathname) { @@ -52,4 +54,4 @@ export default class SolidRest { if (response === 401) return true; } -} // THE END \ No newline at end of file +} // THE END diff --git a/core/src/performRequestedMethod.js b/core/src/performRequestedMethod.js index 427b633..e30adf4 100644 --- a/core/src/performRequestedMethod.js +++ b/core/src/performRequestedMethod.js @@ -21,7 +21,7 @@ export default async function perform(method, pathname, content, ctype) { break; case 'LOGIN': - return await this.storage.login(pathname); + return await this.storage.login(this.request.loginoptions); break; case 'ITEM_TYPE': @@ -132,17 +132,14 @@ export default async function perform(method, pathname, content, ctype) { if (patchStatus !== 200) { return { - headers: { - status: patchStatus - } + status: patchStatus, + statusText: newContent }; } } catch (e) { return { - headers: { - status: parseInt(e), - statusText: e - } + status: parseInt(e), + statusText: e }; } @@ -153,4 +150,4 @@ export default async function perform(method, pathname, content, ctype) { return putStatus; break; } -} // ENDS \ No newline at end of file +} // ENDS diff --git a/dropbox/README.md b/dropbox/README.md index 7fbabdf..355ae88 100644 --- a/dropbox/README.md +++ b/dropbox/README.md @@ -1,4 +1,12 @@ # Solid-Rest-Dropbox -A Solid-Rest plugin for Dropbox +Treat your Dropbox storage as a Solid pod + +**Warning** This library is in development and should be considered experimental at this time. If you run into problems, contact me on the Solid forum (@jeffz) or Gitter channel. (@jeff-zucker). + +This library treats a Dropbox host as a serverless Solid Pod, accepting Solid requests (GET, PUT, etc.) and returning Solid responses (wac-allow headers, turtle representation of folders, etc.). + +The library may be used stand-alone (see [tests](./tests/all.js) for examples). + +© 2021, Jeff Zucker, may be freely used with an MIT license. diff --git a/dropbox/examples/dropbox-read.js b/dropbox/examples/dropbox-read.js new file mode 100644 index 0000000..ea93ccd --- /dev/null +++ b/dropbox/examples/dropbox-read.js @@ -0,0 +1,36 @@ +import {SolidRestDropbox} from '../'; +import {credentials} from '/home/jeff/.solid-identities.js'; +const client = new SolidRestDropbox(); + +const access_token = credentials.dropbox.access_token; +const testFolder = 'dropbox:///'; +const testFile = 'dropbox:///x.txt'; + +async function main(){ + await login({access_token:access_token}); + await readFolder( testFolder ); + await readFile( testFile ); +} +main(); + +async function login(init){ + console.log("logging in ..."); + let response = await client.login(init); + console.log("got status : ",response.status,response.statusText,"\n"); +} +async function readFile(file){ + console.log("reading file ",file); + let response = await client.fetch(file); + console.log("got content : ",await response.text(),"\n"); + console.log("got status : ",response.status,response.statusText,"\n"); +} +async function readFolder(folder){ + console.log("reading folder ",folder); + let response = await client.fetch(folder); + let content = await response.text(); + console.log("got container turtle : ",content.length,"\n"); + console.log("got status : ",response.status,response.statusText,"\n"); +} + + + diff --git a/dropbox/package.json b/dropbox/package.json index 7c5453c..b79ff81 100644 --- a/dropbox/package.json +++ b/dropbox/package.json @@ -1,7 +1,7 @@ { "name": "@solid-rest/dropbox", "author": "Jeff Zucker", - "version": "1.0.5", + "version": "1.0.6", "license": "MIT", "description": "Solid-Rest plugin for Dropbox", "main": "dist/cjs/index.js", diff --git a/dropbox/src/index.js b/dropbox/src/index.js index 3a630b0..b510cf7 100644 --- a/dropbox/src/index.js +++ b/dropbox/src/index.js @@ -1,5 +1,6 @@ import {Dropbox} from "dropbox"; -import SolidRest from "@solid-rest/core"; +//import SolidRest from "@solid-rest/core"; +import SolidRest from "../../../core"; export class SolidRestDropbox { @@ -21,14 +22,13 @@ export class SolidRestDropbox { * @param accessToken // get one in browser from Dropbox * @return true or error object */ - async login(token){ + async login(options){ try { - this.dbx = new Dropbox({ accessToken: token }); + this.dbx = new Dropbox({ accessToken: options.access_token }); return true; } catch(e){ console.log("Dropbox login error : ",e.status) - return false; return { headers: { status:e.status, statusText:e.error, @@ -51,13 +51,6 @@ export class SolidRestDropbox { isContainer : true, mode : { read:1,write:0,append:0,control:0 } }); - else return( { - path : fn, - exists : true, - name : fn, - isContainer : false, - mode : { read:1,write:0,append:0,control:0 } - }); let info = await this.dbx.filesGetMetadata({ path:fn }); let item = { path : info.path_lower, @@ -69,7 +62,7 @@ export class SolidRestDropbox { return( item ) } catch(e){ - console.log("Dropbox getItemInfo error : ",e.status) + console.log("Dropbox getItemInfo error : ",e.status,e.statusText) return { headers: { status:e.status||500, statusText:e.error, @@ -80,7 +73,7 @@ export class SolidRestDropbox { /** * read a folder * @param filePath - * @return on success : a possibly empty array of child fileNames (not paths) + * @return on success : folder contents with fields path, name, isContainer * @return on failure : false */ async getContainer(path) { @@ -102,7 +95,7 @@ export class SolidRestDropbox { return container; } catch(e){ - console.log("Dropbox getContainer error : ",e.status) + console.log("Dropbox getContainer error : ",path,e.status) return { headers: { status:e.status, statusText:e.error, @@ -117,6 +110,7 @@ export class SolidRestDropbox { * @return on failure false | Response object */ async getResource(pathname){ + if( pathname.endsWith('/') ) return getContainer(pathname); try{ let bodyData = await this.dbx.filesDownload({path:pathname}) return bodyData.result.fileBinary; @@ -130,8 +124,115 @@ export class SolidRestDropbox { } } -} + /** + * write a file + * @param filePath + * @param content + * @param content-type + * @return on success : 200 if pre-existed, 201 otherwise + * @return on failure false + */ + async putResource(pathname, content, ctype) { + try { + await this.dbx.filesUpload({ + path:pathname, + content:content, + }); + } + catch(e){ + console.log("Dropbox putResource error : ",e.status) + return { headers: { + status:e.status, + statusText:e.error, + }}; + } + } + + /** + * create folder + * @param filePath + * @return true on success, false on failure + */ + async postContainer(fn) { + fn = fn.replace(/\/$/, ''); + try { + await this.dbx.filesCreateFolderV2({path:fn}); + } + catch(e){ + console.log("Dropbox postContainer error : ",e.status) + return { headers: { + status:e.status, + statusText:e.error, + }}; + } + } + + /** + * create parent and intermediate folders + * @param filePath + * @return true on success, false on failure + */ + async makeContainers(pathname) { + const foldername = libPath.dirname(pathname); -// ENDS + if (!fs.existsSync(foldername)) { + try { + fs.mkdirpSync(foldername); + return Promise.resolve(true); + } catch { + return Promise.resolve(false); + } + } + + return Promise.resolve(true); + } + + /** + * delete file + * @param filePath + * @return true on success, false on failure + */ + async deleteResource(fn) { + try { + console.log('deleting ',fn); + // await this.dbx.filesDelete({path:fn}); + } + catch(e){ + console.log("Dropbox deleteResource error : ",e.status) + return { headers: { + status:e.status, + statusText:e.error, + }}; + } + } + + /** + * delete folder + * @param filePath + * @return true on success, false on failure + */ + async deleteContainer(fn) { + try { + let files = await this.getContainer(fn); + if(files && files.length > 0){ + console.log('Refusing to delete folder - not empty! ',fn); + return 409; + } + else { + console.log('deleting ',fn); + // await this.dbx.filesDelete({path:fn}); + } + } + catch(e){ + console.log("Dropbox deleteResource error : ",e.status) + return { headers: { + status:e.status, + statusText:e.error, + }}; + } + } + +} +// THE END! diff --git a/file/README.md b/file/README.md index 3f225b3..b0d34cf 100644 --- a/file/README.md +++ b/file/README.md @@ -1,6 +1,14 @@ # Solid-Rest-File -This is a plugin for [Solid-Rest](https://github.com/solid/solid-rest) that handles file system read and write. This plugin is best used through other libraries, notably [Solid Node Client](https://github.com/solid/solid-node-client) which combines the plugin with authenticated fetching in making a command-line or server-side app. +**Treat any file system as a serverless Solid pod** + +This library treats a local file system as a serverless Solid Pod, accepting Solid requests (GET, PUT, etc.) and returning Solid responses (wac-allow headers, turtle representation of folders, etc.). + +The library may be used stand-alone (see [tests](./tests/all.js) for examples), or as a plugin to [Solid-Node-Client](https://github.com/solid/solid-node-client) from where it can be integrated into almost any Solid library or app. Simply import Solid-Node-Client and thereafter use file:// URLs almost anywhere that https:// URLs work. See the Solid-Node-Client documentation for details. + +© 2021, Jeff Zucker, may be freely used with an MIT license. + + diff --git a/file/package.json b/file/package.json index 16f677a..646f2ff 100644 --- a/file/package.json +++ b/file/package.json @@ -1,6 +1,6 @@ { "name": "@solid-rest/file", - "version": "2.0.4", + "version": "2.0.5", "author": "Jeff Zucker", "license": "MIT", "description": "solid-rest plugin for filesystems", diff --git a/file/src/index.js b/file/src/index.js index 7c9c4b3..1fcd705 100644 --- a/file/src/index.js +++ b/file/src/index.js @@ -87,9 +87,7 @@ export class SolidRestFile { let exists = await this.itemExists(fn); if (!type && fn.endsWith('/')) type = "Container"; - let read = true, - write = true; - + let read = true, write = true; if (exists) { try { fs.accessSync(fn, fs.constants.R_OK); @@ -317,4 +315,4 @@ function getParent(url) { if (!url.includes('/')) return null; return url.substring(0, url.lastIndexOf('/')) + '/'; -} // ENDS \ No newline at end of file +} // ENDS diff --git a/mem/README.md b/mem/README.md index de1b980..05c9c08 100644 --- a/mem/README.md +++ b/mem/README.md @@ -1,9 +1,16 @@ # Solid-Rest-Mem -A Solid-Rest plugin for virtual in-memory pods +Treat virtual in-memory storage as a Solid pod + +**Warning** This library has not been upgraded to version 2.x yet, so is NOT usable at the moment. If you have need for it before I upgrade, contact me on the Solid forum (@jeffz) or Gitter channel. (@jeff-zucker). + +This library treats a local file system as a serverless Solid Pod, accepting Solid requests (GET, PUT, etc.) and returning Solid responses (wac-allow headers, turtle representation of folders, etc.). + +The library may be used stand-alone (see [tests](./tests/all.js) for examples), or as a plugin to [Solid-Node-Client](https://github.com/solid/solid-node-client) from where it can be integrated into almost any Solid library or app. Simply import Solid-Node-Client and thereafter use file:// URLs almost anywhere that https:// URLs work. See the Solid-Node-Client documentation for details. + +© 2021, Jeff Zucker, may be freely used with an MIT license. + -This is a plugin for [Solid-Rest](https://github.com/solid/solid-rest) that handles in-memory virtual pods. This plugin is best used through other libraries, notably [Solid Node Client](https://github.com/solid/solid-node-client) which combines the plugin with authenticated fetching in making a command-line or server-side app. -**Note** This library has not been upgraded to version 2.x yet, so is NOT usable at the moment. diff --git a/ssh/README.md b/ssh/README.md index de1b980..b3bc517 100644 --- a/ssh/README.md +++ b/ssh/README.md @@ -1,9 +1,11 @@ -# Solid-Rest-Mem +# Solid-Rest-SSH -A Solid-Rest plugin for virtual in-memory pods +Treat any SSH host ass a Solid pod -This is a plugin for [Solid-Rest](https://github.com/solid/solid-rest) that handles in-memory virtual pods. This plugin is best used through other libraries, notably [Solid Node Client](https://github.com/solid/solid-node-client) which combines the plugin with authenticated fetching in making a command-line or server-side app. +**Warning** This library has not been upgraded to version 2.x yet, so is NOT usable at the moment. If you have need for it before I upgrade, contact me on the Solid forum (@jeffz) or Gitter channel. (@jeff-zucker). -**Note** This library has not been upgraded to version 2.x yet, so is NOT usable at the moment. +This library treats an SSH host as a serverless Solid Pod, accepting Solid requests (GET, PUT, etc.) and returning Solid responses (wac-allow headers, turtle representation of folders, etc.). +The library may be used stand-alone (see [tests](./tests/all.js) for examples), or as a plugin to [Solid-Node-Client](https://github.com/solid/solid-node-client) from where it can be integrated into almost any Solid library or app. Simply import Solid-Node-Client and thereafter use file:// URLs almost anywhere that https:// URLs work. See the Solid-Node-Client documentation for details. +© 2021, Jeff Zucker, may be freely used with an MIT license. diff --git a/tests/all.js b/tests/all.js index 89eabf3..da07458 100644 --- a/tests/all.js +++ b/tests/all.js @@ -1,27 +1,9 @@ -// SAME TEST SHOULD WORK FOR solid-rest AND solid-node-client -// -//const SolidNodeClient = require('../').SolidNodeClient -//const client = new SolidNodeClient() - -// global.$rdf = require('rdflib') -// const client = new SolidRest() -// const $rdf = require('rdflib'); import * as $rdf from 'rdflib'; -import SolidRest from '../src/index.js'; -import SolidRestFile from '../plugins/solid-rest-file/src/index.js'; -//import SolidRestMem from '../plugins/solid-rest-mem/src/index.js'; +import {SolidRestFile} from '../file/src/index.js'; import * as libUrl from 'url' -function getRestClient(protocol,parser){ - const plugin = protocol.startsWith('file') ? new SolidRestFile() - : protocol.startsWith('mem') ? new SolidRestMem() - : protocol.startsWith('ssh') ? new SolidRestSsh() : null; - return new SolidRest({ - plugin : plugin, - parser : parser, - }); -} -let client,slug,cSlug; +global.$rdf = $rdf; +const client = new SolidRestFile(); /** Silence rdflib chatty information about patch * Send console.log() to a logfile @@ -36,8 +18,8 @@ process.on('uncaughtException', function(err) { }); */ -let [tests,fails,passes,res] = [0,0,0] -let allfails = 0 +let [tests,fails,passes,res,allfails,slug,cSlug] = [0,0,0,0,'',''] + async function main(){ await run("file:") @@ -53,7 +35,6 @@ async function main(){ main() async function getConfig(scheme){ - client = getRestClient(scheme,$rdf); let host = scheme; if(scheme==="mem:"){ scheme = "mem://" // = protocol diff --git a/tests/shouldSucceed.js b/tests/shouldSucceed.js index 22feaaa..d395c2e 100644 --- a/tests/shouldSucceed.js +++ b/tests/shouldSucceed.js @@ -1,12 +1,9 @@ -import SolidRest from '../'; -import SolidRestFile from '../plugins/solid-rest-file'; -import SolidRestMem from '../plugins/solid-rest-mem'; +import {SolidRestFile} from '../file'; +let client = new SolidRestFile(); let [tests,fails,passes,res] = [0,0,0] let allfails = 0 -let client = getRestClient('file'); - const cfg = { base : `file://${process.cwd()}/`, folder1 : `test-folder`, @@ -53,7 +50,7 @@ console.log('head'); ok( "head container", res.status==200 && res.headers.get('content-type')==='text/turtle',res) res = await HEAD( cfg.file1 ) - ok( "head resource", res.status==200 && res.headers.get('content-type')==='text/plain',res) + ok( "head resource", res.status==200 && res.headers.get('content-type').startsWith('text/plain',res)) console.log('delete'); res = await DELETE( cfg.file1 )