insforge 0.3.3 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +20 -0
- package/.dockerignore +60 -57
- package/.env.example +84 -49
- package/.github/ISSUE_TEMPLATE/bug_report.yml +36 -83
- package/.github/ISSUE_TEMPLATE/config.yml +11 -11
- package/.github/ISSUE_TEMPLATE/feature_request.yml +26 -79
- package/.github/PULL_REQUEST_TEMPLATE.md +7 -0
- package/.github/copilot-instructions.md +146 -146
- package/.github/workflows/build-image.yml +66 -65
- package/.github/workflows/ci-premerge-check.yml +23 -23
- package/.github/workflows/e2e.yml +63 -0
- package/.github/workflows/lint-and-format.yml +32 -32
- package/.prettierignore +64 -64
- package/CHANGELOG.md +44 -3
- package/CLAUDE_PLUGIN.md +104 -0
- package/CODE_OF_CONDUCT.md +128 -0
- package/CONTRIBUTING.md +125 -125
- package/Dockerfile +30 -27
- package/GITHUB_OAUTH_SETUP.md +49 -49
- package/GOOGLE_OAUTH_SETUP.md +148 -148
- package/LICENSE +201 -201
- package/README.md +182 -134
- package/assets/Dark.svg +23 -23
- package/assets/mcpInstallv2.png +0 -0
- package/assets/sampleResponse.png +0 -0
- package/auth/index.html +13 -0
- package/auth/package.json +28 -0
- package/auth/public/favicon.ico +0 -0
- package/auth/src/App.tsx +33 -0
- package/auth/src/components/ErrorCard.tsx +37 -0
- package/auth/src/components/Layout.tsx +13 -0
- package/auth/src/index.css +19 -0
- package/auth/src/lib/broadcastService.ts +117 -0
- package/auth/src/lib/utils.ts +11 -0
- package/auth/src/main.tsx +22 -0
- package/auth/src/pages/ForgotPasswordPage.tsx +11 -0
- package/auth/src/pages/ResetPasswordPage.tsx +11 -0
- package/auth/src/pages/SignInPage.tsx +60 -0
- package/auth/src/pages/SignUpPage.tsx +60 -0
- package/auth/src/pages/VerifyEmailPage.tsx +20 -0
- package/auth/src/vite-env.d.ts +10 -0
- package/auth/tsconfig.json +32 -0
- package/auth/tsconfig.node.json +11 -0
- package/auth/vite.config.ts +25 -0
- package/backend/package.json +78 -75
- package/backend/src/api/{middleware → middlewares}/auth.ts +8 -9
- package/backend/src/api/middlewares/rate-limiters.ts +127 -0
- package/backend/src/api/routes/{ai.ts → ai/index.routes.ts} +22 -26
- package/backend/src/api/routes/auth/index.routes.ts +667 -0
- package/backend/src/api/routes/auth/oauth.routes.ts +473 -0
- package/backend/src/api/routes/{database.advance.ts → database/advance.routes.ts} +128 -65
- package/backend/src/api/routes/database/index.routes.ts +90 -0
- package/backend/src/api/routes/{database.records.ts → database/records.routes.ts} +26 -12
- package/backend/src/api/routes/{database.tables.ts → database/tables.routes.ts} +6 -23
- package/backend/src/api/routes/docs/index.routes.ts +75 -0
- package/backend/src/api/routes/email/index.routes.ts +35 -0
- package/backend/src/api/routes/functions/index.routes.ts +194 -0
- package/backend/src/api/routes/{logs.ts → logs/index.routes.ts} +25 -30
- package/backend/src/api/routes/{metadata.ts → metadata/index.routes.ts} +33 -31
- package/backend/src/api/routes/realtime/channels.routes.ts +81 -0
- package/backend/src/api/routes/realtime/index.routes.ts +12 -0
- package/backend/src/api/routes/realtime/messages.routes.ts +48 -0
- package/backend/src/api/routes/realtime/permissions.routes.ts +19 -0
- package/backend/src/api/routes/{secrets.ts → secrets/index.routes.ts} +27 -22
- package/backend/src/api/routes/{storage.ts → storage/index.routes.ts} +48 -61
- package/backend/src/api/routes/usage/index.routes.ts +91 -0
- package/backend/src/infra/config/app.config.ts +51 -0
- package/backend/src/infra/database/database.manager.ts +182 -0
- package/backend/{migrations → src/infra/database/migrations}/000_create-base-tables.sql +141 -141
- package/backend/{migrations → src/infra/database/migrations}/001_create-helper-functions.sql +40 -40
- package/backend/{migrations → src/infra/database/migrations}/002_rename-auth-tables.sql +29 -29
- package/backend/{migrations → src/infra/database/migrations}/003_create-users-table.sql +55 -55
- package/backend/{migrations → src/infra/database/migrations}/004_add-reload-postgrest-func.sql +23 -23
- package/backend/{migrations → src/infra/database/migrations}/005_enable-project-admin-modify-users.sql +29 -29
- package/backend/{migrations → src/infra/database/migrations}/006_modify-ai-usage-table.sql +24 -24
- package/backend/{migrations → src/infra/database/migrations}/007_drop-metadata-table.sql +1 -1
- package/backend/{migrations → src/infra/database/migrations}/008_add-system-tables.sql +76 -76
- package/backend/{migrations → src/infra/database/migrations}/009_add-function-secrets.sql +23 -23
- package/backend/{migrations → src/infra/database/migrations}/010_modify-ai-config-modalities.sql +93 -93
- package/backend/{migrations → src/infra/database/migrations}/011_refactor-secrets-table.sql +15 -15
- package/backend/{migrations → src/infra/database/migrations}/012_add-storage-uploaded-by.sql +7 -7
- package/backend/src/infra/database/migrations/013_create-auth-schema-functions.sql +44 -0
- package/backend/src/infra/database/migrations/014_add-updated-at-trigger-user-table.sql +8 -0
- package/backend/src/infra/database/migrations/015_create-auth-config-and-email-otp-tables.sql +60 -0
- package/backend/src/infra/database/migrations/016_update-auth-config-and-email-otp.sql +24 -0
- package/backend/src/infra/database/migrations/017_create-realtime-schema.sql +233 -0
- package/backend/src/infra/realtime/realtime.manager.ts +246 -0
- package/backend/src/infra/realtime/webhook-sender.ts +82 -0
- package/backend/src/{core/secrets/encryption.ts → infra/security/encryption.manager.ts} +3 -2
- package/backend/src/infra/security/token.manager.ts +219 -0
- package/backend/src/infra/socket/socket.manager.ts +522 -0
- package/backend/src/providers/ai/openrouter.provider.ts +380 -0
- package/backend/src/providers/email/base.provider.ts +38 -0
- package/backend/src/providers/email/cloud.provider.ts +271 -0
- package/backend/src/{core/logs/providers → providers/logs}/base.provider.ts +11 -11
- package/backend/src/{core/logs/providers → providers/logs}/cloudwatch.provider.ts +61 -38
- package/backend/src/providers/logs/local.provider.ts +185 -0
- package/backend/src/providers/oauth/apple.provider.ts +266 -0
- package/backend/src/providers/oauth/base.provider.ts +29 -0
- package/backend/src/providers/oauth/discord.provider.ts +195 -0
- package/backend/src/providers/oauth/facebook.provider.ts +194 -0
- package/backend/src/providers/oauth/github.provider.ts +208 -0
- package/backend/src/providers/oauth/google.provider.ts +249 -0
- package/backend/src/providers/oauth/index.ts +8 -0
- package/backend/src/providers/oauth/linkedin.provider.ts +240 -0
- package/backend/src/providers/oauth/microsoft.provider.ts +169 -0
- package/backend/src/providers/oauth/x.provider.ts +202 -0
- package/backend/src/providers/storage/base.provider.ts +29 -0
- package/backend/src/providers/storage/local.provider.ts +103 -0
- package/backend/src/providers/storage/s3.provider.ts +313 -0
- package/backend/src/server.ts +317 -288
- package/backend/src/{core/ai/config.ts → services/ai/ai-config.service.ts} +19 -24
- package/backend/src/services/ai/ai-model.service.ts +60 -0
- package/backend/src/{core/ai/usage.ts → services/ai/ai-usage.service.ts} +28 -35
- package/backend/src/{core/ai/chat.ts → services/ai/chat-completion.service.ts} +37 -24
- package/backend/src/services/ai/helpers.ts +64 -0
- package/backend/src/{core/ai/image.ts → services/ai/image-generation.service.ts} +17 -19
- package/backend/src/services/ai/index.ts +13 -0
- package/backend/src/services/auth/auth-config.service.ts +250 -0
- package/backend/src/services/auth/auth-otp.service.ts +424 -0
- package/backend/src/services/auth/auth.service.ts +1150 -0
- package/backend/src/services/auth/index.ts +4 -0
- package/backend/src/{core/auth/oauth.ts → services/auth/oauth-config.service.ts} +106 -52
- package/backend/src/{core/database/advance.ts → services/database/database-advance.service.ts} +97 -131
- package/backend/src/services/database/database-table.service.ts +802 -0
- package/backend/src/services/database/database.service.ts +127 -0
- package/backend/src/services/email/email.service.ts +73 -0
- package/backend/src/{core/functions/functions.ts → services/functions/function.service.ts} +95 -88
- package/backend/src/{core/logs/audit.ts → services/logs/audit.service.ts} +92 -75
- package/backend/src/services/logs/log.service.ts +73 -0
- package/backend/src/services/realtime/index.ts +3 -0
- package/backend/src/services/realtime/realtime-auth.service.ts +104 -0
- package/backend/src/services/realtime/realtime-channel.service.ts +237 -0
- package/backend/src/services/realtime/realtime-message.service.ts +260 -0
- package/backend/src/{core/secrets/secrets.ts → services/secrets/secret.service.ts} +48 -66
- package/backend/src/services/storage/storage.service.ts +617 -0
- package/backend/src/services/usage/usage.service.ts +149 -0
- package/backend/src/types/auth.ts +77 -2
- package/backend/src/types/email.ts +8 -0
- package/backend/src/types/error-constants.ts +4 -0
- package/backend/src/types/logs.ts +0 -29
- package/backend/src/types/realtime.ts +18 -0
- package/backend/src/{core/socket/types.ts → types/socket.ts} +11 -36
- package/backend/src/utils/cookies.ts +35 -0
- package/backend/src/utils/environment.ts +9 -3
- package/backend/src/utils/logger.ts +20 -2
- package/backend/src/utils/s3-config-loader.ts +64 -0
- package/backend/src/utils/seed.ts +301 -205
- package/backend/src/utils/sql-parser.ts +91 -1
- package/backend/src/utils/utils.ts +114 -0
- package/backend/src/utils/validations.ts +40 -4
- package/backend/tests/README.md +133 -133
- package/backend/tests/cleanup-all-test-data.sh +230 -230
- package/backend/tests/cloud/test-s3-multitenant.sh +131 -131
- package/backend/tests/local/comprehensive-curl-tests.sh +155 -155
- package/backend/tests/local/test-ai-config.sh +129 -0
- package/backend/tests/local/test-ai-usage.sh +80 -0
- package/backend/tests/local/test-auth-router.sh +143 -143
- package/backend/tests/local/test-database-router.sh +222 -222
- package/backend/tests/local/test-e2e.sh +240 -240
- package/backend/tests/local/test-fk-errors.sh +96 -96
- package/backend/tests/local/test-functions.sh +123 -0
- package/backend/tests/local/test-id-field.sh +200 -200
- package/backend/tests/local/test-logs.sh +132 -0
- package/backend/tests/local/test-public-bucket.sh +264 -264
- package/backend/tests/local/test-secrets.sh +249 -247
- package/backend/tests/local/test-serverless-functions.sh.disabled +325 -325
- package/backend/tests/local/test-traditional-rest.sh +208 -208
- package/backend/tests/manual/README.md +50 -50
- package/backend/tests/manual/create-large-table-simple.sql +10 -10
- package/backend/tests/manual/seed-large-table.sql +100 -100
- package/backend/tests/manual/setup-large-table-extras.sql +33 -33
- package/backend/tests/manual/test-bulk-upsert.sh +409 -409
- package/backend/tests/manual/test-database-advance.sh +296 -296
- package/backend/tests/manual/test-postgrest-stability.sh +191 -191
- package/backend/tests/manual/test-rawsql-export-import.sh +411 -411
- package/backend/tests/manual/test-rawsql-modes.sh +244 -0
- package/backend/tests/manual/test-universal-storage.sh +263 -263
- package/backend/tests/manual/test-users.sql +17 -17
- package/backend/tests/run-all-tests.sh +139 -139
- package/backend/tests/setup.ts +0 -0
- package/backend/tests/test-config.sh +338 -302
- package/backend/tests/unit/analyze-query.test.ts +697 -0
- package/backend/tests/unit/cloud-token.test.ts +48 -0
- package/backend/tests/unit/constant.test.ts +8 -0
- package/backend/tests/unit/email.test.ts +372 -0
- package/backend/tests/unit/environment.test.ts +59 -0
- package/backend/tests/unit/helpers.test.ts +63 -0
- package/backend/tests/unit/logger.test.ts +22 -0
- package/backend/tests/unit/rate-limit.test.ts +154 -0
- package/backend/tests/unit/response.test.ts +58 -0
- package/backend/tests/unit/sql-parser.test.ts +74 -0
- package/backend/tests/unit/uuid.test.ts +21 -0
- package/backend/tests/unit/validations.test.ts +80 -0
- package/backend/tsconfig.json +22 -22
- package/backend/vitest.config.ts +11 -0
- package/claude-plugin/.claude-plugin/plugin.json +24 -0
- package/claude-plugin/README.md +133 -0
- package/claude-plugin/skills/insforge-schema-patterns/SKILL.md +270 -0
- package/docker-compose.prod.yml +204 -144
- package/docker-compose.yml +232 -167
- package/docker-init/db/db-init.sql +97 -125
- package/docker-init/db/jwt.sql +5 -5
- package/docker-init/db/postgresql.conf +16 -16
- package/docker-init/logs/vector.yml +236 -0
- package/docs/README.md +44 -0
- package/docs/agent-docs/real-time.md +269 -0
- package/docs/changelog.mdx +119 -0
- package/docs/core-concepts/ai/architecture.mdx +373 -0
- package/docs/core-concepts/ai/sdk.mdx +213 -0
- package/docs/core-concepts/authentication/architecture.mdx +278 -0
- package/docs/core-concepts/authentication/sdk.mdx +414 -0
- package/docs/core-concepts/authentication/ui-components/customization.mdx +529 -0
- package/docs/core-concepts/authentication/ui-components/nextjs.mdx +221 -0
- package/docs/core-concepts/authentication/ui-components/react-router.mdx +184 -0
- package/docs/core-concepts/authentication/ui-components/react.mdx +129 -0
- package/docs/core-concepts/database/architecture.mdx +256 -0
- package/docs/core-concepts/database/sdk.mdx +382 -0
- package/docs/core-concepts/email/architecture.mdx +101 -0
- package/docs/core-concepts/email/sdk.mdx +53 -0
- package/docs/core-concepts/functions/architecture.mdx +105 -0
- package/docs/core-concepts/functions/sdk.mdx +184 -0
- package/docs/core-concepts/realtime/architecture.mdx +446 -0
- package/docs/core-concepts/realtime/sdk.mdx +409 -0
- package/docs/core-concepts/storage/architecture.mdx +243 -0
- package/docs/core-concepts/storage/sdk.mdx +253 -0
- package/docs/deployment/README.md +94 -0
- package/docs/deployment/deploy-to-aws-ec2.md +565 -0
- package/docs/deployment/deploy-to-azure-virtual-machines.md +313 -0
- package/docs/deployment/deploy-to-google-cloud-compute-engine.md +613 -0
- package/docs/deployment/deploy-to-render.md +441 -0
- package/docs/deprecated/insforge-auth-api.md +214 -214
- package/docs/deprecated/insforge-auth-sdk.md +99 -99
- package/docs/deprecated/insforge-db-api.md +358 -358
- package/docs/deprecated/insforge-db-sdk.md +139 -139
- package/docs/deprecated/insforge-debug-sdk.md +156 -156
- package/docs/deprecated/insforge-debug.md +64 -64
- package/docs/deprecated/insforge-instructions.md +123 -123
- package/docs/deprecated/insforge-project.md +117 -117
- package/docs/deprecated/insforge-storage-api.md +278 -278
- package/docs/deprecated/insforge-storage-sdk.md +158 -158
- package/docs/docs.json +232 -0
- package/docs/examples/framework-guides/nextjs.mdx +131 -0
- package/docs/examples/framework-guides/nuxt.mdx +165 -0
- package/docs/examples/framework-guides/react.mdx +165 -0
- package/docs/examples/framework-guides/svelte.mdx +153 -0
- package/docs/examples/framework-guides/vue.mdx +159 -0
- package/docs/examples/overview.mdx +67 -0
- package/docs/favicon.svg +19 -0
- package/docs/images/changelog/dec-2025/ai-integration.png +0 -0
- package/docs/images/changelog/dec-2025/ai-models.webp +0 -0
- package/docs/images/changelog/dec-2025/alipay-payment.webp +0 -0
- package/docs/images/changelog/dec-2025/apple-login.jpg +0 -0
- package/docs/images/changelog/dec-2025/mcp-installer.png +0 -0
- package/docs/images/changelog/dec-2025/realtime-module.jpg +0 -0
- package/docs/images/changelog/nov-2025/auth-components.webp +0 -0
- package/docs/images/changelog/nov-2025/database-metadata.webp +0 -0
- package/docs/images/changelog/nov-2025/quickstart-prompts.webp +0 -0
- package/docs/images/changelog/nov-2025/sql-editor.webp +0 -0
- package/docs/images/changelog/nov-2025/usage-page.webp +0 -0
- package/docs/images/changelog/october-2025/csv-upload.webp +0 -0
- package/docs/images/changelog/october-2025/logs-feature.webp +0 -0
- package/docs/images/changelog/october-2025/oauth-providers.webp +0 -0
- package/docs/images/checks-passed.png +0 -0
- package/docs/images/dashboard-connect-expanded.png +0 -0
- package/docs/images/dashboard-connect.png +0 -0
- package/docs/images/hero-dark.png +0 -0
- package/docs/images/hero-light.png +0 -0
- package/docs/images/icons/ai.svg +4 -0
- package/docs/images/icons/auth.svg +1 -0
- package/docs/images/icons/database.svg +1 -0
- package/docs/images/icons/function.svg +1 -0
- package/docs/images/icons/storage.svg +1 -0
- package/docs/images/logos/nextjs.svg +4 -0
- package/docs/images/logos/nuxt.svg +4 -0
- package/docs/images/logos/react.svg +5 -0
- package/docs/images/logos/svelte.svg +4 -0
- package/docs/images/logos/vue.svg +5 -0
- package/docs/images/mcp-install.png +0 -0
- package/docs/images/onboarding-mcp.png +0 -0
- package/docs/insforge-instructions-sdk.md +89 -407
- package/docs/introduction.mdx +45 -0
- package/docs/logo/dark.svg +22 -0
- package/docs/logo/light.svg +20 -0
- package/docs/partnership.mdx +652 -0
- package/docs/quickstart.mdx +83 -0
- package/docs/showcase/2048-arena.png +0 -0
- package/docs/showcase/framegen-cloud.png +0 -0
- package/docs/showcase/line-connect-race.png +0 -0
- package/docs/showcase/moment-vibe.png +0 -0
- package/docs/showcase/national-flags.png +0 -0
- package/docs/showcase/pokemon-vibe.png +0 -0
- package/docs/showcase/pure-browse-buy.png +0 -0
- package/docs/showcase.mdx +52 -0
- package/docs/snippets/sdk-installation.mdx +22 -0
- package/docs/snippets/service-icons.mdx +27 -0
- package/eslint.config.js +10 -3
- package/examples/oauth/frontend-oauth-example.html +250 -250
- package/examples/response-examples.md +443 -443
- package/frontend/components.json +17 -17
- package/frontend/package.json +69 -63
- package/frontend/src/App.tsx +13 -82
- package/frontend/src/assets/icons/checkbox_checked.svg +6 -6
- package/frontend/src/assets/icons/checkbox_undetermined.svg +6 -6
- package/frontend/src/assets/icons/checked.svg +3 -3
- package/frontend/src/assets/icons/connected.svg +3 -0
- package/frontend/src/assets/icons/error.svg +3 -3
- package/frontend/src/assets/icons/loader.svg +9 -0
- package/frontend/src/assets/icons/pencil.svg +4 -4
- package/frontend/src/assets/icons/refresh.svg +4 -4
- package/frontend/src/assets/icons/step_active.svg +3 -3
- package/frontend/src/assets/icons/step_inactive.svg +11 -11
- package/frontend/src/assets/icons/warning.svg +3 -3
- package/frontend/src/assets/logos/apple.svg +4 -0
- package/frontend/src/assets/logos/claude_code.svg +3 -3
- package/frontend/src/assets/logos/cline.svg +6 -6
- package/frontend/src/assets/logos/cursor.svg +20 -20
- package/frontend/src/assets/logos/discord.svg +8 -8
- package/frontend/src/assets/logos/facebook.svg +3 -0
- package/frontend/src/assets/logos/gemini.svg +19 -19
- package/frontend/src/assets/logos/github.svg +5 -5
- package/frontend/src/assets/logos/google.svg +13 -13
- package/frontend/src/assets/logos/grok.svg +10 -10
- package/frontend/src/assets/logos/insforge_dark.svg +15 -15
- package/frontend/src/assets/logos/insforge_light.svg +15 -15
- package/frontend/src/assets/logos/instagram.svg +2 -0
- package/frontend/src/assets/logos/linkedin.svg +3 -0
- package/frontend/src/assets/logos/microsoft.svg +1 -0
- package/frontend/src/assets/logos/openai.svg +10 -10
- package/frontend/src/assets/logos/roo_code.svg +9 -9
- package/frontend/src/assets/logos/spotify.svg +17 -0
- package/frontend/src/assets/logos/tiktok.svg +6 -0
- package/frontend/src/assets/logos/trae.svg +3 -3
- package/frontend/src/assets/logos/windsurf.svg +10 -10
- package/frontend/src/assets/logos/x.svg +3 -0
- package/frontend/src/components/Checkbox.tsx +27 -29
- package/frontend/src/components/CodeBlock.tsx +55 -2
- package/frontend/src/components/CodeEditor.tsx +92 -0
- package/frontend/src/components/ConfirmDialog.tsx +1 -1
- package/frontend/src/components/ConnectCTA.tsx +38 -0
- package/frontend/src/components/CopyButton.tsx +52 -15
- package/frontend/src/components/ErrorState.tsx +1 -2
- package/frontend/src/components/FeatureSidebar.tsx +6 -6
- package/frontend/src/components/FeatureSidebarItem.tsx +2 -2
- package/frontend/src/components/JsonHighlight.tsx +21 -9
- package/frontend/src/components/ProjectInfoModal.tsx +128 -0
- package/frontend/src/components/PromptDialog.tsx +1 -4
- package/frontend/src/components/SearchInput.tsx +1 -2
- package/frontend/src/components/Stepper.tsx +53 -0
- package/frontend/src/components/ThemeToggle.tsx +3 -3
- package/frontend/src/components/datagrid/DataGrid.tsx +25 -32
- package/frontend/src/components/datagrid/cell-editors/DateCellEditor.tsx +1 -2
- package/frontend/src/components/datagrid/cell-editors/JsonCellEditor.tsx +2 -4
- package/frontend/src/components/datagrid/index.ts +23 -0
- package/frontend/src/components/index.ts +23 -30
- package/frontend/src/components/layout/AppHeader.tsx +131 -91
- package/frontend/src/components/layout/AppSidebar.tsx +80 -170
- package/frontend/src/components/layout/Layout.tsx +12 -23
- package/frontend/src/components/layout/PrimaryMenu.tsx +187 -0
- package/frontend/src/components/layout/SecondaryMenu.tsx +70 -0
- package/frontend/src/components/layout/index.ts +5 -0
- package/frontend/src/components/radix/Tooltip.tsx +24 -13
- package/frontend/src/components/radix/index.ts +22 -0
- package/frontend/src/features/ai/components/AIConfigCard.tsx +129 -83
- package/frontend/src/features/ai/components/AIEmptyState.tsx +12 -7
- package/frontend/src/features/ai/components/ModalityFilterSidebar.tsx +101 -0
- package/frontend/src/features/ai/components/ModelSelectionDialog.tsx +135 -0
- package/frontend/src/features/ai/components/ModelSelectionGrid.tsx +51 -0
- package/frontend/src/features/ai/components/SystemPromptDialog.tsx +118 -0
- package/frontend/src/features/ai/components/index.ts +6 -0
- package/frontend/src/features/ai/helpers.ts +57 -71
- package/frontend/src/features/ai/hooks/useAIConfigs.ts +39 -113
- package/frontend/src/features/ai/hooks/useAIUsage.ts +0 -2
- package/frontend/src/features/ai/pages/AIPage.tsx +166 -0
- package/frontend/src/features/ai/services/ai.service.ts +5 -5
- package/frontend/src/features/auth/components/AuthPreview.tsx +96 -0
- package/frontend/src/features/auth/components/OAuthConfigDialog.tsx +54 -30
- package/frontend/src/features/auth/components/UserFormDialog.tsx +13 -6
- package/frontend/src/features/auth/components/UsersDataGrid.tsx +50 -14
- package/frontend/src/features/auth/components/index.ts +5 -0
- package/frontend/src/features/auth/helpers.tsx +208 -0
- package/frontend/src/features/auth/hooks/useAnonToken.ts +30 -0
- package/frontend/src/features/auth/hooks/useAuthConfig.ts +48 -0
- package/frontend/src/features/auth/hooks/useOAuthConfig.ts +14 -10
- package/frontend/src/features/auth/hooks/useUsers.ts +43 -5
- package/frontend/src/features/auth/index.ts +3 -2
- package/frontend/src/features/auth/pages/AuthMethodsPage.tsx +275 -0
- package/frontend/src/features/auth/pages/ConfigurationPage.tsx +395 -0
- package/frontend/src/features/auth/pages/UsersPage.tsx +257 -0
- package/frontend/src/features/auth/services/anonToken.service.ts +11 -0
- package/frontend/src/features/auth/services/config.service.ts +19 -0
- package/frontend/src/features/auth/services/{oauth.service.ts → oauth-config.service.ts} +4 -4
- package/frontend/src/features/auth/services/{auth.service.ts → user.service.ts} +7 -53
- package/frontend/src/features/dashboard/components/ConnectionSuccessBanner.tsx +35 -0
- package/frontend/src/features/dashboard/components/PromptCard.tsx +21 -0
- package/frontend/src/features/dashboard/components/PromptDialog.tsx +103 -0
- package/frontend/src/features/dashboard/components/StatsCard.tsx +50 -0
- package/frontend/src/features/dashboard/components/index.ts +4 -0
- package/frontend/src/features/dashboard/pages/DashboardPage.tsx +212 -0
- package/frontend/src/features/dashboard/prompts/ai-chatbot.ts +13 -0
- package/frontend/src/features/dashboard/prompts/crm-system.ts +13 -0
- package/frontend/src/features/dashboard/prompts/ecommerce-platform.ts +12 -0
- package/frontend/src/features/dashboard/prompts/index.ts +31 -0
- package/frontend/src/features/dashboard/prompts/instagram-clone.ts +11 -0
- package/frontend/src/features/dashboard/prompts/notion-clone.ts +14 -0
- package/frontend/src/features/dashboard/prompts/reddit-clone.ts +12 -0
- package/frontend/src/features/database/components/DatabaseDataGrid.tsx +48 -17
- package/frontend/src/features/database/components/ForeignKeyCell.tsx +15 -34
- package/frontend/src/features/database/components/ForeignKeyPopover.tsx +19 -20
- package/frontend/src/features/database/components/LinkRecordModal.tsx +120 -125
- package/frontend/src/features/database/components/RecordFormDialog.tsx +22 -33
- package/frontend/src/features/database/components/RecordFormField.tsx +45 -47
- package/frontend/src/features/database/components/SQLModal.tsx +75 -0
- package/frontend/src/features/database/components/TableEmptyState.tsx +6 -5
- package/frontend/src/features/database/components/TableForm.tsx +28 -19
- package/frontend/src/features/database/components/TableFormColumn.tsx +2 -3
- package/frontend/src/features/database/components/TableSidebar.tsx +1 -1
- package/frontend/src/features/database/components/TablesEmptyState.tsx +48 -0
- package/frontend/src/features/database/components/TemplateCard.tsx +37 -0
- package/frontend/src/features/database/components/TemplatePreview.tsx +92 -0
- package/frontend/src/features/database/components/index.ts +19 -0
- package/frontend/src/features/database/constants.ts +28 -2
- package/frontend/src/features/database/contexts/SQLEditorContext.tsx +188 -0
- package/frontend/src/features/database/helpers.ts +2 -2
- package/frontend/src/features/database/hooks/useCSVImport.ts +29 -0
- package/frontend/src/features/database/hooks/useDatabase.ts +66 -0
- package/frontend/src/features/database/hooks/useRawSQL.ts +55 -0
- package/frontend/src/features/database/hooks/useRecords.ts +139 -0
- package/frontend/src/features/database/hooks/useTables.ts +135 -0
- package/frontend/src/features/database/index.ts +7 -1
- package/frontend/src/features/database/pages/FunctionsPage.tsx +203 -0
- package/frontend/src/features/database/pages/IndexesPage.tsx +228 -0
- package/frontend/src/features/database/pages/PoliciesPage.tsx +237 -0
- package/frontend/src/features/database/pages/SQLEditorPage.tsx +382 -0
- package/frontend/src/features/database/{page/DatabasePage.tsx → pages/TablesPage.tsx} +168 -209
- package/frontend/src/features/database/pages/TemplatesPage.tsx +39 -0
- package/frontend/src/features/database/pages/TriggersPage.tsx +230 -0
- package/frontend/src/features/database/services/advance.service.ts +40 -0
- package/frontend/src/features/database/services/database.service.ts +33 -194
- package/frontend/src/features/database/services/record.service.ts +219 -0
- package/frontend/src/features/database/services/table.service.ts +58 -0
- package/frontend/src/features/database/templates/ai-chatbot.ts +402 -0
- package/frontend/src/features/database/templates/crm-system.ts +528 -0
- package/frontend/src/features/database/templates/ecommerce-platform.ts +553 -0
- package/frontend/src/features/database/templates/index.ts +34 -0
- package/frontend/src/features/database/templates/instagram-clone.ts +222 -0
- package/frontend/src/features/database/templates/notion-clone.ts +483 -0
- package/frontend/src/features/database/templates/reddit-clone.ts +526 -0
- package/frontend/src/features/functions/components/FunctionRow.tsx +2 -1
- package/frontend/src/features/functions/components/FunctionsSidebar.tsx +1 -1
- package/frontend/src/features/functions/components/SecretRow.tsx +1 -1
- package/frontend/src/features/functions/components/index.ts +5 -0
- package/frontend/src/features/functions/hooks/useFunctions.ts +4 -4
- package/frontend/src/features/{secrets → functions}/hooks/useSecrets.ts +5 -5
- package/frontend/src/features/functions/pages/FunctionsPage.tsx +148 -0
- package/frontend/src/features/functions/{components/SecretsContent.tsx → pages/SecretsPage.tsx} +19 -21
- package/frontend/src/features/functions/services/{functions.service.ts → function.service.ts} +2 -2
- package/frontend/src/features/{secrets/services/secrets.service.ts → functions/services/secret.service.ts} +2 -2
- package/frontend/src/features/login/hooks/usePartnerOrigin.ts +27 -0
- package/frontend/src/features/login/pages/CloudLoginPage.tsx +118 -0
- package/frontend/src/features/login/{page → pages}/LoginPage.tsx +16 -23
- package/frontend/src/features/login/services/partnership.service.ts +65 -0
- package/frontend/src/features/logs/components/LogsDataGrid.tsx +89 -0
- package/frontend/src/features/logs/components/SeverityBadge.tsx +18 -0
- package/frontend/src/features/logs/components/index.ts +2 -0
- package/frontend/src/features/logs/helpers.ts +24 -0
- package/frontend/src/features/logs/hooks/useAuditLogs.ts +4 -4
- package/frontend/src/features/logs/hooks/useLogSources.ts +137 -0
- package/frontend/src/features/logs/hooks/useLogs.ts +163 -0
- package/frontend/src/features/logs/hooks/useMcpUsage.ts +128 -0
- package/frontend/src/features/logs/index.ts +8 -2
- package/frontend/src/features/logs/{page → pages}/AuditsPage.tsx +91 -38
- package/frontend/src/features/logs/pages/LogsPage.tsx +152 -0
- package/frontend/src/features/logs/pages/MCPLogsPage.tsx +84 -0
- package/frontend/src/features/logs/services/audit.service.ts +63 -0
- package/frontend/src/features/logs/services/log.service.ts +15 -110
- package/frontend/src/features/logs/services/usage.service.ts +31 -0
- package/frontend/src/features/onboard/components/McpConnectionStatus.tsx +68 -0
- package/frontend/src/features/onboard/components/OnboardingModal.tsx +267 -0
- package/frontend/src/features/onboard/components/VideoDemoModal.tsx +38 -0
- package/frontend/src/features/onboard/components/index.ts +4 -0
- package/frontend/src/features/onboard/components/mcp/CursorDeeplinkGenerator.tsx +2 -2
- package/frontend/src/features/onboard/components/mcp/{mcp-helper.tsx → helpers.tsx} +8 -8
- package/frontend/src/features/onboard/components/mcp/index.ts +2 -3
- package/frontend/src/features/onboard/index.ts +13 -3
- package/frontend/src/features/realtime/components/ChannelRow.tsx +83 -0
- package/frontend/src/features/realtime/components/EditChannelModal.tsx +246 -0
- package/frontend/src/features/realtime/components/MessageRow.tsx +85 -0
- package/frontend/src/features/realtime/components/RealtimeEmptyState.tsx +30 -0
- package/frontend/src/features/realtime/hooks/useRealtime.ts +218 -0
- package/frontend/src/features/realtime/index.ts +11 -0
- package/frontend/src/features/realtime/pages/RealtimeChannelsPage.tsx +172 -0
- package/frontend/src/features/realtime/pages/RealtimeMessagesPage.tsx +211 -0
- package/frontend/src/features/realtime/pages/RealtimePermissionsPage.tsx +191 -0
- package/frontend/src/features/realtime/services/realtime.service.ts +107 -0
- package/frontend/src/features/storage/components/BucketEmptyState.tsx +9 -6
- package/frontend/src/features/storage/components/BucketFormDialog.tsx +25 -41
- package/frontend/src/features/storage/components/FilePreviewDialog.tsx +20 -8
- package/frontend/src/features/storage/components/StorageDataGrid.tsx +4 -3
- package/frontend/src/features/storage/components/StorageManager.tsx +23 -34
- package/frontend/src/features/storage/components/index.ts +12 -0
- package/frontend/src/features/storage/hooks/useStorage.ts +208 -0
- package/frontend/src/features/storage/{page → pages}/StoragePage.tsx +41 -143
- package/frontend/src/features/storage/services/storage.service.ts +22 -1
- package/frontend/src/features/visualizer/components/AuthNode.tsx +72 -56
- package/frontend/src/features/visualizer/components/BucketNode.tsx +4 -4
- package/frontend/src/features/visualizer/components/SchemaVisualizer.tsx +108 -80
- package/frontend/src/features/visualizer/components/TableNode.tsx +34 -41
- package/frontend/src/features/visualizer/components/VisualizerSkeleton.tsx +12 -4
- package/frontend/src/features/visualizer/pages/VisualizerPage.tsx +97 -0
- package/frontend/src/index.css +1 -0
- package/frontend/src/lib/analytics/posthog.tsx +27 -0
- package/frontend/src/lib/contexts/AuthContext.tsx +38 -31
- package/frontend/src/lib/contexts/SocketContext.tsx +123 -80
- package/frontend/src/{features/metadata → lib}/hooks/useMetadata.ts +1 -1
- package/frontend/src/lib/hooks/useToast.tsx +6 -2
- package/frontend/src/lib/routing/AppRoutes.tsx +99 -0
- package/frontend/src/lib/routing/RequireAuth.tsx +27 -0
- package/frontend/src/lib/utils/cloudMessaging.ts +20 -0
- package/frontend/src/lib/utils/menuItems.ts +207 -0
- package/frontend/src/lib/utils/{validation-schemas.ts → schemaValidations.ts} +10 -5
- package/frontend/src/lib/utils/utils.ts +32 -1
- package/frontend/src/vite-env.d.ts +1 -0
- package/frontend/tsconfig.json +25 -25
- package/frontend/tsconfig.node.json +9 -9
- package/frontend/vite.config.ts +5 -3
- package/functions/deno.json +24 -24
- package/functions/server.ts +315 -290
- package/functions/worker-template.js +15 -4
- package/i18n/README.ar.md +130 -0
- package/i18n/README.de.md +130 -0
- package/i18n/README.es.md +154 -0
- package/i18n/README.fr.md +134 -0
- package/i18n/README.hi.md +129 -0
- package/i18n/README.ja.md +174 -0
- package/i18n/README.ko.md +137 -0
- package/i18n/README.pt-BR.md +131 -0
- package/i18n/README.ru.md +129 -0
- package/i18n/README.zh-CN.md +133 -0
- package/openapi/ai.yaml +715 -688
- package/openapi/auth.yaml +1244 -563
- package/openapi/email.yaml +158 -0
- package/openapi/functions.yaml +475 -475
- package/openapi/health.yaml +29 -29
- package/openapi/logs.yaml +223 -223
- package/openapi/metadata.yaml +177 -177
- package/openapi/realtime.yaml +699 -0
- package/openapi/records.yaml +381 -381
- package/openapi/secrets.yaml +370 -370
- package/openapi/storage.yaml +875 -875
- package/openapi/tables.yaml +463 -463
- package/package.json +97 -88
- package/shared-schemas/package.json +31 -31
- package/shared-schemas/src/ai-api.schema.ts +34 -58
- package/shared-schemas/src/ai.schema.ts +63 -54
- package/shared-schemas/src/auth-api.schema.ts +352 -193
- package/shared-schemas/src/auth.schema.ts +43 -7
- package/shared-schemas/src/cloud-events.schema.ts +57 -0
- package/shared-schemas/src/database-api.schema.ts +35 -4
- package/shared-schemas/src/database.schema.ts +40 -1
- package/shared-schemas/src/docs.schema.ts +26 -0
- package/shared-schemas/src/email-api.schema.ts +30 -0
- package/shared-schemas/src/index.ts +5 -0
- package/shared-schemas/src/logs-api.schema.ts +7 -1
- package/shared-schemas/src/logs.schema.ts +26 -0
- package/shared-schemas/src/metadata.schema.ts +18 -4
- package/shared-schemas/src/realtime-api.schema.ts +111 -0
- package/shared-schemas/src/realtime.schema.ts +143 -0
- package/shared-schemas/tsconfig.json +21 -21
- package/tsconfig.json +7 -7
- package/zeabur/README.md +13 -0
- package/zeabur/template.yml +1032 -0
- package/.github/workflows/deploy-aws.yml +0 -130
- package/backend/src/api/routes/agent.ts +0 -29
- package/backend/src/api/routes/auth.oauth.ts +0 -482
- package/backend/src/api/routes/auth.ts +0 -386
- package/backend/src/api/routes/docs.ts +0 -66
- package/backend/src/api/routes/functions.ts +0 -183
- package/backend/src/api/routes/openapi.ts +0 -82
- package/backend/src/api/routes/usage.ts +0 -96
- package/backend/src/core/ai/client.ts +0 -242
- package/backend/src/core/ai/model.ts +0 -117
- package/backend/src/core/auth/auth.ts +0 -780
- package/backend/src/core/database/manager.ts +0 -178
- package/backend/src/core/database/table.ts +0 -772
- package/backend/src/core/documentation/agent.ts +0 -689
- package/backend/src/core/documentation/openapi.ts +0 -856
- package/backend/src/core/logs/analytics.ts +0 -76
- package/backend/src/core/logs/providers/localdb.provider.ts +0 -246
- package/backend/src/core/socket/socket.ts +0 -388
- package/backend/src/core/storage/storage.ts +0 -923
- package/backend/src/utils/cloud-token.ts +0 -39
- package/backend/src/utils/helpers.ts +0 -49
- package/backend/src/utils/uuid.ts +0 -9
- package/backend/tests/manual/test-better-auth.sh +0 -303
- package/docker-init/db/logs.sql +0 -9
- package/frontend/README.md +0 -112
- package/frontend/src/components/datagrid/index.tsx +0 -20
- package/frontend/src/components/layout/CloudLayout.tsx +0 -95
- package/frontend/src/features/ai/components/AIConfigDialog.tsx +0 -76
- package/frontend/src/features/ai/components/AIConfigForm.tsx +0 -222
- package/frontend/src/features/ai/components/fields/ModalityField.tsx +0 -87
- package/frontend/src/features/ai/components/fields/ModelSelectionField.tsx +0 -134
- package/frontend/src/features/ai/components/fields/SystemPromptField.tsx +0 -33
- package/frontend/src/features/ai/page/AIPage.tsx +0 -178
- package/frontend/src/features/auth/components/AddOAuthDialog.tsx +0 -106
- package/frontend/src/features/auth/components/AuthMethodTab.tsx +0 -238
- package/frontend/src/features/auth/components/UsersTab.tsx +0 -114
- package/frontend/src/features/auth/page/AuthenticationPage.tsx +0 -169
- package/frontend/src/features/dashboard/page/DashboardPage.tsx +0 -194
- package/frontend/src/features/database/hooks/UseLinkModal.tsx +0 -78
- package/frontend/src/features/functions/components/FunctionViewer.tsx +0 -46
- package/frontend/src/features/functions/components/FunctionsContent.tsx +0 -88
- package/frontend/src/features/functions/page/FunctionsPage.tsx +0 -28
- package/frontend/src/features/login/components/AuthErrorBoundary.tsx +0 -87
- package/frontend/src/features/login/components/PrivateRoute.tsx +0 -24
- package/frontend/src/features/login/page/CloudLoginPage.tsx +0 -93
- package/frontend/src/features/logs/components/AnalyticsLogsTable.tsx +0 -313
- package/frontend/src/features/logs/components/LogsTable.tsx +0 -199
- package/frontend/src/features/logs/page/AnalyticsLogsPage.tsx +0 -530
- package/frontend/src/features/metadata/index.ts +0 -0
- package/frontend/src/features/metadata/page/MetadataPage.tsx +0 -136
- package/frontend/src/features/onboard/components/CompletionCard.tsx +0 -41
- package/frontend/src/features/onboard/components/OnboardButton.tsx +0 -84
- package/frontend/src/features/onboard/components/StepContent.tsx +0 -91
- package/frontend/src/features/onboard/components/TestConnectionStep.tsx +0 -53
- package/frontend/src/features/onboard/components/mcp/McpInstallation.tsx +0 -144
- package/frontend/src/features/onboard/page/OnBoardPage.tsx +0 -104
- package/frontend/src/features/onboard/types.ts +0 -8
- package/frontend/src/features/visualizer/page/VisualizerPage.tsx +0 -127
- package/frontend/src/lib/contexts/OnboardStepContext.tsx +0 -68
- package/frontend/src/lib/hooks/useOnboardingCompletion.ts +0 -29
- /package/backend/src/api/{middleware → middlewares}/error.ts +0 -0
- /package/backend/src/api/{middleware → middlewares}/upload.ts +0 -0
- /package/frontend/src/{features/metadata → lib}/services/metadata.service.ts +0 -0
|
@@ -1,410 +1,410 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
|
|
3
|
-
# Bulk Upsert Feature Test Script
|
|
4
|
-
# Tests CSV/JSON bulk imports with various edge cases and upsert scenarios
|
|
5
|
-
|
|
6
|
-
# Get the directory where this script is located
|
|
7
|
-
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
|
8
|
-
|
|
9
|
-
# Source the test configuration
|
|
10
|
-
source "$SCRIPT_DIR/../test-config.sh"
|
|
11
|
-
|
|
12
|
-
echo "🧪 Testing bulk upsert feature..."
|
|
13
|
-
echo "================================"
|
|
14
|
-
|
|
15
|
-
# Configuration
|
|
16
|
-
# Remove /api if it's already included in TEST_API_BASE
|
|
17
|
-
if [[ "$TEST_API_BASE" == */api ]]; then
|
|
18
|
-
API_BASE="$TEST_API_BASE"
|
|
19
|
-
else
|
|
20
|
-
API_BASE="${TEST_API_BASE}/api"
|
|
21
|
-
fi
|
|
22
|
-
ADMIN_EMAIL="$TEST_ADMIN_EMAIL"
|
|
23
|
-
ADMIN_PASSWORD="$TEST_ADMIN_PASSWORD"
|
|
24
|
-
AUTH_TOKEN=""
|
|
25
|
-
|
|
26
|
-
# Dynamic table name to avoid conflicts
|
|
27
|
-
TEST_TABLE="test_bulk_upsert_$(date +%s)"
|
|
28
|
-
DATA_DIR="$SCRIPT_DIR/test-data/bulk-upsert"
|
|
29
|
-
|
|
30
|
-
# Create test data directory
|
|
31
|
-
mkdir -p "$DATA_DIR"
|
|
32
|
-
|
|
33
|
-
# Helper function to create test files
|
|
34
|
-
create_test_files() {
|
|
35
|
-
echo "📁 Creating test data files..."
|
|
36
|
-
|
|
37
|
-
# 1. Basic CSV file
|
|
38
|
-
cat > "$DATA_DIR/products.csv" << 'EOF'
|
|
39
|
-
sku,name,price,quantity,active
|
|
40
|
-
PROD-001,Laptop Computer,1299.99,50,true
|
|
41
|
-
PROD-002,Wireless Mouse,29.99,200,true
|
|
42
|
-
PROD-003,USB-C Cable,19.99,500,false
|
|
43
|
-
PROD-004,Monitor Stand,79.99,100,true
|
|
44
|
-
PROD-005,Keyboard,89.99,150,true
|
|
45
|
-
EOF
|
|
46
|
-
|
|
47
|
-
# 2. CSV with special characters
|
|
48
|
-
cat > "$DATA_DIR/special-chars.csv" << 'EOF'
|
|
49
|
-
sku,name,description,price
|
|
50
|
-
SPEC-001,"Product with ""quotes""","Description with ""quoted text""",99.99
|
|
51
|
-
SPEC-002,"Product, with comma","Has a comma, in the name",149.99
|
|
52
|
-
SPEC-003,"Multi-line
|
|
53
|
-
Product","Description
|
|
54
|
-
spans multiple
|
|
55
|
-
lines",199.99
|
|
56
|
-
SPEC-004,"Special & < > chars","Contains & < > $ % characters",49.99
|
|
57
|
-
EOF
|
|
58
|
-
|
|
59
|
-
# 3. CSV for upsert testing (contains duplicates)
|
|
60
|
-
cat > "$DATA_DIR/upsert-test.csv" << 'EOF'
|
|
61
|
-
sku,name,price,quantity,active
|
|
62
|
-
PROD-001,Updated Laptop,1399.99,45,true
|
|
63
|
-
PROD-002,Updated Mouse,34.99,180,false
|
|
64
|
-
PROD-006,New Headphones,149.99,75,true
|
|
65
|
-
EOF
|
|
66
|
-
|
|
67
|
-
# 4. JSON array file
|
|
68
|
-
cat > "$DATA_DIR/products.json" << 'EOF'
|
|
69
|
-
[
|
|
70
|
-
{
|
|
71
|
-
"sku": "JSON-001",
|
|
72
|
-
"name": "Smart Watch",
|
|
73
|
-
"price": 299.99,
|
|
74
|
-
"quantity": 30,
|
|
75
|
-
"active": true,
|
|
76
|
-
"metadata": {
|
|
77
|
-
"brand": "TechCo",
|
|
78
|
-
"warranty": "2 years"
|
|
79
|
-
}
|
|
80
|
-
},
|
|
81
|
-
{
|
|
82
|
-
"sku": "JSON-002",
|
|
83
|
-
"name": "Bluetooth Speaker",
|
|
84
|
-
"price": 89.99,
|
|
85
|
-
"quantity": 100,
|
|
86
|
-
"active": true,
|
|
87
|
-
"metadata": {
|
|
88
|
-
"color": "black",
|
|
89
|
-
"waterproof": true
|
|
90
|
-
}
|
|
91
|
-
},
|
|
92
|
-
{
|
|
93
|
-
"sku": "JSON-003",
|
|
94
|
-
"name": "Phone Case",
|
|
95
|
-
"price": 24.99,
|
|
96
|
-
"quantity": 250,
|
|
97
|
-
"active": false,
|
|
98
|
-
"metadata": null
|
|
99
|
-
}
|
|
100
|
-
]
|
|
101
|
-
EOF
|
|
102
|
-
|
|
103
|
-
# 5. Single JSON object
|
|
104
|
-
cat > "$DATA_DIR/single-product.json" << 'EOF'
|
|
105
|
-
{
|
|
106
|
-
"sku": "SINGLE-001",
|
|
107
|
-
"name": "Premium Subscription",
|
|
108
|
-
"price": 99.99,
|
|
109
|
-
"quantity": 999,
|
|
110
|
-
"active": true,
|
|
111
|
-
"metadata": {
|
|
112
|
-
"type": "subscription",
|
|
113
|
-
"duration": "annual"
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
EOF
|
|
117
|
-
|
|
118
|
-
# 6. CSV with NULL values
|
|
119
|
-
cat > "$DATA_DIR/null-values.csv" << 'EOF'
|
|
120
|
-
sku,name,price,quantity,active
|
|
121
|
-
NULL-001,,99.99,,true
|
|
122
|
-
NULL-002,Product with nulls,,,false
|
|
123
|
-
NULL-003,Only SKU and Name,,,
|
|
124
|
-
NULL-004,Complete Product,49.99,25,true
|
|
125
|
-
EOF
|
|
126
|
-
|
|
127
|
-
# 7. Large CSV file (1000 rows)
|
|
128
|
-
echo "sku,name,price,quantity,active" > "$DATA_DIR/large-dataset.csv"
|
|
129
|
-
for i in $(seq 1 1000); do
|
|
130
|
-
echo "LARGE-$(printf %04d $i),Product $i,$(echo "scale=2; $RANDOM/100" | bc),$(($RANDOM % 1000)),true" >> "$DATA_DIR/large-dataset.csv"
|
|
131
|
-
done
|
|
132
|
-
|
|
133
|
-
# 8. CSV with Unicode/Emoji
|
|
134
|
-
cat > "$DATA_DIR/unicode.csv" << 'EOF'
|
|
135
|
-
sku,name,price,quantity,description
|
|
136
|
-
UNI-001,Café ☕ Français,15.99,100,Délicieux café
|
|
137
|
-
UNI-002,寿司 🍣 セット,35.99,50,新鮮な魚
|
|
138
|
-
UNI-003,Москва 🇷🇺 Souvenir,25.99,75,Русский сувенир
|
|
139
|
-
UNI-004,🚀 Rocket Toy,19.99,200,Fun emoji product 🎉
|
|
140
|
-
EOF
|
|
141
|
-
|
|
142
|
-
# 9. Invalid CSV (for error testing)
|
|
143
|
-
cat > "$DATA_DIR/invalid.csv" << 'EOF'
|
|
144
|
-
sku,name,price
|
|
145
|
-
INV-001,Product 1,99.99,Extra Column
|
|
146
|
-
INV-002,Missing Price
|
|
147
|
-
INV-003
|
|
148
|
-
EOF
|
|
149
|
-
|
|
150
|
-
# 10. Empty files
|
|
151
|
-
touch "$DATA_DIR/empty.csv"
|
|
152
|
-
echo "[]" > "$DATA_DIR/empty.json"
|
|
153
|
-
|
|
154
|
-
echo "✅ Test files created"
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
# Function to login as admin
|
|
158
|
-
login_admin() {
|
|
159
|
-
echo "🔐 Logging in as admin..."
|
|
160
|
-
|
|
161
|
-
# First check if backend is running
|
|
162
|
-
if ! curl -s "$API_BASE/health" > /dev/null 2>&1; then
|
|
163
|
-
echo "❌ Backend server is not running!"
|
|
164
|
-
echo " Please start the backend first:"
|
|
165
|
-
echo " cd backend && npm run dev"
|
|
166
|
-
exit 1
|
|
167
|
-
fi
|
|
168
|
-
|
|
169
|
-
local response=$(curl -s -X POST "$API_BASE/auth/admin/sessions" \
|
|
170
|
-
-H "Content-Type: application/json" \
|
|
171
|
-
-d "{\"email\":\"$ADMIN_EMAIL\",\"password\":\"$ADMIN_PASSWORD\"}")
|
|
172
|
-
|
|
173
|
-
AUTH_TOKEN=$(echo "$response" | grep -o '"accessToken":"[^"]*' | cut -d'"' -f4)
|
|
174
|
-
|
|
175
|
-
if [ -z "$AUTH_TOKEN" ]; then
|
|
176
|
-
echo "❌ Failed to login as admin"
|
|
177
|
-
echo " Response: $response"
|
|
178
|
-
echo ""
|
|
179
|
-
echo " Make sure you have the correct admin credentials:"
|
|
180
|
-
echo " Email: $ADMIN_EMAIL"
|
|
181
|
-
echo " Password: $ADMIN_PASSWORD"
|
|
182
|
-
echo ""
|
|
183
|
-
echo " You can set these with environment variables:"
|
|
184
|
-
echo " TEST_ADMIN_EMAIL=your@email.com TEST_ADMIN_PASSWORD=yourpassword ./test-bulk-upsert.sh"
|
|
185
|
-
exit 1
|
|
186
|
-
fi
|
|
187
|
-
|
|
188
|
-
echo "✅ Admin login successful"
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
# Function to create test table
|
|
192
|
-
create_test_table() {
|
|
193
|
-
echo "📊 Creating test table: $TEST_TABLE"
|
|
194
|
-
|
|
195
|
-
local response=$(curl -s -X POST "$API_BASE/database/advance/rawsql" \
|
|
196
|
-
-H "Authorization: Bearer $AUTH_TOKEN" \
|
|
197
|
-
-H "Content-Type: application/json" \
|
|
198
|
-
-d "{\"query\": \"CREATE TABLE $TEST_TABLE (id SERIAL PRIMARY KEY, sku VARCHAR(50) UNIQUE, name VARCHAR(255), description TEXT, price DECIMAL(10,2), quantity INTEGER, active BOOLEAN, metadata JSONB)\"}")
|
|
199
|
-
|
|
200
|
-
if echo "$response" | grep -q "error"; then
|
|
201
|
-
echo "❌ Failed to create table: $response"
|
|
202
|
-
exit 1
|
|
203
|
-
fi
|
|
204
|
-
|
|
205
|
-
echo "✅ Table created successfully"
|
|
206
|
-
|
|
207
|
-
# Register table for cleanup
|
|
208
|
-
register_test_table "$TEST_TABLE"
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
# Function to test bulk upsert
|
|
212
|
-
test_bulk_upsert() {
|
|
213
|
-
local file_name=$1
|
|
214
|
-
local table=$2
|
|
215
|
-
local upsert_key=$3
|
|
216
|
-
local description=$4
|
|
217
|
-
local expect_failure=${5:-false}
|
|
218
|
-
|
|
219
|
-
if [ "$expect_failure" != "true" ]; then
|
|
220
|
-
echo ""
|
|
221
|
-
echo "🧪 Test: $description"
|
|
222
|
-
echo " File: $file_name"
|
|
223
|
-
fi
|
|
224
|
-
|
|
225
|
-
# Build form data
|
|
226
|
-
local curl_cmd="curl -s -X POST \"$API_BASE/database/advance/bulk-upsert\" \
|
|
227
|
-
-H \"Authorization: Bearer $AUTH_TOKEN\" \
|
|
228
|
-
-F \"file=@$DATA_DIR/$file_name\" \
|
|
229
|
-
-F \"table=$table\""
|
|
230
|
-
|
|
231
|
-
if [ ! -z "$upsert_key" ]; then
|
|
232
|
-
curl_cmd="$curl_cmd -F \"upsertKey=$upsert_key\""
|
|
233
|
-
fi
|
|
234
|
-
|
|
235
|
-
local response=$(eval $curl_cmd)
|
|
236
|
-
|
|
237
|
-
if echo "$response" | grep -q "\"success\":true"; then
|
|
238
|
-
local rows=$(echo "$response" | grep -o '"rowsAffected":[0-9]*' | cut -d':' -f2)
|
|
239
|
-
local total=$(echo "$response" | grep -o '"totalRecords":[0-9]*' | cut -d':' -f2)
|
|
240
|
-
if [ "$expect_failure" != "true" ]; then
|
|
241
|
-
echo " ✅ Success: $rows/$total rows inserted"
|
|
242
|
-
fi
|
|
243
|
-
return 0
|
|
244
|
-
else
|
|
245
|
-
local error=$(echo "$response" | grep -o '"message":"[^"]*' | cut -d'"' -f4)
|
|
246
|
-
if [ "$expect_failure" != "true" ]; then
|
|
247
|
-
echo " ⚠️ Expected error: $error"
|
|
248
|
-
fi
|
|
249
|
-
return 1
|
|
250
|
-
fi
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
# Function to verify data
|
|
254
|
-
verify_data() {
|
|
255
|
-
local expected_count=$1
|
|
256
|
-
local description=$2
|
|
257
|
-
|
|
258
|
-
echo ""
|
|
259
|
-
echo "🔍 Verifying: $description"
|
|
260
|
-
|
|
261
|
-
local response=$(curl -s -X POST "$API_BASE/database/advance/rawsql" \
|
|
262
|
-
-H "Authorization: Bearer $AUTH_TOKEN" \
|
|
263
|
-
-H "Content-Type: application/json" \
|
|
264
|
-
-d "{\"query\": \"SELECT COUNT(*) as count FROM $TEST_TABLE\"}")
|
|
265
|
-
|
|
266
|
-
local actual_count=$(echo "$response" | grep -o '"count":"[0-9]*' | cut -d'"' -f4)
|
|
267
|
-
|
|
268
|
-
if [ "$actual_count" = "$expected_count" ]; then
|
|
269
|
-
echo " ✅ Correct: $actual_count rows in table"
|
|
270
|
-
else
|
|
271
|
-
echo " ❌ Mismatch: Expected $expected_count, got $actual_count"
|
|
272
|
-
return 1
|
|
273
|
-
fi
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
# Function to check specific data
|
|
277
|
-
check_data() {
|
|
278
|
-
local query=$1
|
|
279
|
-
local expected=$2
|
|
280
|
-
local description=$3
|
|
281
|
-
|
|
282
|
-
echo " 🔍 $description"
|
|
283
|
-
|
|
284
|
-
local response=$(curl -s -X POST "$API_BASE/database/advance/rawsql" \
|
|
285
|
-
-H "Authorization: Bearer $AUTH_TOKEN" \
|
|
286
|
-
-H "Content-Type: application/json" \
|
|
287
|
-
-d "{\"query\": \"$query\"}")
|
|
288
|
-
|
|
289
|
-
if echo "$response" | grep -q "$expected"; then
|
|
290
|
-
echo " ✅ Found: $expected"
|
|
291
|
-
else
|
|
292
|
-
echo " ❌ Not found: $expected"
|
|
293
|
-
echo " Response: $response"
|
|
294
|
-
fi
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
# Cleanup function
|
|
298
|
-
cleanup() {
|
|
299
|
-
echo ""
|
|
300
|
-
echo "🧹 Cleaning up..."
|
|
301
|
-
|
|
302
|
-
# Drop test table
|
|
303
|
-
curl -s -X POST "$API_BASE/database/advance/rawsql" \
|
|
304
|
-
-H "Authorization: Bearer $AUTH_TOKEN" \
|
|
305
|
-
-H "Content-Type: application/json" \
|
|
306
|
-
-d "{\"query\": \"DROP TABLE IF EXISTS $TEST_TABLE CASCADE\"}" > /dev/null
|
|
307
|
-
|
|
308
|
-
# Remove test data directory
|
|
309
|
-
rm -rf "$DATA_DIR"
|
|
310
|
-
|
|
311
|
-
echo "✅ Cleanup complete"
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
# Main test execution
|
|
315
|
-
main() {
|
|
316
|
-
# Setup
|
|
317
|
-
create_test_files
|
|
318
|
-
login_admin
|
|
319
|
-
create_test_table
|
|
320
|
-
|
|
321
|
-
echo ""
|
|
322
|
-
echo "="
|
|
323
|
-
echo "🚀 RUNNING BULK UPSERT TESTS"
|
|
324
|
-
echo "="
|
|
325
|
-
|
|
326
|
-
# Test 1: Basic CSV import
|
|
327
|
-
test_bulk_upsert "products.csv" "$TEST_TABLE" "" "Basic CSV Import"
|
|
328
|
-
verify_data 5 "5 products imported"
|
|
329
|
-
|
|
330
|
-
# Test 2: JSON array import
|
|
331
|
-
test_bulk_upsert "products.json" "$TEST_TABLE" "sku" "JSON Array Import (with upsert)"
|
|
332
|
-
verify_data 8 "3 new products added"
|
|
333
|
-
|
|
334
|
-
# Test 3: Single JSON object
|
|
335
|
-
test_bulk_upsert "single-product.json" "$TEST_TABLE" "sku" "Single JSON Object"
|
|
336
|
-
verify_data 9 "1 product added"
|
|
337
|
-
|
|
338
|
-
# Test 4: CSV with special characters
|
|
339
|
-
test_bulk_upsert "special-chars.csv" "$TEST_TABLE" "sku" "Special Characters in CSV"
|
|
340
|
-
verify_data 13 "4 products with special chars"
|
|
341
|
-
|
|
342
|
-
# Test 5: Upsert test (update existing)
|
|
343
|
-
test_bulk_upsert "upsert-test.csv" "$TEST_TABLE" "sku" "Upsert - Update Existing"
|
|
344
|
-
verify_data 14 "1 new product, 2 updated"
|
|
345
|
-
check_data "SELECT name, price FROM $TEST_TABLE WHERE sku='PROD-001'" "Updated Laptop" "PROD-001 was updated"
|
|
346
|
-
|
|
347
|
-
# Test 6: NULL values handling
|
|
348
|
-
test_bulk_upsert "null-values.csv" "$TEST_TABLE" "sku" "NULL Values in CSV"
|
|
349
|
-
verify_data 18 "4 products with nulls"
|
|
350
|
-
|
|
351
|
-
# Test 7: Unicode/Emoji support
|
|
352
|
-
test_bulk_upsert "unicode.csv" "$TEST_TABLE" "sku" "Unicode and Emoji Support"
|
|
353
|
-
verify_data 22 "4 unicode products"
|
|
354
|
-
check_data "SELECT name FROM $TEST_TABLE WHERE sku='UNI-004'" "🚀 Rocket Toy" "Emoji preserved"
|
|
355
|
-
|
|
356
|
-
# Test 8: Error handling - empty file
|
|
357
|
-
echo ""
|
|
358
|
-
echo "🧪 Test: Error Handling - Empty CSV"
|
|
359
|
-
echo " File: empty.csv"
|
|
360
|
-
if test_bulk_upsert "empty.csv" "$TEST_TABLE" "" "Empty CSV (should fail)" true; then
|
|
361
|
-
echo " ❌ ERROR: Empty file was accepted (should have been rejected)"
|
|
362
|
-
else
|
|
363
|
-
echo " ✅ Correctly rejected empty file (expected behavior)"
|
|
364
|
-
fi
|
|
365
|
-
|
|
366
|
-
# Test 9: Error handling - invalid CSV
|
|
367
|
-
echo ""
|
|
368
|
-
echo "🧪 Test: Error Handling - Invalid CSV"
|
|
369
|
-
echo " File: invalid.csv"
|
|
370
|
-
if test_bulk_upsert "invalid.csv" "$TEST_TABLE" "" "Invalid CSV (should fail)" true; then
|
|
371
|
-
echo " ❌ ERROR: Invalid CSV was accepted (should have been rejected)"
|
|
372
|
-
else
|
|
373
|
-
echo " ✅ Correctly rejected invalid CSV (expected behavior)"
|
|
374
|
-
fi
|
|
375
|
-
|
|
376
|
-
# Test 10: Performance test
|
|
377
|
-
echo ""
|
|
378
|
-
echo "🧪 Test: Large Dataset Performance"
|
|
379
|
-
START_TIME=$(date +%s%N)
|
|
380
|
-
test_bulk_upsert "large-dataset.csv" "$TEST_TABLE" "sku" "1000 rows bulk insert"
|
|
381
|
-
END_TIME=$(date +%s%N)
|
|
382
|
-
ELAPSED=$((($END_TIME - $START_TIME) / 1000000))
|
|
383
|
-
echo " ⏱️ Time: ${ELAPSED}ms ($(echo "scale=1; 1000000/$ELAPSED" | bc) rows/sec)"
|
|
384
|
-
|
|
385
|
-
echo ""
|
|
386
|
-
echo "="
|
|
387
|
-
echo "📊 TEST SUMMARY"
|
|
388
|
-
echo "="
|
|
389
|
-
|
|
390
|
-
# Final verification
|
|
391
|
-
local final_count=$(curl -s -X POST "$API_BASE/database/advance/rawsql" \
|
|
392
|
-
-H "Authorization: Bearer $AUTH_TOKEN" \
|
|
393
|
-
-H "Content-Type: application/json" \
|
|
394
|
-
-d "{\"query\": \"SELECT COUNT(*) as count FROM $TEST_TABLE\"}" | grep -o '"count":"[0-9]*' | cut -d'"' -f4)
|
|
395
|
-
|
|
396
|
-
echo "Total records in table: $final_count"
|
|
397
|
-
echo ""
|
|
398
|
-
|
|
399
|
-
# Cleanup
|
|
400
|
-
cleanup
|
|
401
|
-
|
|
402
|
-
echo ""
|
|
403
|
-
echo "✅ All bulk upsert tests completed!"
|
|
404
|
-
}
|
|
405
|
-
|
|
406
|
-
# Trap to ensure cleanup on exit
|
|
407
|
-
trap cleanup EXIT
|
|
408
|
-
|
|
409
|
-
# Run the tests
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# Bulk Upsert Feature Test Script
|
|
4
|
+
# Tests CSV/JSON bulk imports with various edge cases and upsert scenarios
|
|
5
|
+
|
|
6
|
+
# Get the directory where this script is located
|
|
7
|
+
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
|
8
|
+
|
|
9
|
+
# Source the test configuration
|
|
10
|
+
source "$SCRIPT_DIR/../test-config.sh"
|
|
11
|
+
|
|
12
|
+
echo "🧪 Testing bulk upsert feature..."
|
|
13
|
+
echo "================================"
|
|
14
|
+
|
|
15
|
+
# Configuration
|
|
16
|
+
# Remove /api if it's already included in TEST_API_BASE
|
|
17
|
+
if [[ "$TEST_API_BASE" == */api ]]; then
|
|
18
|
+
API_BASE="$TEST_API_BASE"
|
|
19
|
+
else
|
|
20
|
+
API_BASE="${TEST_API_BASE}/api"
|
|
21
|
+
fi
|
|
22
|
+
ADMIN_EMAIL="$TEST_ADMIN_EMAIL"
|
|
23
|
+
ADMIN_PASSWORD="$TEST_ADMIN_PASSWORD"
|
|
24
|
+
AUTH_TOKEN=""
|
|
25
|
+
|
|
26
|
+
# Dynamic table name to avoid conflicts
|
|
27
|
+
TEST_TABLE="test_bulk_upsert_$(date +%s)"
|
|
28
|
+
DATA_DIR="$SCRIPT_DIR/test-data/bulk-upsert"
|
|
29
|
+
|
|
30
|
+
# Create test data directory
|
|
31
|
+
mkdir -p "$DATA_DIR"
|
|
32
|
+
|
|
33
|
+
# Helper function to create test files
|
|
34
|
+
create_test_files() {
|
|
35
|
+
echo "📁 Creating test data files..."
|
|
36
|
+
|
|
37
|
+
# 1. Basic CSV file
|
|
38
|
+
cat > "$DATA_DIR/products.csv" << 'EOF'
|
|
39
|
+
sku,name,price,quantity,active
|
|
40
|
+
PROD-001,Laptop Computer,1299.99,50,true
|
|
41
|
+
PROD-002,Wireless Mouse,29.99,200,true
|
|
42
|
+
PROD-003,USB-C Cable,19.99,500,false
|
|
43
|
+
PROD-004,Monitor Stand,79.99,100,true
|
|
44
|
+
PROD-005,Keyboard,89.99,150,true
|
|
45
|
+
EOF
|
|
46
|
+
|
|
47
|
+
# 2. CSV with special characters
|
|
48
|
+
cat > "$DATA_DIR/special-chars.csv" << 'EOF'
|
|
49
|
+
sku,name,description,price
|
|
50
|
+
SPEC-001,"Product with ""quotes""","Description with ""quoted text""",99.99
|
|
51
|
+
SPEC-002,"Product, with comma","Has a comma, in the name",149.99
|
|
52
|
+
SPEC-003,"Multi-line
|
|
53
|
+
Product","Description
|
|
54
|
+
spans multiple
|
|
55
|
+
lines",199.99
|
|
56
|
+
SPEC-004,"Special & < > chars","Contains & < > $ % characters",49.99
|
|
57
|
+
EOF
|
|
58
|
+
|
|
59
|
+
# 3. CSV for upsert testing (contains duplicates)
|
|
60
|
+
cat > "$DATA_DIR/upsert-test.csv" << 'EOF'
|
|
61
|
+
sku,name,price,quantity,active
|
|
62
|
+
PROD-001,Updated Laptop,1399.99,45,true
|
|
63
|
+
PROD-002,Updated Mouse,34.99,180,false
|
|
64
|
+
PROD-006,New Headphones,149.99,75,true
|
|
65
|
+
EOF
|
|
66
|
+
|
|
67
|
+
# 4. JSON array file
|
|
68
|
+
cat > "$DATA_DIR/products.json" << 'EOF'
|
|
69
|
+
[
|
|
70
|
+
{
|
|
71
|
+
"sku": "JSON-001",
|
|
72
|
+
"name": "Smart Watch",
|
|
73
|
+
"price": 299.99,
|
|
74
|
+
"quantity": 30,
|
|
75
|
+
"active": true,
|
|
76
|
+
"metadata": {
|
|
77
|
+
"brand": "TechCo",
|
|
78
|
+
"warranty": "2 years"
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
"sku": "JSON-002",
|
|
83
|
+
"name": "Bluetooth Speaker",
|
|
84
|
+
"price": 89.99,
|
|
85
|
+
"quantity": 100,
|
|
86
|
+
"active": true,
|
|
87
|
+
"metadata": {
|
|
88
|
+
"color": "black",
|
|
89
|
+
"waterproof": true
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
"sku": "JSON-003",
|
|
94
|
+
"name": "Phone Case",
|
|
95
|
+
"price": 24.99,
|
|
96
|
+
"quantity": 250,
|
|
97
|
+
"active": false,
|
|
98
|
+
"metadata": null
|
|
99
|
+
}
|
|
100
|
+
]
|
|
101
|
+
EOF
|
|
102
|
+
|
|
103
|
+
# 5. Single JSON object
|
|
104
|
+
cat > "$DATA_DIR/single-product.json" << 'EOF'
|
|
105
|
+
{
|
|
106
|
+
"sku": "SINGLE-001",
|
|
107
|
+
"name": "Premium Subscription",
|
|
108
|
+
"price": 99.99,
|
|
109
|
+
"quantity": 999,
|
|
110
|
+
"active": true,
|
|
111
|
+
"metadata": {
|
|
112
|
+
"type": "subscription",
|
|
113
|
+
"duration": "annual"
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
EOF
|
|
117
|
+
|
|
118
|
+
# 6. CSV with NULL values
|
|
119
|
+
cat > "$DATA_DIR/null-values.csv" << 'EOF'
|
|
120
|
+
sku,name,price,quantity,active
|
|
121
|
+
NULL-001,,99.99,,true
|
|
122
|
+
NULL-002,Product with nulls,,,false
|
|
123
|
+
NULL-003,Only SKU and Name,,,
|
|
124
|
+
NULL-004,Complete Product,49.99,25,true
|
|
125
|
+
EOF
|
|
126
|
+
|
|
127
|
+
# 7. Large CSV file (1000 rows)
|
|
128
|
+
echo "sku,name,price,quantity,active" > "$DATA_DIR/large-dataset.csv"
|
|
129
|
+
for i in $(seq 1 1000); do
|
|
130
|
+
echo "LARGE-$(printf %04d $i),Product $i,$(echo "scale=2; $RANDOM/100" | bc),$(($RANDOM % 1000)),true" >> "$DATA_DIR/large-dataset.csv"
|
|
131
|
+
done
|
|
132
|
+
|
|
133
|
+
# 8. CSV with Unicode/Emoji
|
|
134
|
+
cat > "$DATA_DIR/unicode.csv" << 'EOF'
|
|
135
|
+
sku,name,price,quantity,description
|
|
136
|
+
UNI-001,Café ☕ Français,15.99,100,Délicieux café
|
|
137
|
+
UNI-002,寿司 🍣 セット,35.99,50,新鮮な魚
|
|
138
|
+
UNI-003,Москва 🇷🇺 Souvenir,25.99,75,Русский сувенир
|
|
139
|
+
UNI-004,🚀 Rocket Toy,19.99,200,Fun emoji product 🎉
|
|
140
|
+
EOF
|
|
141
|
+
|
|
142
|
+
# 9. Invalid CSV (for error testing)
|
|
143
|
+
cat > "$DATA_DIR/invalid.csv" << 'EOF'
|
|
144
|
+
sku,name,price
|
|
145
|
+
INV-001,Product 1,99.99,Extra Column
|
|
146
|
+
INV-002,Missing Price
|
|
147
|
+
INV-003
|
|
148
|
+
EOF
|
|
149
|
+
|
|
150
|
+
# 10. Empty files
|
|
151
|
+
touch "$DATA_DIR/empty.csv"
|
|
152
|
+
echo "[]" > "$DATA_DIR/empty.json"
|
|
153
|
+
|
|
154
|
+
echo "✅ Test files created"
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
# Function to login as admin
|
|
158
|
+
login_admin() {
|
|
159
|
+
echo "🔐 Logging in as admin..."
|
|
160
|
+
|
|
161
|
+
# First check if backend is running
|
|
162
|
+
if ! curl -s "$API_BASE/health" > /dev/null 2>&1; then
|
|
163
|
+
echo "❌ Backend server is not running!"
|
|
164
|
+
echo " Please start the backend first:"
|
|
165
|
+
echo " cd backend && npm run dev"
|
|
166
|
+
exit 1
|
|
167
|
+
fi
|
|
168
|
+
|
|
169
|
+
local response=$(curl -s -X POST "$API_BASE/auth/admin/sessions" \
|
|
170
|
+
-H "Content-Type: application/json" \
|
|
171
|
+
-d "{\"email\":\"$ADMIN_EMAIL\",\"password\":\"$ADMIN_PASSWORD\"}")
|
|
172
|
+
|
|
173
|
+
AUTH_TOKEN=$(echo "$response" | grep -o '"accessToken":"[^"]*' | cut -d'"' -f4)
|
|
174
|
+
|
|
175
|
+
if [ -z "$AUTH_TOKEN" ]; then
|
|
176
|
+
echo "❌ Failed to login as admin"
|
|
177
|
+
echo " Response: $response"
|
|
178
|
+
echo ""
|
|
179
|
+
echo " Make sure you have the correct admin credentials:"
|
|
180
|
+
echo " Email: $ADMIN_EMAIL"
|
|
181
|
+
echo " Password: $ADMIN_PASSWORD"
|
|
182
|
+
echo ""
|
|
183
|
+
echo " You can set these with environment variables:"
|
|
184
|
+
echo " TEST_ADMIN_EMAIL=your@email.com TEST_ADMIN_PASSWORD=yourpassword ./test-bulk-upsert.sh"
|
|
185
|
+
exit 1
|
|
186
|
+
fi
|
|
187
|
+
|
|
188
|
+
echo "✅ Admin login successful"
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
# Function to create test table
|
|
192
|
+
create_test_table() {
|
|
193
|
+
echo "📊 Creating test table: $TEST_TABLE"
|
|
194
|
+
|
|
195
|
+
local response=$(curl -s -X POST "$API_BASE/database/advance/rawsql" \
|
|
196
|
+
-H "Authorization: Bearer $AUTH_TOKEN" \
|
|
197
|
+
-H "Content-Type: application/json" \
|
|
198
|
+
-d "{\"query\": \"CREATE TABLE $TEST_TABLE (id SERIAL PRIMARY KEY, sku VARCHAR(50) UNIQUE, name VARCHAR(255), description TEXT, price DECIMAL(10,2), quantity INTEGER, active BOOLEAN, metadata JSONB)\"}")
|
|
199
|
+
|
|
200
|
+
if echo "$response" | grep -q "error"; then
|
|
201
|
+
echo "❌ Failed to create table: $response"
|
|
202
|
+
exit 1
|
|
203
|
+
fi
|
|
204
|
+
|
|
205
|
+
echo "✅ Table created successfully"
|
|
206
|
+
|
|
207
|
+
# Register table for cleanup
|
|
208
|
+
register_test_table "$TEST_TABLE"
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
# Function to test bulk upsert
|
|
212
|
+
test_bulk_upsert() {
|
|
213
|
+
local file_name=$1
|
|
214
|
+
local table=$2
|
|
215
|
+
local upsert_key=$3
|
|
216
|
+
local description=$4
|
|
217
|
+
local expect_failure=${5:-false}
|
|
218
|
+
|
|
219
|
+
if [ "$expect_failure" != "true" ]; then
|
|
220
|
+
echo ""
|
|
221
|
+
echo "🧪 Test: $description"
|
|
222
|
+
echo " File: $file_name"
|
|
223
|
+
fi
|
|
224
|
+
|
|
225
|
+
# Build form data
|
|
226
|
+
local curl_cmd="curl -s -X POST \"$API_BASE/database/advance/bulk-upsert\" \
|
|
227
|
+
-H \"Authorization: Bearer $AUTH_TOKEN\" \
|
|
228
|
+
-F \"file=@$DATA_DIR/$file_name\" \
|
|
229
|
+
-F \"table=$table\""
|
|
230
|
+
|
|
231
|
+
if [ ! -z "$upsert_key" ]; then
|
|
232
|
+
curl_cmd="$curl_cmd -F \"upsertKey=$upsert_key\""
|
|
233
|
+
fi
|
|
234
|
+
|
|
235
|
+
local response=$(eval $curl_cmd)
|
|
236
|
+
|
|
237
|
+
if echo "$response" | grep -q "\"success\":true"; then
|
|
238
|
+
local rows=$(echo "$response" | grep -o '"rowsAffected":[0-9]*' | cut -d':' -f2)
|
|
239
|
+
local total=$(echo "$response" | grep -o '"totalRecords":[0-9]*' | cut -d':' -f2)
|
|
240
|
+
if [ "$expect_failure" != "true" ]; then
|
|
241
|
+
echo " ✅ Success: $rows/$total rows inserted"
|
|
242
|
+
fi
|
|
243
|
+
return 0
|
|
244
|
+
else
|
|
245
|
+
local error=$(echo "$response" | grep -o '"message":"[^"]*' | cut -d'"' -f4)
|
|
246
|
+
if [ "$expect_failure" != "true" ]; then
|
|
247
|
+
echo " ⚠️ Expected error: $error"
|
|
248
|
+
fi
|
|
249
|
+
return 1
|
|
250
|
+
fi
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
# Function to verify data
|
|
254
|
+
verify_data() {
|
|
255
|
+
local expected_count=$1
|
|
256
|
+
local description=$2
|
|
257
|
+
|
|
258
|
+
echo ""
|
|
259
|
+
echo "🔍 Verifying: $description"
|
|
260
|
+
|
|
261
|
+
local response=$(curl -s -X POST "$API_BASE/database/advance/rawsql" \
|
|
262
|
+
-H "Authorization: Bearer $AUTH_TOKEN" \
|
|
263
|
+
-H "Content-Type: application/json" \
|
|
264
|
+
-d "{\"query\": \"SELECT COUNT(*) as count FROM $TEST_TABLE\"}")
|
|
265
|
+
|
|
266
|
+
local actual_count=$(echo "$response" | grep -o '"count":"[0-9]*' | cut -d'"' -f4)
|
|
267
|
+
|
|
268
|
+
if [ "$actual_count" = "$expected_count" ]; then
|
|
269
|
+
echo " ✅ Correct: $actual_count rows in table"
|
|
270
|
+
else
|
|
271
|
+
echo " ❌ Mismatch: Expected $expected_count, got $actual_count"
|
|
272
|
+
return 1
|
|
273
|
+
fi
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
# Function to check specific data
|
|
277
|
+
check_data() {
|
|
278
|
+
local query=$1
|
|
279
|
+
local expected=$2
|
|
280
|
+
local description=$3
|
|
281
|
+
|
|
282
|
+
echo " 🔍 $description"
|
|
283
|
+
|
|
284
|
+
local response=$(curl -s -X POST "$API_BASE/database/advance/rawsql" \
|
|
285
|
+
-H "Authorization: Bearer $AUTH_TOKEN" \
|
|
286
|
+
-H "Content-Type: application/json" \
|
|
287
|
+
-d "{\"query\": \"$query\"}")
|
|
288
|
+
|
|
289
|
+
if echo "$response" | grep -q "$expected"; then
|
|
290
|
+
echo " ✅ Found: $expected"
|
|
291
|
+
else
|
|
292
|
+
echo " ❌ Not found: $expected"
|
|
293
|
+
echo " Response: $response"
|
|
294
|
+
fi
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
# Cleanup function
|
|
298
|
+
cleanup() {
|
|
299
|
+
echo ""
|
|
300
|
+
echo "🧹 Cleaning up..."
|
|
301
|
+
|
|
302
|
+
# Drop test table
|
|
303
|
+
curl -s -X POST "$API_BASE/database/advance/rawsql" \
|
|
304
|
+
-H "Authorization: Bearer $AUTH_TOKEN" \
|
|
305
|
+
-H "Content-Type: application/json" \
|
|
306
|
+
-d "{\"query\": \"DROP TABLE IF EXISTS $TEST_TABLE CASCADE\"}" > /dev/null
|
|
307
|
+
|
|
308
|
+
# Remove test data directory
|
|
309
|
+
rm -rf "$DATA_DIR"
|
|
310
|
+
|
|
311
|
+
echo "✅ Cleanup complete"
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
# Main test execution
|
|
315
|
+
main() {
|
|
316
|
+
# Setup
|
|
317
|
+
create_test_files
|
|
318
|
+
login_admin
|
|
319
|
+
create_test_table
|
|
320
|
+
|
|
321
|
+
echo ""
|
|
322
|
+
echo "="
|
|
323
|
+
echo "🚀 RUNNING BULK UPSERT TESTS"
|
|
324
|
+
echo "="
|
|
325
|
+
|
|
326
|
+
# Test 1: Basic CSV import
|
|
327
|
+
test_bulk_upsert "products.csv" "$TEST_TABLE" "" "Basic CSV Import"
|
|
328
|
+
verify_data 5 "5 products imported"
|
|
329
|
+
|
|
330
|
+
# Test 2: JSON array import
|
|
331
|
+
test_bulk_upsert "products.json" "$TEST_TABLE" "sku" "JSON Array Import (with upsert)"
|
|
332
|
+
verify_data 8 "3 new products added"
|
|
333
|
+
|
|
334
|
+
# Test 3: Single JSON object
|
|
335
|
+
test_bulk_upsert "single-product.json" "$TEST_TABLE" "sku" "Single JSON Object"
|
|
336
|
+
verify_data 9 "1 product added"
|
|
337
|
+
|
|
338
|
+
# Test 4: CSV with special characters
|
|
339
|
+
test_bulk_upsert "special-chars.csv" "$TEST_TABLE" "sku" "Special Characters in CSV"
|
|
340
|
+
verify_data 13 "4 products with special chars"
|
|
341
|
+
|
|
342
|
+
# Test 5: Upsert test (update existing)
|
|
343
|
+
test_bulk_upsert "upsert-test.csv" "$TEST_TABLE" "sku" "Upsert - Update Existing"
|
|
344
|
+
verify_data 14 "1 new product, 2 updated"
|
|
345
|
+
check_data "SELECT name, price FROM $TEST_TABLE WHERE sku='PROD-001'" "Updated Laptop" "PROD-001 was updated"
|
|
346
|
+
|
|
347
|
+
# Test 6: NULL values handling
|
|
348
|
+
test_bulk_upsert "null-values.csv" "$TEST_TABLE" "sku" "NULL Values in CSV"
|
|
349
|
+
verify_data 18 "4 products with nulls"
|
|
350
|
+
|
|
351
|
+
# Test 7: Unicode/Emoji support
|
|
352
|
+
test_bulk_upsert "unicode.csv" "$TEST_TABLE" "sku" "Unicode and Emoji Support"
|
|
353
|
+
verify_data 22 "4 unicode products"
|
|
354
|
+
check_data "SELECT name FROM $TEST_TABLE WHERE sku='UNI-004'" "🚀 Rocket Toy" "Emoji preserved"
|
|
355
|
+
|
|
356
|
+
# Test 8: Error handling - empty file
|
|
357
|
+
echo ""
|
|
358
|
+
echo "🧪 Test: Error Handling - Empty CSV"
|
|
359
|
+
echo " File: empty.csv"
|
|
360
|
+
if test_bulk_upsert "empty.csv" "$TEST_TABLE" "" "Empty CSV (should fail)" true; then
|
|
361
|
+
echo " ❌ ERROR: Empty file was accepted (should have been rejected)"
|
|
362
|
+
else
|
|
363
|
+
echo " ✅ Correctly rejected empty file (expected behavior)"
|
|
364
|
+
fi
|
|
365
|
+
|
|
366
|
+
# Test 9: Error handling - invalid CSV
|
|
367
|
+
echo ""
|
|
368
|
+
echo "🧪 Test: Error Handling - Invalid CSV"
|
|
369
|
+
echo " File: invalid.csv"
|
|
370
|
+
if test_bulk_upsert "invalid.csv" "$TEST_TABLE" "" "Invalid CSV (should fail)" true; then
|
|
371
|
+
echo " ❌ ERROR: Invalid CSV was accepted (should have been rejected)"
|
|
372
|
+
else
|
|
373
|
+
echo " ✅ Correctly rejected invalid CSV (expected behavior)"
|
|
374
|
+
fi
|
|
375
|
+
|
|
376
|
+
# Test 10: Performance test
|
|
377
|
+
echo ""
|
|
378
|
+
echo "🧪 Test: Large Dataset Performance"
|
|
379
|
+
START_TIME=$(date +%s%N)
|
|
380
|
+
test_bulk_upsert "large-dataset.csv" "$TEST_TABLE" "sku" "1000 rows bulk insert"
|
|
381
|
+
END_TIME=$(date +%s%N)
|
|
382
|
+
ELAPSED=$((($END_TIME - $START_TIME) / 1000000))
|
|
383
|
+
echo " ⏱️ Time: ${ELAPSED}ms ($(echo "scale=1; 1000000/$ELAPSED" | bc) rows/sec)"
|
|
384
|
+
|
|
385
|
+
echo ""
|
|
386
|
+
echo "="
|
|
387
|
+
echo "📊 TEST SUMMARY"
|
|
388
|
+
echo "="
|
|
389
|
+
|
|
390
|
+
# Final verification
|
|
391
|
+
local final_count=$(curl -s -X POST "$API_BASE/database/advance/rawsql" \
|
|
392
|
+
-H "Authorization: Bearer $AUTH_TOKEN" \
|
|
393
|
+
-H "Content-Type: application/json" \
|
|
394
|
+
-d "{\"query\": \"SELECT COUNT(*) as count FROM $TEST_TABLE\"}" | grep -o '"count":"[0-9]*' | cut -d'"' -f4)
|
|
395
|
+
|
|
396
|
+
echo "Total records in table: $final_count"
|
|
397
|
+
echo ""
|
|
398
|
+
|
|
399
|
+
# Cleanup
|
|
400
|
+
cleanup
|
|
401
|
+
|
|
402
|
+
echo ""
|
|
403
|
+
echo "✅ All bulk upsert tests completed!"
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
# Trap to ensure cleanup on exit
|
|
407
|
+
trap cleanup EXIT
|
|
408
|
+
|
|
409
|
+
# Run the tests
|
|
410
410
|
main
|