diff --git a/lib/schemaUtils.js b/lib/schemaUtils.js index 3ad72128..bde17cbc 100644 --- a/lib/schemaUtils.js +++ b/lib/schemaUtils.js @@ -534,6 +534,58 @@ module.exports = { return variables; }, + /** + * Given a Postman auth helper produced by `getAuthHelper`, returns an array of + * collection-level Variable objects (empty values) for every `{{variable}}` + * placeholder the helper references. This lets users of an imported collection + * see / fill the auth credentials in the Variables tab alongside `baseUrl`, + * instead of hitting unresolved-variable warnings the first time they send a + * request. + * + * Only variables that are clearly credential placeholders are emitted — the + * helper values are scanned for `{{}}` substrings and each unique + * identifier becomes an empty Variable. No-op for `noauth`. + * + * @param {object} authHelper - the object returned by `getAuthHelper`. + * @returns {Variable[]} collection variables for credential placeholders. + */ + getAuthCollectionVariables: function(authHelper) { + if (!_.isObject(authHelper) || !authHelper.type || authHelper.type === 'noauth') { + return []; + } + + const placeholderKeys = new Set(); + const placeholderRegex = /\{\{\s*([A-Za-z0-9_]+)\s*\}\}/g; + + const scan = (value) => { + if (_.isString(value)) { + let match; + while ((match = placeholderRegex.exec(value)) !== null) { + placeholderKeys.add(match[1]); + } + } + else if (_.isArray(value)) { + value.forEach(scan); + } + else if (_.isObject(value)) { + _.forEach(value, scan); + } + }; + + // Only scan the helper payload, not the top-level `type`. + _.forEach(authHelper, (value, key) => { + if (key !== 'type') { + scan(value); + } + }); + + return Array.from(placeholderKeys).map((key) => new Variable({ + key: key, + value: '', + type: 'string' + })); + }, + /** * Returns params applied to specific operation with resolved references. Params from parent * blocks (collection/folder) are merged, so that the request has a flattened list of params needed. diff --git a/lib/schemapack.js b/lib/schemapack.js index 85bc4e89..ad81a629 100644 --- a/lib/schemapack.js +++ b/lib/schemapack.js @@ -364,6 +364,17 @@ class SchemaPack { generatedStore.collection.variables.add(element); }); + // Mirror the baseUrl treatment for auth credentials — when the spec uses + // a security scheme, expose empty placeholders (basicAuthUsername / + // basicAuthPassword / bearerToken / apiKey / …) in the Variables tab so + // users can fill them in one place instead of hitting unresolved-variable + // warnings on first send. See #894. + if (authHelper) { + schemaUtils.getAuthCollectionVariables(authHelper).forEach((element) => { + generatedStore.collection.variables.add(element); + }); + } + generatedStore.collection.describe(schemaUtils.getCollectionDescription(openapi)); // Only change the stack limit if the optimizeConversion option is true diff --git a/test/unit/convertV2.test.js b/test/unit/convertV2.test.js index ed266b8b..fb97c0f2 100644 --- a/test/unit/convertV2.test.js +++ b/test/unit/convertV2.test.js @@ -1523,6 +1523,22 @@ describe('The convert v2 Function', function() { expect(conversionResult.output[0].data.item.length).to.equal(1); expect(conversionResult.output[0].data.auth.basic[0].value).to.equal('{{basicAuthUsername}}'); expect(conversionResult.output[0].data.auth.basic[1].value).to.equal('{{basicAuthPassword}}'); + + // Empty collection-level variables for the auth placeholders should be + // pushed alongside `baseUrl` so users can fill them in the Variables + // tab. See #894. + const variableKeys = conversionResult.output[0].data.variable.map((v) => v.key); + expect(variableKeys).to.include('basicAuthUsername'); + expect(variableKeys).to.include('basicAuthPassword'); + const basicAuthUsername = conversionResult.output[0].data.variable.find( + (v) => v.key === 'basicAuthUsername' + ); + const basicAuthPassword = conversionResult.output[0].data.variable.find( + (v) => v.key === 'basicAuthPassword' + ); + expect(basicAuthUsername.value).to.equal(''); + expect(basicAuthPassword.value).to.equal(''); + done(); }); });