Repository: hyperjumptech/monika
Pull request #888 (2022-09-22 05:37:36)

Feat-828/Simple Text File
# Monika Pull Request (PR)  

## What feature/issue does this PR add  
1. Create config from simple text file (#828 )

## How did you implement / how did you fix it  
1.  add feature to run config from simple text file
2. add feature to create config from simple text file

## How to test  
1. `/bin/run_dev --text ./src/components/config/__tests__/textfile`
2. `npm run test`

valid url
<img width="965" alt="image" src="https://user-images.githubusercontent.com/42835003/191893899-49841c33-15b4-4c80-8fcf-5455d73838cc.png">

invalid url
<img width="876" alt="image" src="https://user-images.githubusercontent.com/42835003/191893664-41e646e6-50c1-4268-987b-d3cad604d925.png">



codecov[bot]: # [Codecov](https://codecov.io/gh/hyperjumptech/monika/pull/888?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hyperjumptech) Report
> Merging [#888](https://codecov.io/gh/hyperjumptech/monika/pull/888?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hyperjumptech) (3c05072) into [main](https://codecov.io/gh/hyperjumptech/monika/commit/25fa23eaa0eb6e00d7f72e2c0121b6554cdbdbbd?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hyperjumptech) (25fa23e) will **increase** coverage by `0.27%`.
> The diff coverage is `82.60%`.

```diff
@@            Coverage Diff             @@
##             main     #888      +/-   ##
==========================================
+ Coverage   48.21%   48.48%   +0.27%     
==========================================
  Files          74       75       +1     
  Lines        2520     2543      +23     
  Branches      501      508       +7     
==========================================
+ Hits         1215     1233      +18     
- Misses       1123     1125       +2     
- Partials      182      185       +3     
```


| [Impacted Files](https://codecov.io/gh/hyperjumptech/monika/pull/888?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hyperjumptech) | Coverage Δ | |
|---|---|---|
| [src/index.ts](https://codecov.io/gh/hyperjumptech/monika/pull/888/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hyperjumptech#diff-c3JjL2luZGV4LnRz) | `55.13% <ø> (ø)` | |
| [src/components/config/index.ts](https://codecov.io/gh/hyperjumptech/monika/pull/888/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hyperjumptech#diff-c3JjL2NvbXBvbmVudHMvY29uZmlnL2luZGV4LnRz) | `64.82% <57.14%> (+0.07%)` | :arrow_up: |
| [src/components/config/parse-text.ts](https://codecov.io/gh/hyperjumptech/monika/pull/888/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hyperjumptech#diff-c3JjL2NvbXBvbmVudHMvY29uZmlnL3BhcnNlLXRleHQudHM=) | `92.85% <92.85%> (ø)` | |
| [src/components/config/parse.ts](https://codecov.io/gh/hyperjumptech/monika/pull/888/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hyperjumptech#diff-c3JjL2NvbXBvbmVudHMvY29uZmlnL3BhcnNlLnRz) | `54.76% <100.00%> (+2.26%)` | :arrow_up: |
| [src/plugins/validate-response/checkers/index.ts](https://codecov.io/gh/hyperjumptech/monika/pull/888/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hyperjumptech#diff-c3JjL3BsdWdpbnMvdmFsaWRhdGUtcmVzcG9uc2UvY2hlY2tlcnMvaW5kZXgudHM=) | `57.14% <0.00%> (-7.15%)` | :arrow_down: |
| [src/components/config/validate.ts](https://codecov.io/gh/hyperjumptech/monika/pull/888/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hyperjumptech#diff-c3JjL2NvbXBvbmVudHMvY29uZmlnL3ZhbGlkYXRlLnRz) | `60.58% <0.00%> (-0.36%)` | :arrow_down: |

:mega: We’re building smart automated test selection to slash your CI/CD build times. [Learn more](https://about.codecov.io/iterative-testing/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=hyperjumptech)


Patch:
diff --git a/.vscode/launch.json b/.vscode/launch.json
index 4241e0c3..31261334 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -16,10 +16,10 @@
     {
       "type": "node",
       "request": "launch",
-      "name": "Launch Run Dev",
+      "name": "Launch Text File",
       "skipFiles": ["<node_internals>/**"],
       "program": "${workspaceFolder}/bin/run_dev",
-      "args": ["-c", "./monika.yml", "--verbose"],
+      "args": ["--text", "./simple", "--create-config"],
       "sourceMap": false
     },
     {
diff --git a/src/components/config/index.ts b/src/components/config/index.ts
index 34fe72f6..9a1e12bd 100644
--- a/src/components/config/index.ts
+++ b/src/components/config/index.ts
@@ -132,7 +132,7 @@ const watchConfigFile = (
 
 const scheduleRemoteConfigFetcher = (
   url: string,
-  configType: 'monika' | 'har' | 'insomnia' | 'postman' | 'sitemap',
+  configType: 'monika' | 'har' | 'insomnia' | 'postman' | 'sitemap' | 'text',
   interval: number,
   index?: number,
   flags?: any
@@ -155,7 +155,7 @@ const scheduleRemoteConfigFetcher = (
 
 const parseConfigType = async (
   source: string,
-  configType: 'monika' | 'har' | 'insomnia' | 'postman' | 'sitemap',
+  configType: 'monika' | 'har' | 'insomnia' | 'postman' | 'sitemap' | 'text',
   flags: any,
   index?: number
 ): Promise<Partial<Config>> => {
@@ -212,7 +212,8 @@ export const setupConfig = async (flags: any): Promise<void> => {
     flags.har === undefined &&
     flags.postman === undefined &&
     flags.insomnia === undefined &&
-    flags.sitemap === undefined
+    flags.sitemap === undefined &&
+    flags.text === undefined
   ) {
     log.info(`No Monika configuration available, initializing...`)
     const configFilename = await createConfigFile(flags)
@@ -229,6 +230,8 @@ export const setupConfig = async (flags: any): Promise<void> => {
     nonDefaultConfig = await parseConfigType(flags.insomnia, 'insomnia', flags)
   } else if (flags.sitemap) {
     nonDefaultConfig = await parseConfigType(flags.sitemap, 'sitemap', flags)
+  } else if (flags.text) {
+    nonDefaultConfig = await parseConfigType(flags.text, 'text', flags)
   }
 
   if (defaultConfigs.length === 0 && nonDefaultConfig !== undefined) {
@@ -262,6 +265,11 @@ const getPathAndTypeFromFlag = (flags: any) => {
     type = 'sitemap'
   }
 
+  if (flags.text) {
+    path = flags.text
+    type = 'text'
+  }
+
   return {
     path,
     type,
@@ -271,7 +279,13 @@ const getPathAndTypeFromFlag = (flags: any) => {
 // disable warn "any" type parameter
 // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
 export const createConfig = async (flags: any): Promise<void> => {
-  if (!flags.har && !flags.postman && !flags.insomnia && !flags.sitemap) {
+  if (
+    !flags.har &&
+    !flags.postman &&
+    !flags.insomnia &&
+    !flags.sitemap &&
+    !flags.text
+  ) {
     log.info(
       'Opening Monika Configuration Generator in your default browser...'
     )
diff --git a/src/components/config/parse-text.ts b/src/components/config/parse-text.ts
new file mode 100644
index 00000000..8a333654
--- /dev/null
+++ b/src/components/config/parse-text.ts
@@ -0,0 +1,96 @@
+/**********************************************************************************
+ * MIT License                                                                    *
+ *                                                                                *
+ * Copyright (c) 2021 Hyperjump Technology                                        *
+ *                                                                                *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy   *
+ * of this software and associated documentation files (the "Software"), to deal  *
+ * in the Software without restriction, including without limitation the rights   *
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell      *
+ * copies of the Software, and to permit persons to whom the Software is          *
+ * furnished to do so, subject to the following conditions:                       *
+ *                                                                                *
+ * The above copyright notice and this permission notice shall be included in all *
+ * copies or substantial portions of the Software.                                *
+ *                                                                                *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR     *
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,       *
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE    *
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER         *
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,  *
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE  *
+ * SOFTWARE.                                                                      *
+ **********************************************************************************/
+
+/**********************************************************************************
+ * MIT License                                                                    *
+ *                                                                                *
+ * Copyright (c) 2022 Hyperjump Technology                                        *
+ *                                                                                *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy   *
+ * of this software and associated documentation files (the "Software"), to deal  *
+ * in the Software without restriction, including without limitation the rights   *
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell      *
+ * copies of the Software, and to permit persons to whom the Software is          *
+ * furnished to do so, subject to the following conditions:                       *
+ *                                                                                *
+ * The above copyright notice and this permission notice shall be included in all *
+ * copies or substantial portions of the Software.                                *
+ *                                                                                *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR     *
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,       *
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE    *
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER         *
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,  *
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE  *
+ * SOFTWARE.                                                                      *
+ **********************************************************************************/
+
+import { Config } from '../../interfaces/config'
+import { DEFAULT_CONFIG_INTERVAL } from '.'
+import { DEFAULT_THRESHOLD } from '../../looper'
+import { Probe } from '../../interfaces/probe'
+import { isValidURL } from '../../utils/is-valid-url'
+
+export const parseConfigFromText = (configString: string): Config => {
+  let probes: Probe[] = []
+  const urls = configString.split(/\r?\n/)
+  for (const [, url] of urls.entries()) {
+    if (isValidURL(url)) {
+      probes = [
+        ...probes,
+        {
+          id: url,
+          name: url,
+          requests: [
+            {
+              url: url,
+              method: 'GET',
+              timeout: 10_000,
+              body: {} as JSON,
+            },
+          ],
+          incidentThreshold: DEFAULT_THRESHOLD,
+          recoveryThreshold: DEFAULT_THRESHOLD,
+          interval: DEFAULT_CONFIG_INTERVAL,
+          alerts: [
+            {
+              assertion: 'response.status < 200 or response.status > 299',
+              message: 'HTTP Status is not 200',
+            },
+            {
+              assertion: 'response.time > 2000',
+              message: 'Response time is more than 2000ms',
+            },
+          ],
+        },
+      ]
+    }
+    // Ignoring new line from url validating checker
+    else if (url.length > 0) {
+      throw new Error(`The URL ${url} is not a valid URL`)
+    }
+  }
+
+  return { probes }
+}
diff --git a/src/components/config/parse.ts b/src/components/config/parse.ts
index f43c44a3..f5f1bd05 100644
--- a/src/components/config/parse.ts
+++ b/src/components/config/parse.ts
@@ -26,6 +26,7 @@ import { Config } from '../../interfaces/config'
 import { readFileSync } from 'fs'
 import { parseConfigFromPostman } from './parse-postman'
 import { parseConfigFromSitemap } from './parse-sitemap'
+import { parseConfigFromText } from './parse-text'
 import { parseHarFile } from './parse-har'
 import path from 'path'
 import yml from 'js-yaml'
@@ -73,6 +74,7 @@ export const parseConfig = async (
     const ext = path.extname(source)
 
     if (type === 'har') return parseHarFile(configString)
+    if (type === 'text') return parseConfigFromText(configString)
     if (type === 'postman') return parseConfigFromPostman(configString)
     if (type === 'sitemap') return parseConfigFromSitemap(configString, flag)
     if (type === 'insomnia')
diff --git a/src/index.ts b/src/index.ts
index c89014e0..b9f25aa3 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -134,7 +134,7 @@ class Monika extends Command {
 
     'create-config': Flags.boolean({
       description:
-        'Create config from HAR (-H), postman (-p), insomnia (-I), sitemap (--sitemap) export file, or open Monika Configuration Generator using default browser',
+        'Create config from HAR (-H), postman (-p), insomnia (-I), sitemap (--sitemap), textfile (--text) export file, or open Monika Configuration Generator using default browser',
     }),
 
     'config-interval': Flags.integer({
@@ -154,7 +154,7 @@ class Monika extends Command {
     sitemap: Flags.string({
       description: 'Run Monika using a Sitemap xml file.',
       multiple: false,
-      exclusive: ['har', 'insomnia', 'postman'],
+      exclusive: ['har', 'insomnia', 'postman', 'text'],
     }),
 
     'one-probe': Flags.boolean({
@@ -166,21 +166,27 @@ class Monika extends Command {
       char: 'p', // (p)ostman
       description: 'Run Monika using a Postman json file.',
       multiple: false,
-      exclusive: ['har', 'insomnia', 'sitemap'],
+      exclusive: ['har', 'insomnia', 'sitemap', 'text'],
     }),
 
     har: Flags.string({
       char: 'H', // (H)ar file to
       description: 'Run Monika using a HAR file',
       multiple: false,
-      exclusive: ['postman', 'insomnia', 'sitemap'],
+      exclusive: ['postman', 'insomnia', 'sitemap', 'text'],
     }),
 
     insomnia: Flags.string({
       char: 'I', // (I)nsomnia file to
       description: 'Run Monika using an Insomnia json/yaml file',
       multiple: false,
-      exclusive: ['har', 'postman', 'sitemap'],
+      exclusive: ['har', 'postman', 'sitemap', 'text'],
+    }),
+
+    text: Flags.string({
+      description: 'Run Monika using a Simple text file',
+      multiple: false,
+      exclusive: ['postman', 'insomnia', 'sitemap', 'har'],
     }),
 
     logs: Flags.boolean({


Test patch:
diff --git a/src/components/config/__tests__/create-config.test.ts b/src/components/config/__tests__/create-config.test.ts
index aa6b3376..99475c20 100644
--- a/src/components/config/__tests__/create-config.test.ts
+++ b/src/components/config/__tests__/create-config.test.ts
@@ -74,6 +74,26 @@ describe('Har config', () => {
   })
 })
 
+describe('Text config', () => {
+  describe('Create config from text file', () => {
+    it('should create config from text file', async () => {
+      const flags = {
+        text: './src/components/config/__tests__/textfile',
+        output: 'monika.textfile.yml',
+      }
+      await createConfig(flags)
+      expect(fs.lstatSync('monika.textfile.yml').isFile()).to.be.true
+
+      const generated = fs.readFileSync('monika.textfile.yml', 'utf-8')
+      const expected = fs.readFileSync(
+        './src/components/config/__tests__/expected.textfile.yml',
+        'utf-8'
+      )
+      expect(_.isEqual(generated, expected)).to.be.true
+    })
+  })
+})
+
 describe('Sitemap config', () => {
   describe('Create config from xml file', () => {
     it('should create config from xml file', async () => {
diff --git a/src/components/config/__tests__/expected.textfile.yml b/src/components/config/__tests__/expected.textfile.yml
new file mode 100644
index 00000000..3e8b99fa
--- /dev/null
+++ b/src/components/config/__tests__/expected.textfile.yml
@@ -0,0 +1,34 @@
+probes:
+  - id: https://monika.hyperjump.tech
+    name: https://monika.hyperjump.tech
+    requests:
+      - url: https://monika.hyperjump.tech
+        method: GET
+        timeout: 10000
+        body: {}
+    incidentThreshold: 5
+    recoveryThreshold: 5
+    interval: 900
+    alerts:
+      - assertion: response.status < 200 or response.status > 299
+        message: HTTP Status is not 200
+      - assertion: response.time > 2000
+        message: Response time is more than 2000ms
+  - id: https://github.com
+    name: https://github.com
+    requests:
+      - url: https://github.com
+        method: GET
+        timeout: 10000
+        body: {}
+    incidentThreshold: 5
+    recoveryThreshold: 5
+    interval: 900
+    alerts:
+      - assertion: response.status < 200 or response.status > 299
+        message: HTTP Status is not 200
+      - assertion: response.time > 2000
+        message: Response time is more than 2000ms
+notifications:
+  - id: default
+    type: desktop
diff --git a/src/components/config/__tests__/textfile b/src/components/config/__tests__/textfile
new file mode 100644
index 00000000..94c138a2
--- /dev/null
+++ b/src/components/config/__tests__/textfile
@@ -0,0 +1,2 @@
+https://monika.hyperjump.tech
+https://github.com