Now our basic setup is ready, let's make a simple mockup api for our file manager.
File Manager Service
Let's create file-manager-service.ts
under file-manager/services
directory then let's establish it.
// file-manager-service.ts
export class FileManagerService implements FileManagerServiceInterface {
}
We just created an empty class that implements FileManagerServiceInterface
interface which is clearly not defined yet, so let's define it.
FileManagerServiceInterface Interface
Let's think a little bit here, what possibly requests will be used in our file manager, let's list them all, or at least the basic requests.
- List Directory contents
- Upload Files
- Rename Directory/File
- Delete Directory/File
- Create Directory
- Move Directory/File
- Copy Directory/File
So we've like 6-7 requests to do, let's create their interface.
Head to file-manager/types
and create FileManagerServiceInterface.ts
file
// file-manager/types/FileManagerServiceInterface.ts
export default interface FileManagerServiceInterface {
/**
* List directory contents for the given path
*/
list(directoryPath: string): Promise<any>;
/**
* Create new directory
*
* First parameter will be the directory name,
* second parameter will be the directory path that will be created into.
*/
createDirectory(directoryName: string, saveTo: string): Promise<any>;
/**
* Delete directories/files
*/
delete(paths: string[]): Promise<any>;
/**
* Rename directory | file
*
* The first parameter will be the old path,
* the second parameter will be the new path.
*/
rename(path: string, newPath: string): Promise<any>;
/**
* Copy the given files/directories to the given destination
*/
copy(paths: string[], destination: string): Promise<any>;
/**
* Move the given files/directories to the given destination
*/
move(paths: string[], destination: string): Promise<any>;
/**
* Upload the given files into the given directory path
*/
upload(files: File[], directoryPath: string): Promise<any>;
}
Now let's import our interface in our FileManagerService
file.
// file-manager-service.ts
import FileManagerServiceInterface from "../types/FileManagerServiceInterface";
export class FileManagerService implements FileManagerServiceInterface {}
Of course now the Typescript complier will complain that we didn't implement these methods, so let's do it.
But before we do this, let's install first Faker so we can fake some data.
yarn add -D @faker-js/faker
Now let's return to our service class.
import FileManagerServiceInterface from "../types/FileManagerServiceInterface";
export class FileManagerService implements FileManagerServiceInterface {
/**
* {@inheritDoc}
*/
public list(directoryPath: string): Promise<any> {
return new Promise(resolve => {
resolve({
data: {
nodes: listNodes(),
},
});
});
}
}
We made our promise to mock axios
response shape so once we're done with the mockup api, we can easily replace it with real api at anytime.
If you've a real API to work with, i'll be pleased to use in the series :)
and the list method will return list of nodes
as array.
Now let's create listNodes
function from faker, it will be created in utils
directory under data.ts
file.
// file-manager/utils/data.ts
import { faker } from "@faker-js/faker";
import { DirectoryNode, Node } from "../types/FileManager.types";
export function newNode(): Node {
const node: Node | DirectoryNode = {
name: faker.system.fileName(),
path: faker.system.filePath(),
size: faker.datatype.number({ min: 1, max: 100000 }),
isDirectory: faker.datatype.boolean(),
};
if (node.isDirectory) {
(node as DirectoryNode).children = listNodes(3);
}
return node;
}
export function listNodes(maxNodes = 10): Node[] {
return faker.datatype
.array(faker.datatype.number({ min: 1, max: maxNodes }))
.map(newNode);
}
In the second function listNodes
it will create list of nodes randomly and it accepts one parameter, the max generated nodes, this is important so we don't get in infinity loop when generating children nodes as we maximize it with only 3 children nodes (in newNode
function).
newNode
function is used to generated random node data, we set the node that it can be Node
or DirectoryNode
.
Actually, let's work only with Node
and set the children
property inside it, so if the node is directory it can have children.
Let's navigate to types/FileManager.types.ts
to remove FileNode
and DirectoryNode
then update our Node
.
// file-manager/types/FileManager.types.ts
/**
* File Manager node is the primary data structure for the File Manager.
* It can be a directory or a file.
* It contains the following properties:
*/
export type Node = {
/**
* Node Name
*/
name: string;
/**
* Node full path to root
*/
path: string;
/**
* Node size in bits
*/
size: number;
/**
* Is node directory
*/
isDirectory: boolean;
/**
* Node children
* This should be present (event with empty array) if the node is directory
*/
children?: Node[];
};
This will make our code less confused so do us lol, so the node can have two types of shapes, a directory shape that contains children and a file shape that does not have children.
Let's go to FileManager
class and update the current directory node.
// file-manager/utils/FileManager.ts
import { Node } from "../types/FileManager.types";
export default class FileManager {
/**
* Root path
*/
protected rootPath = "/";
/**
* Current directory path
*/
protected currentDirectoryPath = "/";
/**
* Current directory node
*/
protected currentDirectoryNode?: Node;
}
Finally let's update again our utils/data.ts
file
// file-manager/utils/data.ts
import { faker } from "@faker-js/faker";
import { Node } from "../types/FileManager.types";
export function newNode(): Node {
const node: Node = {
name: faker.system.fileName(),
path: faker.system.filePath(),
size: faker.datatype.number({ min: 1, max: 100000 }),
isDirectory: faker.datatype.boolean(),
};
if (node.isDirectory) {
node.children = listNodes(3);
}
return node;
}
export function listNodes(maxNodes = 10): Node[] {
return faker.datatype
.array(faker.datatype.number({ min: 1, max: maxNodes }))
.map(newNode);
}
Now our code is much cleaner.
Pro Tip: you don't get the perfect code from the first time, that's why we're going to enhance and develop our code gradually as we progress in our project.
Heading back to our File Manager Service.
import FileManagerServiceInterface from "../types/FileManagerServiceInterface";
import { listNodes } from "../utils/data";
export class FileManagerService implements FileManagerServiceInterface {
/**
* {@inheritDoc}
*/
public list(directoryPath: string): Promise<any> {
return new Promise(resolve => {
resolve({
data: {
nodes: listNodes(),
},
});
});
}
/**
* {@inheritDoc}
*/
public createDirectory(directoryName: string, saveTo: string): Promise<any> {
throw new Error("Method not implemented.");
}
/**
* {@inheritDoc}
*/
public delete(paths: string[]): Promise<any> {
throw new Error("Method not implemented.");
}
/**
* {@inheritDoc}
*/
public rename(path: string, newPath: string): Promise<any> {
throw new Error("Method not implemented.");
}
/**
* {@inheritDoc}
*/
public copy(paths: string[], destination: string): Promise<any> {
throw new Error("Method not implemented.");
}
/**
* {@inheritDoc}
*/
public move(paths: string[], destination: string): Promise<any> {
throw new Error("Method not implemented.");
}
/**
* {@inheritDoc}
*/
public upload(files: File[], directoryPath: string): Promise<any> {
throw new Error("Method not implemented.");
}
}
I made the rest of the code to be auto injected using vscode
feature or so, so it doesn't pop any compiler error.
So far so good, i'll stop at the moment at this point.
You can see the results in the HomePage
component.
Head to the component and paste the following code inside it then see the results in the console.
// home/components/HomePage/HomePage.tsx
import Helmet from "@mongez/react-helmet";
import { FileManagerService } from "app/file-manager/services/file-manager-service";
const fileManager = new FileManagerService();
export default function HomePage() {
fileManager.list("/").then(response => {
console.log(response.data);
});
return (
<>
<Helmet title="home" appendAppName={false} />
<h1>Welcome To Home Page</h1>
</>
);
}
Article Repository
You can see chapter files in Github Repository
Don't forget the
main
branch has the latest updated code.
Top comments (0)