Skip to content

Commit 36aa1bb

Browse files
authored
Merge pull request #65 from strapi/feat/update-file-metadata
Update file metadata
2 parents 57ffa35 + 4ce2e1c commit 36aa1bb

File tree

8 files changed

+386
-14
lines changed

8 files changed

+386
-14
lines changed

README.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,7 @@ The `files` property provides access to the Strapi Media Library through the Upl
209209

210210
- `find(params?: FileQueryParams): Promise<FileListResponse>` - Retrieves a list of file metadata based on optional query parameters
211211
- `findOne(fileId: number): Promise<FileResponse>` - Retrieves the metadata for a single file by its ID
212+
- `update(fileId: number, fileInfo: FileUpdateData): Promise<FileResponse>` - Updates metadata for an existing file
212213

213214
#### Example: Finding Files
214215

@@ -250,6 +251,26 @@ console.log(file.url); // The file URL
250251
console.log(file.mime); // The file MIME type
251252
```
252253

254+
#### Example: Updating File Metadata
255+
256+
```typescript
257+
// Initialize the client
258+
const client = strapi({
259+
baseURL: 'http://localhost:1337/api',
260+
auth: 'your-api-token',
261+
});
262+
263+
// Update file metadata
264+
const updatedFile = await client.files.update(1, {
265+
name: 'New file name',
266+
alternativeText: 'Descriptive alt text for accessibility',
267+
caption: 'A caption for the file',
268+
});
269+
270+
console.log(updatedFile.name); // Updated file name
271+
console.log(updatedFile.alternativeText); // Updated alt text
272+
```
273+
253274
## 🐛 Debug
254275

255276
This section provides guidance on enabling and managing debug logs for the SDK,

demo/node-javascript/src/index.js

Lines changed: 60 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
const { strapi } = require('@strapi/client');
22
require('dotenv').config();
3+
const os = require('os');
34

45
const api_token = process.env.FULL_ACCESS_TOKEN; // READ_ONLY_TOKEN is also available
56

@@ -20,7 +21,9 @@ async function main() {
2021
// Create a collection type query manager for the categories
2122
const categories = client.collection('categories');
2223

23-
console.log('\n=== Basic Category Data ===\n');
24+
console.log(os.EOL);
25+
console.log('=== Basic Category Data ===');
26+
console.log(os.EOL);
2427

2528
// Fetch the list of all categories (basic demo)
2629
const basicCategoryData = await categories.find();
@@ -33,7 +36,9 @@ async function main() {
3336
});
3437

3538
// Example: Category with image files
36-
console.log('\n=== Categories with their images ===\n');
39+
console.log(os.EOL);
40+
console.log('=== Categories with their images ===');
41+
console.log(os.EOL);
3742

3843
// Fetch all categories with their related images
3944
const result = await categories.find({
@@ -57,7 +62,9 @@ async function main() {
5762
}
5863

5964
// Example: Direct file operations
60-
console.log('\n=== Direct file queries ===\n');
65+
console.log(os.EOL);
66+
console.log('=== Direct file queries ===');
67+
console.log(os.EOL);
6168

6269
const techCategoryResult = await categories.find({
6370
filters: {
@@ -68,7 +75,7 @@ async function main() {
6875
populate: ['image'],
6976
});
7077

71-
if (techCategoryResult.data) {
78+
if (techCategoryResult.data && techCategoryResult.data.length > 0) {
7279
const categoryData = techCategoryResult.data[0];
7380
console.log(`Working with category: ${categoryData.name} (ID: ${categoryData.id})`);
7481

@@ -78,7 +85,8 @@ async function main() {
7885

7986
// Get the specific file by ID
8087
const fileInfo = await client.files.findOne(imageId);
81-
console.log('\nFile details:');
88+
console.log(os.EOL);
89+
console.log('File details:');
8290
console.log(` Name: ${fileInfo.name}`);
8391
console.log(` Alternative Text: ${fileInfo.alternativeText || 'None'}`);
8492
console.log(` Caption: ${fileInfo.caption || 'None'}`);
@@ -89,6 +97,53 @@ async function main() {
8997
console.log(` URL: ${fileInfo.url}`);
9098
}
9199
}
100+
101+
// Example: Update file metadata
102+
console.log(os.EOL);
103+
console.log('=== File Update Operations ===');
104+
console.log(os.EOL);
105+
106+
if (techCategoryResult.data && techCategoryResult.data.length > 0) {
107+
const categoryData = techCategoryResult.data[0];
108+
109+
// Only proceed if the category has an image
110+
if (categoryData.image) {
111+
const imageId = categoryData.image.id;
112+
console.log(`Working with image: ${categoryData.image.name} (ID: ${imageId})`);
113+
114+
// Update the file metadata
115+
// For demo purposes, we'll update the alternative text and caption
116+
const updatedAltText = `Updated alt text for ${categoryData.image.name} - ${new Date().toISOString()}`;
117+
const updatedCaption = `Updated caption - ${new Date().toISOString()}`;
118+
119+
console.log(os.EOL);
120+
console.log('Updating file metadata...');
121+
console.log(` New Alt Text: ${updatedAltText}`);
122+
console.log(` New Caption: ${updatedCaption}`);
123+
124+
try {
125+
const updatedFile = await client.files.update(imageId, {
126+
alternativeText: updatedAltText,
127+
caption: updatedCaption,
128+
});
129+
130+
console.log(os.EOL);
131+
console.log('File metadata updated successfully!');
132+
console.log(` Name: ${updatedFile.name}`);
133+
console.log(` Alternative Text: ${updatedFile.alternativeText || 'None'}`);
134+
console.log(` Caption: ${updatedFile.caption || 'None'}`);
135+
console.log(` Updated At: ${new Date(updatedFile.updatedAt).toLocaleString()}`);
136+
} catch (error) {
137+
console.error('Error updating file:', error);
138+
}
139+
} else {
140+
console.log('No image associated with this category to update');
141+
}
142+
} else {
143+
console.log(
144+
'Tech category not found. Make sure you have a category with slug "tech" in your Strapi instance.'
145+
);
146+
}
92147
}
93148

94149
main().catch(console.error);

demo/node-typescript/src/index.ts

Lines changed: 76 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { strapi } from '@strapi/client';
22
import * as dotenv from 'dotenv';
3+
import * as os from 'os';
34
dotenv.config();
45

56
const api_token = process.env.FULL_ACCESS_TOKEN; // READ_ONLY_TOKEN is also available
@@ -74,10 +75,13 @@ async function runDemo() {
7475
await demonstrateBasicCategoryFunctionality();
7576
await demonstrateCategoryImageInteractions();
7677
await demonstrateDirectFileOperations();
78+
await demonstrateFileUpdates();
7779
}
7880

7981
async function demonstrateBasicCategoryFunctionality() {
80-
console.log('\n=== Basic Category Data ===\n');
82+
console.log(os.EOL);
83+
console.log('=== Basic Category Data ===');
84+
console.log(os.EOL);
8185

8286
const categories = client.collection('categories');
8387

@@ -90,7 +94,9 @@ async function demonstrateBasicCategoryFunctionality() {
9094
}
9195

9296
async function demonstrateCategoryImageInteractions() {
93-
console.log('\n=== Categories with their images ===\n');
97+
console.log(os.EOL);
98+
console.log('=== Categories with their images ===');
99+
console.log(os.EOL);
94100

95101
const categories = client.collection('categories');
96102

@@ -115,7 +121,9 @@ async function demonstrateCategoryImageInteractions() {
115121
}
116122

117123
async function demonstrateDirectFileOperations() {
118-
console.log('\n=== Direct file queries ===\n');
124+
console.log(os.EOL);
125+
console.log('=== Direct file queries ===');
126+
console.log(os.EOL);
119127

120128
const categories = client.collection('categories');
121129

@@ -139,7 +147,9 @@ async function demonstrateDirectFileOperations() {
139147

140148
// Get the specific file by ID
141149
const fileInfo = (await client.files.findOne(imageId)) as unknown as FileAttributes;
142-
console.log('\nFile details:');
150+
151+
console.log(os.EOL);
152+
console.log('File details:');
143153
console.log(` Name: ${fileInfo.name}`);
144154
console.log(` Alternative Text: ${fileInfo.alternativeText || 'None'}`);
145155
console.log(` Caption: ${fileInfo.caption || 'None'}`);
@@ -152,6 +162,66 @@ async function demonstrateDirectFileOperations() {
152162
}
153163
}
154164

165+
async function demonstrateFileUpdates() {
166+
console.log(os.EOL);
167+
console.log('=== File Update Operations ===');
168+
console.log(os.EOL);
169+
170+
const categories = client.collection('categories');
171+
172+
// Get a specific category using find with a filter
173+
const techCategoryResult = (await categories.find({
174+
filters: {
175+
slug: {
176+
$eq: 'tech',
177+
},
178+
},
179+
populate: ['image'],
180+
})) as unknown as CategoryResponse;
181+
182+
if (techCategoryResult.data && techCategoryResult.data.length > 0) {
183+
const categoryData = techCategoryResult.data[0];
184+
185+
// Only proceed if the category has an image
186+
if (categoryData.image) {
187+
const imageId = categoryData.image.id;
188+
console.log(`Working with image: ${categoryData.image.name} (ID: ${imageId})`);
189+
190+
// Update the file metadata
191+
// For demo purposes, we'll update the alternative text and caption
192+
const updatedAltText = `Updated alt text for ${categoryData.image.name} - ${new Date().toISOString()}`;
193+
const updatedCaption = `Updated caption - ${new Date().toISOString()}`;
194+
195+
console.log(os.EOL);
196+
console.log('Updating file metadata...');
197+
console.log(` New Alt Text: ${updatedAltText}`);
198+
console.log(` New Caption: ${updatedCaption}`);
199+
200+
try {
201+
const updatedFile = await client.files.update(imageId, {
202+
alternativeText: updatedAltText,
203+
caption: updatedCaption,
204+
});
205+
206+
console.log(os.EOL);
207+
console.log('File metadata updated successfully!');
208+
console.log(` Name: ${updatedFile.name}`);
209+
console.log(` Alternative Text: ${updatedFile.alternativeText || 'None'}`);
210+
console.log(` Caption: ${updatedFile.caption || 'None'}`);
211+
console.log(` Updated At: ${new Date(updatedFile.updatedAt).toLocaleString()}`);
212+
} catch (error) {
213+
console.error('Error updating file:', error);
214+
}
215+
} else {
216+
console.log('No image associated with this category to update');
217+
}
218+
} else {
219+
console.log(
220+
'Tech category not found. Make sure you have a category with slug "tech" in your Strapi instance.'
221+
);
222+
}
223+
}
224+
155225
// Helper function to format file sizes
156226
function formatFileSize(bytes: number): string {
157227
if (bytes < 1024) return `${bytes} B`;
@@ -163,7 +233,8 @@ function formatFileSize(bytes: number): string {
163233
// Run the demo
164234
runDemo()
165235
.then(() => {
166-
console.log('\nDemo completed successfully!');
236+
console.log(os.EOL);
237+
console.log('Demo completed successfully!');
167238
})
168239
.catch((error) => {
169240
console.error('Error running demo:', error);

src/files/constants.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
export const FILE_API_PREFIX = '/upload/files';
1+
export const FILE_API_PREFIX = '/upload';

src/files/manager.ts

Lines changed: 49 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { URLHelper } from '../utilities';
55

66
import { FILE_API_PREFIX } from './constants';
77
import { FileErrorMapper } from './errors';
8-
import { FileQueryParams, FileListResponse, FileResponse } from './types';
8+
import { FileQueryParams, FileListResponse, FileResponse, FileUpdateData } from './types';
99

1010
const debug = createDebug('strapi:files');
1111

@@ -90,7 +90,7 @@ export class FilesManager {
9090
debug('finding files');
9191

9292
try {
93-
let url = FILE_API_PREFIX;
93+
let url = `${FILE_API_PREFIX}/files`;
9494

9595
if (queryParams) {
9696
url = URLHelper.appendQueryParams(url, queryParams);
@@ -130,7 +130,7 @@ export class FilesManager {
130130
async findOne(fileId: number): Promise<FileResponse> {
131131
debug('finding file with ID %o', fileId);
132132

133-
const url = `${FILE_API_PREFIX}/${fileId}`;
133+
const url = `${FILE_API_PREFIX}/files/${fileId}`;
134134
const client = this.createFileHttpClient(fileId);
135135

136136
try {
@@ -146,4 +146,50 @@ export class FilesManager {
146146
throw error;
147147
}
148148
}
149+
150+
/**
151+
* Updates metadata for an existing file.
152+
*
153+
* @param fileId - The numeric identifier of the file to update.
154+
* @param fileInfo - An object containing the fields to update.
155+
* @returns A promise that resolves to the updated file object.
156+
*
157+
* @throws {FileNotFoundError} if the file with the specified ID does not exist.
158+
* @throws {HTTPError} if the HTTP client encounters connection issues, the server is unreachable, or authentication fails.
159+
*
160+
* @example
161+
* ```typescript
162+
* const filesManager = new FilesManager(httpClient);
163+
*
164+
* const updatedFile = await filesManager.update(1, {
165+
* name: 'New file name',
166+
* alternativeText: 'Descriptive alt text for accessibility',
167+
* caption: 'A caption for the file'
168+
* });
169+
*
170+
* console.log(updatedFile); // Updated file object
171+
* ```
172+
*/
173+
async update(fileId: number, fileInfo: FileUpdateData): Promise<FileResponse> {
174+
debug('updating file with ID %o with data %o', fileId, fileInfo);
175+
176+
try {
177+
const url = `${FILE_API_PREFIX}?id=${fileId}`;
178+
const client = this.createFileHttpClient(fileId);
179+
180+
// Create FormData and add fileInfo as JSON string
181+
const formData = new FormData();
182+
formData.append('fileInfo', JSON.stringify(fileInfo));
183+
184+
const response = await client.post(url, formData);
185+
const json = await response.json();
186+
187+
debug('successfully updated file with ID %o', fileId);
188+
189+
return json;
190+
} catch (error) {
191+
debug('error updating file with ID %o: %o', fileId, error);
192+
throw error;
193+
}
194+
}
149195
}

src/files/types.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,15 @@ export interface FileQueryParams {
1818
sort?: string | string[];
1919
}
2020

21+
/**
22+
* Interface defining the fields that can be updated for a file.
23+
*/
24+
export interface FileUpdateData {
25+
name?: string;
26+
alternativeText?: string;
27+
caption?: string;
28+
}
29+
2130
/**
2231
* Response structure for a single file from the Strapi upload plugin.
2332
* This interface represents the actual response structure from the API.

0 commit comments

Comments
 (0)