Skip to content
Open
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
0b79b20
Allow file adapter to override file name and location + give createFi…
AdrianCurtin Jan 16, 2025
2036369
Only call getFileLocation if no url is provided
AdrianCurtin Jan 16, 2025
1df8f1a
Modify description and typescript
AdrianCurtin Jan 17, 2025
a9381bc
Add optional location element too
AdrianCurtin Jan 17, 2025
46b1e14
Update FilesAdapter.js
AdrianCurtin Jan 17, 2025
7c4e69f
Remove trialing space
AdrianCurtin Jan 17, 2025
c87b86d
change to undefined as a fallback
AdrianCurtin Jan 17, 2025
ce2c8f9
Add tests for different return conditions
AdrianCurtin Jan 17, 2025
6298c0c
Update description of config slightly
AdrianCurtin Jan 17, 2025
657dba6
Modify mock adapter initialization to use struct in beginning
AdrianCurtin Feb 1, 2025
39f48bd
Update to include config in arguments for createFileSpy
AdrianCurtin Feb 1, 2025
59ed527
Remove space + move preserve filename args to files controller initia…
AdrianCurtin Feb 7, 2025
3fee23a
Split into separate test
AdrianCurtin Aug 13, 2025
623d083
Limit the file adapter config to just the minimum it needs to get loc…
AdrianCurtin Aug 13, 2025
a0a1782
Match the actual test keys
AdrianCurtin Aug 13, 2025
ff23067
Change domain to example
AdrianCurtin Aug 14, 2025
6114fbf
Use config and stripped down config
AdrianCurtin Aug 14, 2025
028103a
Rename to basicServerConfig
AdrianCurtin Aug 14, 2025
587fccf
Merge branch 'alpha' into alpha
AdrianCurtin Sep 9, 2025
7319884
Revert to full config
AdrianCurtin Sep 9, 2025
46e277b
Reduce test expectations to only check application Id
AdrianCurtin Sep 9, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions spec/CloudCode.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -3695,11 +3695,13 @@ describe('saveFile hooks', () => {
foo: 'bar',
},
};
const config = Config.get('test');
expect(createFileSpy).toHaveBeenCalledWith(
jasmine.any(String),
newData,
'text/plain',
newOptions
newOptions,
config
);
});

Expand Down Expand Up @@ -3727,11 +3729,13 @@ describe('saveFile hooks', () => {
foo: 'bar',
},
};
const config = Config.get('test');
expect(createFileSpy).toHaveBeenCalledWith(
jasmine.any(String),
newData,
newContentType,
newOptions
newOptions,
config
);
const expectedFileName = 'donald_duck.pdf';
expect(file._name.indexOf(expectedFileName)).toBe(file._name.length - expectedFileName.length);
Expand All @@ -3757,11 +3761,13 @@ describe('saveFile hooks', () => {
metadata: { foo: 'bar' },
tags: { bar: 'foo' },
};
const config = Config.get('test');
expect(createFileSpy).toHaveBeenCalledWith(
jasmine.any(String),
jasmine.any(Buffer),
'text/plain',
options
options,
config
);
});

Expand Down
98 changes: 98 additions & 0 deletions spec/FilesController.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -218,4 +218,102 @@ describe('FilesController', () => {
expect(gridFSAdapter.validateFilename(fileName)).not.toBe(null);
done();
});

it('should return filename and url when adapter returns both', async () => {
const config = Config.get(Parse.applicationId);
const adapterWithReturn = { ...mockAdapter };
adapterWithReturn.createFile = () => {
return Promise.resolve({
name: 'newFilename.txt',
url: 'http://new.url/newFilename.txt'
});
};
adapterWithReturn.getFileLocation = () => {
return Promise.resolve('http://default.url/file.txt');
};
const controllerWithReturn = new FilesController(adapterWithReturn, null, { preserveFileName: true });

const result = await controllerWithReturn.createFile(
config,
'originalFile.txt',
'data',
'text/plain'
);

expect(result.name).toBe('newFilename.txt');
expect(result.url).toBe('http://new.url/newFilename.txt');
});

