Skip to content

Commit e43ba79

Browse files
authored
Add dynamic folder feature (#559)
1 parent 407c803 commit e43ba79

File tree

10 files changed

+189
-6
lines changed

10 files changed

+189
-6
lines changed

lib-es5/api.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,17 @@ exports.resource_by_asset_id = function resource_by_asset_id(asset_id, callback)
104104
return call_api("get", uri, getResourceParams(options), callback, options);
105105
};
106106

107+
exports.resources_by_asset_folder = function resources_by_asset_folder(asset_folder, callback) {
108+
var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
109+
110+
var params = void 0,
111+
uri = void 0;
112+
uri = ["resources", 'by_asset_folder'];
113+
params = pickOnlyExistingValues(options, "next_cursor", "max_results", "tags", "context", "moderations");
114+
params.asset_folder = asset_folder;
115+
return call_api("get", uri, params, callback, options);
116+
};
117+
107118
exports.resources_by_asset_ids = function resources_by_asset_ids(asset_ids, callback) {
108119
var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
109120

lib-es5/utils/index.js

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -386,7 +386,8 @@ function build_upload_params(options) {
386386
use_filename: utils.as_safe_bool(options.use_filename),
387387
use_filename_as_display_name: utils.as_safe_bool(options.use_filename_as_display_name),
388388
quality_override: options.quality_override,
389-
accessibility_analysis: utils.as_safe_bool(options.accessibility_analysis)
389+
accessibility_analysis: utils.as_safe_bool(options.accessibility_analysis),
390+
use_asset_folder_as_public_id_prefix: utils.as_safe_bool(options.use_asset_folder_as_public_id_prefix)
390391
};
391392
return utils.updateable_resource_params(options, params);
392393
}
@@ -726,6 +727,15 @@ function updateable_resource_params(options) {
726727
if (options.quality_override != null) {
727728
params.quality_override = options.quality_override;
728729
}
730+
if (options.asset_folder != null) {
731+
params.asset_folder = options.asset_folder;
732+
}
733+
if (options.display_name != null) {
734+
params.display_name = options.display_name;
735+
}
736+
if (options.unique_display_name != null) {
737+
params.unique_display_name = options.unique_display_name;
738+
}
729739
return params;
730740
}
731741

