diff --git a/.gitignore b/.gitignore index d3ca5f7a..e6b7b9bf 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ node_modules flotiqApi flotiqApiBuildJs +src/codegen-ts-watch-config.json \ No newline at end of file diff --git a/README.md b/README.md index 2fdc5538..a002d018 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,6 @@ flotiq-codegen-ts This package generates Typescript Fetch API integration for your Flotiq project. - ## See it in action! ![Flotiq API accessible through TypeScript](./.images/flotiq-typescript-intellisense-2.gif) @@ -23,20 +22,23 @@ npx flotiq-codegen-ts generate ## Usage -Run `flotiq-codegen-ts`, provide your API key and wait for your Typescript code to be generated in the `flotiqApi` folder. +Run `flotiq-codegen-ts`, provide your API key and wait for your Typescript code to be generated in the `flotiqApi` +folder. Then start using it: ```javascript -import { FlotiqApi } from 'flotiqApi/src'; +import {FlotiqApi} from 'flotiqApi/src'; + const FLOTIQ_RO_API_KEY = 'YOUR_API_KEY'; const flotiq = new FlotiqApi(FLOTIQ_RO_API_KEY); - + // Use your IDE IntelliSense to work with types & api endpoints! -const eventList = await flotiq.EventAPI.list({limit:100}); +const eventList = await flotiq.EventAPI.list({limit: 100}); ``` ## Usage in JS project + If you wish to use `flotiqApi` in JS project you can use `flotiq-codegen-ts` with `--compiled-js` flag ``` @@ -47,26 +49,53 @@ Now set of compiled `d.ts` and `.js` will be automatically generated in your `fl You can now import and use the API in your project: ```javascript -import { FlotiqApi } from 'flotiqApi/index'; +import {FlotiqApi} from 'flotiqApi/index'; + const FLOTIQ_RO_API_KEY = 'YOUR_API_KEY'; const flotiq = new FlotiqApi(FLOTIQ_RO_API_KEY); - + // Use your IDE IntelliSense to work with types & api endpoints! -const eventList = await flotiq.EventAPI.list({limit:100}); +const eventList = await flotiq.EventAPI.list({limit: 100}); +``` + +## Watching for changes in your data in Flotiq + +The `flotiq-codegen-ts` tool offers a feature to continuously monitor changes in the content on your Flotiq account. It +automatically regenerates the SDK whenever changes are detected, ensuring that developers always have the most +up-to-date data models in their IDE without manual intervention. + +The `--watch` option for `flotiq-codegen-ts` ensures your SDK stays up-to-date by automatically monitoring and regenerating +based on content changes. + +If you want to see changes made in Flotiq by your content editor in your IDE, use `flotiq-codegen-ts` with `--watch` +flag + +``` +npx flotiq-codegen-ts generate --watch +``` + +or, if you want your SDK to be directly compiled to JavaScript use `flotiq-codegen-ts` with flags `--watch` +and `--compiled-js` + +``` +npx flotiq-codegen-ts generate --watch --compiled-js ``` +Now, `flotiq-codegen-ts` will check for changes in your Flotiq content every 10 seconds. If changes are detected, it will +automatically regenerate your SDK to reflect the updates. ## Developing To start developing this project, follow these steps: -1. Clone the repository `git clone git@github.com:flotiq/flotiq-codegen-ts.git` +1. Clone the repository `git clone git@github.com:flotiq/flotiq-codegen-ts.git` 2. Install dependencies `yarn install` 3. Run the project `yarn start` ## Collaborating -If you wish to talk with us about this project, feel free to hop on our [![Discord Chat](https://img.shields.io/discord/682699728454025410.svg)](https://discord.gg/FwXcHnX). +If you wish to talk with us about this project, feel free to hop on +our [![Discord Chat](https://img.shields.io/discord/682699728454025410.svg)](https://discord.gg/FwXcHnX). If you found a bug, please report it in [issues](https://github.com/flotiq/flotiq-codegen-ts). diff --git a/index.js b/index.js index 892e1eff..a6ac056b 100755 --- a/index.js +++ b/index.js @@ -8,16 +8,27 @@ const dotenv = require('dotenv'); const yargs = require('yargs'); const admZip = require('adm-zip'); const os = require('os'); -const buildToJs = require("./build_to_js"); -const cleanDuplicateImport = require("./clean_duplicate_import"); +const buildToJs = require("./src/build_to_js"); +const cleanDuplicateImport = require("./src/clean_duplicate_import"); +const loading = require('loading-cli'); const compileToJsFlag = "compiled-js"; +const watchFlag = "watch"; +const watchInterval = 10000; const CLI_GREEN = "\x1b[32m%s\x1b[0m"; const CLI_BLUE = "\x1b[36m%s\x1b[0m"; +const colorYellow = (str) => { + return `\x1b[33m${str}\x1b[0m`; +} + const getWorkingPath = () => fs.mkdtempSync(`${os.tmpdir()}${path.sep}`); +const loader = loading({ + text: colorYellow("Watching for changes ..."), color: "yellow" +}); + async function lambdaInvoke(url) { try { @@ -41,6 +52,13 @@ async function lambdaInvoke(url) { const argv = yargs(process.argv) .command("flotiq-codegen-ts generate [options]", "Generate api integration for your Flotiq project", {}) .usage("Use flotiq-codegen-ts generates typescript Fetch API integration for your Flotiq project.") + .option(watchFlag, { + description: "Listen for changes in Flotiq Api Schema, and regenerate SDK after detected change", + alias: "", + type: "boolean", + default: false, + demandOption: false, + }) .option(compileToJsFlag, { description: "Generates Fetch API integration compiled to JavaScript", alias: "", @@ -51,61 +69,13 @@ const argv = yargs(process.argv) async function confirm(msg) { - const response = await inquirer.prompt([ - { - name: 'confirmation', - type: 'confirm', - message: msg - } - ]); + const response = await inquirer.prompt([{ + name: 'confirmation', type: 'confirm', message: msg + }]); return response.confirmation; } -async function main() { - - const envfiles = [ - '.env', - '.env.local', - '.env.development', - 'env.local', - 'env.development' - ]; - const envName = "FLOTIQ_API_KEY"; - let apiKey = '' - for (const file of envfiles) { - const filepath = path.join(process.cwd(), file) - - if (fs.existsSync(filepath)) { - dotenv.config({path: filepath}) - - if (process.env[envName]) { - - query = await confirm(`${envName} found in '${file}' file. \n Do you want to use API key from ${file}?`) - if (query) { - //using API key from file - apiKey = process.env[envName]; - break; - } - } - } - } - - if (!apiKey) { - const answers = await inquirer.prompt([ - { - type: 'input', - name: 'apiKey', - message: 'Please enter your Flotiq API key:', - validate: input => !!input || 'API key cannot be empty.' - } - ]); - - apiKey = answers.apiKey; - - } - - const compileToJs = argv[compileToJsFlag]; - +async function generateSDK(apiKey, compileToJs) { try { console.log('Generating client from schema...'); @@ -144,4 +114,122 @@ async function main() { } } +async function checkForChanges(apiKey) { + const updatedAtResult = await makeRequest(apiKey, 'updatedAt'); + const createdAtResult = await makeRequest(apiKey, 'createdAt'); + + return { + updatedAt: updatedAtResult.data[0].updatedAt, createdAt: createdAtResult.data[0].createdAt, + } +} + + +async function makeRequest(apiKey, orderBy) { + const FILTERS_URL = "https://api.flotiq.com/api/v1/internal/contenttype" + + try { + const response = await axios.get(FILTERS_URL, { + params: { + order_by: orderBy, limit: 1, order_direction: 'desc' + }, headers: { + ['X-AUTH-TOKEN']: apiKey + } + }); + + return response.data; + } catch (error) { + loader.stop(); + loader.clear(); + console.error('An error occurred in listening for changes. Details: ', error.response.data); + process.exit(1); + } +} + +async function watchChanges(apiKey, compileToJs) { + const configFile = path.join(__dirname, '/src/codegen-ts-watch-config.json'); + const data = await checkForChanges(apiKey); + if (!fce.existsSync(configFile)) { + fce.createFileSync(configFile); + fce.writeJsonSync(configFile, data); + } + + const configData = fce.readJsonSync(configFile); + + if (JSON.stringify(data) === JSON.stringify(configData)) { + return; + } + + loader.stop(); + loader.succeed('Detected changes in content!') + console.log(CLI_GREEN, 'Detected changes in content!'); + + fce.writeJsonSync(configFile, data); + await generateSDK(apiKey, compileToJs); + loader.start(); +} + + +async function main() { + const envfiles = ['.env', '.env.local', '.env.development', 'env.local', 'env.development']; + const envName = "FLOTIQ_API_KEY"; + let apiKey = '' + for (const file of envfiles) { + const filepath = path.join(process.cwd(), file) + + if (fs.existsSync(filepath)) { + dotenv.config({path: filepath}) + + if (process.env[envName]) { + + query = await confirm(`${envName} found in '${file}' file. \n Do you want to use API key from ${file}?`) + if (query) { + //using API key from file + apiKey = process.env[envName]; + break; + } + } + } + } + + if (!apiKey) { + const answers = await inquirer.prompt([{ + type: 'input', + name: 'apiKey', + message: 'Please enter your Flotiq API key:', + validate: input => !!input || 'API key cannot be empty.' + }]); + + apiKey = answers.apiKey; + + } + + const compileToJs = argv[compileToJsFlag]; + const watch = argv[watchFlag]; + + if (!watch) { + await generateSDK(apiKey, compileToJs); + return; + } + + loader.start(); + await watchChanges(apiKey, compileToJs); + setInterval( + await watchChanges, + watchInterval, + apiKey, + compileToJs + ); +} + +/** + * SIGINT (ctrl + c) handler + * we have to stop the handler, otherwise it causes disappear of console cursor + */ +process.on('SIGINT', function () { + loader.stop(); + loader.clear(); + console.log(colorYellow("Application terminated !")); + process.exit(0); +}); + main(); diff --git a/package-lock.json b/package-lock.json index c05eda71..e3dac695 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,6 +15,7 @@ "dotenv": "^16.4.5", "fs-extra": "^11.2.0", "inquirer": "^8.2.0", + "loading-cli": "^1.1.2", "typescript": "^4.0", "yargs": "^15.3.1" }, @@ -420,6 +421,17 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, + "node_modules/colors-cli": { + "version": "1.0.33", + "resolved": "https://registry.npmjs.org/colors-cli/-/colors-cli-1.0.33.tgz", + "integrity": "sha512-PWGsmoJFdOB0t+BeHgmtuoRZUQucOLl5ii81NBzOOGVxlgE04muFNHlR5j8i8MKbOPELBl3243AI6lGBTj5ICQ==", + "bin": { + "colors": "bin/colors" + }, + "funding": { + "url": "https://jaywcjlove.github.io/#/sponsor" + } + }, "node_modules/combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -934,6 +946,17 @@ "graceful-fs": "^4.1.6" } }, + "node_modules/loading-cli": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/loading-cli/-/loading-cli-1.1.2.tgz", + "integrity": "sha512-M1ntfXHpdGoQxfaqKBOQPwSrTr9EIoTgj664Q9UVSbSnJvAFdribo+Ij//1jvACgrGHaTvfKoD9PG3NOxGj44g==", + "dependencies": { + "colors-cli": "^1.0.26" + }, + "funding": { + "url": "https://jaywcjlove.github.io/#/sponsor" + } + }, "node_modules/locate-path": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", diff --git a/package.json b/package.json index 43e0e7fe..00508037 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "flotiq-codegen-ts", - "version": "1.2.0", + "version": "1.2.1", "description": "CLI tool to generate API clients using Flotiq API and OpenAPI Generator", "main": "index.js", "bin": { @@ -23,6 +23,7 @@ "dotenv": "^16.4.5", "fs-extra": "^11.2.0", "inquirer": "^8.2.0", + "loading-cli": "^1.1.2", "typescript": "^4.0", "yargs": "^15.3.1" } diff --git a/build_to_js.js b/src/build_to_js.js similarity index 100% rename from build_to_js.js rename to src/build_to_js.js diff --git a/clean_duplicate_import.js b/src/clean_duplicate_import.js similarity index 100% rename from clean_duplicate_import.js rename to src/clean_duplicate_import.js diff --git a/yarn.lock b/yarn.lock index 2865e498..c6f510f6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -232,6 +232,11 @@ color-name@~1.1.4: resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== +colors-cli@^1.0.26: + version "1.0.33" + resolved "https://registry.npmjs.org/colors-cli/-/colors-cli-1.0.33.tgz" + integrity sha512-PWGsmoJFdOB0t+BeHgmtuoRZUQucOLl5ii81NBzOOGVxlgE04muFNHlR5j8i8MKbOPELBl3243AI6lGBTj5ICQ== + combined-stream@^1.0.8: version "1.0.8" resolved "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz" @@ -514,6 +519,13 @@ jsonfile@^6.0.1: optionalDependencies: graceful-fs "^4.1.6" +loading-cli@^1.1.2: + version "1.1.2" + resolved "https://registry.npmjs.org/loading-cli/-/loading-cli-1.1.2.tgz" + integrity sha512-M1ntfXHpdGoQxfaqKBOQPwSrTr9EIoTgj664Q9UVSbSnJvAFdribo+Ij//1jvACgrGHaTvfKoD9PG3NOxGj44g== + dependencies: + colors-cli "^1.0.26" + locate-path@^5.0.0: version "5.0.0" resolved "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz"