From 9823c1685d265ea74fb914fbfd60d1f9260404e7 Mon Sep 17 00:00:00 2001 From: Patrick Date: Sun, 22 Nov 2020 16:22:15 +0200 Subject: [PATCH] Add support for URL configuration of domains and sub-domains --- .github/pull_request_template.md | 2 +- .../unit/config/cloudinaryConfig.test.ts | 8 +- __TESTS__/unit/urlConfig.test.ts | 74 ++++++++++++++++++- jest.config.json | 2 +- src/config/CloudinaryConfig.ts | 2 +- src/config/URLConfig.ts | 8 +- src/interfaces/Config/IURLConfig.ts | 50 ++++++++++--- src/internalConstants.ts | 2 - src/url/cloudinaryURL.ts | 56 ++++++++++---- 9 files changed, 165 insertions(+), 39 deletions(-) diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 42432b74..554ab7e2 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -1,4 +1,4 @@ -### Pull reuqest for @Cloudianry/Base +### Pull request for @Cloudinary/Base #### What does this PR solve? diff --git a/__TESTS__/unit/config/cloudinaryConfig.test.ts b/__TESTS__/unit/config/cloudinaryConfig.test.ts index 69a79f17..ecec8ab6 100644 --- a/__TESTS__/unit/config/cloudinaryConfig.test.ts +++ b/__TESTS__/unit/config/cloudinaryConfig.test.ts @@ -44,12 +44,10 @@ describe('Tests for CloudinaryConfiguration', () => { signUrl: true, longUrlSignature: true, useRootPath: true, - cdnSubdomain: true, cname: 'cname', - secureCdnSubdomain: true, privateCdn: true, secure: true, - secureDistribution: true + secureDistribution: '' } }); @@ -58,12 +56,10 @@ describe('Tests for CloudinaryConfiguration', () => { expect(conf.url.signUrl).toBe(true); expect(conf.url.longUrlSignature).toBe(true); expect(conf.url.useRootPath).toBe(true); - expect(conf.url.cdnSubdomain).toBe(true); expect(conf.url.cname).toBe('cname'); - expect(conf.url.secureCdnSubdomain).toBe(true); expect(conf.url.privateCdn).toBe(true); expect(conf.url.secure).toBe(true); - expect(conf.url.secureDistribution).toBe(true); + expect(conf.url.secureDistribution).toBe(''); }); diff --git a/__TESTS__/unit/urlConfig.test.ts b/__TESTS__/unit/urlConfig.test.ts index f98c3656..580b0449 100644 --- a/__TESTS__/unit/urlConfig.test.ts +++ b/__TESTS__/unit/urlConfig.test.ts @@ -1,6 +1,24 @@ import TransformableImage from "../../src/transformation/TransformableImage"; import * as Resize from "../../src/actions/resize/Resize"; import ICloudinaryConfigurations from "../../src/interfaces/Config/ICloudinaryConfigurations"; +import IURLConfig from "../../src/interfaces/Config/IURLConfig"; +import CloudinaryConfig from "../../src/config/CloudinaryConfig"; +import createCloudinaryURL from "../../src/url/cloudinaryURL"; + +/** + * @description Create CloudinaryURL based on a URL Configuration, and return the URL + * @param urlConfig + */ +function createURLFromConfig(urlConfig: IURLConfig) { + const conf = new CloudinaryConfig({ + cloud: { + cloudName: 'demo' + }, + url: urlConfig + }); + + return createCloudinaryURL(conf, { publicID: 'sample' }); +} const DEMO_CONFIG = { cloud: { @@ -9,7 +27,7 @@ const DEMO_CONFIG = { } }; -describe('It tests a combination of Cloudianry URL and Configuration', () => { +describe('It tests a combination of Cloudinary URL and Configuration', () => { it ('Generates a URL', () => { const url = new TransformableImage('my_image') .setConfig(DEMO_CONFIG) @@ -74,4 +92,58 @@ describe('It tests a combination of Cloudianry URL and Configuration', () => { expect(url).toBe('https://res.cloudinary.com/MY_CLOUD_NAME/avatar/fetch/c_fill,h_100,w_100/sample'); }); + + + + it('Secure by default', () => { + const url = createURLFromConfig({}); + expect(url).toContain('https://res.cloudinary.com/demo'); + }); + + it('Supports secure:false', () => { + const url = createURLFromConfig({ + secure:false + }); + expect(url).toContain('http://res.cloudinary.com/demo'); + }); + + it('Support cname with secure false', () => { + const url = createURLFromConfig({ + cname:'hello.com', + secure: false + }); + expect(url).toContain('http://hello.com/demo'); + }); + + it('Support secureDistribution with secure true', () => { + const url = createURLFromConfig({ + secureDistribution:'foobar.com' + }); + expect(url).toContain('https://foobar.com/demo'); + }); + + it('Support private CDN with secure true', () => { + const url = createURLFromConfig({ + privateCdn:true + }); + expect(url).toContain(`https://demo-res.cloudinary.com/image/upload`); + }); + + it('Support secureDistribution with secure true', () => { + const url = createURLFromConfig({ + secure: true, + privateCdn: true, + secureDistribution: "something.cloudfront.net" + }); + expect(url).toContain('https://something.cloudfront.net/image/upload/'); + }); }); + + +/** + * http://res.cloudinary.com/{cloudName} + * https://res.cloudinary.com/{cloudName} + * https://{cloudName}-res.cloudinary.com/ + * http://{domain} + * https://{domain} + */ diff --git a/jest.config.json b/jest.config.json index ee0603b5..88b2d64c 100644 --- a/jest.config.json +++ b/jest.config.json @@ -28,7 +28,7 @@ ["jest-html-reporters", { "publicPath" : "./public/progress/", "filename": "cloudinary-base-progress-report.html", - "pageTitle": "Cloudianry JS 2.0 Progress Report", + "pageTitle": "Cloudinary JS 2.0 Progress Report", "logoImgPath": "./__DOC_RESOURCES__/cloudinary-logo.png", "expand": true }] diff --git a/src/config/CloudinaryConfig.ts b/src/config/CloudinaryConfig.ts index 9de52d8d..e34d5450 100644 --- a/src/config/CloudinaryConfig.ts +++ b/src/config/CloudinaryConfig.ts @@ -6,7 +6,7 @@ class CloudinaryConfig { public cloud: CloudConfig; public url: URLConfig; - constructor(configurations: ICloudinaryConfigurations) { + constructor(configurations: ICloudinaryConfigurations = {}) { this.cloud = new CloudConfig(configurations.cloud); this.url = new URLConfig(configurations.url || {}); } diff --git a/src/config/URLConfig.ts b/src/config/URLConfig.ts index 146c93d2..edf26e60 100644 --- a/src/config/URLConfig.ts +++ b/src/config/URLConfig.ts @@ -4,10 +4,8 @@ import {ALLOWED_URL_CONFIG} from "../internalConstants"; import ICloudConfig from "../interfaces/Config/ICloudConfig"; class URLConfig extends Config implements IURLConfig { - cdnSubdomain?: boolean; - secureCdnSubdomain?: boolean; cname?: string; // User subdomain (example.cloudinary.com) - secureDistribution?: boolean; + secureDistribution?: string; privateCdn?: boolean; signUrl?: boolean; longUrlSignature?: boolean; @@ -22,7 +20,9 @@ class URLConfig extends Config implements IURLConfig { constructor(userURLConfig: IURLConfig | unknown) { super(); const urlConfig = this.filterOutNonSupportedKeys(userURLConfig, ALLOWED_URL_CONFIG); - Object.assign(this, {secure: true}, urlConfig); + Object.assign(this, { + secure: true + }, urlConfig); } extend(userURLConfig: ICloudConfig | unknown): URLConfig { diff --git a/src/interfaces/Config/IURLConfig.ts b/src/interfaces/Config/IURLConfig.ts index 78643f53..c01b6e66 100644 --- a/src/interfaces/Config/IURLConfig.ts +++ b/src/interfaces/Config/IURLConfig.ts @@ -1,10 +1,7 @@ /** * @name ICloudinaryAssetConfigurations * @description - * Defines the configuration needed for URL-related options when creating Cloudianry URL - * - * @prop {boolean} [cdnSubdomain] - * @prop {boolean} [secureCdnSubdomain] + * Defines the configuration needed for URL-related options when creating Cloudinary URL * @prop {string} [cname] * @prop {boolean} [secureDistribution] * @prop {boolean} [privateCdn] @@ -17,18 +14,51 @@ * @prop {boolean} [analytics] */ interface IURLConfig { - cdnSubdomain?: boolean; - secureCdnSubdomain?: boolean; - cname?: string; // User subdomain (example.cloudinary.com) - secureDistribution?: boolean; + /** + * Replace the asset domain when secure is false + * http://{cname}/{cloudName}/image/upload + */ + cname?: string; + + /** + * Replace the asset domain when secure is true + * https://{cname}/{cloudName}/image/upload + */ + secureDistribution?: string; + + /** + * When Cname or secureDistribution are provided (with secure accordingly), + * privateCdn removes the cloudName from the URL: + * + * https://{cname|secureDistribution}/image/upload + * instead of + * * https://{cname|secureDistribution}/{cloudName}image/upload + * + * When privateCdn is provided without cname or secure distribution, + * it moves the cloudName from the URL to the domain: + * + * https://{cloudName}-res.cloudinary.com/image/upload + * instead of + * https://res.cloudinary.com/{cloudName}/image/upload + */ privateCdn?: boolean; + + /** + * use HTTPS or HTTP + */ + secure?: boolean; + + + /** + * Whether or not to include the SDK version signature in the URL + */ + analytics?: boolean; + signUrl?: boolean; longUrlSignature?: boolean; shorten?: boolean; useRootPath?: boolean; - secure?: boolean; forceVersion?: boolean; - analytics?: boolean; } export default IURLConfig; diff --git a/src/internalConstants.ts b/src/internalConstants.ts index 2e61d983..f25bf2e2 100644 --- a/src/internalConstants.ts +++ b/src/internalConstants.ts @@ -7,8 +7,6 @@ * @private */ export const ALLOWED_URL_CONFIG = [ - 'cdnSubdomain', - 'secureCdnSubdomain', 'cname', 'secureDistribution', 'privateCdn', diff --git a/src/url/cloudinaryURL.ts b/src/url/cloudinaryURL.ts index bbf60518..44147506 100644 --- a/src/url/cloudinaryURL.ts +++ b/src/url/cloudinaryURL.ts @@ -12,7 +12,7 @@ import ICloudinaryConfigurations from "../interfaces/Config/ICloudinaryConfigura * @param {Object} config * @param {Object} descriptor * @param {Transformation} transformation - * @return {string} CloudianryURL + * @return {string} CloudinaryURL */ function createCloudinaryURL(config: ICloudinaryConfigurations, descriptor?: IDescriptor, transformation?: Transformation): string { const prefix = getUrlPrefix(config.cloud.cloudName, config.url); @@ -33,19 +33,49 @@ function createCloudinaryURL(config: ICloudinaryConfigurations, descriptor?: IDe /** * Create the URL prefix for Cloudinary resources. + * Available use cases + * http://res.cloudinary.com/{cloudName} + * https://res.cloudinary.com/{cloudName} + * https://{cloudName}-res.cloudinary.com/ + * http://{domain}/${cloudName} + * https://{domain}/${cloudName} + * https://{domain} * @private + * * @param {string} cloudName * @param {IURLConfig} urlConfig */ -function getUrlPrefix(cloudName: string, urlConfig:IURLConfig) { - // defaults - const protocol = urlConfig.secure ? "https://" : "http://"; - const cdnPart = ""; - const subdomain = "res"; - const host = ".cloudinary.com"; - const path = `/${cloudName}`; - - return [protocol, cdnPart, subdomain, host, path].join(""); +function getUrlPrefix(cloudName: string, urlConfig: IURLConfig) { + const secure = urlConfig.secure; + const privateCDN = urlConfig.privateCdn; + const cname = urlConfig.cname; + const secureDistribution = urlConfig.secureDistribution; + + if (!secure && !cname) { + return `http://res.cloudinary.com/${cloudName}`; + } + + if (secure && !secureDistribution && privateCDN) { + return `https://${cloudName}-res.cloudinary.com`; + } + + if (secure && !secureDistribution) { + return `https://res.cloudinary.com/${cloudName}`; + } + + if (secure && secureDistribution && privateCDN) { + return `https://${secureDistribution}`; + } + + if (secure && secureDistribution) { + return `https://${secureDistribution}/${cloudName}`; + } + + if (!secure && cname) { + return `http://${cname}/${cloudName}`; + } else { + return 'ERROR'; + } } /** @@ -54,7 +84,7 @@ function getUrlPrefix(cloudName: string, urlConfig:IURLConfig) { */ function handleAssetType(descriptor: IDescriptor) { //default to image - if(!descriptor || !descriptor.assetType) { + if (!descriptor || !descriptor.assetType) { return 'image'; } @@ -67,7 +97,7 @@ function handleAssetType(descriptor: IDescriptor) { */ function handleStorageType(descriptor: IDescriptor) { //default to upload - if(!descriptor || !descriptor.storageType) { + if (!descriptor || !descriptor.storageType) { return 'upload'; } @@ -78,7 +108,7 @@ function handleStorageType(descriptor: IDescriptor) { * @private * @param descriptor */ -function getUrlVersion(urlConfig:IURLConfig, descriptor: IDescriptor) { +function getUrlVersion(urlConfig: IURLConfig, descriptor: IDescriptor) { const shouldForceVersion = urlConfig.forceVersion !== false; if (descriptor.version) {