it('should use original filename and generate url when adapter returns nothing', async () => {
const config = Config.get(Parse.applicationId);
const adapterWithoutReturn = { ...mockAdapter };
adapterWithoutReturn.createFile = () => {
return Promise.resolve();
};
adapterWithoutReturn.getFileLocation = (config, filename) => {
return Promise.resolve(`http://default.url/${filename}`);
};

const controllerWithoutReturn = new FilesController(adapterWithoutReturn, null, { preserveFileName: true });
const result = await controllerWithoutReturn.createFile(
config,
'originalFile.txt',
'data',
'text/plain',
{}
);

expect(result.name).toBe('originalFile.txt');
expect(result.url).toBe('http://default.url/originalFile.txt');
});

it('should use original filename when adapter returns only url', async () => {
const config = Config.get(Parse.applicationId);
const adapterWithOnlyURL = { ...mockAdapter };
adapterWithOnlyURL.createFile = () => {
return Promise.resolve({
url: 'http://new.url/partialFile.txt'
});
};
adapterWithOnlyURL.getFileLocation = () => {
return Promise.resolve('http://default.url/file.txt');
};

const controllerWithPartial = new FilesController(adapterWithOnlyURL, null, { preserveFileName: true });
const result = await controllerWithPartial.createFile(
config,
'originalFile.txt',
'data',
'text/plain',
{}
);

expect(result.name).toBe('originalFile.txt');
expect(result.url).toBe('http://new.url/partialFile.txt');
});

it('should use adapter filename and generate url when adapter returns only filename', async () => {
const config = Config.get(Parse.applicationId);
const adapterWithOnlyFilename = { ...mockAdapter };
adapterWithOnlyFilename.createFile = () => {
return Promise.resolve({
name: 'newname.txt'
});
};
adapterWithOnlyFilename.getFileLocation = (config, filename) => {
return Promise.resolve(`http://default.url/${filename}`);
};

const controllerWithOnlyFilename = new FilesController(adapterWithOnlyFilename, null, { preserveFileName: true });
const result = await controllerWithOnlyFilename.createFile(
config,
'originalFile.txt',
'data',
'text/plain',
{}
);

expect(result.name).toBe('newname.txt');
expect(result.url).toBe('http://default.url/newname.txt');
});
});
8 changes: 5 additions & 3 deletions src/Adapters/Files/FilesAdapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,14 @@ export class FilesAdapter {
* @discussion the contentType can be undefined if the controller was not able to determine it
* @param {object} options - (Optional) options to be passed to file adapter (S3 File Adapter Only)
* - tags: object containing key value pairs that will be stored with file
* - metadata: object containing key value pairs that will be sotred with file (https://docs.aws.amazon.com/AmazonS3/latest/user-guide/add-object-metadata.html)
* - metadata: object containing key value pairs that will be stored with file (https://docs.aws.amazon.com/AmazonS3/latest/user-guide/add-object-metadata.html)
* @discussion options are not supported by all file adapters. Check the your adapter's documentation for compatibility
* @param {Config} config - (Optional) server configuration
* @discussion config may be passed to adapter to allow for more complex configuration and internal call of getFileLocation (if needed). This argument is not supported by all file adapters. Check the your adapter's documentation for compatibility
*
* @return {Promise} a promise that should fail if the storage didn't succeed
* @return {Promise<{url?: string, name?: string, location?: string}>|Promise<undefined>} Either a plain promise that should fail if storage didn't succeed, or a promise resolving to an object containing url and/or an updated filename and/or location (if relevant)
*/
createFile(filename: string, data, contentType: string, options: Object): Promise {}
createFile(filename: string, data, contentType: string, options: Object, config: Config): Promise {}

/** Responsible for deleting the specified file
*
Expand Down
9 changes: 6 additions & 3 deletions src/Controllers/FilesController.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,13 @@ export class FilesController extends AdaptableController {
filename = randomHexString(32) + '_' + filename;
}

const location = await this.adapter.getFileLocation(config, filename);
await this.adapter.createFile(filename, data, contentType, options);
const createResult = await this.adapter.createFile(filename, data, contentType, options, config);
filename = createResult?.name || filename; // if createFile returns a new filename, use it

const url = createResult?.url || await this.adapter.getFileLocation(config, filename); // if createFile returns a new url, use it otherwise get the url from the adapter

return {
url: location,
url: url,
name: filename,
}
}
Expand Down