Skip to content

Commit

Permalink
feat: implement it
Browse files Browse the repository at this point in the history
  • Loading branch information
targos committed Dec 2, 2019
1 parent 0df4867 commit 029e83f
Show file tree
Hide file tree
Showing 9 changed files with 296 additions and 29 deletions.
24 changes: 12 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,24 +1,26 @@
# adonis-datadrive

[![NPM version][npm-image]][npm-url]

<!-- [![build status][ci-image]][ci-url] -->

[![build status][ci-image]][ci-url]
[![npm download][download-image]][download-url]

XXX.
Extended drive provider for AdonisJs.

## Installation

`$ npm i adonis-datadrive`
```console
npm i adonis-datadrive
node ace invoke adonis-datadrive
```

## Usage

```js
import { myModule } from 'adonis-datadrive';
import DataDrive from '@ioc:DataDrive';

const result = myModule(args);
// result is ...
const drive = DataDrive.drive('myDrive');

// drive.put('myfile.txt', 'mycontent').then(...);
```

## License
Expand All @@ -27,9 +29,7 @@ const result = myModule(args);

[npm-image]: https://img.shields.io/npm/v/adonis-datadrive.svg
[npm-url]: https://www.npmjs.com/package/adonis-datadrive

<!-- [ci-image]: https://github.com/zakodium/adonis-datadrive/workflows/Node.js%20CI/badge.svg?branch=master
[ci-url]: https://github.com/zakodium/adonis-datadrive/actions?query=workflow%3A%22Node.js+CI%22 -->

[ci-image]: https://github.com/zakodium/adonis-datadrive/workflows/Node.js%20CI/badge.svg?branch=master
[ci-url]: https://github.com/zakodium/adonis-datadrive/actions?query=workflow%3A%22Node.js+CI%22
[download-image]: https://img.shields.io/npm/dm/adonis-datadrive.svg
[download-url]: https://www.npmjs.com/package/adonis-datadrive
15 changes: 15 additions & 0 deletions config/datadrive.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { DataDriveConfig } from '@ioc:DataDrive';

const dataDriveConfig: DataDriveConfig = {
drives: {
local: {
// Disk refers to an existing disk in Drive's config.
disk: 'local',
// All files will be placed in a location under the prefix.
// prefix must contain two parts separated by a slash.
prefix: 'my/prefix',
},
},
};

export default dataDriveConfig;
43 changes: 35 additions & 8 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,22 +1,38 @@
{
"name": "adonis-datadrive",
"version": "0.0.0",
"description": "XXX",
"main": "./lib/index.js",
"types": "./lib/index.d.ts",
"keywords": [],
"description": "Extended drive provider for AdonisJs",
"main": "./lib/providers/DataDriveProvider.js",
"types": "./lib/src/types.d.ts",
"keywords": [
"adonisjs",
"adonis",
"drive",
"datadrive"
],
"author": "Michaël Zasso",
"license": "MIT",
"files": [
"src",
"lib"
],
"adonisjs": {
"templates": {
"basePath": "./build/config",
"config": [
"datadrive.txt"
]
},
"types": "adonis-datadrive",
"providers": [
"adonis-datadrive"
]
},
"scripts": {
"clean": "rimraf lib",
"eslint": "eslint src --ext ts --cache",
"eslint": "eslint src providers --ext ts --cache",
"eslint-fix": "npm run eslint -- --fix",
"prepublishOnly": "npm run tsc",
"test": "npm run test-coverage && npm run eslint",
"prepublishOnly": "npm run tsc && copyfiles \"config/**/*.txt\" lib",
"test": "npm run eslint",
"test-coverage": "npm run test-only -- --coverage",
"test-only": "jest",
"tsc": "npm run clean && npm run tsc-cjs",
Expand All @@ -42,7 +58,10 @@
"trailingComma": "all"
},
"devDependencies": {
"@adonisjs/fold": "^6.2.3",
"@targos/adonis-drive": "^4.0.3",
"@types/jest": "^24.0.23",
"@types/uuid": "^3.4.6",
"@typescript-eslint/eslint-plugin": "^2.9.0",
"@typescript-eslint/parser": "^2.9.0",
"eslint": "^6.7.2",
Expand All @@ -56,5 +75,13 @@
"rimraf": "^3.0.0",
"ts-jest": "^24.2.0",
"typescript": "^3.7.2"
},
"dependencies": {
"@slynova/flydrive": "^1.0.0-0",
"@types/node": "^12.12.14",
"uuid": "^3.3.3"
},
"peerDependencies": {
"@targos/adonis-drive": "^4.0.3"
}
}
19 changes: 19 additions & 0 deletions providers/DataDriveProvider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { IocContract } from '@adonisjs/fold';

import { DataDriveManager } from '../src/DataDriveManager';

export default class DataDriveProvider {
private $container: IocContract;

public constructor(container: IocContract) {
this.$container = container;
}

public register(): void {
this.$container.singleton('DataDrive', () => {
const Drive = this.$container.use('Drive');
const config = this.$container.use('Adonis/Core/Config').get('datadrive');
return new DataDriveManager(Drive, config);
});
}
}
112 changes: 112 additions & 0 deletions src/DataDrive.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import { extname } from 'path';
import { Readable } from 'stream';

import { Storage, SignedUrlOptions, StatResponse } from '@slynova/flydrive';
import { v4 as uuidV4 } from 'uuid';

import {
DataDriveFile,
DataDriveFileWithSize,
GraphqlUpload,
} from '@ioc:DataDrive';

const goodPrefix = /^[a-zA-Z0-9-]+$/;

