Repository: BRIKEV/express-jsdoc-swagger
Pull request #218 (2022-04-29 15:40:26)

feat: support for dictionaries
### What kind of change does this PR introduce? (check at least one)
 - [ ] Bugfix
 - [x ] Feature
 - [ ] Test
 - [ ] Docs
 - [ ] Refactor
 - [ ] Build-related changes
 - [ ] Other, please describe:

### Description:

This PR adds support for dictionaries with objects as values as described in the OpenAPI spec under "Value type" (second example here: https://swagger.io/docs/specification/data-models/dictionaries/
As my understanding of the codebase is limited I don't think the implementation is optimal but hopefully someone with more familiarity of the codebase can refactor it to a satisfactory level?

sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!&nbsp; &nbsp; [![Quality Gate passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/passed-16px.png 'Quality Gate passed')](https://sonarcloud.io/dashboard?id=BRIKEV_express-jsdoc-swagger&pullRequest=218)

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=BRIKEV_express-jsdoc-swagger&pullRequest=218&resolved=false&types=BUG) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=BRIKEV_express-jsdoc-swagger&pullRequest=218&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=BRIKEV_express-jsdoc-swagger&pullRequest=218&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=BRIKEV_express-jsdoc-swagger&pullRequest=218&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=BRIKEV_express-jsdoc-swagger&pullRequest=218&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=BRIKEV_express-jsdoc-swagger&pullRequest=218&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=BRIKEV_express-jsdoc-swagger&pullRequest=218&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=BRIKEV_express-jsdoc-swagger&pullRequest=218&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=BRIKEV_express-jsdoc-swagger&pullRequest=218&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=BRIKEV_express-jsdoc-swagger&pullRequest=218&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=BRIKEV_express-jsdoc-swagger&pullRequest=218&resolved=false&types=CODE_SMELL) [0 Code Smells](https://sonarcloud.io/project/issues?id=BRIKEV_express-jsdoc-swagger&pullRequest=218&resolved=false&types=CODE_SMELL)

[![No Coverage information](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/NoCoverageInfo-16px.png 'No Coverage information')](https://sonarcloud.io/component_measures?id=BRIKEV_express-jsdoc-swagger&pullRequest=218) No Coverage information  
[![No Duplication information](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/NoDuplicationInfo-16px.png 'No Duplication information')](https://sonarcloud.io/component_measures?id=BRIKEV_express-jsdoc-swagger&pullRequest=218&metric=duplicated_lines_density&view=list) No Duplication information


sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!&nbsp; &nbsp; [![Quality Gate passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/passed-16px.png 'Quality Gate passed')](https://sonarcloud.io/dashboard?id=BRIKEV_express-jsdoc-swagger&pullRequest=218)

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=BRIKEV_express-jsdoc-swagger&pullRequest=218&resolved=false&types=BUG) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=BRIKEV_express-jsdoc-swagger&pullRequest=218&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=BRIKEV_express-jsdoc-swagger&pullRequest=218&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=BRIKEV_express-jsdoc-swagger&pullRequest=218&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=BRIKEV_express-jsdoc-swagger&pullRequest=218&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=BRIKEV_express-jsdoc-swagger&pullRequest=218&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=BRIKEV_express-jsdoc-swagger&pullRequest=218&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=BRIKEV_express-jsdoc-swagger&pullRequest=218&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=BRIKEV_express-jsdoc-swagger&pullRequest=218&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=BRIKEV_express-jsdoc-swagger&pullRequest=218&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=BRIKEV_express-jsdoc-swagger&pullRequest=218&resolved=false&types=CODE_SMELL) [0 Code Smells](https://sonarcloud.io/project/issues?id=BRIKEV_express-jsdoc-swagger&pullRequest=218&resolved=false&types=CODE_SMELL)

[![No Coverage information](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/NoCoverageInfo-16px.png 'No Coverage information')](https://sonarcloud.io/component_measures?id=BRIKEV_express-jsdoc-swagger&pullRequest=218) No Coverage information  
[![No Duplication information](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/NoDuplicationInfo-16px.png 'No Duplication information')](https://sonarcloud.io/component_measures?id=BRIKEV_express-jsdoc-swagger&pullRequest=218&metric=duplicated_lines_density&view=list) No Duplication information


sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!&nbsp; &nbsp; [![Quality Gate passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/passed-16px.png 'Quality Gate passed')](https://sonarcloud.io/dashboard?id=BRIKEV_express-jsdoc-swagger&pullRequest=218)

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=BRIKEV_express-jsdoc-swagger&pullRequest=218&resolved=false&types=BUG) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=BRIKEV_express-jsdoc-swagger&pullRequest=218&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=BRIKEV_express-jsdoc-swagger&pullRequest=218&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=BRIKEV_express-jsdoc-swagger&pullRequest=218&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=BRIKEV_express-jsdoc-swagger&pullRequest=218&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=BRIKEV_express-jsdoc-swagger&pullRequest=218&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=BRIKEV_express-jsdoc-swagger&pullRequest=218&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=BRIKEV_express-jsdoc-swagger&pullRequest=218&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=BRIKEV_express-jsdoc-swagger&pullRequest=218&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=BRIKEV_express-jsdoc-swagger&pullRequest=218&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=BRIKEV_express-jsdoc-swagger&pullRequest=218&resolved=false&types=CODE_SMELL) [0 Code Smells](https://sonarcloud.io/project/issues?id=BRIKEV_express-jsdoc-swagger&pullRequest=218&resolved=false&types=CODE_SMELL)

[![No Coverage information](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/NoCoverageInfo-16px.png 'No Coverage information')](https://sonarcloud.io/component_measures?id=BRIKEV_express-jsdoc-swagger&pullRequest=218) No Coverage information  
[![No Duplication information](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/NoDuplicationInfo-16px.png 'No Duplication information')](https://sonarcloud.io/component_measures?id=BRIKEV_express-jsdoc-swagger&pullRequest=218&metric=duplicated_lines_density&view=list) No Duplication information


sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!&nbsp; &nbsp; [![Quality Gate passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/passed-16px.png 'Quality Gate passed')](https://sonarcloud.io/dashboard?id=BRIKEV_express-jsdoc-swagger&pullRequest=218)

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=BRIKEV_express-jsdoc-swagger&pullRequest=218&resolved=false&types=BUG) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=BRIKEV_express-jsdoc-swagger&pullRequest=218&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=BRIKEV_express-jsdoc-swagger&pullRequest=218&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=BRIKEV_express-jsdoc-swagger&pullRequest=218&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=BRIKEV_express-jsdoc-swagger&pullRequest=218&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=BRIKEV_express-jsdoc-swagger&pullRequest=218&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=BRIKEV_express-jsdoc-swagger&pullRequest=218&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=BRIKEV_express-jsdoc-swagger&pullRequest=218&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=BRIKEV_express-jsdoc-swagger&pullRequest=218&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=BRIKEV_express-jsdoc-swagger&pullRequest=218&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=BRIKEV_express-jsdoc-swagger&pullRequest=218&resolved=false&types=CODE_SMELL) [0 Code Smells](https://sonarcloud.io/project/issues?id=BRIKEV_express-jsdoc-swagger&pullRequest=218&resolved=false&types=CODE_SMELL)

[![No Coverage information](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/NoCoverageInfo-16px.png 'No Coverage information')](https://sonarcloud.io/component_measures?id=BRIKEV_express-jsdoc-swagger&pullRequest=218) No Coverage information  
[![No Duplication information](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/NoDuplicationInfo-16px.png 'No Duplication information')](https://sonarcloud.io/component_measures?id=BRIKEV_express-jsdoc-swagger&pullRequest=218&metric=duplicated_lines_density&view=list) No Duplication information


sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!&nbsp; &nbsp; [![Quality Gate passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/passed-16px.png 'Quality Gate passed')](https://sonarcloud.io/dashboard?id=BRIKEV_express-jsdoc-swagger&pullRequest=218)

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=BRIKEV_express-jsdoc-swagger&pullRequest=218&resolved=false&types=BUG) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=BRIKEV_express-jsdoc-swagger&pullRequest=218&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=BRIKEV_express-jsdoc-swagger&pullRequest=218&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=BRIKEV_express-jsdoc-swagger&pullRequest=218&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=BRIKEV_express-jsdoc-swagger&pullRequest=218&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=BRIKEV_express-jsdoc-swagger&pullRequest=218&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=BRIKEV_express-jsdoc-swagger&pullRequest=218&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=BRIKEV_express-jsdoc-swagger&pullRequest=218&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=BRIKEV_express-jsdoc-swagger&pullRequest=218&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=BRIKEV_express-jsdoc-swagger&pullRequest=218&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=BRIKEV_express-jsdoc-swagger&pullRequest=218&resolved=false&types=CODE_SMELL) [0 Code Smells](https://sonarcloud.io/project/issues?id=BRIKEV_express-jsdoc-swagger&pullRequest=218&resolved=false&types=CODE_SMELL)

[![No Coverage information](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/NoCoverageInfo-16px.png 'No Coverage information')](https://sonarcloud.io/component_measures?id=BRIKEV_express-jsdoc-swagger&pullRequest=218) No Coverage information  
[![No Duplication information](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/NoDuplicationInfo-16px.png 'No Duplication information')](https://sonarcloud.io/component_measures?id=BRIKEV_express-jsdoc-swagger&pullRequest=218&metric=duplicated_lines_density&view=list) No Duplication information


kevinccbsg: @LonelyPrincess thanks a lot for all your help reviewing the code! and thanks @joelabrahamsson for the contribution

@all-contributors please add @joelabrahamsson for code

allcontributors[bot]: @kevinccbsg 

I've put up [a pull request](https://github.com/BRIKEV/express-jsdoc-swagger/pull/225) to add @joelabrahamsson! :tada:

Patch:
diff --git a/transforms/components/index.js b/transforms/components/index.js
index 777fd0b..ecbe66f 100644
--- a/transforms/components/index.js
+++ b/transforms/components/index.js
@@ -64,6 +64,18 @@ const getRequiredProperties = properties => (
 
 const formatRequiredProperties = requiredProperties => requiredProperties.map(getPropertyName);
 
+const addDictionaryAdditionalProperties = typedef => {
+  if (!typedef.type.expression || typedef.type.expression.name !== 'Dictionary') {
+    return {};
+  }
+
+  return {
+    additionalProperties: {
+      $ref: `#/components/schemas/${typedef.type.applications[0].name}`,
+    },
+  };
+};
+
 const parseSchema = (schema, options = {}) => {
   const typedef = getTagInfo(schema.tags, 'typedef');
   const propertyValues = getTagsInfo(schema.tags, 'property');
@@ -88,6 +100,7 @@ const parseSchema = (schema, options = {}) => {
       } : {}),
       ...(format ? { format } : {}),
       ...addEnumValues(enumValues),
+      ...addDictionaryAdditionalProperties(typedef),
       ...(jsonOptions || {}),
     },
   };


Test patch:
diff --git a/test/transforms/components/index.test.js b/test/transforms/components/index.test.js
index d5fcfa0..633a960 100644
--- a/test/transforms/components/index.test.js
+++ b/test/transforms/components/index.test.js
@@ -865,4 +865,48 @@ describe('parseComponents method', () => {
     const result = parseComponents({}, parsedJSDocs);
     expect(result).toEqual(expected);
   });
+
+  it('Should parse jsdoc component spec dictionary', () => {
+    const jsodInput = [`
+       /**
+        * Profile
+        * @typedef {object} Profile
+        *
+        * @property {string} email
+        */
+      `,
+    `
+       /**
+        * Profiles dict
+        * @typedef {Dictionary<Profile>} Profiles
+        */
+    `];
+    const expected = {
+      components: {
+        schemas: {
+          Profile: {
+            type: 'object',
+            description: 'Profile',
+            properties: {
+              email: {
+                type: 'string',
+                description: '',
+              },
+            },
+          },
+          Profiles: {
+            type: 'object',
+            description: 'Profiles dict',
+            properties: {},
+            additionalProperties: {
+              $ref: '#/components/schemas/Profile',
+            },
+          },
+        },
+      },
+    };
+    const parsedJSDocs = jsdocInfo()(jsodInput);
+    const result = parseComponents({}, parsedJSDocs);
+    expect(result).toEqual(expected);
+  });
 });