diff --git a/.github/dependabot.yml b/.github/dependabot.yml index a9aab58..aac594e 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -6,11 +6,6 @@ updates: - package-ecosystem: "github-actions" directory: "/" - # https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file#allow - allow: - - dependency-name: "actions/checkout" - - dependency-name: "actions/setup-node" - - dependency-name: "actions/upload-artifact" # https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file#scheduleinterval schedule: interval: "daily" @@ -20,7 +15,27 @@ updates: commit-message: prefix: "ci" assignees: [ "php-coder" ] - reviewers: [ "php-coder" ] labels: [ "kind/dependency-update", "area/ci" ] + # https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file#rebase-strategy + rebase-strategy: "disabled" + # https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file#open-pull-requests-limit + open-pull-requests-limit: 1 + + - package-ecosystem: "npm" + directory: "/" + # https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file#scheduleinterval + schedule: + interval: "daily" + time: "06:00" + timezone: "Asia/Novosibirsk" + # https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file#commit-message + commit-message: + prefix: "build" + # https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file#versioning-strategy + versioning-strategy: "increase" + assignees: [ "php-coder" ] + labels: [ "kind/dependency-update" ] + # https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file#rebase-strategy + rebase-strategy: "disabled" # https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file#open-pull-requests-limit open-pull-requests-limit: 1 diff --git a/.github/workflows/generate-go-app.yml b/.github/workflows/generate-go-app.yml index e88527c..b74fc9e 100644 --- a/.github/workflows/generate-go-app.yml +++ b/.github/workflows/generate-go-app.yml @@ -20,17 +20,17 @@ jobs: generate-app: name: Generate app # https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idruns-on - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 steps: - name: Clone source code - uses: actions/checkout@v4.1.3 # https://github.com/actions/checkout + uses: actions/checkout@v4.2.2 # https://github.com/actions/checkout with: # Whether to configure the token or SSH key with the local git config. Default: true persist-credentials: false - name: Setup NodeJS - uses: actions/setup-node@v4.0.2 # https://github.com/actions/setup-node + uses: actions/setup-node@v4.4.0 # https://github.com/actions/setup-node with: node-version: 18 cache: 'npm' @@ -39,7 +39,7 @@ jobs: run: npm ci --no-audit --no-fund # https://docs.npmjs.com/cli/v8/commands/npm-ci - name: Generate Golang + Chi application - run: npm run gen-go-example + run: npm run example:go - name: Check whether all modified files have been committed run: >- diff --git a/.github/workflows/generate-js-app.yml b/.github/workflows/generate-js-app.yml index 78ca035..46f3369 100644 --- a/.github/workflows/generate-js-app.yml +++ b/.github/workflows/generate-js-app.yml @@ -20,17 +20,17 @@ jobs: generate-app: name: Generate app # https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idruns-on - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 steps: - name: Clone source code - uses: actions/checkout@v4.1.3 # https://github.com/actions/checkout + uses: actions/checkout@v4.2.2 # https://github.com/actions/checkout with: # Whether to configure the token or SSH key with the local git config. Default: true persist-credentials: false - name: Setup NodeJS - uses: actions/setup-node@v4.0.2 # https://github.com/actions/setup-node + uses: actions/setup-node@v4.4.0 # https://github.com/actions/setup-node with: node-version: 18 cache: 'npm' @@ -39,7 +39,7 @@ jobs: run: npm ci --no-audit --no-fund # https://docs.npmjs.com/cli/v8/commands/npm-ci - name: Generate JavaScript + Express application - run: npm run gen-js-example + run: npm run example:js - name: Check whether all modified files have been committed run: >- diff --git a/.github/workflows/generate-python-app.yml b/.github/workflows/generate-python-app.yml index 70d86e9..cbe2bfd 100644 --- a/.github/workflows/generate-python-app.yml +++ b/.github/workflows/generate-python-app.yml @@ -20,17 +20,17 @@ jobs: generate-app: name: Generate app # https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idruns-on - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 steps: - name: Clone source code - uses: actions/checkout@v4.1.3 # https://github.com/actions/checkout + uses: actions/checkout@v4.2.2 # https://github.com/actions/checkout with: # Whether to configure the token or SSH key with the local git config. Default: true persist-credentials: false - name: Setup NodeJS - uses: actions/setup-node@v4.0.2 # https://github.com/actions/setup-node + uses: actions/setup-node@v4.4.0 # https://github.com/actions/setup-node with: node-version: 18 cache: 'npm' @@ -39,7 +39,7 @@ jobs: run: npm ci --no-audit --no-fund # https://docs.npmjs.com/cli/v8/commands/npm-ci - name: Generate Python + FastAPI application - run: npm run gen-py-example + run: npm run example:py - name: Check whether all modified files have been committed run: >- diff --git a/.github/workflows/generate-ts-app.yml b/.github/workflows/generate-ts-app.yml index 19cb1a3..a312862 100644 --- a/.github/workflows/generate-ts-app.yml +++ b/.github/workflows/generate-ts-app.yml @@ -20,17 +20,17 @@ jobs: generate-app: name: Generate app # https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idruns-on - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 steps: - name: Clone source code - uses: actions/checkout@v4.1.3 # https://github.com/actions/checkout + uses: actions/checkout@v4.2.2 # https://github.com/actions/checkout with: # Whether to configure the token or SSH key with the local git config. Default: true persist-credentials: false - name: Setup NodeJS - uses: actions/setup-node@v4.0.2 # https://github.com/actions/setup-node + uses: actions/setup-node@v4.4.0 # https://github.com/actions/setup-node with: node-version: 18 cache: 'npm' @@ -39,7 +39,7 @@ jobs: run: npm ci --no-audit --no-fund # https://docs.npmjs.com/cli/v8/commands/npm-ci - name: Generate TypeScript + Express application - run: npm run gen-ts-example + run: npm run example:ts - name: Check whether all modified files have been committed run: >- diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index b0489e0..c8206a9 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -20,7 +20,7 @@ jobs: run-integration-tests: name: Integration Tests # https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idruns-on - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 # https://docs.github.com/en/actions/using-jobs/using-a-matrix-for-your-jobs strategy: # https://docs.github.com/en/actions/using-jobs/using-a-matrix-for-your-jobs#handling-failures @@ -56,7 +56,7 @@ jobs: steps: - name: Clone source code - uses: actions/checkout@v4.1.3 # https://github.com/actions/checkout + uses: actions/checkout@v4.2.2 # https://github.com/actions/checkout with: # Whether to configure the token or SSH key with the local git config. Default: true persist-credentials: false @@ -82,24 +82,32 @@ jobs: working-directory: docker run: docker compose ps - - name: Install Hurl - run: | - DEB=hurl_4.2.0_amd64.deb - curl --location --no-progress-meter --remote-name https://github.com/Orange-OpenSource/hurl/releases/download/4.2.0/$DEB - sudo dpkg --install $DEB + - name: Install mise to install Hurl + uses: jdx/mise-action@v2.2.3 # https://github.com/jdx/mise-action + with: + version: 2025.5.14 # [default: latest] mise version to install + install: true # [default: true] run `mise install` + cache: true # [default: true] cache mise using GitHub's cache + log_level: info # [default: info] log level + working_directory: tests # [default: .] directory to run mise in + env: + # Workaround: don't install some dependencies that we don't use (go, node, python) + # See: https://github.com/jdx/mise-action/issues/183 + # https://mise.jdx.dev/configuration/settings.html#disable_tools + MISE_DISABLE_TOOLS: go,node,python - name: Show Hurl version + working-directory: tests run: hurl --version - name: Run integration tests + working-directory: tests run: >- hurl \ --error-format long \ --variable SERVER_URL=http://127.0.0.1:${{ matrix.application-port }} \ --variable skip_500_error_testing=${{ matrix.skip_500_error_testing }} \ - --test \ - tests/crud.hurl \ - tests/misc.hurl + --test - name: Show application logs if: failure() diff --git a/README.md b/README.md index f238ed8..9e0f2d0 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,8 @@ # Query To App Generates the endpoints (or a whole app) from a mapping (SQL query -> URL) ->:warning: This is a proof of concept at this moment. Until it reaches a stable version, it might (and will) break a compatibility. +> [!WARNING] +> This is a proof of concept at this moment. Until it reaches a stable version, it might (and will) break a compatibility. # How to use diff --git a/docker/docker-compose.local.yaml b/docker/docker-compose.local.yaml index 3bedabb..9d72791 100644 --- a/docker/docker-compose.local.yaml +++ b/docker/docker-compose.local.yaml @@ -7,7 +7,6 @@ # # docker compose -f docker-compose.yaml -f docker-compose.local.yaml up -d # -version: '3' services: mysql: diff --git a/docker/docker-compose.yaml b/docker/docker-compose.yaml index 80a9ad0..44d2420 100644 --- a/docker/docker-compose.yaml +++ b/docker/docker-compose.yaml @@ -4,7 +4,6 @@ # docker compose exec mysql mysql -u test -ptest test -e 'SELECT * FROM categories' # docker compose exec postgres psql -U test -c 'SELECT * FROM categories' # -version: '3' services: mysql: diff --git a/examples/go/chi/mysql/routes.go b/examples/go/chi/mysql/routes.go index 4bf7012..d468a64 100644 --- a/examples/go/chi/mysql/routes.go +++ b/examples/go/chi/mysql/routes.go @@ -137,9 +137,9 @@ func registerRoutes(r chi.Router, db *sqlx.DB) { , :name_ru , :slug , :hidden - , NOW() + , CURRENT_TIMESTAMP , :user_id - , NOW() + , CURRENT_TIMESTAMP , :user_id )`, args, @@ -235,7 +235,7 @@ func registerRoutes(r chi.Router, db *sqlx.DB) { , name_ru = :name_ru , slug = :slug , hidden = :hidden - , updated_at = NOW() + , updated_at = CURRENT_TIMESTAMP , updated_by = :user_id WHERE id = :categoryId`, args, diff --git a/examples/js/express/mysql/endpoints.yaml b/examples/js/express/mysql/endpoints.yaml index 7afd1f5..eb59f3c 100644 --- a/examples/js/express/mysql/endpoints.yaml +++ b/examples/js/express/mysql/endpoints.yaml @@ -64,9 +64,9 @@ , :b.name_ru , :b.slug , :b.hidden - , NOW() + , CURRENT_TIMESTAMP , :b.user_id - , NOW() + , CURRENT_TIMESTAMP , :b.user_id ) dto: @@ -122,7 +122,7 @@ , name_ru = :b.name_ru , slug = :b.slug , hidden = :b.hidden - , updated_at = NOW() + , updated_at = CURRENT_TIMESTAMP , updated_by = :b.user_id WHERE id = :p.categoryId dto: diff --git a/examples/js/express/mysql/routes.js b/examples/js/express/mysql/routes.js index b5362d6..5e345f4 100644 --- a/examples/js/express/mysql/routes.js +++ b/examples/js/express/mysql/routes.js @@ -78,9 +78,9 @@ const register = (app, pool) => { , :name_ru , :slug , :hidden - , NOW() + , CURRENT_TIMESTAMP , :user_id - , NOW() + , CURRENT_TIMESTAMP , :user_id )`, { @@ -152,7 +152,7 @@ const register = (app, pool) => { , name_ru = :name_ru , slug = :slug , hidden = :hidden - , updated_at = NOW() + , updated_at = CURRENT_TIMESTAMP , updated_by = :user_id WHERE id = :categoryId`, { diff --git a/examples/python/fastapi/postgres/routes.py b/examples/python/fastapi/postgres/routes.py index 0cbdbc3..7499161 100644 --- a/examples/python/fastapi/postgres/routes.py +++ b/examples/python/fastapi/postgres/routes.py @@ -117,9 +117,9 @@ def post_v1_categories(body: CreateCategoryDto, conn=Depends(db_connection)): , %(name_ru)s , %(slug)s , %(hidden)s - , NOW() + , CURRENT_TIMESTAMP , %(user_id)s - , NOW() + , CURRENT_TIMESTAMP , %(user_id)s ) """, { @@ -134,7 +134,7 @@ def post_v1_categories(body: CreateCategoryDto, conn=Depends(db_connection)): @router.get('/v1/categories/search') -def get_list_v1_categories_search(hidden, conn=Depends(db_connection)): +def get_list_v1_categories_search(hidden: bool, conn=Depends(db_connection)): try: with conn: with conn.cursor(cursor_factory=psycopg2.extras.RealDictCursor) as cur: @@ -192,7 +192,7 @@ def put_v1_categories_category_id(body: CreateCategoryDto, categoryId, conn=Depe , name_ru = %(name_ru)s , slug = %(slug)s , hidden = %(hidden)s - , updated_at = NOW() + , updated_at = CURRENT_TIMESTAMP , updated_by = %(user_id)s WHERE id = %(categoryId)s """, { diff --git a/examples/ts/express/mysql/routes.ts b/examples/ts/express/mysql/routes.ts index 36c6470..57a28a1 100644 --- a/examples/ts/express/mysql/routes.ts +++ b/examples/ts/express/mysql/routes.ts @@ -81,9 +81,9 @@ const register = (app: Express, pool: Pool) => { , :name_ru , :slug , :hidden - , NOW() + , CURRENT_TIMESTAMP , :user_id - , NOW() + , CURRENT_TIMESTAMP , :user_id )`, { @@ -155,7 +155,7 @@ const register = (app: Express, pool: Pool) => { , name_ru = :name_ru , slug = :slug , hidden = :hidden - , updated_at = NOW() + , updated_at = CURRENT_TIMESTAMP , updated_by = :user_id WHERE id = :categoryId`, { diff --git a/mise.toml b/mise.toml new file mode 100644 index 0000000..560d20e --- /dev/null +++ b/mise.toml @@ -0,0 +1,4 @@ +[tools] +go = "1.14.15" +node = "18.12.0" +python = "3.7.17" diff --git a/package-lock.json b/package-lock.json index 9ea4c5e..e231f22 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,10 +6,10 @@ "packages": { "": { "name": "query2app", - "version": "0.0.2", + "version": "0.0.3", "license": "GPL-2.0", "dependencies": { - "ejs": "~3.1.9", + "ejs": "~3.1.10", "js-yaml": "~3.14.0", "minimist": "~1.2.8", "node-sql-parser": "~3.0.4" @@ -104,9 +104,9 @@ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" }, "node_modules/ejs": { - "version": "3.1.9", - "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.9.tgz", - "integrity": "sha512-rC+QVNMJWv+MtPgkt0y+0rVEIdbtxVADApW9JXrUVlzHetgcyczP/E7DJmWJ4fJCZF2cPcBk0laWO9ZHMG3DmQ==", + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", + "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", "dependencies": { "jake": "^10.8.5" }, @@ -309,9 +309,9 @@ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" }, "ejs": { - "version": "3.1.9", - "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.9.tgz", - "integrity": "sha512-rC+QVNMJWv+MtPgkt0y+0rVEIdbtxVADApW9JXrUVlzHetgcyczP/E7DJmWJ4fJCZF2cPcBk0laWO9ZHMG3DmQ==", + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", + "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", "requires": { "jake": "^10.8.5" } diff --git a/package.json b/package.json index 5994ce7..6382885 100644 --- a/package.json +++ b/package.json @@ -26,13 +26,14 @@ "src/**" ], "scripts": { - "gen-js-example": "cd examples/js/express/mysql; ../../../../src/cli.js --lang js", - "gen-ts-example": "cd examples/ts/express/mysql; ../../../../src/cli.js --lang ts", - "gen-go-example": "cd examples/go/chi/mysql; ../../../../src/cli.js --lang go", - "gen-py-example": "cd examples/python/fastapi/postgres; ../../../../src/cli.js --lang python" + "example:all": "npm run example:js && npm run example:ts && npm run example:go && npm run example:py", + "example:js": "cd examples/js/express/mysql; ../../../../src/cli.js --lang js", + "example:ts": "cd examples/ts/express/mysql; ../../../../src/cli.js --lang ts", + "example:go": "cd examples/go/chi/mysql; ../../../../src/cli.js --lang go", + "example:py": "cd examples/python/fastapi/postgres; ../../../../src/cli.js --lang python" }, "dependencies": { - "ejs": "~3.1.9", + "ejs": "~3.1.10", "js-yaml": "~3.14.0", "minimist": "~1.2.8", "node-sql-parser": "~3.0.4" diff --git a/src/cli.js b/src/cli.js index 7439712..d445c92 100755 --- a/src/cli.js +++ b/src/cli.js @@ -120,7 +120,11 @@ const createDb = async (destDir, { lang }) => { console.log('Generate', fileName) const resultFile = path.join(destDir, fileName) - return fsPromises.copyFile(`${__dirname}/templates/${fileName}`, resultFile) + const resultedCode = await ejs.renderFile( + `${__dirname}/templates/${fileName}.ejs` + ) + + return fsPromises.writeFile(resultFile, resultedCode) } // "-- comment\nSELECT * FROM foo" => "SELECT * FROM foo" diff --git a/src/generator/PyGenerator.js b/src/generator/PyGenerator.js index 1d871e0..14f5b6f 100644 --- a/src/generator/PyGenerator.js +++ b/src/generator/PyGenerator.js @@ -5,7 +5,7 @@ module.exports = class PyGenerator { pip install -r requirements.txt to install its dependencies and export DB_NAME=db DB_USER=user DB_PASSWORD=secret - uvicorn app:app + uvicorn app:app --port 3000 afteward to run` } diff --git a/src/templates/db.py b/src/templates/db.py.ejs similarity index 100% rename from src/templates/db.py rename to src/templates/db.py.ejs diff --git a/src/templates/routes.py.ejs b/src/templates/routes.py.ejs index 2f23cdd..fe5c12d 100644 --- a/src/templates/routes.py.ejs +++ b/src/templates/routes.py.ejs @@ -85,6 +85,18 @@ function findOutType(fieldsInfo, fieldName) { return 'str' } +// "q.title" => "q.title: str" +// "q.active" => "q.active: bool" +// "q.age" => "q.age: int" +// "p.id" => "p.id" +// "b.name" => "b.name" +function appendVariableTypeToQueryParam(paramsInfo, varName) { + if (varName.startsWith('q.')) { + return `${varName}: ${findOutType(paramsInfo, stipOurPrefixes(varName))}` + } + return varName +} + // LATER: reduce duplication with routes.go.ejs function addTypes(props, fieldsInfo) { return props.map(prop => { @@ -187,7 +199,8 @@ endpoints.forEach(function(endpoint) { const pythonMethodName = generate_method_name(method.name, path) // LATER: add support for aggregated_queries (#17) - const argsFromQuery = method.query ? extractParamsFromQuery(method.query).map(stipOurPrefixes) : [] + const queryParamsInfo = method.params && method.params.query ? method.params.query : {} + const argsFromQuery = method.query ? extractParamsFromQuery(method.query).map(param => appendVariableTypeToQueryParam(queryParamsInfo, param)).map(stipOurPrefixes) : [] // define before "if", to make them available later let methodArgs diff --git a/tests/mise.toml b/tests/mise.toml new file mode 100644 index 0000000..89c7edf --- /dev/null +++ b/tests/mise.toml @@ -0,0 +1,2 @@ +[tools] +hurl = "6.1.1"