@jrmc/adonis-attachment 2.4.2 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/build/bin/test.d.ts +1 -0
  2. package/build/bin/test.js +34 -0
  3. package/build/index.d.ts +1 -0
  4. package/build/providers/attachment_provider.d.ts +2 -2
  5. package/build/services/main.d.ts +2 -2
  6. package/build/src/adapters/exif.d.ts +7 -1
  7. package/build/src/adapters/exif.js +8 -5
  8. package/build/src/attachment_manager.d.ts +7 -4
  9. package/build/src/attachment_manager.js +21 -8
  10. package/build/src/define_config.d.ts +16 -2
  11. package/build/src/define_config.js +8 -9
  12. package/build/src/errors.d.ts +18 -14
  13. package/build/src/errors.js +4 -0
  14. package/build/src/mixins/attachmentable.d.ts +127 -112
  15. package/build/src/mixins/attachmentable.js +2 -2
  16. package/build/src/types/attachment.d.ts +2 -1
  17. package/build/src/types/config.d.ts +22 -19
  18. package/build/src/types/index.d.ts +5 -0
  19. package/build/src/types/index.js +5 -0
  20. package/build/src/utils/helpers.d.ts +2 -0
  21. package/build/src/utils/helpers.js +35 -1
  22. package/build/stubs/config.stub +11 -5
  23. package/build/tests/attachment-manager.spec.d.ts +7 -0
  24. package/build/tests/attachment-manager.spec.js +234 -0
  25. package/build/tests/attachment.spec.d.ts +7 -0
  26. package/build/tests/attachment.spec.js +16 -0
  27. package/build/tests/commands.spec.d.ts +7 -0
  28. package/build/tests/commands.spec.js +58 -0
  29. package/build/tests/fixtures/converters/image_converter.d.ts +12 -0
  30. package/build/tests/fixtures/converters/image_converter.js +12 -0
  31. package/build/tests/fixtures/factories/user.d.ts +8 -0
  32. package/build/tests/fixtures/factories/user.js +19 -0
  33. package/build/tests/fixtures/factories/user_with_variants.d.ts +8 -0
  34. package/build/tests/fixtures/factories/user_with_variants.js +19 -0
  35. package/build/tests/fixtures/migrations/create_users_table.d.ts +12 -0
  36. package/build/tests/fixtures/migrations/create_users_table.js +23 -0
  37. package/build/tests/fixtures/models/user.d.ts +466 -0
  38. package/build/tests/fixtures/models/user.js +36 -0
  39. package/build/tests/fixtures/models/user_with_variants.d.ts +465 -0
  40. package/build/tests/fixtures/models/user_with_variants.js +33 -0
  41. package/build/tests/helpers/app.d.ts +29 -0
  42. package/build/tests/helpers/app.js +104 -0
  43. package/build/tests/helpers/index.d.ts +7 -0
  44. package/build/tests/helpers/index.js +7 -0
  45. package/build/tests/options.spec.d.ts +7 -0
  46. package/build/tests/options.spec.js +126 -0
  47. package/build/tests/variants.spec.d.ts +7 -0
  48. package/build/tests/variants.spec.js +21 -0
  49. package/package.json +39 -14
@@ -0,0 +1,5 @@
1
+ export * from './attachment.js';
2
+ export * from './config.js';
3
+ export * from './converter.js';
4
+ export * from './input.js';
5
+ export * from './mixin.js';
@@ -0,0 +1,5 @@
1
+ export * from './attachment.js';
2
+ export * from './config.js';
3
+ export * from './converter.js';
4
+ export * from './input.js';
5
+ export * from './mixin.js';
@@ -20,4 +20,6 @@ export declare function cleanObject(obj: any): any;
20
20
  export declare function clone(object: Object): any;
21
21
  export declare function use(module: string): Promise<any>;
22
22
  export declare function bufferToTempFile(input: Buffer): Promise<string>;
23
+ export declare function streamToTempFile(input: NodeJS.ReadableStream): Promise<string>;
24
+ export declare function downloadToTempFile(input: URL): Promise<string>;
23
25
  export declare function isBase64(str: string): boolean;
@@ -6,13 +6,18 @@
6
6
  */
7
7
  import os from 'node:os';
8
8
  import path from 'node:path';
9
- import fs from 'fs/promises';
9
+ import https from 'node:https';
10
+ import fs from 'node:fs/promises';
11
+ import { pipeline } from 'node:stream';
12
+ import { promisify } from 'node:util';
13
+ import { createWriteStream } from 'node:fs';
10
14
  import { cuid } from '@adonisjs/core/helpers';
11
15
  import string from '@adonisjs/core/helpers/string';
12
16
  import { fileTypeFromBuffer, fileTypeFromFile } from 'file-type';
13
17
  import { Attachment } from '../attachments/attachment.js';
14
18
  import * as errors from '../errors.js';
15
19
  import { optionsSym } from './symbols.js';
20
+ const streamPipeline = promisify(pipeline);
16
21
  export function getAttachmentAttributeNames(modelInstance) {
17
22
  return Object.keys(modelInstance.$attributes).filter((attr) => modelInstance.$attributes[attr] instanceof Attachment);
18
23
  }
@@ -83,6 +88,35 @@ export async function bufferToTempFile(input) {
83
88
  await fs.writeFile(tempFilePath, input);
84
89
  return tempFilePath;
85
90
  }
91
+ // TODO
92
+ // gestion des erreurs
93
+ export async function streamToTempFile(input) {
94
+ const folder = os.tmpdir();
95
+ const tempFilePath = path.join(folder, `tempfile-${Date.now()}.tmp`);
96
+ const writeStream = createWriteStream(tempFilePath);
97
+ try {
98
+ await streamPipeline(input, writeStream);
99
+ return tempFilePath;
100
+ }
101
+ catch (err) {
102
+ throw new errors.E_CANNOT_GENERATE_TEMP_FILE([err.message]);
103
+ }
104
+ }
105
+ export async function downloadToTempFile(input) {
106
+ return await new Promise((resolve) => {
107
+ https.get(input, (response) => {
108
+ if (response.statusCode === 200) {
109
+ resolve(streamToTempFile(response));
110
+ }
111
+ else {
112
+ // reject(`${response.statusCode}`)
113
+ throw new errors.E_CANNOT_GENERATE_TEMP_FILE(['']);
114
+ }
115
+ }).on('error', (err) => {
116
+ throw new errors.E_CANNOT_GENERATE_TEMP_FILE([err.message]);
117
+ });
118
+ });
119
+ }
86
120
  export function isBase64(str) {
87
121
  const base64Regex = /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/;
88
122
  if (!base64Regex.test(str)) {
@@ -2,15 +2,21 @@
2
2
  exports({ to: app.configPath('attachment.ts') })
3
3
  }}}
4
4
  import { defineConfig } from '@jrmc/adonis-attachment'
5
+ import { InferConverters } from '@jrmc/adonis-attachment/types/config'
5
6
 