export class DataDrive {
private prefix: string;
private disk: Storage;

public constructor(prefix: string, disk: Storage) {
if (typeof prefix !== 'string') {
throw new TypeError('prefix must be a string');
}
const splitted = prefix.split('/');
if (splitted.length !== 2 || splitted[0] === '' || splitted[0] === '') {
throw new TypeError('prefix must have two parts separated by a slash');
}
if (!goodPrefix.test(splitted[0]) || !goodPrefix.test(splitted[1])) {
throw new Error(`bad prefix: ${prefix}`);
}
this.prefix = prefix;
this.disk = disk;
}

private _destPath(file: DataDriveFile): string {
return `${this.prefix}/${file.id.substring(0, 2)}/${file.id.substring(
2,
4,
)}/${file.id + extname(file.filename)}`;
}

public async copy(
src: DataDriveFile,
dest: string,
): Promise<DataDriveFileWithSize> {
const id = uuidV4();
const destPath = this._destPath({ id, filename: dest });
await this.disk.copy(this._destPath(src), destPath, {});
const { size } = await this.disk.getStat(destPath);
return { id, filename: dest, size };
}

public async delete(file: DataDriveFile): Promise<void> {
await this.disk.delete(this._destPath(file));
}

public async get(file: DataDriveFile, encoding?: string): Promise<string> {
const result = await this.disk.get(this._destPath(file), encoding);
return result.content;
}

public async getBuffer(file: DataDriveFile): Promise<Buffer> {
const result = await this.disk.getBuffer(this._destPath(file));
return result.content;
}

public async getSignedUrl(
file: DataDriveFile,
options?: SignedUrlOptions,
): Promise<string> {
const result = await this.disk.getSignedUrl(this._destPath(file), options);
return result.signedUrl;
}

public async getStat(file: DataDriveFile): Promise<StatResponse> {
return this.disk.getStat(this._destPath(file));
}

public getStream(file: DataDriveFile): Readable {
return this.disk.getStream(this._destPath(file));
}

public async put(
filename: string,
content: Buffer | Readable | string,
): Promise<DataDriveFileWithSize> {
const id = uuidV4();
const destPath = this._destPath({ id, filename });
await this.disk.put(destPath, content);
const { size } = await this.disk.getStat(destPath);
return {
id,
filename,
size,
};
}

public async storeGraphQLUpload(
upload: Promise<GraphqlUpload>,
): Promise<DataDriveFileWithSize> {
const pdf = await upload;
const id = uuidV4();
const { createReadStream, filename } = pdf;
const destPath = this._destPath({ id, filename });
await this.disk.put(destPath, createReadStream());
const { size } = await this.disk.getStat(destPath);
return {
id,
filename,
size,
};
}
}
24 changes: 24 additions & 0 deletions src/DataDriveManager.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { DataDrive } from './DataDrive';

import AdonisDrive from '@ioc:Drive';
import { DataDriveConfig } from '@ioc:DataDrive';

export class DataDriveManager {
private $drives: Record<string, DataDrive>;

public drive(name: string): DataDrive {
if (this.$drives[name]) {
return this.$drives[name];
} else {
throw new Error(`unknown drive: ${name}`);
}
}

public constructor(Drive: typeof AdonisDrive, config: DataDriveConfig) {
this.$drives = {};
for (const name in config.drives) {
const conf = config.drives[name];
this.$drives[name] = new DataDrive(conf.prefix, Drive.disk(conf.disk));
}
}
}
7 changes: 0 additions & 7 deletions src/index.ts

This file was deleted.

76 changes: 76 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
declare module '@ioc:DataDrive' {
import { Readable } from 'stream';

import {
Storage,
Response,
ContentResponse,
SignedUrlOptions,
SignedUrlResponse,
StatResponse,
} from '@slynova/flydrive';

import Drive from '@ioc:Drive';

export interface GraphqlUpload {
filename: string;
mimetype: string;
encoding: string;
createReadStream: () => Readable;
}

export interface DataDriveConfig {
drives: {
[key: string]: {
/**
* Name of the disk from adonis-drive to use.
*/
disk: string;
/**
* All files will be placed in a location under the `prefix`.
* `prefix` must contain two parts separated by a slash.
*/
prefix: string;
};
};
}

export interface DataDriveFile {
id: string;
filename: string;
}

export interface DataDriveFileWithSize extends DataDriveFile {
size: number;
}

export class DataDrive {
public copy(
src: DataDriveFile,
dest: string,
): Promise<DataDriveFileWithSize>;
public delete(file: DataDriveFile): Promise<void>;
public get(file: DataDriveFile, encoding?: string): Promise<string>;
public getBuffer(file: DataDriveFile): Promise<Buffer>;
public getSignedUrl(
file: DataDriveFile,
options?: SignedUrlOptions,
): Promise<string>;
public getStat(file: DataDriveFile): Promise<StatResponse>;
public getStream(file: DataDriveFile): Readable;
public put(
filename: string,
content: Buffer | Readable | string,
): Promise<DataDriveFileWithSize>;
public storeGraphQLUpload(
upload: Promise<GraphqlUpload>,
): Promise<DataDriveFileWithSize>;
}

class DataDriveManager {
public drive(name: string): DataDrive;
}

const dataDriveManager: DataDriveManager;
export default dataDriveManager;
}
5 changes: 3 additions & 2 deletions tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
"moduleResolution": "node",
"sourceMap": true,
"strict": true,
"target": "esnext"
"target": "esnext",
"types": ["@targos/adonis-drive", "node"]
},
"include": ["./src/**/*"]
"include": ["./src/**/*", "./providers/**/*"]
}

0 comments on commit 029e83f

Please sign in to comment.