Repository: BRIKEV/express-jsdoc-swagger
Pull request #25 (2020-06-04 22:41:44)

Tags examples
### What kind of change does this PR introduce? (check at least one)
 - [ ] Bugfix
 - [x] Feature
 - [x] Test
 - [x] Docs
 - [x] Refactor
 - [ ] Build-related changes
 - [ ] Other, please describe:

### Description:

- Added tags example.
- Added more tags test.
- Refactor of tags methods.
- Added funtionality to sort tags by name.


Patch:
diff --git a/examples/README.md b/examples/README.md
index 6077e94..273f95f 100644
--- a/examples/README.md
+++ b/examples/README.md
@@ -8,6 +8,7 @@ This page will includes some examples on how to use this package.
 - request body - WIP
 - [parameters](https://github.com/BRIKEV/express-jsdoc-swagger/tree/master/examples/parameters)
 - [security](https://github.com/BRIKEV/express-jsdoc-swagger/tree/master/examples/security)
+- [tags](https://github.com/BRIKEV/express-jsdoc-swagger/tree/master/examples/tags)
 - albumAPI - WIP
 
 
diff --git a/examples/package.json b/examples/package.json
index 0247e86..dbd3d89 100644
--- a/examples/package.json
+++ b/examples/package.json
@@ -13,7 +13,8 @@
     "responses:components": "node responses/components.js",
     "responses:full-example": "node responses/full-example.js",
     "security:basic-auth": "node security/basic-auth.js",
-    "components:simple": "node components/simple.js"
+    "components:simple": "node components/simple.js",
+    "tags:simple": "node tags/simple.js"
   },
   "keywords": [],
   "author": "",
diff --git a/examples/tags/simple.js b/examples/tags/simple.js
new file mode 100644
index 0000000..b722c85
--- /dev/null
+++ b/examples/tags/simple.js
@@ -0,0 +1,61 @@
+const express = require('express');
+
+const logger = require('../utils/logger');
+const expressJSDocSwagger = require('../..');
+
+const options = {
+  info: {
+    version: '1.0.0',
+    title: 'Albums store',
+    license: {
+      name: 'MIT',
+    },
+  },
+  file: './simple.js',
+  baseDir: __dirname,
+};
+
+const app = express();
+const port = 3000;
+
+expressJSDocSwagger(app)(options);
+
+/**
+ * GET /api/v1/album
+ * @tags Album
+ */
+
+/**
+ * GET /api/v1/songs
+ * @tags Album
+ * @tags Songs
+ */
+
+/**
+ * POST /api/v1/song
+ * @summary Create new song
+ * @tags Songs - everything about songs
+ */
+
+/**
+ * PUT /api/v1/song/{songId}
+ * @param {number} songId.path.required - song id
+ * @summary Edit song
+ * @tags Songs
+ */
+
+/**
+ * DELETE /api/v1/song/{songId}
+ * @param {number} songId.path.required - song id
+ * @summary Delete song
+ * @tags Songs
+ */
+
+/**
+ * GET /api/v1/song/{songId}
+ * @param {number} songId.path.required - song id
+ * @summary Get song detail
+ * @tags Songs
+ */
+
+app.listen(port, () => logger.info(`Example app listening at http://localhost:${port}`));
diff --git a/transforms/paths/index.js b/transforms/paths/index.js
index 8ac3713..3fe5471 100644
--- a/transforms/paths/index.js
+++ b/transforms/paths/index.js
@@ -2,10 +2,13 @@ const debug = require('debug')('express-jsdoc-swagger:transforms:paths');
 const responsesGenerator = require('./responses');
 const parametersGenerator = require('./parameters');
 const requestBodyGenerator = require('./requestBody');
-const { getTagInfo, getTagsInfo } = require('../utils/tags');
+const { getTagInfo, getTagsInfo, formatDescriptionTag } = require('../utils/tags');
 const bodyMethods = require('./validRequestBodyMethods');
 
-const formatTags = (tags = []) => tags.map(({ description: name }) => name);
+const formatTags = (tags = []) => tags.map(({ description }) => {
+  const { name } = formatDescriptionTag(description);
+  return name;
+});
 
 const bodyParams = ({ name }) => name.includes('request.body');
 
diff --git a/transforms/tags/index.js b/transforms/tags/index.js
index 97cc017..e7d28e8 100644
--- a/transforms/tags/index.js
+++ b/transforms/tags/index.js
@@ -1,12 +1,12 @@
-const { getTagsInfo } = require('../utils/tags');
+const { getTagsInfo, formatDescriptionTag } = require('../utils/tags');
 const { flatArray, getIndexBy } = require('../utils/arrays');
 
 const FILTER_TAG_KEY = 'name';
 
 const formatTags = ({ tags = [] }) => {
   const infoTags = getTagsInfo(tags, 'tags');
-  return infoTags.map(tag => {
-    const [name, description] = tag.description.split(' - ');
+  return infoTags.map(({ description: tagDescription }) => {
+    const { name, description = '' } = formatDescriptionTag(tagDescription);
     return {
       name,
       description,
@@ -14,6 +14,15 @@ const formatTags = ({ tags = [] }) => {
   });
 };
 
+const sortByDescription = tags => [...tags].sort((a, b) => {
+  if (!a.description && b.description) {
+    return 1;
+  }
+  return -1;
+});
+
+const sortTagsByName = tags => [...tags].sort((a, b) => ((a.name > b.name) ? 1 : -1));
+
 const filterDuplicateTags = tags => (
   tags.filter(({ name }, i) => getIndexBy(tags, FILTER_TAG_KEY, name) === i)
 );
@@ -21,10 +30,11 @@ const filterDuplicateTags = tags => (
 const parseTags = (swaggerObject = {}, data) => {
   if (!data || !Array.isArray(data)) return { tags: [] };
   const tags = flatArray(data.map(formatTags));
-  const uniqTags = filterDuplicateTags(tags);
+  const ordererTags = sortByDescription(tags);
+  const uniqTags = filterDuplicateTags(ordererTags);
   return {
     ...swaggerObject,
-    tags: uniqTags,
+    tags: sortTagsByName(uniqTags),
   };
 };
 
diff --git a/transforms/utils/tags.js b/transforms/utils/tags.js
index 4586aa6..8341688 100644
--- a/transforms/utils/tags.js
+++ b/transforms/utils/tags.js
@@ -2,7 +2,13 @@ const getTagInfo = (tags, key) => tags.find(({ title }) => title === key);
 
 const getTagsInfo = (tags, key) => tags.filter(({ title }) => title === key);
 
+const formatDescriptionTag = desc => {
+  const [name, description] = desc.split(' - ');
+  return { name, description };
+};
+
 module.exports = {
   getTagInfo,
   getTagsInfo,
+  formatDescriptionTag,
 };


Test patch:
diff --git a/test/transforms/paths/tags.test.js b/test/transforms/paths/tags.test.js
index 90bf544..0856440 100644
--- a/test/transforms/paths/tags.test.js
+++ b/test/transforms/paths/tags.test.js
@@ -57,4 +57,33 @@ describe('Paths - tags', () => {
     const result = setPaths({}, parsedJSDocs);
     expect(result).toEqual(expected);
   });
+
+  it('Should parse jsdoc multiple tags params into array of tags without description', () => {
+    const jsodInput = [`
+      /**
+       * GET /api/v1
+       * @tags album
+       * @tags Years - tag description
+       */
+    `];
+    const expected = {
+      paths: {
+        '/api/v1': {
+          get: {
+            deprecated: false,
+            summary: '',
+            tags: [
+              'album',
+              'Years',
+            ],
+            responses: {},
+            parameters: [],
+          },
+        },
+      },
+    };
+    const parsedJSDocs = jsdocInfo()(jsodInput);
+    const result = setPaths({}, parsedJSDocs);
+    expect(result).toEqual(expected);
+  });
 });
diff --git a/test/transforms/tags/index.test.js b/test/transforms/tags/index.test.js
index 5d8f48d..e03ce98 100644
--- a/test/transforms/tags/index.test.js
+++ b/test/transforms/tags/index.test.js
@@ -102,4 +102,60 @@ describe('parseTags method', () => {
     const result = parseTags({}, parsedJSDocs);
     expect(result).toEqual(expected);
   });
+
+  it('Should return an empty string if the tag has no description', () => {
+    const jsodInput = [`
+      /**
+       * GET /api/v1/album
+       * @tags album - album tag description
+       */
+    `,
+    `
+      /**
+       * GET /api/v1/years
+       * @tags artist
+       */
+    `];
+    const expected = {
+      tags: [
+        {
+          name: 'album',
+          description: 'album tag description',
+        },
+        {
+          name: 'artist',
+          description: '',
+        },
+      ],
+    };
+    const parsedJSDocs = jsdocInfo()(jsodInput);
+    const result = parseTags({}, parsedJSDocs);
+    expect(result).toEqual(expected);
+  });
+
+  it('Should return one tag with its description', () => {
+    const jsodInput = [`
+      /**
+       * GET /api/v1/album
+       * @tags album
+       */
+    `,
+    `
+      /**
+       * GET /api/v1/years
+       * @tags album - album tag description
+       */
+    `];
+    const expected = {
+      tags: [
+        {
+          name: 'album',
+          description: 'album tag description',
+        },
+      ],
+    };
+    const parsedJSDocs = jsdocInfo()(jsodInput);
+    const result = parseTags({}, parsedJSDocs);
+    expect(result).toEqual(expected);
+  });
 });