6
- export default defineConfig({
7
- converters: [
8
- {
9
- key: 'thumbnail',
7
+ const attachmentConfig = defineConfig({
8
+ converters: {
9
+ thumbnail: {
10
10
  converter: () => import('@jrmc/adonis-attachment/converters/image_converter'),
11
11
  options: {
12
12
  resize: 300,
13
13
  }
14
14
  }
15
- ]
15
+ }
16
16
  })
17
+
18
+ export default attachmentConfig
19
+
20
+ declare module '@jrmc/adonis-attachment' {
21
+ interface AttachmentVariants extends InferConverters<typeof attachmentConfig> {}
22
+ }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * @jrmc/adonis-attachment
3
+ *
4
+ * @license MIT
5
+ * @copyright Jeremy Chaufourier <jeremy@chaufourier.fr>
6
+ */
7
+ export {};
@@ -0,0 +1,234 @@
1
+ /**
2
+ * @jrmc/adonis-attachment
3
+ *
4
+ * @license MIT
5
+ * @copyright Jeremy Chaufourier <jeremy@chaufourier.fr>
6
+ */
7
+ import https from 'node:https';
8
+ import { readFile } from 'node:fs/promises';
9
+ import { test } from '@japa/runner';
10
+ import app from '@adonisjs/core/services/app';
11
+ import drive from '@adonisjs/drive/services/main';
12
+ import { MultipartFileFactory } from '@adonisjs/core/factories/bodyparser';
13
+ import { UserFactory } from './fixtures/factories/user.js';
14
+ import { attachmentManager } from '../index.js';
15
+ test.group('attachment-manager', () => {
16
+ test('save method - should result in noop when attachment is created from db response', async ({ assert, }) => {
17
+ const attachmentManager = await app.container.make('jrmc.attachment');
18
+ const attachment = attachmentManager.createFromDbResponse(JSON.stringify({
19
+ size: 1440,
20
+ name: 'foo123.jpg',
21
+ originalName: 'foo.jpg',
22
+ extname: 'jpg',
23
+ mimeType: 'image/jpg',
24
+ }));
25
+ assert.equal(attachment?.originalName, 'foo.jpg');
26
+ });
27
+ test('Attachment - should be null when db response is null', async ({ assert }) => {
28
+ const attachmentManager = await app.container.make('jrmc.attachment');
29
+ const attachment = attachmentManager.createFromDbResponse(null);
30
+ assert.isNull(attachment);
31
+ });
32
+ test('Attachment path default is uploads', async ({ assert }) => {
33
+ const attachmentManager = await app.container.make('jrmc.attachment');
34
+ const attachment = attachmentManager.createFromDbResponse(JSON.stringify({
35
+ size: 1440,
36
+ name: 'foo.jpg',
37
+ extname: 'jpg',
38
+ mimeType: 'image/jpg',
39
+ }));
40
+ assert.equal(attachment?.path, 'uploads/foo.jpg');
41
+ });
42
+ test('Attachment path - should be custom', async ({ assert }) => {
43
+ const attachmentManager = await app.container.make('jrmc.attachment');
44
+ const attachment = attachmentManager.createFromDbResponse(JSON.stringify({
45
+ size: 1440,
46
+ name: 'foo.jpg',
47
+ extname: 'jpg',
48
+ mimeType: 'image/jpg',
49
+ }));
50
+ attachment?.setOptions({ folder: 'avatar' });
51
+ assert.equal(attachment?.path, 'avatar/foo.jpg');
52
+ });
53
+ test('Attachment get url', async ({ assert, cleanup }) => {
54
+ drive.fake('fs');
55
+ cleanup(() => drive.restore('fs'));
56
+ const attachmentManager = await app.container.make('jrmc.attachment');
57
+ const attachment = attachmentManager.createFromDbResponse(JSON.stringify({
58
+ size: 1440,
59
+ name: 'foo.jpg',
60
+ extname: 'jpg',
61
+ mimeType: 'image/jpg',
62
+ }));
63
+ attachment?.setOptions({ folder: 'avatars' });
64
+ const url = await attachment?.getUrl();
65
+ const signedUrl = await attachment?.getSignedUrl();
66
+ assert.match(url, /\/drive\/fakes\/avatars\/foo\.jpg/);
67
+ assert.match(signedUrl, /\/drive\/fakes\/signed\/avatars\/foo\.jpg/);
68
+ });
69
+ test('Precompute file url', async ({ assert, cleanup }) => {
70
+ drive.fake('fs');
71
+ cleanup(() => drive.restore('fs'));
72
+ const attachmentManager = await app.container.make('jrmc.attachment');
73
+ const attachment = attachmentManager.createFromDbResponse(JSON.stringify({
74
+ size: 1440,
75
+ name: 'foo.jpg',
76
+ extname: 'jpg',
77
+ mimeType: 'image/jpg',
78
+ }));
79
+ attachment?.setOptions({ preComputeUrl: true, folder: 'avatars' });
80
+ await attachmentManager.preComputeUrl(attachment);
81
+ assert.match(attachment?.url, /\/drive\/fakes\/avatars\/foo\.jpg/);
82
+ });
83
+ test('with base64 (prefix)', async ({ assert }) => {
84
+ const avatar = await attachmentManager.createFromBase64('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAApgAAAKYB3X3/OAAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAANCSURBVEiJtZZPbBtFFMZ/M7ubXdtdb1xSFyeilBapySVU8h8OoFaooFSqiihIVIpQBKci6KEg9Q6H9kovIHoCIVQJJCKE1ENFjnAgcaSGC6rEnxBwA04Tx43t2FnvDAfjkNibxgHxnWb2e/u992bee7tCa00YFsffekFY+nUzFtjW0LrvjRXrCDIAaPLlW0nHL0SsZtVoaF98mLrx3pdhOqLtYPHChahZcYYO7KvPFxvRl5XPp1sN3adWiD1ZAqD6XYK1b/dvE5IWryTt2udLFedwc1+9kLp+vbbpoDh+6TklxBeAi9TL0taeWpdmZzQDry0AcO+jQ12RyohqqoYoo8RDwJrU+qXkjWtfi8Xxt58BdQuwQs9qC/afLwCw8tnQbqYAPsgxE1S6F3EAIXux2oQFKm0ihMsOF71dHYx+f3NND68ghCu1YIoePPQN1pGRABkJ6Bus96CutRZMydTl+TvuiRW1m3n0eDl0vRPcEysqdXn+jsQPsrHMquGeXEaY4Yk4wxWcY5V/9scqOMOVUFthatyTy8QyqwZ+kDURKoMWxNKr2EeqVKcTNOajqKoBgOE28U4tdQl5p5bwCw7BWquaZSzAPlwjlithJtp3pTImSqQRrb2Z8PHGigD4RZuNX6JYj6wj7O4TFLbCO/Mn/m8R+h6rYSUb3ekokRY6f/YukArN979jcW+V/S8g0eT/N3VN3kTqWbQ428m9/8k0P/1aIhF36PccEl6EhOcAUCrXKZXXWS3XKd2vc/TRBG9O5ELC17MmWubD2nKhUKZa26Ba2+D3P+4/MNCFwg59oWVeYhkzgN/JDR8deKBoD7Y+ljEjGZ0sosXVTvbc6RHirr2reNy1OXd6pJsQ+gqjk8VWFYmHrwBzW/n+uMPFiRwHB2I7ih8ciHFxIkd/3Omk5tCDV1t+2nNu5sxxpDFNx+huNhVT3/zMDz8usXC3ddaHBj1GHj/As08fwTS7Kt1HBTmyN29vdwAw+/wbwLVOJ3uAD1wi/dUH7Qei66PfyuRj4Ik9is+hglfbkbfR3cnZm7chlUWLdwmprtCohX4HUtlOcQjLYCu+fzGJH2QRKvP3UNz8bWk1qMxjGTOMThZ3kvgLI5AzFfo379UAAAAASUVORK5CYII=', 'avatar.png');
85
+ const user = await UserFactory.merge({ avatar }).create();
86
+ const data = await user.serialize();
87
+ assert.deepEqual(data.avatar, {
88
+ extname: 'png',
89
+ meta: {
90
+ dimension: {
91
+ height: 24,
92
+ width: 24,
93
+ },
94
+ host: 'www.inkscape.org',
95
+ },
96
+ mimeType: 'image/png',
97
+ name: data.avatar.name,
98
+ originalName: 'avatar.png',
99
+ size: 965,
100
+ });
101
+ });
102
+ test('with base64 (no prefix)', async ({ assert }) => {
103
+ const avatar = await attachmentManager.createFromBase64('iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAApgAAAKYB3X3/OAAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAANCSURBVEiJtZZPbBtFFMZ/M7ubXdtdb1xSFyeilBapySVU8h8OoFaooFSqiihIVIpQBKci6KEg9Q6H9kovIHoCIVQJJCKE1ENFjnAgcaSGC6rEnxBwA04Tx43t2FnvDAfjkNibxgHxnWb2e/u992bee7tCa00YFsffekFY+nUzFtjW0LrvjRXrCDIAaPLlW0nHL0SsZtVoaF98mLrx3pdhOqLtYPHChahZcYYO7KvPFxvRl5XPp1sN3adWiD1ZAqD6XYK1b/dvE5IWryTt2udLFedwc1+9kLp+vbbpoDh+6TklxBeAi9TL0taeWpdmZzQDry0AcO+jQ12RyohqqoYoo8RDwJrU+qXkjWtfi8Xxt58BdQuwQs9qC/afLwCw8tnQbqYAPsgxE1S6F3EAIXux2oQFKm0ihMsOF71dHYx+f3NND68ghCu1YIoePPQN1pGRABkJ6Bus96CutRZMydTl+TvuiRW1m3n0eDl0vRPcEysqdXn+jsQPsrHMquGeXEaY4Yk4wxWcY5V/9scqOMOVUFthatyTy8QyqwZ+kDURKoMWxNKr2EeqVKcTNOajqKoBgOE28U4tdQl5p5bwCw7BWquaZSzAPlwjlithJtp3pTImSqQRrb2Z8PHGigD4RZuNX6JYj6wj7O4TFLbCO/Mn/m8R+h6rYSUb3ekokRY6f/YukArN979jcW+V/S8g0eT/N3VN3kTqWbQ428m9/8k0P/1aIhF36PccEl6EhOcAUCrXKZXXWS3XKd2vc/TRBG9O5ELC17MmWubD2nKhUKZa26Ba2+D3P+4/MNCFwg59oWVeYhkzgN/JDR8deKBoD7Y+ljEjGZ0sosXVTvbc6RHirr2reNy1OXd6pJsQ+gqjk8VWFYmHrwBzW/n+uMPFiRwHB2I7ih8ciHFxIkd/3Omk5tCDV1t+2nNu5sxxpDFNx+huNhVT3/zMDz8usXC3ddaHBj1GHj/As08fwTS7Kt1HBTmyN29vdwAw+/wbwLVOJ3uAD1wi/dUH7Qei66PfyuRj4Ik9is+hglfbkbfR3cnZm7chlUWLdwmprtCohX4HUtlOcQjLYCu+fzGJH2QRKvP3UNz8bWk1qMxjGTOMThZ3kvgLI5AzFfo379UAAAAASUVORK5CYII=', 'avatar.png');
104
+ const user = await UserFactory.merge({ avatar }).create();
105
+ const data = await user.serialize();
106
+ assert.deepEqual(data.avatar, {
107
+ extname: 'png',
108
+ meta: {
109
+ dimension: {
110
+ height: 24,
111
+ width: 24,
112
+ },
113
+ host: 'www.inkscape.org',
114
+ },
115
+ mimeType: 'image/png',
116
+ name: data.avatar.name,
117
+ originalName: 'avatar.png',
118
+ size: 965,
119
+ });
120
+ });
121
+ test('with buffer', async ({ assert }) => {
122
+ const buffer = await readFile(app.makePath('../fixtures/images/img.jpg'));
123
+ const avatar = await attachmentManager.createFromBuffer(buffer, 'avatar.jpg');
124
+ const user = await UserFactory.merge({ avatar }).create();
125
+ const data = await user.serialize();
126
+ assert.deepEqual(data.avatar, {
127
+ extname: 'jpg',
128
+ meta: {
129
+ dimension: {
130
+ height: 1313,
131
+ width: 1920,
132
+ },
133
+ },
134
+ mimeType: 'image/jpeg',
135
+ name: data.avatar.name,
136
+ originalName: 'avatar.jpg',
137
+ size: 122851,
138
+ });
139
+ });
140
+ test('with file', async ({ assert }) => {
141
+ const file = new MultipartFileFactory()
142
+ .merge({
143
+ size: 4000000,
144
+ extname: 'jpg',
145
+ type: 'image',
146
+ subtype: 'jpeg',
147
+ })
148
+ .create();
149
+ file.tmpPath = app.makePath('../fixtures/images/img.jpg');
150
+ const avatar = await attachmentManager.createFromFile(file);
151
+ const user = await UserFactory.merge({ avatar }).create();
152
+ const data = await user.serialize();
153
+ assert.deepEqual(data.avatar, {
154
+ extname: 'jpg',
155
+ meta: {
156
+ dimension: {
157
+ height: 1313,
158
+ width: 1920,
159
+ },
160
+ },
161
+ mimeType: 'image/jpeg',
162
+ name: data.avatar.name,
163
+ originalName: 'file.jpg',
164
+ size: 4000000,
165
+ });
166
+ });
167
+ test('with path', async ({ assert }) => {
168
+ const path = app.makePath('../fixtures/images/img.jpg');
169
+ const avatar = await attachmentManager.createFromPath(path, 'file.jpg');
170
+ const user = await UserFactory.merge({ avatar }).create();
171
+ const data = await user.serialize();
172
+ assert.deepEqual(data.avatar, {
173
+ extname: 'jpg',
174
+ meta: {
175
+ dimension: {
176
+ height: 1313,
177
+ width: 1920,
178
+ },
179
+ },
180
+ mimeType: 'image/jpeg',
181
+ name: data.avatar.name,
182
+ originalName: 'file.jpg',
183
+ size: 83,
184
+ });
185
+ });
186
+ test('with url', async ({ assert }) => {
187
+ const url = new URL('https://raw.githubusercontent.com/batosai/adonis-attachment/refs/heads/develop/tests/fixtures/images/img.jpg');
188
+ const avatar = await attachmentManager.createFromUrl(url, 'file.jpg');
189
+ const user = await UserFactory.merge({ avatar }).create();
190
+ const data = await user.serialize();
191
+ assert.deepEqual(data.avatar, {
192
+ extname: 'jpg',
193
+ meta: {
194
+ dimension: {
195
+ height: 1313,
196
+ width: 1920,
197
+ },
198
+ },
199
+ mimeType: 'image/jpeg',
200
+ name: data.avatar.name,
201
+ originalName: 'file.jpg',
202
+ size: 31,
203
+ });
204
+ });
205
+ test('with stream', async ({ assert }) => {
206
+ async function downloadImageStream(input) {
207
+ return await new Promise((resolve) => {
208
+ https.get(input, (response) => {
209
+ if (response.statusCode === 200) {
210
+ resolve(response);
211
+ }
212
+ });
213
+ });
214
+ }
215
+ const url = new URL('https://raw.githubusercontent.com/batosai/adonis-attachment/refs/heads/develop/tests/fixtures/images/img.jpg');
216
+ const stream = await downloadImageStream(url);
217
+ const avatar = await attachmentManager.createFromStream(stream, 'file.jpg');
218
+ const user = await UserFactory.merge({ avatar }).create();
219
+ const data = await user.serialize();
220
+ assert.deepEqual(data.avatar, {
221
+ extname: 'jpg',
222
+ meta: {
223
+ dimension: {
224
+ height: 1313,
225
+ width: 1920,
226
+ },
227
+ },
228
+ mimeType: 'image/jpeg',
229
+ name: data.avatar.name,
230
+ originalName: 'file.jpg',
231
+ size: 31,
232
+ });
233
+ });
234
+ });
@@ -0,0 +1,7 @@
1
+ /**
2
+ * @jrmc/adonis-attachment
3
+ *
4
+ * @license MIT
5
+ * @copyright Jeremy Chaufourier <jeremy@chaufourier.fr>
6
+ */
7
+ export {};
@@ -0,0 +1,16 @@
1
+ /**
2
+ * @jrmc/adonis-attachment
3
+ *
4
+ * @license MIT
5
+ * @copyright Jeremy Chaufourier <jeremy@chaufourier.fr>
6
+ */
7
+ import { test } from '@japa/runner';
8
+ import { UserFactory } from './fixtures/factories/user.js';
9
+ test.group('attachment', () => {
10
+ test('delete', async ({ assert }) => {
11
+ const user = await UserFactory.create();
12
+ user.avatar = null;
13
+ await user.save();
14
+ assert.isNull(user.avatar);
15
+ });
16
+ });
@@ -0,0 +1,7 @@
1
+ /**
2
+ * @jrmc/adonis-attachment
3
+ *
4
+ * @license MIT
5
+ * @copyright Jeremy Chaufourier <jeremy@chaufourier.fr>
6
+ */
7
+ export {};
@@ -0,0 +1,58 @@
1
+ /**
2
+ * @jrmc/adonis-attachment
3
+ *
4
+ * @license MIT
5
+ * @copyright Jeremy Chaufourier <jeremy@chaufourier.fr>
6
+ */
7
+ import { test } from '@japa/runner';
8
+ import Configure from '@adonisjs/core/commands/configure';
9
+ import { IgnitorFactory } from '@adonisjs/core/factories';
10
+ import MakeConverter from '../commands/make/converter.js';
11
+ import { BASE_URL } from './helpers/index.js';
12
+ async function setupFakeAdonisProject(fs) {
13
+ await Promise.all([
14
+ fs.create('.env', ''),
15
+ fs.createJson('tsconfig.json', {}),
16
+ fs.create('adonisrc.ts', `export default defineConfig({})`),
17
+ ]);
18
+ }
19
+ async function setupApp() {
20
+ const ignitor = new IgnitorFactory()
21
+ .withCoreProviders()
22
+ .withCoreConfig()
23
+ .create(BASE_URL, {
24
+ importer: (filePath) => {
25
+ if (filePath.startsWith('./') || filePath.startsWith('../')) {
26
+ return import(new URL(filePath, BASE_URL).href);
27
+ }
28
+ return import(filePath);
29
+ },
30
+ });
31
+ const app = ignitor.createApp('web');
32
+ await app.init();
33
+ await app.boot();
34
+ const ace = await app.container.make('ace');
35
+ ace.ui.switchMode('raw');
36
+ return { ace, app };
37
+ }
38
+ test.group('configure', (group) => {
39
+ group.tap((t) => t.timeout(20_000));
40
+ group.each.setup(async ({ context }) => setupFakeAdonisProject(context.fs));
41
+ test('add provider, config file, and command', async ({ assert }) => {
42
+ const { ace } = await setupApp();
43
+ const command = await ace.create(Configure, ['../../configure.js']);
44
+ await command.exec();
45
+ command.assertSucceeded();
46
+ await assert.fileExists('config/attachment.ts');
47
+ await assert.fileExists('adonisrc.ts');
48
+ await assert.fileContains('adonisrc.ts', '@jrmc/adonis-attachment/attachment_provider');
49
+ await assert.fileContains('adonisrc.ts', '@jrmc/adonis-attachment/commands');
50
+ });
51
+ test('create converter', async ({ assert }) => {
52
+ const { ace } = await setupApp();
53
+ const command = await ace.create(MakeConverter, ['thumb']);
54
+ await command.exec();
55
+ command.assertSucceeded();
56
+ await assert.fileExists('app/converters/thumb_converter.ts');
57
+ });
58
+ });
@@ -0,0 +1,12 @@
1
+ /**
2
+ * @jrmc/adonis-attachment
3
+ *
4
+ * @license MIT
5
+ * @copyright Jeremy Chaufourier <jeremy@chaufourier.fr>
6
+ */
7
+ import type { ConverterAttributes } from '../../../src/types/converter.js';
8
+ import type { Input } from '../../../src/types/input.js';
9
+ import Converter from '../../../src/converters/converter.js';
10
+ export default class ImageConverter extends Converter {
11
+ handle({ input }: ConverterAttributes): Promise<Input>;
12
+ }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * @jrmc/adonis-attachment
3
+ *
4
+ * @license MIT
5
+ * @copyright Jeremy Chaufourier <jeremy@chaufourier.fr>
6
+ */
7
+ import Converter from '../../../src/converters/converter.js';
8
+ export default class ImageConverter extends Converter {
9
+ async handle({ input }) {
10
+ return input;
11
+ }
12
+ }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * @jrmc/adonis-attachment
3
+ *
4
+ * @license MIT
5
+ * @copyright Jeremy Chaufourier <jeremy@chaufourier.fr>
6
+ */
7
+ import User from '../models/user.js';
8
+ export declare const UserFactory: import("@adonisjs/lucid/types/factory").FactoryBuilderQueryContract<typeof User, import("@adonisjs/lucid/types/factory").FactoryModelContract<typeof User>>;
@@ -0,0 +1,19 @@
1
+ /**
2
+ * @jrmc/adonis-attachment
3
+ *
4
+ * @license MIT
5
+ * @copyright Jeremy Chaufourier <jeremy@chaufourier.fr>
6
+ */
7
+ import { readFile } from 'node:fs/promises';
8
+ import User from '../models/user.js';
9
+ import Factory from '@adonisjs/lucid/factories';
10
+ import app from '@adonisjs/core/services/app';
11
+ export const UserFactory = Factory.define(User, async ({ faker }) => {
12
+ const attachmentManager = await app.container.make('jrmc.attachment');
13
+ const buffer = await readFile(app.makePath('../fixtures/images/img.jpg'));
14
+ const avatar = await attachmentManager.createFromBuffer(buffer, 'avatar.jpg');
15
+ return {
16
+ name: faker.person.lastName(),
17
+ avatar,
18
+ };
19
+ }).build();
@@ -0,0 +1,8 @@
1
+ /**
2
+ * @jrmc/adonis-attachment
3
+ *
4
+ * @license MIT
5
+ * @copyright Jeremy Chaufourier <jeremy@chaufourier.fr>
6
+ */
7
+ import User from '../models/user_with_variants.js';
8
+ export declare const UserFactory: import("@adonisjs/lucid/types/factory").FactoryBuilderQueryContract<typeof User, import("@adonisjs/lucid/types/factory").FactoryModelContract<typeof User>>;
@@ -0,0 +1,19 @@
1
+ /**
2
+ * @jrmc/adonis-attachment
3
+ *
4
+ * @license MIT
5
+ * @copyright Jeremy Chaufourier <jeremy@chaufourier.fr>
6
+ */
7
+ import { readFile } from 'node:fs/promises';
8
+ import User from '../models/user_with_variants.js';
9
+ import Factory from '@adonisjs/lucid/factories';
10
+ import app from '@adonisjs/core/services/app';
11
+ export const UserFactory = Factory.define(User, async ({ faker }) => {
12
+ const attachmentManager = await app.container.make('jrmc.attachment');
13
+ const buffer = await readFile(app.makePath('../fixtures/images/img.jpg'));
14
+ const avatar = await attachmentManager.createFromBuffer(buffer, 'avatar.jpg');
15
+ return {
16
+ name: faker.person.lastName(),
17
+ avatar,
18
+ };
19
+ }).build();
@@ -0,0 +1,12 @@
1
+ /**
2
+ * @jrmc/adonis-attachment
3
+ *
4
+ * @license MIT
5
+ * @copyright Jeremy Chaufourier <jeremy@chaufourier.fr>
6
+ */
7
+ import { BaseSchema } from '@adonisjs/lucid/schema';
8
+ export default class extends BaseSchema {
9
+ protected tableName: string;
10
+ up(): Promise<void>;
11
+ down(): Promise<void>;
12
+ }
@@ -0,0 +1,23 @@
1
+ /**
2
+ * @jrmc/adonis-attachment
3
+ *
4
+ * @license MIT
5
+ * @copyright Jeremy Chaufourier <jeremy@chaufourier.fr>
6
+ */
7
+ import { BaseSchema } from '@adonisjs/lucid/schema';
8
+ export default class extends BaseSchema {
9
+ tableName = 'users';
10
+ async up() {
11
+ this.schema.createTable(this.tableName, (table) => {
12
+ table.increments('id');
13
+ table.string('name');
14
+ table.json('avatar');
15
+ table.json('avatar_2');
16
+ table.timestamp('created_at');
17
+ table.timestamp('updated_at');
18
+ });
19
+ }
20
+ async down() {
21
+ this.schema.dropTable(this.tableName);
22
+ }
23
+ }