Skip to content

Commit 538ce83

Browse files
committed
feat(desk): add entry-item-repository
1 parent 2f2400d commit 538ce83

File tree

13 files changed

+344
-36
lines changed

13 files changed

+344
-36
lines changed

packages/core/__tests__/app.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import CrudManager from '../gateways/crud-manager'
22
import DriveManager from '../gateways/drive/manager'
33
import NodeVMEvaluation from '../gateways/evaluation/implementations/node-vm-evaluation'
44
import CollectionRepository from '../repositories/collection/implementations/collection-repository'
5+
import EntryItemRepository from '../repositories/item/implementations/entry-item-repository'
56
import AppService, { AppServiceArgs } from '../services/app-service'
67
import InMemoryCrud from './gateways/in-memory-crud'
78
import InMemoryDrive from './gateways/in-memory-drive'
@@ -28,6 +29,7 @@ export default class InMemoryApp extends AppService {
2829
const repositories: AppServiceArgs['repositories'] = {
2930
workspace: workspaceRepository,
3031
collection: new CollectionRepository(managers.drive),
32+
item: new EntryItemRepository(managers.drive),
3133
}
3234

3335
super({

packages/core/__tests__/gateways/in-memory-drive.ts

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ export default class InMemoryDrive implements Drive {
3737

3838
if (!entry) return
3939

40+
entry.name = DirectoryEntry.basename(target)
41+
4042
entry.path = target
4143
}
4244

@@ -55,13 +57,17 @@ export default class InMemoryDrive implements Drive {
5557
public async write(path: string, content: Uint8Array) {
5658
this.content.set(DirectoryEntry.normalize(path), content)
5759

58-
this.entries.push(DirectoryEntry.file(path))
60+
if (!this.entries.some((e) => e.path === path)) {
61+
this.entries.push(DirectoryEntry.file(path))
62+
}
5963
}
6064

61-
public async mkdir(entryPath: string) {
62-
const entry = DirectoryEntry.directory(entryPath)
65+
public async mkdir(path: string) {
66+
const entry = DirectoryEntry.directory(path)
6367

64-
this.entries.push(entry)
68+
if (!this.entries.some((e) => e.path === path)) {
69+
this.entries.push(entry)
70+
}
6571

6672
return entry
6773
}
@@ -76,8 +82,8 @@ export default class InMemoryDrive implements Drive {
7682
this.entries.splice(index, 1)
7783
}
7884

79-
public createFile(entryPath: string, content: any = '') {
80-
const entry = DirectoryEntry.file(entryPath)
85+
public createFile(path: string, content: any = '') {
86+
const entry = DirectoryEntry.file(path)
8187

8288
if (Array.isArray(content)) {
8389
content = JSON.stringify(content)
@@ -87,7 +93,10 @@ export default class InMemoryDrive implements Drive {
8793
content = JSON.stringify(content)
8894
}
8995

90-
this.entries.push(entry)
96+
if (!this.entries.some((e) => e.path === path)) {
97+
this.entries.push(entry)
98+
}
99+
91100
this.content.set(entry.path, Buffer.from(content))
92101
}
93102

packages/core/entities/collection.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import Column from './column'
33
export default class Collection {
44
public id: string
55
public workspaceId?: string
6+
67
public name: string
78
public path: string
89

packages/core/entities/directory-entry.ts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,11 @@ export default class DirectoryEntry {
88
}
99

1010
public static normalize(...paths: string[]) {
11-
let result = paths
11+
const result = paths
1212
.map((p) => p.split('/'))
1313
.reduce((all, p) => all.concat(p), [])
1414
.join('/')
1515

16-
if (result[0] === '/') {
17-
result = result.slice(1)
18-
}
19-
2016
return result
2117
}
2218

packages/core/exceptions/item-not-found.spec.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,13 @@ import ItemNotFound from './item-not-found'
33

44
test.group('item not found exception (unit)', () => {
55
test('should have correct format', ({ expect }) => {
6-
const workspaceId = 'hello'
76
const collectionId = 'word'
87
const itemId = 'my-item'
98

10-
const error = new ItemNotFound(workspaceId, collectionId, itemId)
9+
const error = new ItemNotFound(collectionId, itemId)
1110

1211
expect(error.message).toBe('Item not found')
1312
expect(error.i18nKey).toBe('errors.itemNotFound')
14-
expect(error.i18nArgs).toEqual([workspaceId, collectionId, itemId])
13+
expect(error.i18nArgs).toEqual([collectionId, itemId])
1514
})
1615
})
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import BaseException from './base'
22

33
export default class ItemNotFound extends BaseException {
4-
constructor(workspaceId: string, collectionId: string, itemId: string) {
4+
constructor(collectionId: string, itemId: string) {
55
super('Item not found')
66

77
this.i18nKey = 'errors.itemNotFound'
88

9-
this.i18nArgs = [workspaceId, collectionId, itemId]
9+
this.i18nArgs = [collectionId, itemId]
1010
}
1111
}

packages/core/gateways/drive/manager.ts

Lines changed: 29 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import DirectoryEntry from '../../entities/directory-entry'
12
import Drive from './drive'
23

34
export default class DriveManager<T extends Record<string, Drive> = any> implements Drive {
@@ -47,49 +48,61 @@ export default class DriveManager<T extends Record<string, Drive> = any> impleme
4748
return result
4849
}
4950

50-
public async exists(entryPath: string) {
51-
return this.execute((d) => d.exists(entryPath))
51+
public async exists(...paths: string[]) {
52+
const normalize = DirectoryEntry.normalize(...paths)
53+
54+
return this.execute((d) => d.exists(normalize))
5255
}
5356

54-
public async list(entryPath: string) {
55-
return this.execute((d) => d.list(entryPath))
57+
public async list(...paths: string[]) {
58+
const normalize = DirectoryEntry.normalize(...paths)
59+
60+
return this.execute((d) => d.list(normalize))
5661
}
5762

58-
public async get(entryPath: string) {
59-
return this.execute((d) => d.get(entryPath))
63+
public async get(...paths: string[]) {
64+
const normalize = DirectoryEntry.normalize(...paths)
65+
66+
return this.execute((d) => d.get(normalize))
6067
}
6168

62-
public async mkdir(entryPath: string) {
63-
return this.execute((d) => d.mkdir(entryPath))
69+
public async mkdir(...paths: string[]) {
70+
const normalize = DirectoryEntry.normalize(...paths)
71+
72+
return this.execute((d) => d.mkdir(normalize))
6473
}
6574

6675
public async move(source: string, target: string) {
6776
return this.execute((d) => d.move(source, target))
6877
}
6978

70-
public async read(entryPath: string) {
71-
return this.execute((d) => d.read(entryPath))
79+
public async read(...paths: string[]) {
80+
const normalize = DirectoryEntry.normalize(...paths)
81+
82+
return this.execute((d) => d.read(normalize))
7283
}
7384

74-
public async readAsString(entryPath: string) {
85+
public async readAsString(...paths: string[]) {
7586
const decoder = new TextDecoder()
7687

77-
const uint = await this.execute((d) => d.read(entryPath))
88+
const uint = await this.read(...paths)
7889

7990
return uint ? decoder.decode(uint) : uint
8091
}
8192

82-
public async write(entryPath: string, content: Uint8Array | string) {
93+
public async write(path: string, content: Uint8Array | string) {
8394
const encoder = new TextEncoder()
8495

8596
if (typeof content === 'string') {
8697
content = encoder.encode(content)
8798
}
8899

89-
return this.execute((d) => d.write(entryPath, content as Uint8Array))
100+
return this.execute((d) => d.write(path, content as Uint8Array))
90101
}
91102

92-
public async delete(entryPath: string) {
93-
return this.execute((d) => d.delete(entryPath))
103+
public async delete(...paths: string[]) {
104+
const normalize = DirectoryEntry.normalize(...paths)
105+
106+
return this.execute((d) => d.delete(normalize))
94107
}
95108
}

packages/core/gateways/evaluation/implementations/node-vm-evaluation.spec.ts.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { test } from '@japa/runner'
22
import NodeVMEvaluation from './node-vm-evaluation'
33

4-
test.group('script-service (service)', () => {
4+
test.group('node-vm-evaluation (unit)', () => {
55
const service = new NodeVMEvaluation()
66

77
test('should execute script and return result', async ({ expect }) => {

packages/core/gateways/evaluation/implementations/node-vm-evaluation.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import vm from 'vm'
22
import util from 'util'
33

44
import * as ts from 'typescript'
5-
import IEvaluationService from '../evaluation'
65

76
function tsCompile(source: string, options: ts.TranspileOptions = {}): string {
87
// Default options -- you could also perform a merge, or use the project tsconfig.json
@@ -20,7 +19,7 @@ const format = (args: any) => {
2019
return util.inspect(args)
2120
}
2221

23-
export default class NodeVMEvaluation implements IEvaluationService {
22+
export default class ScriptService {
2423
protected async _evaluate(code: string, scope?: Record<string, any>) {
2524
const logs: string[] = []
2625
let result = null
Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
import { test } from '@japa/runner'
2+
import { faker } from '@faker-js/faker'
3+
4+
import DirectoryEntry from '../../../entities/directory-entry'
5+
import Item from '../../../entities/item'
6+
import DriveManager from '../../../gateways/drive/manager'
7+
import CollectionFactory from '../../../__tests__/factories/collections'
8+
import InMemoryDrive from '../../../__tests__/gateways/in-memory-drive'
9+
import EntryItemRepository from './entry-item-repository'
10+
import ItemNotFound from '../../../exceptions/item-not-found'
11+
12+
test.group('entry-item-repository (repository)', (group) => {
13+
const memory = new InMemoryDrive()
14+
15+
const manager = new DriveManager({ memory }, 'memory')
16+
17+
const repository = new EntryItemRepository(manager)
18+
19+
const collection = CollectionFactory.create()
20+
21+
function saveMetas(metas: any[]) {
22+
memory.createFile(DirectoryEntry.normalize(collection.path, '.is', 'metas.json'), metas)
23+
}
24+
25+
function getMetas() {
26+
return memory.readArray(DirectoryEntry.normalize(collection.path, '.is', 'metas.json'))
27+
}
28+
29+
group.each.teardown(() => memory.clear())
30+
31+
test('should list entries as items', async ({ expect }) => {
32+
const items = Array.from({ length: 20 })
33+
.map((_, i) => String(i))
34+
.map((i) => memory.createDir(collection.path, i))
35+
.map(({ name }) => {
36+
const data = {
37+
gender: faker.name.gender(),
38+
age: faker.datatype.number({ min: 1, max: 99 }),
39+
}
40+
41+
return new Item(data, name)
42+
})
43+
44+
saveMetas(items)
45+
46+
const result = await repository.list(collection)
47+
48+
expect(result).toEqual(items)
49+
})
50+
51+
test('should show method return a item by id', async ({ expect }) => {
52+
const entry = memory.createDir(collection.path, 'hello-word')
53+
54+
const result = await repository.show(collection, entry.name)
55+
56+
expect(result).toEqual(new Item({}, entry.name))
57+
})
58+
59+
test('should show method throw an error if item not exists', async ({ expect }) => {
60+
expect.assertions(1)
61+
62+
await repository
63+
.show(collection, 'invalid')
64+
.catch((err) => expect(err).toEqual(new ItemNotFound(collection.path, 'invalid')))
65+
})
66+
67+
test('should create a item', async ({ expect }) => {
68+
const item = new Item({ age: faker.random.numeric() }, 'hello')
69+
70+
await repository.create(collection, item)
71+
72+
const [result] = await repository.list(collection)
73+
74+
expect(result.id).toEqual(item.id)
75+
expect(result.age).toEqual(item.age)
76+
77+
expect(manager.exists(collection.path, 'hello'))
78+
})
79+
80+
test('should update method throw an error if item not exists', async ({ expect }) => {
81+
expect.assertions(1)
82+
83+
await repository
84+
.update(collection, 'invalid', {
85+
hello: 'word',
86+
})
87+
.catch((err) => expect(err).toEqual(new ItemNotFound(collection.path, 'invalid')))
88+
})
89+
90+
test('should update a item', async ({ expect }) => {
91+
const item = await repository.create(
92+
collection,
93+
new Item({ age: faker.random.numeric() }, 'hello')
94+
)
95+
96+
saveMetas([item])
97+
98+
await repository.update(collection, item.id, {
99+
hello: 'word',
100+
})
101+
102+
const [result] = await repository.list(collection)
103+
104+
expect(result.id).toBe(item.id)
105+
expect(result.age).toBe(item.age)
106+
expect(result.hello).toBe('word')
107+
expect(result._createdAt).toBe(item._createdAt)
108+
expect(result._updatedAt).not.toBe(item._updatedAt)
109+
})
110+
111+
test('should update method move item folder when id is defined', async ({ expect }) => {
112+
const item = await repository.create(
113+
collection,
114+
new Item({ age: faker.random.numeric() }, 'hello')
115+
)
116+
117+
saveMetas([item])
118+
119+
await repository.update(collection, item.id, {
120+
id: 'new-dir',
121+
})
122+
123+
const newItem = await repository.show(collection, 'new-dir')
124+
125+
const folderExists = await manager.exists(collection.path, 'new-dir')
126+
127+
expect(folderExists).toBe(true)
128+
expect(newItem.age).toBe(item.age)
129+
})
130+
131+
test('should destroy method throw an error if item not exists', async ({ expect }) => {
132+
expect.assertions(1)
133+
134+
await repository
135+
.destroy(collection, 'invalid')
136+
.catch((err) => expect(err).toEqual(new ItemNotFound(collection.path, 'invalid')))
137+
})
138+
139+
test('should delete an item', async ({ expect }) => {
140+
const item = await repository.create(
141+
collection,
142+
new Item({ age: faker.random.numeric() }, 'hello')
143+
)
144+
145+
saveMetas([item])
146+
147+
await repository.destroy(collection, item.id)
148+
149+
const items = await repository.list(collection)
150+
const metas = await getMetas()
151+
152+
expect(items.length).toBe(0)
153+
expect(metas.length).toBe(0)
154+
})
155+
})

0 commit comments

Comments
 (0)