Skip to content

Commit d38df02

Browse files
author
RTLcoil
authored
Support for creating folders using Admin API (#370)
* Add `create_folder` to Admin API
1 parent b0143d2 commit d38df02

File tree

6 files changed

+123
-75
lines changed

6 files changed

+123
-75
lines changed

lib-es5/api.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -338,6 +338,22 @@ exports.sub_folders = function sub_folders(path, callback) {
338338
return call_api("get", uri, {}, callback, options);
339339
};
340340

341+
/**
342+
* Creates an empty folder
343+
*
344+
* @param {string} path The folder path to create
345+
* @param {function} callback Callback function
346+
* @param {object} options Configuration options
347+
* @returns {*}
348+
*/
349+
exports.create_folder = function create_folder(path, callback) {
350+
var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
351+
352+
var uri = void 0;
353+
uri = ["folders", path];
354+
return call_api("post", uri, {}, callback, options);
355+
};
356+
341357
exports.delete_folder = function delete_folder(path, callback) {
342358
var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
343359

lib-es5/v2/api.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ v1_adapters(exports, api, {
3434
root_folders: 0,
3535
sub_folders: 1,
3636
delete_folder: 1,
37+
create_folder: 1,
3738
upload_mappings: 0,
3839
upload_mapping: 1,
3940
delete_upload_mapping: 1,

lib/api.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,20 @@ exports.sub_folders = function sub_folders(path, callback, options = {}) {
248248
return call_api("get", uri, {}, callback, options);
249249
};
250250

251+
/**
252+
* Creates an empty folder
253+
*
254+
* @param {string} path The folder path to create
255+
* @param {function} callback Callback function
256+
* @param {object} options Configuration options
257+
* @returns {*}
258+
*/
259+
exports.create_folder = function create_folder(path, callback, options = {}) {
260+
let uri;
261+
uri = ["folders", path];
262+
return call_api("post", uri, {}, callback, options);
263+
};
264+
251265
exports.delete_folder = function delete_folder(path, callback, options = {}) {
252266
let uri;
253267
uri = ["folders", path];

lib/v2/api.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ v1_adapters(exports, api, {
3232
root_folders: 0,
3333
sub_folders: 1,
3434
delete_folder: 1,
35+
create_folder: 1,
3536
upload_mappings: 0,
3637
upload_mapping: 1,
3738
delete_upload_mapping: 1,

test/api_spec.js

Lines changed: 89 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@ const {
2424
UNIQUE_JOB_SUFFIX_ID,
2525
PRESETS,
2626
TRANSFORMATIONS,
27-
PUBLIC_ID_PREFIX
27+
PUBLIC_ID_PREFIX,
28+
UNIQUE_TEST_FOLDER,
2829
} = testConstants;
2930

3031
const {
@@ -905,83 +906,96 @@ describe("api", function () {
905906
));
906907
}));
907908
});
908-
// For this test to work, "Auto-create folders" should be enabled in the Upload Settings.
909-
// Replace `it` with `it.skip` below if you want to disable it.
910-
it("should list folders in cloudinary", function () {
911-
this.timeout(TIMEOUT.LONG);
912-
return Q.all([
913-
uploadImage({
914-
public_id: 'test_folder1/item',
915-
tags: UPLOAD_TAGS,
916-
}),
917-
uploadImage({
918-
public_id: 'test_folder2/item',
919-
tags: UPLOAD_TAGS,
920-
}),
921-
uploadImage({
922-
public_id: 'test_folder2/item',
923-
tags: UPLOAD_TAGS,
924-
}),
925-
uploadImage({
926-
public_id: 'test_folder1/test_subfolder1/item',
927-
tags: UPLOAD_TAGS,
928-
}),
929-
uploadImage({
930-
public_id: 'test_folder1/test_subfolder2/item',
931-
tags: UPLOAD_TAGS,
932-
}),
933-
]).then(wait(3000)).then(function (results) {
934-
return Q.all([cloudinary.v2.api.root_folders(), cloudinary.v2.api.sub_folders('test_folder1')]);
935-
}).then(function (results) {
936-
var folder, root, root_folders, sub_1;
937-
root = results[0];
938-
root_folders = (() => {
939-
var j, len, ref, results1;
940-
ref = root.folders;
941-
results1 = [];
942-
for (j = 0, len = ref.length; j < len; j++) {
943-
folder = ref[j];
944-
results1.push(folder.name);
945-
}
946-
return results1;
947-
})();
948-
sub_1 = results[1];
949-
expect(root_folders).to.contain('test_folder1');
950-
expect(root_folders).to.contain('test_folder2');
951-
expect(sub_1.folders[0].path).to.eql('test_folder1/test_subfolder1');
952-
expect(sub_1.folders[1].path).to.eql('test_folder1/test_subfolder2');
953-
return cloudinary.v2.api.sub_folders('test_folder_not_exists');
954-
}).then((result) => {
955-
console.log('error test_folder_not_exists should not pass to "then" handler but "catch"');
956-
expect().fail('error test_folder_not_exists should not pass to "then" handler but "catch"');
957-
}).catch((err) => {
958-
let error = err.error;
959-
return expect(error.message).to.eql('Can\'t find folder with path test_folder_not_exists');
960-
});
961-
});
962-
describe("delete folders", function() {
963-
this.timeout(TIMEOUT.MEDIUM);
964-
const folderPath= "test_folder/delete_folder/"+TEST_TAG;
965-
before(function(){
966-
return uploadImage({
967-
folder: folderPath,
968-
tags: UPLOAD_TAGS
969-
}).delay(2 * 1000).then(function() {
970-
return cloudinary.v2.api.delete_resources_by_prefix(folderPath)
971-
.then(() => cloudinary.v2.api.sub_folders(folderPath).then(folder => {
972-
expect(folder).not.to.be(null);
973-
expect(folder["total_count"]).to.eql(0);
974-
expect(folder["folders"]).to.be.empty;
975-
}));
909+
describe("folders", function () {
910+
// For this test to work, "Auto-create folders" should be enabled in the Upload Settings.
911+
// Replace `it` with `it.skip` below if you want to disable it.
912+
it("should list folders in cloudinary", function () {
913+
this.timeout(TIMEOUT.LONG);
914+
return Q.all([
915+
uploadImage({
916+
public_id: 'test_folder1/item',
917+
tags: UPLOAD_TAGS,
918+
}),
919+
uploadImage({
920+
public_id: 'test_folder2/item',
921+
tags: UPLOAD_TAGS,
922+
}),
923+
uploadImage({
924+
public_id: 'test_folder2/item',
925+
tags: UPLOAD_TAGS,
926+
}),
927+
uploadImage({
928+
public_id: 'test_folder1/test_subfolder1/item',
929+
tags: UPLOAD_TAGS,
930+
}),
931+
uploadImage({
932+
public_id: 'test_folder1/test_subfolder2/item',
933+
tags: UPLOAD_TAGS,
934+
}),
935+
]).then(wait(TIMEOUT.SHORT))
936+
.then(function (results) {
937+
return Q.all([cloudinary.v2.api.root_folders(), cloudinary.v2.api.sub_folders('test_folder1')]);
938+
}).then(function (results) {
939+
var folder, root, root_folders, sub_1;
940+
root = results[0];
941+
root_folders = (() => {
942+
var j, len, ref, results1;
943+
ref = root.folders;
944+
results1 = [];
945+
for (j = 0, len = ref.length; j < len; j++) {
946+
folder = ref[j];
947+
results1.push(folder.name);
948+
}
949+
return results1;
950+
})();
951+
sub_1 = results[1];
952+
expect(root_folders).to.contain('test_folder1');
953+
expect(root_folders).to.contain('test_folder2');
954+
expect(sub_1.folders[0].path).to.eql('test_folder1/test_subfolder1');
955+
expect(sub_1.folders[1].path).to.eql('test_folder1/test_subfolder2');
956+
return cloudinary.v2.api.sub_folders('test_folder_not_exists');
957+
}).then(wait(TIMEOUT.LONG)).then((result) => {
958+
console.log('error test_folder_not_exists should not pass to "then" handler but "catch"');
959+
expect().fail('error test_folder_not_exists should not pass to "then" handler but "catch"');
960+
}).catch(({ error }) => expect(error.message).to.eql('Can\'t find folder with path test_folder_not_exists'));
961+
});
962+
describe("create_folder", function () {
963+
it("should create a new folder", function () {
964+
const folderPath = `${UNIQUE_TEST_FOLDER}`;
965+
const expectedPath = `folders/${folderPath}`;
966+
return helper.mockPromise(function (xhr, write, request) {
967+
cloudinary.v2.api.create_folder(folderPath);
968+
sinon.assert.calledWith(request, sinon.match({
969+
pathname: sinon.match(expectedPath),
970+
method: sinon.match("POST"),
971+
}));
972+
});
976973
});
977974
});
978-
it('should delete an empty folder', function () {
975+
describe("delete_folder", function () {
979976
this.timeout(TIMEOUT.MEDIUM);
980-
return cloudinary.v2.api.delete_folder(
981-
folderPath
982-
).delay(2 * 1000).then(() => cloudinary.v2.api.sub_folders(folderPath)
983-
).then(()=> expect().fail()
984-
).catch(({error}) => expect(error.message).to.contain("Can't find folder with path"));
977+
const folderPath = "test_folder/delete_folder/" + TEST_TAG;
978+
before(function () {
979+
return uploadImage({
980+
folder: folderPath,
981+
tags: UPLOAD_TAGS,
982+
}).delay(2 * 1000).then(function () {
983+
return cloudinary.v2.api.delete_resources_by_prefix(folderPath)
984+
.then(() => cloudinary.v2.api.sub_folders(folderPath).then(folder => {
985+
expect(folder).not.to.be(null);
986+
expect(folder["total_count"]).to.eql(0);
987+
expect(folder["folders"]).to.be.empty;
988+
}));
989+
});
990+
});
991+
it('should delete an empty folder', function () {
992+
this.timeout(TIMEOUT.MEDIUM);
993+
return cloudinary.v2.api.delete_folder(
994+
folderPath
995+
).delay(2 * 1000).then(() => cloudinary.v2.api.sub_folders(folderPath)
996+
).then(()=> expect().fail()
997+
).catch(({error}) => expect(error.message).to.contain("Can't find folder with path"));
998+
});
985999
});
9861000
});
9871001
describe('.restore', function () {

test/testUtils/testConstants.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,12 @@ const SDK_TAG = "SDK_TEST"; // identifies resources created by all SDKs tests
99
const TEST_TAG_PREFIX = "cloudinary_npm_test"; // identifies resources created by this SDKs tests
1010
const TEST_TAG = `${TEST_TAG_PREFIX}_${UNIQUE_JOB_SUFFIX_ID}`;
1111
const UPLOAD_TAGS = [TEST_TAG, TEST_TAG_PREFIX, SDK_TAG];
12+
const UNIQUE_TEST_FOLDER = `${TEST_TAG}_${UNIQUE_JOB_SUFFIX_ID}_folder`;
1213

1314
module.exports = {
1415
TEST_TAG_PREFIX,
1516
PUBLIC_ID_PREFIX,
17+
UNIQUE_TEST_FOLDER,
1618
TIMEOUT: {
1719
SHORT: 5000,
1820
MEDIUM: 20000,

0 commit comments

Comments
 (0)