ownfiles
Version:
A library to manage files in a Solid User's Pod
182 lines (164 loc) • 5.5 kB
text/typescript
import url from 'url';
import mime from 'mime';
import { Namespace } from 'rdflib';
import ns from 'solid-namespace';
import FileClient from './fileClient';
import { Quad_Subject } from 'rdflib/lib/tf-types';
const typeNamespaceUrl = 'http://www.w3.org/ns/iana/media-types/';
const types = Namespace(typeNamespaceUrl);
export interface CreateOptions {
name: string;
contents: any[] | Blob | string;
contentType: string;
headers?: Headers | Record<string, string>;
}
export type ExtendedResponseType = Response & {
responseText: string;
req?: Quad_Subject;
size: number;
error: string;
};
export const create = function(
this: FileClient,
resourceAddress: string,
options: Partial<CreateOptions> = {},
) {
if (!resourceAddress) {
throw new Error('Please specify the location for the new resource.');
} else if (url.parse(resourceAddress).protocol !== 'https:') {
throw new Error(
'Please specify a valid location for the new resource.',
);
}
const paths = resourceAddress.split('/');
if (!options.name) {
if (paths[paths.length - 1] !== '') {
options.name = paths[paths.length - 1];
paths.pop();
return this.createFile(paths.join('/') + '/', options);
} else {
options.name = paths[paths.length - 2];
paths.pop();
paths.pop();
return this.createFolder(paths.join('/') + '/', options);
}
} else if (resourceAddress.endsWith('/')) {
paths.pop();
paths.pop();
return this.createFolder(paths.join('/') + '/', options);
} else {
paths.pop();
return this.createFile(paths.join('/') + '/', options);
}
};
export const createFolder = async function(
this: FileClient,
folderAddress: string,
options?: Partial<CreateOptions>,
) {
const request = {
method: 'POST',
headers: {
Slug: options?.name ?? 'Untitled Folder',
Link: '<http://www.w3.org/ns/ldp#BasicContainer>; rel="type"',
'Content-Type': 'text/turtle',
},
};
const res = (await this.fetcher._fetch(
folderAddress,
request,
)) as ExtendedResponseType;
const location = res.headers.get('Location');
if (location && res.status < 304) {
const parsedUrl = url.parse(folderAddress);
const rootUrl = `${parsedUrl.protocol}//${parsedUrl.host}`;
await this.addToIndex(
{ url: rootUrl + location, types: [ns().ldp('Container')] },
{ force: true },
);
}
return res;
};
export const createFile = async function(
this: FileClient,
fileAddress: string,
options: Partial<CreateOptions> = {},
): Promise<Response> {
const stringContent = options.contents as string;
const blobContent = options.contents as Blob;
const anyContent = options.contents as any[];
const body = stringContent ?? blobContent ?? bodyFromContent(anyContent);
if (options.name) {
options.contentType =
options.contentType ?? mime.getType(options.name) ?? 'text/turtle';
const fileExtension =
(options.contentType === 'image/jpeg' ||
options.contentType === 'application/octet-stream') &&
options.name &&
options.name.split('.')[1] === 'jpg'
? 'jpg'
: mime.getExtension(options.contentType);
fileAddress = `${fileAddress}${
options.name.split('.')[0]
}.${fileExtension}`;
}
const request = {
contentType: options.contentType,
data: body,
};
const res = await this.fetcher.webOperation('PUT', fileAddress, request);
const location =
res.headers.get('Location') ?? url.parse(fileAddress).pathname;
if (location && res.status < 300) {
const parsedUrl = url.parse(fileAddress);
const rootUrl = `${parsedUrl.protocol}//${parsedUrl.host}`;
await this.addToIndex({
url: rootUrl + location,
types: [
types(options.contentType ?? 'text/plain' + '#Resource'),
ns().ldp('Resource'),
],
});
}
return await res;
};
export const createIfNotExist = function(
this: FileClient,
resourceAddress: string,
options: Partial<CreateOptions> = { headers: {} },
): Promise<Response | undefined> {
return this.fetcher
._fetch(resourceAddress, { method: 'HEAD', headers: options.headers })
.then((res: Response) => {
if (res.status === 200) {
return;
} else {
return this.create(resourceAddress, options);
}
})
.catch((err) => {
if (err.response.status === 404)
return this.create(resourceAddress, options);
});
};
function bodyFromContent(content: any[] | string | undefined) {
if (content) {
if (Array.isArray(content)) {
if (
content.every((el) => {
return el.constructor.name === 'Statement';
})
) {
let bodyString = '';
content.forEach((st) => {
bodyString += st.toNT() + '\n';
});
return bodyString;
}
} else {
return content;
}
} else {
return '';
}
}