Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add dynamic folder feature #559

Merged
merged 4 commits into from
Sep 11, 2022
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
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
11 changes: 11 additions & 0 deletions lib-es5/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,17 @@ exports.resource_by_asset_id = function resource_by_asset_id(asset_id, callback)
return call_api("get", uri, getResourceParams(options), callback, options);
};

exports.resources_by_asset_folder = function resources_by_asset_folder(asset_folder, callback) {
var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};

var params = void 0,
uri = void 0;
uri = ["resources", 'by_asset_folder'];
params = pickOnlyExistingValues(options, "next_cursor", "max_results", "tags", "context", "moderations");
params.asset_folder = asset_folder;
return call_api("get", uri, params, callback, options);
};

exports.resources_by_asset_ids = function resources_by_asset_ids(asset_ids, callback) {
var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};

Expand Down
12 changes: 11 additions & 1 deletion lib-es5/utils/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -386,7 +386,8 @@ function build_upload_params(options) {
use_filename: utils.as_safe_bool(options.use_filename),
use_filename_as_display_name: utils.as_safe_bool(options.use_filename_as_display_name),
quality_override: options.quality_override,
accessibility_analysis: utils.as_safe_bool(options.accessibility_analysis)
accessibility_analysis: utils.as_safe_bool(options.accessibility_analysis),
use_asset_folder_as_public_id_prefix: utils.as_safe_bool(options.use_asset_folder_as_public_id_prefix)
};
return utils.updateable_resource_params(options, params);
}
Expand Down Expand Up @@ -726,6 +727,15 @@ function updateable_resource_params(options) {
if (options.quality_override != null) {
params.quality_override = options.quality_override;
}
if (options.asset_folder != null) {
params.asset_folder = options.asset_folder;
}
if (options.display_name != null) {
params.display_name = options.display_name;
}
if (options.unique_display_name != null) {
params.unique_display_name = options.unique_display_name;
}
return params;
}

Expand Down
1 change: 1 addition & 0 deletions lib-es5/v2/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ v1_adapters(exports, api, {
resource_by_asset_id: 1,
resources_by_asset_ids: 1,
resources_by_ids: 1,
resources_by_asset_folder: 1,
resource: 1,
restore: 1,
update: 1,
Expand Down
8 changes: 8 additions & 0 deletions lib/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,14 @@ exports.resource_by_asset_id = function resource_by_asset_id(asset_id, callback,
return call_api("get", uri, getResourceParams(options), callback, options);
}

exports.resources_by_asset_folder = function resources_by_asset_folder(asset_folder, callback, options = {}) {
let params, uri;
uri = ["resources", 'by_asset_folder'];
params = pickOnlyExistingValues(options, "next_cursor", "max_results", "tags", "context", "moderations");
params.asset_folder = asset_folder;
return call_api("get", uri, params, callback, options);
};

exports.resources_by_asset_ids = function resources_by_asset_ids(asset_ids, callback, options = {}) {
let params, uri;
uri = ["resources", "by_asset_ids"];
Expand Down
12 changes: 11 additions & 1 deletion lib/utils/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -365,7 +365,8 @@ function build_upload_params(options) {
use_filename: utils.as_safe_bool(options.use_filename),
use_filename_as_display_name: utils.as_safe_bool(options.use_filename_as_display_name),
quality_override: options.quality_override,
accessibility_analysis: utils.as_safe_bool(options.accessibility_analysis)
accessibility_analysis: utils.as_safe_bool(options.accessibility_analysis),
use_asset_folder_as_public_id_prefix: utils.as_safe_bool(options.use_asset_folder_as_public_id_prefix)
};
return utils.updateable_resource_params(options, params);
}
Expand Down Expand Up @@ -644,6 +645,15 @@ function updateable_resource_params(options, params = {}) {
if (options.quality_override != null) {
params.quality_override = options.quality_override;
}
if (options.asset_folder != null){
params.asset_folder = options.asset_folder;
}
if (options.display_name != null){
params.display_name = options.display_name;
}
if (options.unique_display_name != null){
params.unique_display_name = options.unique_display_name;
}
return params;
}

Expand Down
1 change: 1 addition & 0 deletions lib/v2/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ v1_adapters(exports, api, {
resource_by_asset_id: 1,
resources_by_asset_ids: 1,
resources_by_ids: 1,
resources_by_asset_folder: 1,
resource: 1,
restore: 1,
update: 1,
Expand Down
74 changes: 74 additions & 0 deletions test/integration/api/admin/api_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@ const ADDON_OCR = helper.ADDON_OCR;
const callReusableTest = require('../../../testUtils/reusableTests/reusableTests').callReusableTest;
const testConstants = require('../../../testUtils/testConstants');
const retry = require('../../../testUtils/helpers/retry');
const {shouldTestFeature} = require("../../../spechelper");
const API_V2 = cloudinary.v2.api;
const DYNAMIC_FOLDERS = helper.DYNAMIC_FOLDERS;

const {
TIMEOUT,
Expand Down Expand Up @@ -1122,6 +1124,78 @@ describe("api", function () {
callReusableTest("a list with a cursor", cloudinary.v2.api.sub_folders, '/');
});
});
describe("dynamic folders", () => {
it('should create upload_preset when use_asset_folder_as_public_id_prefix is true', async function () {
if (!shouldTestFeature(DYNAMIC_FOLDERS)) {
this.skip();
}

this.timeout(TIMEOUT.LONG);
let preset = await cloudinary.v2.api.create_upload_preset({
use_asset_folder_as_public_id_prefix: true
})
let preset_details = await cloudinary.v2.api.upload_preset(preset.name);
expect(preset_details.settings).to.eql({ use_asset_folder_as_public_id_prefix: true })
});

it('should update upload_preset when use_asset_folder_as_public_id_prefix is true', async function () {
if (!shouldTestFeature(DYNAMIC_FOLDERS)) {
this.skip();
}
this.timeout(TIMEOUT.LONG);
let preset = await cloudinary.v2.api.create_upload_preset();
await cloudinary.v2.api.update_upload_preset(preset.name,
{
use_asset_folder_as_public_id_prefix: true
});

let preset_details = await cloudinary.v2.api.upload_preset(preset.name);
expect(preset_details.settings).to.eql({ use_asset_folder_as_public_id_prefix: true })
});

it('should update asset_folder', async function () {
if (!shouldTestFeature(DYNAMIC_FOLDERS)) {
this.skip();
}
const asset_folder = "asset_folder";
return uploadImage({
asset_folder
}).then(result => {
return cloudinary.v2.api.update(result.public_id, {
asset_folder: 'updated_asset_folder'
}).then(res => {
expect(res.asset_folder).to.eql('updated_asset_folder')
})
});
});

it('should update asset_folder with unique_display_name', () => {
return helper.provideMockObjects((mockXHR, writeSpy, requestSpy) => {
uploadImage().then(result => {
cloudinary.v2.api.update(result.public_id, {
unique_display_name: true
})
return sinon.assert.calledWith(requestSpy, sinon.match({
query: sinon.match(helper.apiParamMatcher("unique_display_name", "true"))
}));
});
});
});

it('should list resources_by_asset_folder', async function () {
if (!shouldTestFeature(DYNAMIC_FOLDERS)) {
this.skip();
}

const asset_folder = "new_asset_folder";
return uploadImage({
asset_folder
}).then(async () => {
const result = await cloudinary.v2.api.resources_by_asset_folder('new_asset_folder')
expect(result.total_count).to.eql(1)
});
});
});
describe('.restore', function () {
this.timeout(TIMEOUT.MEDIUM);

Expand Down
46 changes: 44 additions & 2 deletions test/integration/api/uploader/uploader_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ const METADATA_SAMPLE_DATA_ENCODED = "metadata_color=red|metadata_shape=dodecahe
const createTestConfig = require('../../../testUtils/createTestConfig');

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

const {
Expand Down Expand Up @@ -773,9 +774,9 @@ describe("uploader", function () {
});
});
});
describe("folder decoupling", () => {
describe("dynamic folders", () => {
const mocked = helper.mockTest();
it('should pass folder decoupling params', () => {
it('should pass dynamic folder params', () => {
const public_id_prefix = "fd_public_id_prefix";
const asset_folder = "asset_folder";
const display_name = "display_name";
Expand All @@ -794,6 +795,47 @@ describe("uploader", function () {
sinon.assert.calledWithMatch(mocked.write, helper.uploadParamMatcher("use_filename_as_display_name", 1));
sinon.assert.calledWithMatch(mocked.write, helper.uploadParamMatcher("folder", folder));
});

it('should not contain asset_folder in public_id', async function () {
if (!shouldTestFeature(DYNAMIC_FOLDERS)) {
this.skip();
}

const asset_folder = "asset_folder";
return UPLOADER_V2.upload(IMAGE_FILE, {
asset_folder
}).then((result) => {
expect(result.public_id).to.not.contain('asset_folder')
});
});

it('should not contain asset_folder in public_id when use_asset_folder_as_public_id_prefix is false', async function () {
if (!shouldTestFeature(DYNAMIC_FOLDERS)) {
this.skip();
}

const asset_folder = "asset_folder";
return UPLOADER_V2.upload(IMAGE_FILE, {
asset_folder,
use_asset_folder_as_public_id_prefix: false
}).then((result) => {
expect(result.public_id).to.not.contain('asset_folder')
});
});

it('should contain asset_folder in public_id when use_asset_folder_as_public_id_prefix is true', async function () {
if (!shouldTestFeature(DYNAMIC_FOLDERS)) {
this.skip();
}

const asset_folder = "asset_folder";
return UPLOADER_V2.upload(IMAGE_FILE, {
asset_folder,
use_asset_folder_as_public_id_prefix: true
}).then((result) => {
expect(result.public_id).to.contain('asset_folder')
});
});
});
it("should support unsigned uploading using presets", async function () {
this.timeout(TIMEOUT.LONG);
Expand Down
22 changes: 20 additions & 2 deletions test/spechelper.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ exports.ICON_FILE = "test/.resources/favicon.ico";
exports.VIDEO_URL = "http://res.cloudinary.com/demo/video/upload/dog.mp4";
exports.IMAGE_URL = "http://res.cloudinary.com/demo/image/upload/sample";

exports.ADDON_ALL = 'all'; // Test all addons.
const ADDON_ALL = 'all'; // Test all addons.
exports.ADDON_ASPOSE = 'aspose'; // Aspose document conversion.
exports.ADDON_AZURE = 'azure'; // Microsoft azure video indexer.
exports.ADDON_BG_REMOVAL = 'bgremoval'; // Cloudinary AI background removal.
Expand All @@ -52,6 +52,9 @@ exports.ADDON_REKOGNITION = 'rekognition'; /* Amazon rekognition AI moderation,
exports.ADDON_URL2PNG = 'url2png'; // URL2PNG website screenshots.
exports.ADDON_VIESUS = 'viesus'; // VIESUS automatic image enhancement.
exports.ADDON_WEBPURIFY = 'webpurify'; // WebPurify image moderation.
exports.DYNAMIC_FOLDERS = 'dynamic_folders'

const ALL = 'all';

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

Expand Down Expand Up @@ -293,10 +296,25 @@ exports.toISO8601DateOnly = function (timestamp) {
*/
exports.shouldTestAddOn = function (addOn) {
const cldTestAddons = (process.env.CLD_TEST_ADDONS || '').toLowerCase();
if (cldTestAddons === this.ADDON_ALL) {
if (cldTestAddons === ADDON_ALL) {
return true;
}
return cldTestAddons.trim().split(',').includes(addOn.toLowerCase())
}

/**
* Should a certain feature be tested?
*
* @param {string} feature The feature to test.
*
* @return boolean
*/
exports.shouldTestFeature = function(feature){
const cldTestFeatures = (process.env.CLD_TEST_FEATURES || '').toLowerCase();
if (cldTestFeatures === ALL) {
return true;
}
return cldTestFeatures.trim().split(',').includes(feature.toLowerCase())
}


8 changes: 8 additions & 0 deletions types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -438,6 +438,9 @@ declare module 'cloudinary' {
moderation_status?: string;
unsafe_update?: object;
allowed_for_strict?: boolean;
asset_folder?: string;
unique_display_name?: boolean;
display_name?: string

[futureKey: string]: any;
}
Expand Down Expand Up @@ -515,6 +518,7 @@ declare module 'cloudinary' {
chunk_size?: number;
disable_promises?: boolean;
oauth_token?: string;
use_asset_folder_as_public_id_prefix?: boolean;

[futureKey: string]: any;
}
Expand Down Expand Up @@ -844,6 +848,10 @@ declare module 'cloudinary' {

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

function resources_by_asset_folder(asset_folder: string, options?: AdminAndResourceOptions, callback?: ResponseCallback): Promise<ResourceApiResponse>;

function resources_by_asset_folder(asset_folder: string, callback?: ResponseCallback): Promise<ResourceApiResponse>;

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

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