lib-es5/v2/api.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ v1_adapters(exports, api, {
1414
resource_by_asset_id: 1,
1515
resources_by_asset_ids: 1,
1616
resources_by_ids: 1,
17+
resources_by_asset_folder: 1,
1718
resource: 1,
1819
restore: 1,
1920
update: 1,

lib/api.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,14 @@ exports.resource_by_asset_id = function resource_by_asset_id(asset_id, callback,
7676
return call_api("get", uri, getResourceParams(options), callback, options);
7777
}
7878

79+
exports.resources_by_asset_folder = function resources_by_asset_folder(asset_folder, callback, options = {}) {
80+
let params, uri;
81+
uri = ["resources", 'by_asset_folder'];
82+
params = pickOnlyExistingValues(options, "next_cursor", "max_results", "tags", "context", "moderations");
83+
params.asset_folder = asset_folder;
84+
return call_api("get", uri, params, callback, options);
85+
};
86+
7987
exports.resources_by_asset_ids = function resources_by_asset_ids(asset_ids, callback, options = {}) {
8088
let params, uri;
8189
uri = ["resources", "by_asset_ids"];

lib/utils/index.js

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -365,7 +365,8 @@ function build_upload_params(options) {
365365
use_filename: utils.as_safe_bool(options.use_filename),
366366
use_filename_as_display_name: utils.as_safe_bool(options.use_filename_as_display_name),
367367
quality_override: options.quality_override,
368-
accessibility_analysis: utils.as_safe_bool(options.accessibility_analysis)
368+
accessibility_analysis: utils.as_safe_bool(options.accessibility_analysis),
369+
use_asset_folder_as_public_id_prefix: utils.as_safe_bool(options.use_asset_folder_as_public_id_prefix)
369370
};
370371
return utils.updateable_resource_params(options, params);
371372
}
@@ -644,6 +645,15 @@ function updateable_resource_params(options, params = {}) {
644645
if (options.quality_override != null) {
645646
params.quality_override = options.quality_override;
646647
}
648+
if (options.asset_folder != null){
649+
params.asset_folder = options.asset_folder;
650+
}
651+
if (options.display_name != null){
652+
params.display_name = options.display_name;
653+
}
654+
if (options.unique_display_name != null){
655+
params.unique_display_name = options.unique_display_name;
656+
}
647657
return params;
648658
}
649659

lib/v2/api.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ v1_adapters(exports, api, {
1212
resource_by_asset_id: 1,
1313
resources_by_asset_ids: 1,
1414
resources_by_ids: 1,
15+
resources_by_asset_folder: 1,
1516
resource: 1,
1617
restore: 1,
1718
update: 1,

test/integration/api/admin/api_spec.js

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@ const ADDON_OCR = helper.ADDON_OCR;
1515
const callReusableTest = require('../../../testUtils/reusableTests/reusableTests').callReusableTest;
1616
const testConstants = require('../../../testUtils/testConstants');
1717
const retry = require('../../../testUtils/helpers/retry');
18+
const {shouldTestFeature} = require("../../../spechelper");
1819
const API_V2 = cloudinary.v2.api;
20+
const DYNAMIC_FOLDERS = helper.DYNAMIC_FOLDERS;
1921

2022
const {
2123
TIMEOUT,
@@ -1122,6 +1124,78 @@ describe("api", function () {
11221124
callReusableTest("a list with a cursor", cloudinary.v2.api.sub_folders, '/');
11231125
});
11241126
});
1127+
describe("dynamic folders", () => {
1128+
it('should create upload_preset when use_asset_folder_as_public_id_prefix is true', async function () {
1129+
if (!shouldTestFeature(DYNAMIC_FOLDERS)) {
1130+
this.skip();
1131+
}
1132+
1133+
this.timeout(TIMEOUT.LONG);
1134+
let preset = await cloudinary.v2.api.create_upload_preset({
1135+
use_asset_folder_as_public_id_prefix: true
1136+
})
1137+
let preset_details = await cloudinary.v2.api.upload_preset(preset.name);
1138+
expect(preset_details.settings).to.eql({ use_asset_folder_as_public_id_prefix: true })
1139+
});
1140+
1141+
it('should update upload_preset when use_asset_folder_as_public_id_prefix is true', async function () {
1142+
if (!shouldTestFeature(DYNAMIC_FOLDERS)) {
1143+
this.skip();
1144+
}
1145+
this.timeout(TIMEOUT.LONG);
1146+
let preset = await cloudinary.v2.api.create_upload_preset();
1147+
await cloudinary.v2.api.update_upload_preset(preset.name,
1148+
{
1149+
use_asset_folder_as_public_id_prefix: true
1150+
});
1151+
1152+
let preset_details = await cloudinary.v2.api.upload_preset(preset.name);
1153+
expect(preset_details.settings).to.eql({ use_asset_folder_as_public_id_prefix: true })
1154+
});
1155+
1156+
it('should update asset_folder', async function () {
1157+
if (!shouldTestFeature(DYNAMIC_FOLDERS)) {
1158+
this.skip();
1159+
}
1160+
const asset_folder = "asset_folder";
1161+
return uploadImage({
1162+
asset_folder
1163+
}).then(result => {
1164+
return cloudinary.v2.api.update(result.public_id, {
1165+
asset_folder: 'updated_asset_folder'
1166+
}).then(res => {
1167+
expect(res.asset_folder).to.eql('updated_asset_folder')
1168+
})
1169+
});
1170+
});
1171+
1172+
it('should update asset_folder with unique_display_name', () => {
1173+
return helper.provideMockObjects((mockXHR, writeSpy, requestSpy) => {
1174+
uploadImage().then(result => {
1175+
cloudinary.v2.api.update(result.public_id, {
1176+
unique_display_name: true
1177+
})
1178+
return sinon.assert.calledWith(requestSpy, sinon.match({
1179+
query: sinon.match(helper.apiParamMatcher("unique_display_name", "true"))
1180+
}));
1181+
});
1182+
});
1183+
});
1184+
1185+
it('should list resources_by_asset_folder', async function () {
1186+
if (!shouldTestFeature(DYNAMIC_FOLDERS)) {
1187+
this.skip();
1188+
}
1189+
1190+
const asset_folder = "new_asset_folder";
1191+
return uploadImage({
1192+
asset_folder
1193+
}).then(async () => {
1194+
const result = await cloudinary.v2.api.resources_by_asset_folder('new_asset_folder')
1195+
expect(result.total_count).to.eql(1)
1196+
});
1197+
});
1198+
});
11251199
describe('.restore', function () {
11261200
this.timeout(TIMEOUT.MEDIUM);
11271201

test/integration/api/uploader/uploader_spec.js

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ const METADATA_SAMPLE_DATA_ENCODED = "metadata_color=red|metadata_shape=dodecahe
3030
const createTestConfig = require('../../../testUtils/createTestConfig');
3131

3232
const testConstants = require('../../../testUtils/testConstants');
33+
const {shouldTestFeature, DYNAMIC_FOLDERS} = require("../../../spechelper");
3334
const UPLOADER_V2 = cloudinary.v2.uploader;
3435

3536
const {
@@ -773,9 +774,9 @@ describe("uploader", function () {
773774
});
774775
});
775776
});
776-
describe("folder decoupling", () => {
777+
describe("dynamic folders", () => {
777778
const mocked = helper.mockTest();
778-
it('should pass folder decoupling params', () => {
779+
it('should pass dynamic folder params', () => {
779780
const public_id_prefix = "fd_public_id_prefix";
780781
const asset_folder = "asset_folder";
781782
const display_name = "display_name";
@@ -794,6 +795,47 @@ describe("uploader", function () {
794795
sinon.assert.calledWithMatch(mocked.write, helper.uploadParamMatcher("use_filename_as_display_name", 1));
795796
sinon.assert.calledWithMatch(mocked.write, helper.uploadParamMatcher("folder", folder));
796797
});
798+
799+
it('should not contain asset_folder in public_id', async function () {
800+
if (!shouldTestFeature(DYNAMIC_FOLDERS)) {
801+
this.skip();
802+
}
803+
804+
const asset_folder = "asset_folder";
805+
return UPLOADER_V2.upload(IMAGE_FILE, {
806+
asset_folder
807+
}).then((result) => {
808+
expect(result.public_id).to.not.contain('asset_folder')
809+
});
810+
});
811+
812+
it('should not contain asset_folder in public_id when use_asset_folder_as_public_id_prefix is false', async function () {
813+
if (!shouldTestFeature(DYNAMIC_FOLDERS)) {
814+
this.skip();
815+
}
816+
817+
const asset_folder = "asset_folder";
818+
return UPLOADER_V2.upload(IMAGE_FILE, {
819+
asset_folder,
820+
use_asset_folder_as_public_id_prefix: false
821+
}).then((result) => {
822+
expect(result.public_id).to.not.contain('asset_folder')
823+
});
824+
});
825+
826+
it('should contain asset_folder in public_id when use_asset_folder_as_public_id_prefix is true', async function () {
827+
if (!shouldTestFeature(DYNAMIC_FOLDERS)) {
828+
this.skip();
829+
}
830+
831+
const asset_folder = "asset_folder";
832+
return UPLOADER_V2.upload(IMAGE_FILE, {
833+
asset_folder,
834+
use_asset_folder_as_public_id_prefix: true
835+
}).then((result) => {
836+
expect(result.public_id).to.contain('asset_folder')
837+
});
838+
});
797839
});
798840
it("should support unsigned uploading using presets", async function () {
799841
this.timeout(TIMEOUT.LONG);

test/spechelper.js

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ exports.ICON_FILE = "test/.resources/favicon.ico";
2727
exports.VIDEO_URL = "http://res.cloudinary.com/demo/video/upload/dog.mp4";
2828
exports.IMAGE_URL = "http://res.cloudinary.com/demo/image/upload/sample";
2929

30-
exports.ADDON_ALL = 'all'; // Test all addons.
30+
const ADDON_ALL = 'all'; // Test all addons.
3131
exports.ADDON_ASPOSE = 'aspose'; // Aspose document conversion.
3232
exports.ADDON_AZURE = 'azure'; // Microsoft azure video indexer.
3333
exports.ADDON_BG_REMOVAL = 'bgremoval'; // Cloudinary AI background removal.
@@ -52,6 +52,9 @@ exports.ADDON_REKOGNITION = 'rekognition'; /* Amazon rekognition AI moderation,
5252
exports.ADDON_URL2PNG = 'url2png'; // URL2PNG website screenshots.
5353
exports.ADDON_VIESUS = 'viesus'; // VIESUS automatic image enhancement.
5454
exports.ADDON_WEBPURIFY = 'webpurify'; // WebPurify image moderation.
55+
exports.DYNAMIC_FOLDERS = 'dynamic_folders'
56+
57+
const ALL = 'all';
5558

5659
const { TEST_TAG } = require('./testUtils/testConstants').TAGS;
5760

@@ -293,10 +296,25 @@ exports.toISO8601DateOnly = function (timestamp) {
293296
*/
294297
exports.shouldTestAddOn = function (addOn) {
295298
const cldTestAddons = (process.env.CLD_TEST_ADDONS || '').toLowerCase();
296-
if (cldTestAddons === this.ADDON_ALL) {
299+
if (cldTestAddons === ADDON_ALL) {
297300
return true;
298301
}
299302
return cldTestAddons.trim().split(',').includes(addOn.toLowerCase())
300303
}
301304

305+
/**
306+
* Should a certain feature be tested?
307+
*
308+
* @param {string} feature The feature to test.
309+
*
310+
* @return boolean
311+
*/
312+
exports.shouldTestFeature = function(feature){
313+
const cldTestFeatures = (process.env.CLD_TEST_FEATURES || '').toLowerCase();
314+
if (cldTestFeatures === ALL) {
315+
return true;
316+
}
317+
return cldTestFeatures.trim().split(',').includes(feature.toLowerCase())
318+
}
319+
302320

types/index.d.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -438,6 +438,9 @@ declare module 'cloudinary' {
438438
moderation_status?: string;
439439
unsafe_update?: object;
440440
allowed_for_strict?: boolean;
441+
asset_folder?: string;
442+
unique_display_name?: boolean;
443+
display_name?: string
441444

442445
[futureKey: string]: any;
443446
}
@@ -515,6 +518,7 @@ declare module 'cloudinary' {
515518
chunk_size?: number;
516519
disable_promises?: boolean;
517520
oauth_token?: string;
521+
use_asset_folder_as_public_id_prefix?: boolean;
518522

519523
[futureKey: string]: any;
520524
}
@@ -844,6 +848,10 @@ declare module 'cloudinary' {
844848

845849
function resources_by_ids(public_ids: string[] | string, callback?: ResponseCallback): Promise<ResourceApiResponse>;
846850

851+
function resources_by_asset_folder(asset_folder: string, options?: AdminAndResourceOptions, callback?: ResponseCallback): Promise<ResourceApiResponse>;
852+
853+
function resources_by_asset_folder(asset_folder: string, callback?: ResponseCallback): Promise<ResourceApiResponse>;
854+
847855
function resources_by_moderation(moderation: ModerationKind, status: Status, options?: AdminAndResourceOptions, callback?: ResponseCallback): Promise<ResourceApiResponse>;
848856

849857
function resources_by_moderation(moderation: ModerationKind, status: Status, callback?: ResponseCallback): Promise<ResourceApiResponse>;

0 commit comments

Comments
 (0)