diff --git a/.github/workflows/issue-triage.lock.yml b/.github/workflows/issue-triage.lock.yml index 3249affe00..3fc2d5d190 100644 --- a/.github/workflows/issue-triage.lock.yml +++ b/.github/workflows/issue-triage.lock.yml @@ -5,7 +5,7 @@ # # Source: githubnext/agentics/workflows/issue-triage.md@0837fb7b24c3b84ee77fb7c8cfa8735c48be347a # -# Effective stop-time: 2025-11-27 03:00:29 +# Effective stop-time: 2025-12-03 20:01:19 # # Job Dependency Graph: # ```mermaid @@ -33,18 +33,29 @@ # add_labels --> update_reaction # missing_tool --> update_reaction # ``` +# +# Pinned GitHub Actions: +# - actions/checkout@v5 (08c6903cd8c0fde910a37f88322edcfb5dd907a8) +# https://github.com/actions/checkout/commit/08c6903cd8c0fde910a37f88322edcfb5dd907a8 +# - actions/download-artifact@v5 (634f93cb2916e3fdff6788551b99b062d0335ce0) +# https://github.com/actions/download-artifact/commit/634f93cb2916e3fdff6788551b99b062d0335ce0 +# - actions/github-script@v8 (ed597411d8f924073f98dfc5c65a23a2325f34cd) +# https://github.com/actions/github-script/commit/ed597411d8f924073f98dfc5c65a23a2325f34cd +# - actions/setup-node@v6 (2028fbc5c25fe9cf00d9f06a71cc4710d4507903) +# https://github.com/actions/setup-node/commit/2028fbc5c25fe9cf00d9f06a71cc4710d4507903 +# - actions/upload-artifact@v4 (ea165f8d65b6e75b540449e92b4886f43607fa02) +# https://github.com/actions/upload-artifact/commit/ea165f8d65b6e75b540449e92b4886f43607fa02 name: "Agentic Triage" "on": - issues: - types: - - opened - - reopened + schedule: + - cron: 0 0 * * * + workflow_dispatch: null permissions: read-all concurrency: - group: "gh-aw-${{ github.workflow }}-${{ github.event.issue.number }}" + group: "gh-aw-${{ github.workflow }}" run-name: "Agentic Triage" @@ -52,7 +63,7 @@ jobs: activation: needs: pre_activation if: needs.pre_activation.outputs.activated == 'true' - runs-on: ubuntu-latest + runs-on: ubuntu-slim permissions: discussions: write issues: write @@ -63,24 +74,82 @@ jobs: comment_url: ${{ steps.react.outputs.comment-url }} reaction_id: ${{ steps.react.outputs.reaction-id }} steps: + - name: Checkout workflows + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 + with: + sparse-checkout: | + .github/workflows + sparse-checkout-cone-mode: false + fetch-depth: 1 + persist-credentials: false - name: Check workflow file timestamps - run: | - WORKFLOW_FILE="${GITHUB_WORKSPACE}/.github/workflows/$(basename "$GITHUB_WORKFLOW" .lock.yml).md" - LOCK_FILE="${GITHUB_WORKSPACE}/.github/workflows/$GITHUB_WORKFLOW" - - if [ -f "$WORKFLOW_FILE" ] && [ -f "$LOCK_FILE" ]; then - if [ "$WORKFLOW_FILE" -nt "$LOCK_FILE" ]; then - echo "🔴🔴🔴 WARNING: Lock file '$LOCK_FILE' is outdated! The workflow file '$WORKFLOW_FILE' has been modified more recently. Run 'gh aw compile' to regenerate the lock file." >&2 - echo "## ⚠️ Workflow Lock File Warning" >> $GITHUB_STEP_SUMMARY - echo "🔴🔴🔴 **WARNING**: Lock file \`$LOCK_FILE\` is outdated!" >> $GITHUB_STEP_SUMMARY - echo "The workflow file \`$WORKFLOW_FILE\` has been modified more recently." >> $GITHUB_STEP_SUMMARY - echo "Run \`gh aw compile\` to regenerate the lock file." >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - fi - fi + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd + env: + GH_AW_WORKFLOW_FILE: "issue-triage.lock.yml" + with: + script: | + const fs = require("fs"); + const path = require("path"); + async function main() { + const workspace = process.env.GITHUB_WORKSPACE; + const workflowFile = process.env.GH_AW_WORKFLOW_FILE; + if (!workspace) { + core.setFailed("Configuration error: GITHUB_WORKSPACE not available."); + return; + } + if (!workflowFile) { + core.setFailed("Configuration error: GH_AW_WORKFLOW_FILE not available."); + return; + } + const workflowBasename = path.basename(workflowFile, ".lock.yml"); + const workflowMdFile = path.join(workspace, ".github", "workflows", `${workflowBasename}.md`); + const lockFile = path.join(workspace, ".github", "workflows", workflowFile); + core.info(`Checking workflow timestamps:`); + core.info(` Source: ${workflowMdFile}`); + core.info(` Lock file: ${lockFile}`); + let workflowExists = false; + let lockExists = false; + try { + fs.accessSync(workflowMdFile, fs.constants.F_OK); + workflowExists = true; + } catch (error) { + core.info(`Source file does not exist: ${workflowMdFile}`); + } + try { + fs.accessSync(lockFile, fs.constants.F_OK); + lockExists = true; + } catch (error) { + core.info(`Lock file does not exist: ${lockFile}`); + } + if (!workflowExists || !lockExists) { + core.info("Skipping timestamp check - one or both files not found"); + return; + } + const workflowStat = fs.statSync(workflowMdFile); + const lockStat = fs.statSync(lockFile); + const workflowMtime = workflowStat.mtime.getTime(); + const lockMtime = lockStat.mtime.getTime(); + core.info(` Source modified: ${workflowStat.mtime.toISOString()}`); + core.info(` Lock modified: ${lockStat.mtime.toISOString()}`); + if (workflowMtime > lockMtime) { + const warningMessage = `🔴🔴🔴 WARNING: Lock file '${lockFile}' is outdated! The workflow file '${workflowMdFile}' has been modified more recently. Run 'gh aw compile' to regenerate the lock file.`; + core.error(warningMessage); + await core.summary + .addRaw("## ⚠️ Workflow Lock File Warning\n\n") + .addRaw(`🔴🔴🔴 **WARNING**: Lock file \`${lockFile}\` is outdated!\n\n`) + .addRaw(`The workflow file \`${workflowMdFile}\` has been modified more recently.\n\n`) + .addRaw("Run `gh aw compile` to regenerate the lock file.\n\n") + .write(); + } else { + core.info("✅ Lock file is up to date"); + } + } + main().catch(error => { + core.setFailed(error instanceof Error ? error.message : String(error)); + }); - name: Add eyes reaction to the triggering item id: react - if: github.event_name == 'issues' || github.event_name == 'issue_comment' || github.event_name == 'pull_request_review_comment' || github.event_name == 'discussion' || github.event_name == 'discussion_comment' || (github.event_name == 'pull_request') && (github.event.pull_request.head.repo.full_name == github.repository) + if: github.event_name == 'issues' || github.event_name == 'issue_comment' || github.event_name == 'pull_request_review_comment' || github.event_name == 'discussion' || github.event_name == 'discussion_comment' || (github.event_name == 'pull_request') && (github.event.pull_request.head.repo.id == github.repository_id) uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd env: GH_AW_REACTION: eyes @@ -414,9 +483,9 @@ jobs: - agent - detection if: > - ((!cancelled()) && (contains(needs.agent.outputs.output_types, 'add_comment'))) && (((github.event.issue.number) || - (github.event.pull_request.number)) || (github.event.discussion.number)) - runs-on: ubuntu-latest + (((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'add_comment'))) && + (((github.event.issue.number) || (github.event.pull_request.number)) || (github.event.discussion.number)) + runs-on: ubuntu-slim permissions: contents: read discussions: write @@ -805,9 +874,9 @@ jobs: - agent - detection if: > - ((!cancelled()) && (contains(needs.agent.outputs.output_types, 'add_labels'))) && ((github.event.issue.number) || - (github.event.pull_request.number)) - runs-on: ubuntu-latest + (((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'add_labels'))) && + ((github.event.issue.number) || (github.event.pull_request.number)) + runs-on: ubuntu-slim permissions: contents: read issues: write @@ -1046,6 +1115,8 @@ jobs: needs: activation runs-on: ubuntu-latest permissions: read-all + concurrency: + group: "gh-aw-copilot-${{ github.workflow }}" env: GH_AW_SAFE_OUTPUTS: /tmp/gh-aw/safeoutputs/outputs.jsonl GH_AW_SAFE_OUTPUTS_CONFIG: "{\"add_comment\":{\"max\":1},\"add_labels\":{\"max\":5},\"missing_tool\":{}}" @@ -1055,14 +1126,22 @@ jobs: steps: - name: Checkout repository uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 + with: + persist-credentials: false - name: Create gh-aw temp directory run: | mkdir -p /tmp/gh-aw/agent echo "Created /tmp/gh-aw/agent directory for agentic workflow temporary files" - name: Configure Git credentials + env: + REPO_NAME: ${{ github.repository }} run: | git config --global user.email "github-actions[bot]@users.noreply.github.com" - git config --global user.name "${{ github.workflow }}" + git config --global user.name "github-actions[bot]" + # Re-authenticate git with GitHub token + SERVER_URL="${{ github.server_url }}" + SERVER_URL="${SERVER_URL#https://}" + git remote set-url origin "https://x-access-token:${{ github.token }}@${SERVER_URL}/${REPO_NAME}.git" echo "Git configured with standard GitHub Actions identity" - name: Checkout PR branch if: | @@ -1114,15 +1193,15 @@ jobs: env: COPILOT_CLI_TOKEN: ${{ secrets.COPILOT_CLI_TOKEN }} - name: Setup Node.js - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 + uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 with: node-version: '24' - name: Install GitHub Copilot CLI - run: npm install -g @github/copilot@0.0.351 + run: npm install -g @github/copilot@0.0.353 - name: Downloading container images run: | set -e - docker pull ghcr.io/github/github-mcp-server:v0.19.1 + docker pull ghcr.io/github/github-mcp-server:v0.20.1 docker pull mcp/fetch - name: Setup Safe Outputs Collector MCP run: | @@ -1913,6 +1992,13 @@ jobs: chmod +x /tmp/gh-aw/safeoutputs/mcp-server.cjs - name: Setup MCPs + env: + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GH_AW_SAFE_OUTPUTS_CONFIG: ${{ toJSON(env.GH_AW_SAFE_OUTPUTS_CONFIG) }} + GH_AW_ASSETS_BRANCH: ${{ env.GH_AW_ASSETS_BRANCH }} + GH_AW_ASSETS_MAX_SIZE_KB: ${{ env.GH_AW_ASSETS_MAX_SIZE_KB }} + GH_AW_ASSETS_ALLOWED_EXTS: ${{ env.GH_AW_ASSETS_ALLOWED_EXTS }} run: | mkdir -p /tmp/gh-aw/mcp-config mkdir -p /home/runner/.copilot @@ -1932,7 +2018,7 @@ jobs: "GITHUB_READ_ONLY=1", "-e", "GITHUB_TOOLSETS=default", - "ghcr.io/github/github-mcp-server:v0.19.1" + "ghcr.io/github/github-mcp-server:v0.20.1" ], "tools": ["*"], "env": { @@ -1949,7 +2035,9 @@ jobs: "GH_AW_SAFE_OUTPUTS_CONFIG": "\${GH_AW_SAFE_OUTPUTS_CONFIG}", "GH_AW_ASSETS_BRANCH": "\${GH_AW_ASSETS_BRANCH}", "GH_AW_ASSETS_MAX_SIZE_KB": "\${GH_AW_ASSETS_MAX_SIZE_KB}", - "GH_AW_ASSETS_ALLOWED_EXTS": "\${GH_AW_ASSETS_ALLOWED_EXTS}" + "GH_AW_ASSETS_ALLOWED_EXTS": "\${GH_AW_ASSETS_ALLOWED_EXTS}", + "GITHUB_REPOSITORY": "\${GITHUB_REPOSITORY}", + "GITHUB_SERVER_URL": "\${GITHUB_SERVER_URL}" } }, "web-fetch": { @@ -1978,25 +2066,28 @@ jobs: GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} run: | mkdir -p $(dirname "$GH_AW_PROMPT") - cat > $GH_AW_PROMPT << 'PROMPT_EOF' + cat > "$GH_AW_PROMPT" << 'PROMPT_EOF' # Agentic Triage - You're a triage assistant for GitHub issues. Your task is to analyze issue #${{ github.event.issue.number }} and perform some initial triage tasks related to that issue. + You're a triage assistant for GitHub issues. Your task is to analyze issues created in the last 24 hours and perform initial triage tasks for each of them. - 1. Select appropriate labels for the issue from the provided list. + 1. First, use the `list_issues` tool to retrieve all issues created in the last 24 hours. Filter issues by using the `since` parameter with a timestamp from 24 hours ago (calculate: current time minus 24 hours in ISO 8601 format). - 2. Retrieve the issue content using the `get_issue` tool. If the issue is obviously spam, or generated by bot, or something else that is not an actual issue to be worked on, then add an issue comment to the issue with a one sentence analysis and exit the workflow. + 2. For each issue found, perform the following triage tasks: - 3. Next, use the GitHub tools to gather additional context about the issue: + 3. Select appropriate labels for the issue from the provided list. + + 4. Retrieve the issue content using the `get_issue` tool. If the issue is obviously spam, or generated by bot, or something else that is not an actual issue to be worked on, then add an issue comment to the issue with a one sentence analysis and move to the next issue. + + 5. Next, use the GitHub tools to gather additional context about the issue: - Fetch the list of labels available in this repository. Use 'gh label list' bash command to fetch the labels. This will give you the labels you can use for triaging issues. - Fetch any comments on the issue using the `get_issue_comments` tool - - Find similar issues if needed using the `search_issues` tool - - List the issues to see other open issues in the repository using the `list_issues` tool + - **Search for duplicate and related issues**: Use the `search_issues` tool to find similar issues by searching for key terms from the issue title and description. Look for both open and closed issues that might be related or duplicates. - 4. Analyze the issue content, considering: + 6. Analyze the issue content, considering: - The issue title and description - The type of issue (bug report, feature request, question, etc.) @@ -2005,9 +2096,9 @@ jobs: - User impact - Components affected - 5. Write notes, ideas, nudges, resource links, debugging strategies and/or reproduction steps for the team to consider relevant to the issue. + 7. Write notes, ideas, nudges, resource links, debugging strategies and/or reproduction steps for the team to consider relevant to the issue. - 6. Select appropriate labels from the available labels list provided above: + 8. Select appropriate labels from the available labels list provided above: - Choose labels that accurately reflect the issue's nature - Be specific but comprehensive @@ -2017,15 +2108,16 @@ jobs: - Only select labels from the provided list above - It's okay to not add any labels if none are clearly applicable - 7. Apply the selected labels: + 9. Apply the selected labels: - Use the `update_issue` tool to apply the labels to the issue - DO NOT communicate directly with users - If no labels are clearly applicable, do not apply any labels - 8. Add an issue comment to the issue with your analysis: + 10. Add an issue comment to the issue with your analysis: - Start with "🎯 Agentic Issue Triage" - Provide a brief summary of the issue + - **If duplicate or related issues were found**, add a section listing them with links (e.g., "### 🔗 Potentially Related Issues" followed by a bullet list of related issues with their titles and links) - Mention any relevant details that might help the team understand the issue better - Include any debugging strategies or reproduction steps if applicable - Suggest resources or links that might be helpful for resolving the issue or learning skills related to the issue or the particular area of the codebase affected by it @@ -2035,12 +2127,14 @@ jobs: - If appropriate break the issue down to sub-tasks and write a checklist of things to do. - Use collapsed-by-default sections in the GitHub markdown to keep the comment tidy. Collapse all sections except the short main summary at the top. + 11. After processing all issues, provide a summary of how many issues were triaged. If no issues were created in the last 24 hours, simply note that no new issues needed triage. + PROMPT_EOF - name: Append XPIA security instructions to prompt env: GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt run: | - cat >> $GH_AW_PROMPT << 'PROMPT_EOF' + cat >> "$GH_AW_PROMPT" << 'PROMPT_EOF' --- @@ -2072,7 +2166,7 @@ jobs: env: GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt run: | - cat >> $GH_AW_PROMPT << 'PROMPT_EOF' + cat >> "$GH_AW_PROMPT" << 'PROMPT_EOF' --- @@ -2085,7 +2179,7 @@ jobs: env: GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt run: | - cat >> $GH_AW_PROMPT << 'PROMPT_EOF' + cat >> "$GH_AW_PROMPT" << 'PROMPT_EOF' --- @@ -2110,7 +2204,7 @@ jobs: env: GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt run: | - cat >> $GH_AW_PROMPT << 'PROMPT_EOF' + cat >> "$GH_AW_PROMPT" << 'PROMPT_EOF' --- @@ -2179,14 +2273,14 @@ jobs: env: GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt run: | - echo "
" >> $GITHUB_STEP_SUMMARY - echo "Generated Prompt" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo '```markdown' >> $GITHUB_STEP_SUMMARY - cat $GH_AW_PROMPT >> $GITHUB_STEP_SUMMARY - echo '```' >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "
" >> $GITHUB_STEP_SUMMARY + echo "
" >> "$GITHUB_STEP_SUMMARY" + echo "Generated Prompt" >> "$GITHUB_STEP_SUMMARY" + echo "" >> "$GITHUB_STEP_SUMMARY" + echo '```markdown' >> "$GITHUB_STEP_SUMMARY" + cat "$GH_AW_PROMPT" >> "$GITHUB_STEP_SUMMARY" + echo '```' >> "$GITHUB_STEP_SUMMARY" + echo "" >> "$GITHUB_STEP_SUMMARY" + echo "
" >> "$GITHUB_STEP_SUMMARY" - name: Upload prompt if: always() uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 @@ -2194,13 +2288,6 @@ jobs: name: prompt.txt path: /tmp/gh-aw/aw-prompts/prompt.txt if-no-files-found: warn - - name: Capture agent version - run: | - VERSION_OUTPUT=$(copilot --version 2>&1 || echo "unknown") - # Extract semantic version pattern (e.g., 1.2.3, v1.2.3-beta) - CLEAN_VERSION=$(echo "$VERSION_OUTPUT" | grep -oE 'v?[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9]+)?' | head -n1 || echo "unknown") - echo "AGENT_VERSION=$CLEAN_VERSION" >> $GITHUB_ENV - echo "Agent version: $VERSION_OUTPUT" - name: Generate agentic run info uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd with: @@ -2212,7 +2299,7 @@ jobs: engine_name: "GitHub Copilot CLI", model: "", version: "", - agent_version: process.env.AGENT_VERSION || "", + agent_version: "0.0.353", workflow_name: "Agentic Triage", experimental: false, supports_tools_allowlist: true, @@ -2226,6 +2313,9 @@ jobs: actor: context.actor, event_name: context.eventName, staged: false, + steps: { + firewall: "" + }, created_at: new Date().toISOString() }; @@ -2262,9 +2352,12 @@ jobs: GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} GH_AW_SAFE_OUTPUTS_CONFIG: "{\"add_comment\":{\"max\":1},\"add_labels\":{\"max\":5},\"missing_tool\":{}}" + GITHUB_HEAD_REF: ${{ github.head_ref }} GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + GITHUB_REF_NAME: ${{ github.ref_name }} GITHUB_STEP_SUMMARY: ${{ env.GITHUB_STEP_SUMMARY }} GITHUB_TOKEN: ${{ secrets.COPILOT_CLI_TOKEN }} + GITHUB_WORKSPACE: ${{ github.workspace }} XDG_CONFIG_HOME: /home/runner - name: Redact secrets in logs if: always() @@ -2399,71 +2492,135 @@ jobs: script: | async function main() { const fs = require("fs"); - const maxBodyLength = 65000; - function sanitizeContent(content, maxLength) { - if (!content || typeof content !== "string") { - return ""; - } - const allowedDomainsEnv = process.env.GH_AW_ALLOWED_DOMAINS; - const defaultAllowedDomains = ["github.com", "github.io", "githubusercontent.com", "githubassets.com", "github.dev", "codespaces.new"]; - const allowedDomains = allowedDomainsEnv - ? allowedDomainsEnv - .split(",") - .map(d => d.trim()) - .filter(d => d) - : defaultAllowedDomains; - let sanitized = content; - sanitized = neutralizeMentions(sanitized); - sanitized = removeXmlComments(sanitized); - sanitized = sanitized.replace(/\x1b\[[0-9;]*[mGKH]/g, ""); - sanitized = sanitized.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g, ""); - sanitized = sanitizeUrlProtocols(sanitized); - sanitized = sanitizeUrlDomains(sanitized); - const lines = sanitized.split("\n"); - const maxLines = 65000; - maxLength = maxLength || 524288; - if (lines.length > maxLines) { - const truncationMsg = "\n[Content truncated due to line count]"; - const truncatedLines = lines.slice(0, maxLines).join("\n") + truncationMsg; - if (truncatedLines.length > maxLength) { - sanitized = truncatedLines.substring(0, maxLength - truncationMsg.length) + truncationMsg; - } else { - sanitized = truncatedLines; - } - } else if (sanitized.length > maxLength) { - sanitized = sanitized.substring(0, maxLength) + "\n[Content truncated due to length]"; - } - sanitized = neutralizeBotTriggers(sanitized); - return sanitized.trim(); - function sanitizeUrlDomains(s) { - return s.replace(/\bhttps:\/\/[^\s\])}'"<>&\x00-\x1f,;]+/gi, match => { - const urlAfterProtocol = match.slice(8); - const hostname = urlAfterProtocol.split(/[\/:\?#]/)[0].toLowerCase(); - const isAllowed = allowedDomains.some(allowedDomain => { - const normalizedAllowed = allowedDomain.toLowerCase(); - return hostname === normalizedAllowed || hostname.endsWith("." + normalizedAllowed); - }); - return isAllowed ? match : "(redacted)"; - }); - } - function sanitizeUrlProtocols(s) { - return s.replace(/\b(\w+):\/\/[^\s\])}'"<>&\x00-\x1f]+/gi, (match, protocol) => { - return protocol.toLowerCase() === "https" ? match : "(redacted)"; - }); - } - function neutralizeMentions(s) { - return s.replace( - /(^|[^\w`])@([A-Za-z0-9](?:[A-Za-z0-9-]{0,37}[A-Za-z0-9])?(?:\/[A-Za-z0-9._-]+)?)/g, - (_m, p1, p2) => `${p1}\`@${p2}\`` - ); - } - function removeXmlComments(s) { - return s.replace(//g, "").replace(//g, ""); - } - function neutralizeBotTriggers(s) { - return s.replace(/\b(fixes?|closes?|resolves?|fix|close|resolve)\s+#(\w+)/gi, (match, action, ref) => `\`${action} #${ref}\``); - } + function sanitizeContent(content, maxLength) { + if (!content || typeof content !== "string") { + return ""; } + const allowedDomainsEnv = process.env.GH_AW_ALLOWED_DOMAINS; + const defaultAllowedDomains = ["github.com", "github.io", "githubusercontent.com", "githubassets.com", "github.dev", "codespaces.new"]; + const allowedDomains = allowedDomainsEnv + ? allowedDomainsEnv + .split(",") + .map(d => d.trim()) + .filter(d => d) + : defaultAllowedDomains; + let sanitized = content; + sanitized = neutralizeCommands(sanitized); + sanitized = neutralizeMentions(sanitized); + sanitized = removeXmlComments(sanitized); + sanitized = convertXmlTags(sanitized); + sanitized = sanitized.replace(/\x1b\[[0-9;]*[mGKH]/g, ""); + sanitized = sanitized.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g, ""); + sanitized = sanitizeUrlProtocols(sanitized); + sanitized = sanitizeUrlDomains(sanitized); + const lines = sanitized.split("\n"); + const maxLines = 65000; + maxLength = maxLength || 524288; + if (lines.length > maxLines) { + const truncationMsg = "\n[Content truncated due to line count]"; + const truncatedLines = lines.slice(0, maxLines).join("\n") + truncationMsg; + if (truncatedLines.length > maxLength) { + sanitized = truncatedLines.substring(0, maxLength - truncationMsg.length) + truncationMsg; + } else { + sanitized = truncatedLines; + } + } else if (sanitized.length > maxLength) { + sanitized = sanitized.substring(0, maxLength) + "\n[Content truncated due to length]"; + } + sanitized = neutralizeBotTriggers(sanitized); + return sanitized.trim(); + function sanitizeUrlDomains(s) { + s = s.replace(/\bhttps:\/\/([^\s\])}'"<>&\x00-\x1f,;]+)/gi, (match, rest) => { + const hostname = rest.split(/[\/:\?#]/)[0].toLowerCase(); + const isAllowed = allowedDomains.some(allowedDomain => { + const normalizedAllowed = allowedDomain.toLowerCase(); + return hostname === normalizedAllowed || hostname.endsWith("." + normalizedAllowed); + }); + if (isAllowed) { + return match; + } + const domain = hostname; + const truncated = domain.length > 12 ? domain.substring(0, 12) + "..." : domain; + core.info(`Redacted URL: ${truncated}`); + core.debug(`Redacted URL (full): ${match}`); + const urlParts = match.split(/([?&#])/); + let result = "(redacted)"; + for (let i = 1; i < urlParts.length; i++) { + if (urlParts[i].match(/^[?&#]$/)) { + result += urlParts[i]; + } else { + result += sanitizeUrlDomains(urlParts[i]); + } + } + return result; + }); + return s; + } + function sanitizeUrlProtocols(s) { + return s.replace(/(?&\x00-\x1f]+/g, (match, protocol) => { + if (protocol.toLowerCase() === "https") { + return match; + } + if (match.includes("::")) { + return match; + } + if (match.includes("://")) { + const domainMatch = match.match(/^[^:]+:\/\/([^\/\s?#]+)/); + const domain = domainMatch ? domainMatch[1] : match; + const truncated = domain.length > 12 ? domain.substring(0, 12) + "..." : domain; + core.info(`Redacted URL: ${truncated}`); + core.debug(`Redacted URL (full): ${match}`); + return "(redacted)"; + } + const dangerousProtocols = ["javascript", "data", "vbscript", "file", "about", "mailto", "tel", "ssh", "ftp"]; + if (dangerousProtocols.includes(protocol.toLowerCase())) { + const truncated = match.length > 12 ? match.substring(0, 12) + "..." : match; + core.info(`Redacted URL: ${truncated}`); + core.debug(`Redacted URL (full): ${match}`); + return "(redacted)"; + } + return match; + }); + } + function neutralizeCommands(s) { + const commandName = process.env.GH_AW_COMMAND; + if (!commandName) { + return s; + } + const escapedCommand = commandName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); + return s.replace(new RegExp(`^(\\s*)/(${escapedCommand})\\b`, "i"), "$1`/$2`"); + } + function neutralizeMentions(s) { + return s.replace( + /(^|[^\w`])@([A-Za-z0-9](?:[A-Za-z0-9-]{0,37}[A-Za-z0-9])?(?:\/[A-Za-z0-9._-]+)?)/g, + (_m, p1, p2) => `${p1}\`@${p2}\`` + ); + } + function removeXmlComments(s) { + return s.replace(//g, "").replace(//g, ""); + } + function convertXmlTags(s) { + const allowedTags = ["details", "summary", "code", "em", "b"]; + s = s.replace(//g, (match, content) => { + const convertedContent = content.replace(/<(\/?[A-Za-z][A-Za-z0-9]*(?:[^>]*?))>/g, "($1)"); + return `(![CDATA[${convertedContent}]])`; + }); + return s.replace(/<(\/?[A-Za-z!][^>]*?)>/g, (match, tagContent) => { + const tagNameMatch = tagContent.match(/^\/?\s*([A-Za-z][A-Za-z0-9]*)/); + if (tagNameMatch) { + const tagName = tagNameMatch[1].toLowerCase(); + if (allowedTags.includes(tagName)) { + return match; + } + } + return `(${tagContent})`; + }); + } + function neutralizeBotTriggers(s) { + return s.replace(/\b(fixes?|closes?|resolves?|fix|close|resolve)\s+#(\w+)/gi, (match, action, ref) => `\`${action} #${ref}\``); + } + } + const maxBodyLength = 65000; function getMaxAllowedForType(itemType, config) { const itemConfig = config?.[itemType]; if (itemConfig && typeof itemConfig === "object" && "max" in itemConfig && itemConfig.max) { @@ -4295,7 +4452,9 @@ jobs: detection: needs: agent runs-on: ubuntu-latest - permissions: read-all + permissions: {} + concurrency: + group: "gh-aw-copilot-${{ github.workflow }}" timeout-minutes: 10 steps: - name: Download prompt artifact @@ -4444,11 +4603,11 @@ jobs: env: COPILOT_CLI_TOKEN: ${{ secrets.COPILOT_CLI_TOKEN }} - name: Setup Node.js - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 + uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 with: node-version: '24' - name: Install GitHub Copilot CLI - run: npm install -g @github/copilot@0.0.351 + run: npm install -g @github/copilot@0.0.353 - name: Execute GitHub Copilot CLI id: agentic_execution # Copilot CLI tool arguments (sorted): @@ -4471,8 +4630,11 @@ jobs: env: COPILOT_AGENT_RUNNER_TYPE: STANDALONE GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt + GITHUB_HEAD_REF: ${{ github.head_ref }} + GITHUB_REF_NAME: ${{ github.ref_name }} GITHUB_STEP_SUMMARY: ${{ env.GITHUB_STEP_SUMMARY }} GITHUB_TOKEN: ${{ secrets.COPILOT_CLI_TOKEN }} + GITHUB_WORKSPACE: ${{ github.workspace }} XDG_CONFIG_HOME: /home/runner - name: Parse threat detection results uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd @@ -4522,8 +4684,8 @@ jobs: needs: - agent - detection - if: (!cancelled()) && (contains(needs.agent.outputs.output_types, 'missing_tool')) - runs-on: ubuntu-latest + if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'missing_tool')) + runs-on: ubuntu-slim permissions: contents: read timeout-minutes: 5 @@ -4651,89 +4813,15 @@ jobs: }); pre_activation: - runs-on: ubuntu-latest + runs-on: ubuntu-slim outputs: - activated: ${{ (steps.check_membership.outputs.is_team_member == 'true') && (steps.check_stop_time.outputs.stop_time_ok == 'true') }} + activated: ${{ steps.check_stop_time.outputs.stop_time_ok == 'true' }} steps: - - name: Check team membership for workflow - id: check_membership - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd - env: - GH_AW_REQUIRED_ROLES: admin,maintainer,write - with: - script: | - async function main() { - const { eventName } = context; - const actor = context.actor; - const { owner, repo } = context.repo; - const requiredPermissionsEnv = process.env.GH_AW_REQUIRED_ROLES; - const requiredPermissions = requiredPermissionsEnv ? requiredPermissionsEnv.split(",").filter(p => p.trim() !== "") : []; - if (eventName === "workflow_dispatch") { - const hasWriteRole = requiredPermissions.includes("write"); - if (hasWriteRole) { - core.info(`✅ Event ${eventName} does not require validation (write role allowed)`); - core.setOutput("is_team_member", "true"); - core.setOutput("result", "safe_event"); - return; - } - core.info(`Event ${eventName} requires validation (write role not allowed)`); - } - const safeEvents = ["workflow_run", "schedule"]; - if (safeEvents.includes(eventName)) { - core.info(`✅ Event ${eventName} does not require validation`); - core.setOutput("is_team_member", "true"); - core.setOutput("result", "safe_event"); - return; - } - if (!requiredPermissions || requiredPermissions.length === 0) { - core.warning("❌ Configuration error: Required permissions not specified. Contact repository administrator."); - core.setOutput("is_team_member", "false"); - core.setOutput("result", "config_error"); - core.setOutput("error_message", "Configuration error: Required permissions not specified"); - return; - } - try { - core.info(`Checking if user '${actor}' has required permissions for ${owner}/${repo}`); - core.info(`Required permissions: ${requiredPermissions.join(", ")}`); - const repoPermission = await github.rest.repos.getCollaboratorPermissionLevel({ - owner: owner, - repo: repo, - username: actor, - }); - const permission = repoPermission.data.permission; - core.info(`Repository permission level: ${permission}`); - for (const requiredPerm of requiredPermissions) { - if (permission === requiredPerm || (requiredPerm === "maintainer" && permission === "maintain")) { - core.info(`✅ User has ${permission} access to repository`); - core.setOutput("is_team_member", "true"); - core.setOutput("result", "authorized"); - core.setOutput("user_permission", permission); - return; - } - } - core.warning(`User permission '${permission}' does not meet requirements: ${requiredPermissions.join(", ")}`); - core.setOutput("is_team_member", "false"); - core.setOutput("result", "insufficient_permissions"); - core.setOutput("user_permission", permission); - core.setOutput( - "error_message", - `Access denied: User '${actor}' is not authorized. Required permissions: ${requiredPermissions.join(", ")}` - ); - } catch (repoError) { - const errorMessage = repoError instanceof Error ? repoError.message : String(repoError); - core.warning(`Repository permission check failed: ${errorMessage}`); - core.setOutput("is_team_member", "false"); - core.setOutput("result", "api_error"); - core.setOutput("error_message", `Repository permission check failed: ${errorMessage}`); - return; - } - } - await main(); - name: Check stop-time limit id: check_stop_time uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd env: - GH_AW_STOP_TIME: 2025-11-27 03:00:29 + GH_AW_STOP_TIME: 2025-12-03 20:01:19 GH_AW_WORKFLOW_NAME: "Agentic Triage" with: script: | @@ -4776,7 +4864,7 @@ jobs: if: > (((((always()) && (needs.agent.result != 'skipped')) && (needs.activation.outputs.comment_id)) && (!contains(needs.agent.outputs.output_types, 'add_comment'))) && (!contains(needs.agent.outputs.output_types, 'create_pull_request'))) && (!contains(needs.agent.outputs.output_types, 'push_to_pull_request_branch')) - runs-on: ubuntu-latest + runs-on: ubuntu-slim permissions: contents: read discussions: write diff --git a/.github/workflows/issue-triage.md b/.github/workflows/issue-triage.md index 2b4739988a..087f009106 100644 --- a/.github/workflows/issue-triage.md +++ b/.github/workflows/issue-triage.md @@ -1,7 +1,8 @@ --- on: - issues: - types: [opened, reopened] + schedule: + - cron: '0 0 * * *' # Run daily at midnight UTC + workflow_dispatch: # Enable manual trigger stop-after: +30d # workflow will no longer trigger after 30 days. Remove this and recompile to run indefinitely reaction: eyes @@ -25,20 +26,23 @@ source: githubnext/agentics/workflows/issue-triage.md@0837fb7b24c3b84ee77fb7c8cf -You're a triage assistant for GitHub issues. Your task is to analyze issue #${{ github.event.issue.number }} and perform some initial triage tasks related to that issue. +You're a triage assistant for GitHub issues. Your task is to analyze issues created in the last 24 hours and perform initial triage tasks for each of them. -1. Select appropriate labels for the issue from the provided list. +1. First, use the `list_issues` tool to retrieve all issues created in the last 24 hours. Filter issues by using the `since` parameter with a timestamp from 24 hours ago (calculate: current time minus 24 hours in ISO 8601 format). -2. Retrieve the issue content using the `get_issue` tool. If the issue is obviously spam, or generated by bot, or something else that is not an actual issue to be worked on, then add an issue comment to the issue with a one sentence analysis and exit the workflow. +2. For each issue found, perform the following triage tasks: -3. Next, use the GitHub tools to gather additional context about the issue: +3. Select appropriate labels for the issue from the provided list. + +4. Retrieve the issue content using the `get_issue` tool. If the issue is obviously spam, or generated by bot, or something else that is not an actual issue to be worked on, then add an issue comment to the issue with a one sentence analysis and move to the next issue. + +5. Next, use the GitHub tools to gather additional context about the issue: - Fetch the list of labels available in this repository. Use 'gh label list' bash command to fetch the labels. This will give you the labels you can use for triaging issues. - Fetch any comments on the issue using the `get_issue_comments` tool - - Find similar issues if needed using the `search_issues` tool - - List the issues to see other open issues in the repository using the `list_issues` tool + - **Search for duplicate and related issues**: Use the `search_issues` tool to find similar issues by searching for key terms from the issue title and description. Look for both open and closed issues that might be related or duplicates. -4. Analyze the issue content, considering: +6. Analyze the issue content, considering: - The issue title and description - The type of issue (bug report, feature request, question, etc.) @@ -47,9 +51,9 @@ You're a triage assistant for GitHub issues. Your task is to analyze issue #${{ - User impact - Components affected -5. Write notes, ideas, nudges, resource links, debugging strategies and/or reproduction steps for the team to consider relevant to the issue. +7. Write notes, ideas, nudges, resource links, debugging strategies and/or reproduction steps for the team to consider relevant to the issue. -6. Select appropriate labels from the available labels list provided above: +8. Select appropriate labels from the available labels list provided above: - Choose labels that accurately reflect the issue's nature - Be specific but comprehensive @@ -59,15 +63,16 @@ You're a triage assistant for GitHub issues. Your task is to analyze issue #${{ - Only select labels from the provided list above - It's okay to not add any labels if none are clearly applicable -7. Apply the selected labels: +9. Apply the selected labels: - Use the `update_issue` tool to apply the labels to the issue - DO NOT communicate directly with users - If no labels are clearly applicable, do not apply any labels -8. Add an issue comment to the issue with your analysis: +10. Add an issue comment to the issue with your analysis: - Start with "🎯 Agentic Issue Triage" - Provide a brief summary of the issue + - **If duplicate or related issues were found**, add a section listing them with links (e.g., "### 🔗 Potentially Related Issues" followed by a bullet list of related issues with their titles and links) - Mention any relevant details that might help the team understand the issue better - Include any debugging strategies or reproduction steps if applicable - Suggest resources or links that might be helpful for resolving the issue or learning skills related to the issue or the particular area of the codebase affected by it @@ -76,3 +81,5 @@ You're a triage assistant for GitHub issues. Your task is to analyze issue #${{ - If you have any debugging strategies, include them in the comment - If appropriate break the issue down to sub-tasks and write a checklist of things to do. - Use collapsed-by-default sections in the GitHub markdown to keep the comment tidy. Collapse all sections except the short main summary at the top. + +11. After processing all issues, provide a summary of how many issues were triaged. If no issues were created in the last 24 hours, simply note that no new issues needed triage. diff --git a/Dockerfile b/Dockerfile index 3d03ce112c..d097edf0ca 100755 --- a/Dockerfile +++ b/Dockerfile @@ -12,7 +12,7 @@ RUN composer install --ignore-platform-reqs --optimize-autoloader \ --no-plugins --no-scripts --prefer-dist \ `if [ "$TESTING" != "true" ]; then echo "--no-dev"; fi` -FROM appwrite/base:0.10.4 AS final +FROM appwrite/base:0.10.5 AS final LABEL maintainer="team@appwrite.io" @@ -28,8 +28,6 @@ RUN \ apk add boost boost-dev; \ fi -RUN apk add libwebp - WORKDIR /usr/src/code COPY --from=composer /usr/local/src/vendor /usr/src/code/vendor diff --git a/README.md b/README.md index 1f24d5c5f2..50c1ed399b 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -> We just announced Transactions API for Appwrite Databases - [Learn more](https://appwrite.io/blog/post/announcing-transactions-api) +> We just announced DB operators for Appwrite Databases - [Learn more](https://appwrite.io/blog/post/announcing-db-operators) > Appwrite Cloud is now Generally Available - [Learn more](https://appwrite.io/cloud-ga) diff --git a/app/config/collections/common.php b/app/config/collections/common.php index 6de7eb224b..d8e1c1699a 100644 --- a/app/config/collections/common.php +++ b/app/config/collections/common.php @@ -364,6 +364,61 @@ return [ 'array' => false, 'filters' => ['datetime'], ], + [ + '$id' => ID::custom('emailCanonical'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 320, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('emailIsFree'), + 'type' => Database::VAR_BOOLEAN, + 'format' => '', + 'size' => 0, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('emailIsDisposable'), + 'type' => Database::VAR_BOOLEAN, + 'format' => '', + 'size' => 0, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('emailIsCorporate'), + 'type' => Database::VAR_BOOLEAN, + 'format' => '', + 'size' => 0, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('emailIsCanonical'), + 'type' => Database::VAR_BOOLEAN, + 'format' => '', + 'size' => 0, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], ], 'indexes' => [ [ diff --git a/app/config/collections/projects.php b/app/config/collections/projects.php index bf0cee3527..dae0337dc9 100644 --- a/app/config/collections/projects.php +++ b/app/config/collections/projects.php @@ -2345,7 +2345,7 @@ return [ '$id' => ID::custom('errors'), 'type' => Database::VAR_STRING, 'format' => '', - 'size' => 65535, + 'size' => 1_000_000, 'signed' => true, 'required' => true, 'default' => null, diff --git a/app/config/frameworks.php b/app/config/frameworks.php index 0ab4a8a7db..47e26ac91e 100644 --- a/app/config/frameworks.php +++ b/app/config/frameworks.php @@ -273,7 +273,7 @@ return [ 'key' => 'flutter', 'name' => 'Flutter', 'screenshotSleep' => 5000, - 'buildRuntime' => 'flutter-3.29', + 'buildRuntime' => 'flutter-3.35', 'runtimes' => getVersions($templateRuntimes['FLUTTER']['versions'], 'flutter'), 'adapters' => [ 'static' => [ @@ -282,6 +282,7 @@ return [ 'installCommand' => 'flutter pub get', 'outputDirectory' => './build/web', 'startCommand' => 'bash helpers/server.sh', + 'fallbackFile' => 'index.html' ], ], ], diff --git a/app/config/platforms.php b/app/config/platforms.php index 361ec6b935..2b5c107648 100644 --- a/app/config/platforms.php +++ b/app/config/platforms.php @@ -60,7 +60,7 @@ return [ [ 'key' => 'flutter', 'name' => 'Flutter', - 'version' => '20.3.0', + 'version' => '20.3.1', 'url' => 'https://github.com/appwrite/sdk-for-flutter', 'package' => 'https://pub.dev/packages/appwrite', 'enabled' => true, @@ -226,7 +226,7 @@ return [ [ 'key' => 'cli', 'name' => 'Command Line', - 'version' => '11.1.0', + 'version' => '11.1.1', 'url' => 'https://github.com/appwrite/sdk-for-cli', 'package' => 'https://www.npmjs.com/package/appwrite-cli', 'enabled' => true, @@ -281,7 +281,7 @@ return [ [ 'key' => 'php', 'name' => 'PHP', - 'version' => '17.5.0', + 'version' => '18.0.1', 'url' => 'https://github.com/appwrite/sdk-for-php', 'package' => 'https://packagist.org/packages/appwrite/appwrite', 'enabled' => true, @@ -376,7 +376,7 @@ return [ [ 'key' => 'dart', 'name' => 'Dart', - 'version' => '19.3.0', + 'version' => '19.4.0', 'url' => 'https://github.com/appwrite/sdk-for-dart', 'package' => 'https://pub.dev/packages/dart_appwrite', 'enabled' => true, diff --git a/app/config/specs/open-api3-1.8.x-client.json b/app/config/specs/open-api3-1.8.x-client.json index dde8390820..9adb21d960 100644 --- a/app/config/specs/open-api3-1.8.x-client.json +++ b/app/config/specs/open-api3-1.8.x-client.json @@ -6313,7 +6313,8 @@ "x-example": "[\"read(\"any\")\"]", "items": { "type": "string" - } + }, + "x-nullable": true }, "documents": { "type": "array", @@ -6326,7 +6327,8 @@ "transactionId": { "type": "string", "description": "Transaction ID for staging the operation.", - "x-example": "" + "x-example": "", + "x-nullable": true } } } @@ -6583,12 +6585,14 @@ "x-example": "[\"read(\"any\")\"]", "items": { "type": "string" - } + }, + "x-nullable": true }, "transactionId": { "type": "string", "description": "Transaction ID for staging the operation.", - "x-example": "" + "x-example": "", + "x-nullable": true } }, "required": [ @@ -6701,12 +6705,14 @@ "x-example": "[\"read(\"any\")\"]", "items": { "type": "string" - } + }, + "x-nullable": true }, "transactionId": { "type": "string", "description": "Transaction ID for staging the operation.", - "x-example": "" + "x-example": "", + "x-nullable": true } } } @@ -6801,7 +6807,8 @@ "transactionId": { "type": "string", "description": "Transaction ID for staging the operation.", - "x-example": "" + "x-example": "", + "x-nullable": true } } } @@ -6920,12 +6927,14 @@ "min": { "type": "number", "description": "Minimum value for the attribute. If the current value is lesser than this value, an exception will be thrown.", - "x-example": null + "x-example": null, + "x-nullable": true }, "transactionId": { "type": "string", "description": "Transaction ID for staging the operation.", - "x-example": "" + "x-example": "", + "x-nullable": true } } } @@ -7044,12 +7053,14 @@ "max": { "type": "number", "description": "Maximum value for the attribute. If the current value is greater than this value, an error will be thrown.", - "x-example": null + "x-example": null, + "x-nullable": true }, "transactionId": { "type": "string", "description": "Transaction ID for staging the operation.", - "x-example": "" + "x-example": "", + "x-nullable": true } } } @@ -7251,7 +7262,8 @@ "scheduledAt": { "type": "string", "description": "Scheduled execution time in [ISO 8601](https:\/\/www.iso.org\/iso-8601-date-and-time-format.html) format. DateTime value must be in future with precision in minutes.", - "x-example": "" + "x-example": "", + "x-nullable": true } } } @@ -8194,7 +8206,8 @@ "x-example": "[\"read(\"any\")\"]", "items": { "type": "string" - } + }, + "x-nullable": true } }, "required": [ @@ -8360,7 +8373,8 @@ "name": { "type": "string", "description": "Name of the file", - "x-example": "" + "x-example": "", + "x-nullable": true }, "permissions": { "type": "array", @@ -8368,7 +8382,8 @@ "x-example": "[\"read(\"any\")\"]", "items": { "type": "string" - } + }, + "x-nullable": true } } } @@ -9500,7 +9515,8 @@ "x-example": "[\"read(\"any\")\"]", "items": { "type": "string" - } + }, + "x-nullable": true }, "rows": { "type": "array", @@ -9513,7 +9529,8 @@ "transactionId": { "type": "string", "description": "Transaction ID for staging the operation.", - "x-example": "" + "x-example": "", + "x-nullable": true } } } @@ -9763,12 +9780,14 @@ "x-example": "[\"read(\"any\")\"]", "items": { "type": "string" - } + }, + "x-nullable": true }, "transactionId": { "type": "string", "description": "Transaction ID for staging the operation.", - "x-example": "" + "x-example": "", + "x-nullable": true } } } @@ -9877,12 +9896,14 @@ "x-example": "[\"read(\"any\")\"]", "items": { "type": "string" - } + }, + "x-nullable": true }, "transactionId": { "type": "string", "description": "Transaction ID for staging the operation.", - "x-example": "" + "x-example": "", + "x-nullable": true } } } @@ -9976,7 +9997,8 @@ "transactionId": { "type": "string", "description": "Transaction ID for staging the operation.", - "x-example": "" + "x-example": "", + "x-nullable": true } } } @@ -10094,12 +10116,14 @@ "min": { "type": "number", "description": "Minimum value for the column. If the current value is lesser than this value, an exception will be thrown.", - "x-example": null + "x-example": null, + "x-nullable": true }, "transactionId": { "type": "string", "description": "Transaction ID for staging the operation.", - "x-example": "" + "x-example": "", + "x-nullable": true } } } @@ -10217,12 +10241,14 @@ "max": { "type": "number", "description": "Maximum value for the column. If the current value is greater than this value, an error will be thrown.", - "x-example": null + "x-example": null, + "x-nullable": true }, "transactionId": { "type": "string", "description": "Transaction ID for staging the operation.", - "x-example": "" + "x-example": "", + "x-nullable": true } } } diff --git a/app/config/specs/open-api3-1.8.x-console.json b/app/config/specs/open-api3-1.8.x-console.json index aa1e81dbeb..6b6ab7409f 100644 --- a/app/config/specs/open-api3-1.8.x-console.json +++ b/app/config/specs/open-api3-1.8.x-console.json @@ -7059,7 +7059,8 @@ "x-example": "[\"read(\"any\")\"]", "items": { "type": "string" - } + }, + "x-nullable": true }, "documentSecurity": { "type": "boolean", @@ -7245,7 +7246,8 @@ "x-example": "[\"read(\"any\")\"]", "items": { "type": "string" - } + }, + "x-nullable": true }, "documentSecurity": { "type": "boolean", @@ -7524,7 +7526,8 @@ "default": { "type": "boolean", "description": "Default value for attribute when not provided. Cannot be set when attribute is required.", - "x-example": false + "x-example": false, + "x-nullable": true }, "array": { "type": "boolean", @@ -7644,7 +7647,8 @@ "newKey": { "type": "string", "description": "New attribute key.", - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -7869,7 +7873,8 @@ "newKey": { "type": "string", "description": "New attribute key.", - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -7974,7 +7979,8 @@ "default": { "type": "string", "description": "Default value for attribute when not provided. Cannot be set when attribute is required.", - "x-example": "email@example.com" + "x-example": "email@example.com", + "x-nullable": true }, "array": { "type": "boolean", @@ -8094,7 +8100,8 @@ "newKey": { "type": "string", "description": "New Attribute Key.", - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -8207,7 +8214,8 @@ "default": { "type": "string", "description": "Default value for attribute when not provided. Cannot be set when attribute is required.", - "x-example": "" + "x-example": "", + "x-nullable": true }, "array": { "type": "boolean", @@ -8336,7 +8344,8 @@ "newKey": { "type": "string", "description": "New Attribute Key.", - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -8442,17 +8451,20 @@ "min": { "type": "number", "description": "Minimum value.", - "x-example": null + "x-example": null, + "x-nullable": true }, "max": { "type": "number", "description": "Maximum value.", - "x-example": null + "x-example": null, + "x-nullable": true }, "default": { "type": "number", "description": "Default value. Cannot be set when required.", - "x-example": null + "x-example": null, + "x-nullable": true }, "array": { "type": "boolean", @@ -8566,12 +8578,14 @@ "min": { "type": "number", "description": "Minimum value.", - "x-example": null + "x-example": null, + "x-nullable": true }, "max": { "type": "number", "description": "Maximum value.", - "x-example": null + "x-example": null, + "x-nullable": true }, "default": { "type": "number", @@ -8582,7 +8596,8 @@ "newKey": { "type": "string", "description": "New Attribute Key.", - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -8687,17 +8702,20 @@ "min": { "type": "integer", "description": "Minimum value", - "x-example": null + "x-example": null, + "x-nullable": true }, "max": { "type": "integer", "description": "Maximum value", - "x-example": null + "x-example": null, + "x-nullable": true }, "default": { "type": "integer", "description": "Default value. Cannot be set when attribute is required.", - "x-example": null + "x-example": null, + "x-nullable": true }, "array": { "type": "boolean", @@ -8811,12 +8829,14 @@ "min": { "type": "integer", "description": "Minimum value", - "x-example": null + "x-example": null, + "x-nullable": true }, "max": { "type": "integer", "description": "Maximum value", - "x-example": null + "x-example": null, + "x-nullable": true }, "default": { "type": "integer", @@ -8827,7 +8847,8 @@ "newKey": { "type": "string", "description": "New Attribute Key.", - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -8932,7 +8953,8 @@ "default": { "type": "string", "description": "Default value. Cannot be set when attribute is required.", - "x-example": null + "x-example": null, + "x-nullable": true }, "array": { "type": "boolean", @@ -9052,7 +9074,8 @@ "newKey": { "type": "string", "description": "New Attribute Key.", - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -9287,7 +9310,8 @@ "newKey": { "type": "string", "description": "New attribute key.", - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -9521,7 +9545,8 @@ "newKey": { "type": "string", "description": "New attribute key.", - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -9755,7 +9780,8 @@ "newKey": { "type": "string", "description": "New attribute key.", - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -9872,12 +9898,14 @@ "key": { "type": "string", "description": "Attribute Key.", - "x-example": null + "x-example": null, + "x-nullable": true }, "twoWayKey": { "type": "string", "description": "Two Way Attribute Key.", - "x-example": null + "x-example": null, + "x-nullable": true }, "onDelete": { "type": "string", @@ -9999,7 +10027,8 @@ "default": { "type": "string", "description": "Default value for attribute when not provided. Cannot be set when attribute is required.", - "x-example": "" + "x-example": "", + "x-nullable": true }, "array": { "type": "boolean", @@ -10125,12 +10154,14 @@ "size": { "type": "integer", "description": "Maximum size of the string attribute.", - "x-example": 1 + "x-example": 1, + "x-nullable": true }, "newKey": { "type": "string", "description": "New Attribute Key.", - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -10235,7 +10266,8 @@ "default": { "type": "string", "description": "Default value for attribute when not provided. Cannot be set when attribute is required.", - "x-example": "https:\/\/example.com" + "x-example": "https:\/\/example.com", + "x-nullable": true }, "array": { "type": "boolean", @@ -10355,7 +10387,8 @@ "newKey": { "type": "string", "description": "New Attribute Key.", - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -10656,12 +10689,14 @@ "setNull" ], "x-enum-name": "RelationMutate", - "x-enum-keys": [] + "x-enum-keys": [], + "x-nullable": true }, "newKey": { "type": "string", "description": "New Attribute Key.", - "x-example": null + "x-example": null, + "x-nullable": true } } } @@ -10945,7 +10980,8 @@ "x-example": "[\"read(\"any\")\"]", "items": { "type": "string" - } + }, + "x-nullable": true }, "documents": { "type": "array", @@ -10958,7 +10994,8 @@ "transactionId": { "type": "string", "description": "Transaction ID for staging the operation.", - "x-example": "" + "x-example": "", + "x-nullable": true } } } @@ -11089,7 +11126,8 @@ "transactionId": { "type": "string", "description": "Transaction ID for staging the operation.", - "x-example": "" + "x-example": "", + "x-nullable": true } }, "required": [ @@ -11195,7 +11233,8 @@ "transactionId": { "type": "string", "description": "Transaction ID for staging the operation.", - "x-example": "" + "x-example": "", + "x-nullable": true } } } @@ -11293,7 +11332,8 @@ "transactionId": { "type": "string", "description": "Transaction ID for staging the operation.", - "x-example": "" + "x-example": "", + "x-nullable": true } } } @@ -11550,12 +11590,14 @@ "x-example": "[\"read(\"any\")\"]", "items": { "type": "string" - } + }, + "x-nullable": true }, "transactionId": { "type": "string", "description": "Transaction ID for staging the operation.", - "x-example": "" + "x-example": "", + "x-nullable": true } }, "required": [ @@ -11668,12 +11710,14 @@ "x-example": "[\"read(\"any\")\"]", "items": { "type": "string" - } + }, + "x-nullable": true }, "transactionId": { "type": "string", "description": "Transaction ID for staging the operation.", - "x-example": "" + "x-example": "", + "x-nullable": true } } } @@ -11768,7 +11812,8 @@ "transactionId": { "type": "string", "description": "Transaction ID for staging the operation.", - "x-example": "" + "x-example": "", + "x-nullable": true } } } @@ -11984,12 +12029,14 @@ "min": { "type": "number", "description": "Minimum value for the attribute. If the current value is lesser than this value, an exception will be thrown.", - "x-example": null + "x-example": null, + "x-nullable": true }, "transactionId": { "type": "string", "description": "Transaction ID for staging the operation.", - "x-example": "" + "x-example": "", + "x-nullable": true } } } @@ -12108,12 +12155,14 @@ "max": { "type": "number", "description": "Maximum value for the attribute. If the current value is greater than this value, an error will be thrown.", - "x-example": null + "x-example": null, + "x-nullable": true }, "transactionId": { "type": "string", "description": "Transaction ID for staging the operation.", - "x-example": "" + "x-example": "", + "x-nullable": true } } } @@ -13105,6 +13154,7 @@ "dart-3.3", "dart-3.5", "dart-3.8", + "dart-3.9", "dotnet-6.0", "dotnet-7.0", "dotnet-8.0", @@ -13131,7 +13181,8 @@ "flutter-3.24", "flutter-3.27", "flutter-3.29", - "flutter-3.32" + "flutter-3.32", + "flutter-3.35" ], "x-enum-name": null, "x-enum-keys": [] @@ -13746,6 +13797,7 @@ "dart-3.3", "dart-3.5", "dart-3.8", + "dart-3.9", "dotnet-6.0", "dotnet-7.0", "dotnet-8.0", @@ -13772,7 +13824,8 @@ "flutter-3.24", "flutter-3.27", "flutter-3.29", - "flutter-3.32" + "flutter-3.32", + "flutter-3.35" ], "x-enum-name": null, "x-enum-keys": [] @@ -14166,12 +14219,14 @@ "entrypoint": { "type": "string", "description": "Entrypoint File.", - "x-example": "" + "x-example": "", + "x-nullable": true }, "commands": { "type": "string", "description": "Build Commands.", - "x-example": "" + "x-example": "", + "x-nullable": true }, "code": { "type": "string", @@ -14959,7 +15014,8 @@ "scheduledAt": { "type": "string", "description": "Scheduled execution time in [ISO 8601](https:\/\/www.iso.org\/iso-8601-date-and-time-format.html) format. DateTime value must be in future with precision in minutes.", - "x-example": "" + "x-example": "", + "x-nullable": true } } } @@ -15488,12 +15544,14 @@ "value": { "type": "string", "description": "Variable value. Max length: 8192 chars.", - "x-example": "" + "x-example": "", + "x-nullable": true }, "secret": { "type": "boolean", "description": "Secret variables can be updated or deleted, but only functions can read them during build and runtime.", - "x-example": false + "x-example": false, + "x-nullable": true } }, "required": [ @@ -17599,7 +17657,8 @@ "scheduledAt": { "type": "string", "description": "Scheduled delivery time for message in [ISO 8601](https:\/\/www.iso.org\/iso-8601-date-and-time-format.html) format. DateTime value must be in future.", - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -17685,7 +17744,8 @@ "x-example": null, "items": { "type": "string" - } + }, + "x-nullable": true }, "users": { "type": "array", @@ -17693,7 +17753,8 @@ "x-example": null, "items": { "type": "string" - } + }, + "x-nullable": true }, "targets": { "type": "array", @@ -17701,27 +17762,32 @@ "x-example": null, "items": { "type": "string" - } + }, + "x-nullable": true }, "subject": { "type": "string", "description": "Email Subject.", - "x-example": "" + "x-example": "", + "x-nullable": true }, "content": { "type": "string", "description": "Email Content.", - "x-example": "" + "x-example": "", + "x-nullable": true }, "draft": { "type": "boolean", "description": "Is message a draft", - "x-example": false + "x-example": false, + "x-nullable": true }, "html": { "type": "boolean", "description": "Is content of type HTML", - "x-example": false + "x-example": false, + "x-nullable": true }, "cc": { "type": "array", @@ -17729,7 +17795,8 @@ "x-example": null, "items": { "type": "string" - } + }, + "x-nullable": true }, "bcc": { "type": "array", @@ -17737,12 +17804,14 @@ "x-example": null, "items": { "type": "string" - } + }, + "x-nullable": true }, "scheduledAt": { "type": "string", "description": "Scheduled delivery time for message in [ISO 8601](https:\/\/www.iso.org\/iso-8601-date-and-time-format.html) format. DateTime value must be in future.", - "x-example": null + "x-example": null, + "x-nullable": true }, "attachments": { "type": "array", @@ -17750,7 +17819,8 @@ "x-example": null, "items": { "type": "string" - } + }, + "x-nullable": true } } } @@ -17855,7 +17925,8 @@ "data": { "type": "object", "description": "Additional key-value pair data for push notification.", - "x-example": "{}" + "x-example": "{}", + "x-nullable": true }, "action": { "type": "string", @@ -17900,7 +17971,8 @@ "scheduledAt": { "type": "string", "description": "Scheduled delivery time for message in [ISO 8601](https:\/\/www.iso.org\/iso-8601-date-and-time-format.html) format. DateTime value must be in future.", - "x-example": null + "x-example": null, + "x-nullable": true }, "contentAvailable": { "type": "boolean", @@ -18005,7 +18077,8 @@ "x-example": null, "items": { "type": "string" - } + }, + "x-nullable": true }, "users": { "type": "array", @@ -18013,7 +18086,8 @@ "x-example": null, "items": { "type": "string" - } + }, + "x-nullable": true }, "targets": { "type": "array", @@ -18021,77 +18095,92 @@ "x-example": null, "items": { "type": "string" - } + }, + "x-nullable": true }, "title": { "type": "string", "description": "Title for push notification.", - "x-example": "" + "x-example": "<TITLE>", + "x-nullable": true }, "body": { "type": "string", "description": "Body for push notification.", - "x-example": "<BODY>" + "x-example": "<BODY>", + "x-nullable": true }, "data": { "type": "object", "description": "Additional Data for push notification.", - "x-example": "{}" + "x-example": "{}", + "x-nullable": true }, "action": { "type": "string", "description": "Action for push notification.", - "x-example": "<ACTION>" + "x-example": "<ACTION>", + "x-nullable": true }, "image": { "type": "string", "description": "Image for push notification. Must be a compound bucket ID to file ID of a jpeg, png, or bmp image in Appwrite Storage. It should be formatted as <BUCKET_ID>:<FILE_ID>.", - "x-example": "<ID1:ID2>" + "x-example": "<ID1:ID2>", + "x-nullable": true }, "icon": { "type": "string", "description": "Icon for push notification. Available only for Android and Web platforms.", - "x-example": "<ICON>" + "x-example": "<ICON>", + "x-nullable": true }, "sound": { "type": "string", "description": "Sound for push notification. Available only for Android and iOS platforms.", - "x-example": "<SOUND>" + "x-example": "<SOUND>", + "x-nullable": true }, "color": { "type": "string", "description": "Color for push notification. Available only for Android platforms.", - "x-example": "<COLOR>" + "x-example": "<COLOR>", + "x-nullable": true }, "tag": { "type": "string", "description": "Tag for push notification. Available only for Android platforms.", - "x-example": "<TAG>" + "x-example": "<TAG>", + "x-nullable": true }, "badge": { "type": "integer", "description": "Badge for push notification. Available only for iOS platforms.", - "x-example": null + "x-example": null, + "x-nullable": true }, "draft": { "type": "boolean", "description": "Is message a draft", - "x-example": false + "x-example": false, + "x-nullable": true }, "scheduledAt": { "type": "string", "description": "Scheduled delivery time for message in [ISO 8601](https:\/\/www.iso.org\/iso-8601-date-and-time-format.html) format. DateTime value must be in future.", - "x-example": null + "x-example": null, + "x-nullable": true }, "contentAvailable": { "type": "boolean", "description": "If set to true, the notification will be delivered in the background. Available only for iOS Platform.", - "x-example": false + "x-example": false, + "x-nullable": true }, "critical": { "type": "boolean", "description": "If set to true, the notification will be marked as critical. This requires the app to have the critical notification entitlement. Available only for iOS Platform.", - "x-example": false + "x-example": false, + "x-nullable": true }, "priority": { "type": "string", @@ -18102,7 +18191,8 @@ "high" ], "x-enum-name": "MessagePriority", - "x-enum-keys": [] + "x-enum-keys": [], + "x-nullable": true } } } @@ -18275,7 +18365,8 @@ "scheduledAt": { "type": "string", "description": "Scheduled delivery time for message in [ISO 8601](https:\/\/www.iso.org\/iso-8601-date-and-time-format.html) format. DateTime value must be in future.", - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -18426,7 +18517,8 @@ "x-example": null, "items": { "type": "string" - } + }, + "x-nullable": true }, "users": { "type": "array", @@ -18434,7 +18526,8 @@ "x-example": null, "items": { "type": "string" - } + }, + "x-nullable": true }, "targets": { "type": "array", @@ -18442,22 +18535,26 @@ "x-example": null, "items": { "type": "string" - } + }, + "x-nullable": true }, "content": { "type": "string", "description": "Email Content.", - "x-example": "<CONTENT>" + "x-example": "<CONTENT>", + "x-nullable": true }, "draft": { "type": "boolean", "description": "Is message a draft", - "x-example": false + "x-example": false, + "x-nullable": true }, "scheduledAt": { "type": "string", "description": "Scheduled delivery time for message in [ISO 8601](https:\/\/www.iso.org\/iso-8601-date-and-time-format.html) format. DateTime value must be in future.", - "x-example": null + "x-example": null, + "x-nullable": true } } } @@ -19002,7 +19099,8 @@ "enabled": { "type": "boolean", "description": "Set as enabled.", - "x-example": false + "x-example": false, + "x-nullable": true } }, "required": [ @@ -19157,7 +19255,8 @@ "enabled": { "type": "boolean", "description": "Set as enabled.", - "x-example": false + "x-example": false, + "x-nullable": true }, "authKey": { "type": "string", @@ -19182,7 +19281,8 @@ "sandbox": { "type": "boolean", "description": "Use APNS sandbox environment.", - "x-example": false + "x-example": false, + "x-nullable": true } } } @@ -19320,12 +19420,14 @@ "serviceAccountJSON": { "type": "object", "description": "FCM service account JSON.", - "x-example": "{}" + "x-example": "{}", + "x-nullable": true }, "enabled": { "type": "boolean", "description": "Set as enabled.", - "x-example": false + "x-example": false, + "x-nullable": true } }, "required": [ @@ -19472,12 +19574,14 @@ "enabled": { "type": "boolean", "description": "Set as enabled.", - "x-example": false + "x-example": false, + "x-nullable": true }, "serviceAccountJSON": { "type": "object", "description": "FCM service account JSON.", - "x-example": "{}" + "x-example": "{}", + "x-nullable": true } } } @@ -19563,7 +19667,8 @@ "isEuRegion": { "type": "boolean", "description": "Set as EU region.", - "x-example": false + "x-example": false, + "x-nullable": true }, "fromName": { "type": "string", @@ -19588,7 +19693,8 @@ "enabled": { "type": "boolean", "description": "Set as enabled.", - "x-example": false + "x-example": false, + "x-nullable": true } }, "required": [ @@ -19685,12 +19791,14 @@ "isEuRegion": { "type": "boolean", "description": "Set as EU region.", - "x-example": false + "x-example": false, + "x-nullable": true }, "enabled": { "type": "boolean", "description": "Set as enabled.", - "x-example": false + "x-example": false, + "x-nullable": true }, "fromName": { "type": "string", @@ -19801,7 +19909,8 @@ "enabled": { "type": "boolean", "description": "Set as enabled.", - "x-example": false + "x-example": false, + "x-nullable": true } }, "required": [ @@ -19888,7 +19997,8 @@ "enabled": { "type": "boolean", "description": "Set as enabled.", - "x-example": false + "x-example": false, + "x-nullable": true }, "templateId": { "type": "string", @@ -20004,7 +20114,8 @@ "enabled": { "type": "boolean", "description": "Set as enabled.", - "x-example": false + "x-example": false, + "x-nullable": true } }, "required": [ @@ -20091,7 +20202,8 @@ "enabled": { "type": "boolean", "description": "Set as enabled.", - "x-example": false + "x-example": false, + "x-nullable": true }, "apiKey": { "type": "string", @@ -20217,7 +20329,8 @@ "enabled": { "type": "boolean", "description": "Set as enabled.", - "x-example": false + "x-example": false, + "x-nullable": true } }, "required": [ @@ -20304,7 +20417,8 @@ "enabled": { "type": "boolean", "description": "Set as enabled.", - "x-example": false + "x-example": false, + "x-nullable": true }, "apiKey": { "type": "string", @@ -20551,7 +20665,8 @@ "enabled": { "type": "boolean", "description": "Set as enabled.", - "x-example": false + "x-example": false, + "x-nullable": true } }, "required": [ @@ -20724,7 +20839,8 @@ "port": { "type": "integer", "description": "SMTP port.", - "x-example": 1 + "x-example": 1, + "x-nullable": true }, "username": { "type": "string", @@ -20751,7 +20867,8 @@ "autoTLS": { "type": "boolean", "description": "Enable SMTP AutoTLS feature.", - "x-example": false + "x-example": false, + "x-nullable": true }, "mailer": { "type": "string", @@ -20781,7 +20898,8 @@ "enabled": { "type": "boolean", "description": "Set as enabled.", - "x-example": false + "x-example": false, + "x-nullable": true } } } @@ -20872,7 +20990,8 @@ "enabled": { "type": "boolean", "description": "Set as enabled.", - "x-example": false + "x-example": false, + "x-nullable": true } }, "required": [ @@ -20959,7 +21078,8 @@ "enabled": { "type": "boolean", "description": "Set as enabled.", - "x-example": false + "x-example": false, + "x-nullable": true }, "customerId": { "type": "string", @@ -21065,7 +21185,8 @@ "enabled": { "type": "boolean", "description": "Set as enabled.", - "x-example": false + "x-example": false, + "x-nullable": true } }, "required": [ @@ -21152,7 +21273,8 @@ "enabled": { "type": "boolean", "description": "Set as enabled.", - "x-example": false + "x-example": false, + "x-nullable": true }, "username": { "type": "string", @@ -21258,7 +21380,8 @@ "enabled": { "type": "boolean", "description": "Set as enabled.", - "x-example": false + "x-example": false, + "x-nullable": true } }, "required": [ @@ -21345,7 +21468,8 @@ "enabled": { "type": "boolean", "description": "Set as enabled.", - "x-example": false + "x-example": false, + "x-nullable": true }, "accountSid": { "type": "string", @@ -21451,7 +21575,8 @@ "enabled": { "type": "boolean", "description": "Set as enabled.", - "x-example": false + "x-example": false, + "x-nullable": true } }, "required": [ @@ -21538,7 +21663,8 @@ "enabled": { "type": "boolean", "description": "Set as enabled.", - "x-example": false + "x-example": false, + "x-nullable": true }, "apiKey": { "type": "string", @@ -22146,7 +22272,8 @@ "name": { "type": "string", "description": "Topic Name.", - "x-example": "<NAME>" + "x-example": "<NAME>", + "x-nullable": true }, "subscribe": { "type": "array", @@ -22154,7 +22281,8 @@ "x-example": "[\"any\"]", "items": { "type": "string" - } + }, + "x-nullable": true } } } @@ -22898,7 +23026,7 @@ "tags": [ "migrations" ], - "description": "Export documents to a CSV file from your Appwrite database. This endpoint allows you to export documents to a CSV file stored in an Appwrite Storage bucket.", + "description": "Export documents to a CSV file from your Appwrite database. This endpoint allows you to export documents to a CSV file stored in a secure internal bucket. You'll receive an email with a download link when the export is complete.", "responses": { "202": { "description": "Migration", @@ -22948,11 +23076,6 @@ "description": "Composite ID in the format {databaseId:collectionId}, identifying a collection within a database to export.", "x-example": "<ID1:ID2>" }, - "bucketId": { - "type": "string", - "description": "Storage bucket unique ID where the exported CSV will be stored.", - "x-example": "<BUCKET_ID>" - }, "filename": { "type": "string", "description": "The name of the file to be created for the export, excluding the .csv extension.", @@ -23002,7 +23125,6 @@ }, "required": [ "resourceId", - "bucketId", "filename" ] } @@ -24227,12 +24349,14 @@ "value": { "type": "string", "description": "Variable value. Max length: 8192 chars.", - "x-example": "<VALUE>" + "x-example": "<VALUE>", + "x-nullable": true }, "secret": { "type": "boolean", "description": "Secret variables can be updated or deleted, but only projects can read them during build and runtime.", - "x-example": false + "x-example": false, + "x-nullable": true } }, "required": [ @@ -26552,12 +26676,14 @@ "x-example": null, "items": { "type": "string" - } + }, + "x-nullable": true }, "expire": { "type": "string", "description": "Expiration time in [ISO 8601](https:\/\/www.iso.org\/iso-8601-date-and-time-format.html) format. Use null for unlimited expiration.", - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -26723,12 +26849,14 @@ "x-example": null, "items": { "type": "string" - } + }, + "x-nullable": true }, "expire": { "type": "string", "description": "Expiration time in [ISO 8601](https:\/\/www.iso.org\/iso-8601-date-and-time-format.html) format. Use null for unlimited expiration.", - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -26918,17 +27046,20 @@ "appId": { "type": "string", "description": "Provider app ID. Max length: 256 chars.", - "x-example": "<APP_ID>" + "x-example": "<APP_ID>", + "x-nullable": true }, "secret": { "type": "string", "description": "Provider secret key. Max length: 512 chars.", - "x-example": "<SECRET>" + "x-example": "<SECRET>", + "x-nullable": true }, "enabled": { "type": "boolean", "description": "Provider status. Set to 'false' to disable new session creation.", - "x-example": false + "x-example": false, + "x-nullable": true } }, "required": [ @@ -30954,6 +31085,7 @@ "dart-3.3", "dart-3.5", "dart-3.8", + "dart-3.9", "dotnet-6.0", "dotnet-7.0", "dotnet-8.0", @@ -30980,7 +31112,8 @@ "flutter-3.24", "flutter-3.27", "flutter-3.29", - "flutter-3.32" + "flutter-3.32", + "flutter-3.35" ], "x-enum-name": null, "x-enum-keys": [] @@ -31601,6 +31734,7 @@ "dart-3.3", "dart-3.5", "dart-3.8", + "dart-3.9", "dotnet-6.0", "dotnet-7.0", "dotnet-8.0", @@ -31627,7 +31761,8 @@ "flutter-3.24", "flutter-3.27", "flutter-3.29", - "flutter-3.32" + "flutter-3.32", + "flutter-3.35" ], "x-enum-name": null, "x-enum-keys": [] @@ -31983,17 +32118,20 @@ "installCommand": { "type": "string", "description": "Install Commands.", - "x-example": "<INSTALL_COMMAND>" + "x-example": "<INSTALL_COMMAND>", + "x-nullable": true }, "buildCommand": { "type": "string", "description": "Build Commands.", - "x-example": "<BUILD_COMMAND>" + "x-example": "<BUILD_COMMAND>", + "x-nullable": true }, "outputDirectory": { "type": "string", "description": "Output Directory.", - "x-example": "<OUTPUT_DIRECTORY>" + "x-example": "<OUTPUT_DIRECTORY>", + "x-nullable": true }, "code": { "type": "string", @@ -33186,12 +33324,14 @@ "value": { "type": "string", "description": "Variable value. Max length: 8192 chars.", - "x-example": "<VALUE>" + "x-example": "<VALUE>", + "x-nullable": true }, "secret": { "type": "boolean", "description": "Secret variables can be updated or deleted, but only sites can read them during build and runtime.", - "x-example": false + "x-example": false, + "x-nullable": true } }, "required": [ @@ -33418,7 +33558,8 @@ "x-example": "[\"read(\"any\")\"]", "items": { "type": "string" - } + }, + "x-nullable": true }, "fileSecurity": { "type": "boolean", @@ -33611,7 +33752,8 @@ "x-example": "[\"read(\"any\")\"]", "items": { "type": "string" - } + }, + "x-nullable": true }, "fileSecurity": { "type": "boolean", @@ -33902,7 +34044,8 @@ "x-example": "[\"read(\"any\")\"]", "items": { "type": "string" - } + }, + "x-nullable": true } }, "required": [ @@ -34068,7 +34211,8 @@ "name": { "type": "string", "description": "Name of the file", - "x-example": "<NAME>" + "x-example": "<NAME>", + "x-nullable": true }, "permissions": { "type": "array", @@ -34076,7 +34220,8 @@ "x-example": "[\"read(\"any\")\"]", "items": { "type": "string" - } + }, + "x-nullable": true } } } @@ -35761,7 +35906,8 @@ "x-example": "[\"read(\"any\")\"]", "items": { "type": "string" - } + }, + "x-nullable": true }, "rowSecurity": { "type": "boolean", @@ -35945,7 +36091,8 @@ "x-example": "[\"read(\"any\")\"]", "items": { "type": "string" - } + }, + "x-nullable": true }, "rowSecurity": { "type": "boolean", @@ -36221,7 +36368,8 @@ "default": { "type": "boolean", "description": "Default value for column when not provided. Cannot be set when column is required.", - "x-example": false + "x-example": false, + "x-nullable": true }, "array": { "type": "boolean", @@ -36340,7 +36488,8 @@ "newKey": { "type": "string", "description": "New Column Key.", - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -36563,7 +36712,8 @@ "newKey": { "type": "string", "description": "New Column Key.", - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -36667,7 +36817,8 @@ "default": { "type": "string", "description": "Default value for column when not provided. Cannot be set when column is required.", - "x-example": "email@example.com" + "x-example": "email@example.com", + "x-nullable": true }, "array": { "type": "boolean", @@ -36786,7 +36937,8 @@ "newKey": { "type": "string", "description": "New Column Key.", - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -36898,7 +37050,8 @@ "default": { "type": "string", "description": "Default value for column when not provided. Cannot be set when column is required.", - "x-example": "<DEFAULT>" + "x-example": "<DEFAULT>", + "x-nullable": true }, "array": { "type": "boolean", @@ -37026,7 +37179,8 @@ "newKey": { "type": "string", "description": "New Column Key.", - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -37131,17 +37285,20 @@ "min": { "type": "number", "description": "Minimum value", - "x-example": null + "x-example": null, + "x-nullable": true }, "max": { "type": "number", "description": "Maximum value", - "x-example": null + "x-example": null, + "x-nullable": true }, "default": { "type": "number", "description": "Default value. Cannot be set when required.", - "x-example": null + "x-example": null, + "x-nullable": true }, "array": { "type": "boolean", @@ -37254,12 +37411,14 @@ "min": { "type": "number", "description": "Minimum value", - "x-example": null + "x-example": null, + "x-nullable": true }, "max": { "type": "number", "description": "Maximum value", - "x-example": null + "x-example": null, + "x-nullable": true }, "default": { "type": "number", @@ -37270,7 +37429,8 @@ "newKey": { "type": "string", "description": "New Column Key.", - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -37374,17 +37534,20 @@ "min": { "type": "integer", "description": "Minimum value", - "x-example": null + "x-example": null, + "x-nullable": true }, "max": { "type": "integer", "description": "Maximum value", - "x-example": null + "x-example": null, + "x-nullable": true }, "default": { "type": "integer", "description": "Default value. Cannot be set when column is required.", - "x-example": null + "x-example": null, + "x-nullable": true }, "array": { "type": "boolean", @@ -37497,12 +37660,14 @@ "min": { "type": "integer", "description": "Minimum value", - "x-example": null + "x-example": null, + "x-nullable": true }, "max": { "type": "integer", "description": "Maximum value", - "x-example": null + "x-example": null, + "x-nullable": true }, "default": { "type": "integer", @@ -37513,7 +37678,8 @@ "newKey": { "type": "string", "description": "New Column Key.", - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -37617,7 +37783,8 @@ "default": { "type": "string", "description": "Default value. Cannot be set when column is required.", - "x-example": null + "x-example": null, + "x-nullable": true }, "array": { "type": "boolean", @@ -37736,7 +37903,8 @@ "newKey": { "type": "string", "description": "New Column Key.", - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -37969,7 +38137,8 @@ "newKey": { "type": "string", "description": "New Column Key.", - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -38201,7 +38370,8 @@ "newKey": { "type": "string", "description": "New Column Key.", - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -38433,7 +38603,8 @@ "newKey": { "type": "string", "description": "New Column Key.", - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -38549,12 +38720,14 @@ "key": { "type": "string", "description": "Column Key.", - "x-example": null + "x-example": null, + "x-nullable": true }, "twoWayKey": { "type": "string", "description": "Two Way Column Key.", - "x-example": null + "x-example": null, + "x-nullable": true }, "onDelete": { "type": "string", @@ -38675,7 +38848,8 @@ "default": { "type": "string", "description": "Default value for column when not provided. Cannot be set when column is required.", - "x-example": "<DEFAULT>" + "x-example": "<DEFAULT>", + "x-nullable": true }, "array": { "type": "boolean", @@ -38800,12 +38974,14 @@ "size": { "type": "integer", "description": "Maximum size of the string column.", - "x-example": 1 + "x-example": 1, + "x-nullable": true }, "newKey": { "type": "string", "description": "New Column Key.", - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -38909,7 +39085,8 @@ "default": { "type": "string", "description": "Default value for column when not provided. Cannot be set when column is required.", - "x-example": "https:\/\/example.com" + "x-example": "https:\/\/example.com", + "x-nullable": true }, "array": { "type": "boolean", @@ -39028,7 +39205,8 @@ "newKey": { "type": "string", "description": "New Column Key.", - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -39326,12 +39504,14 @@ "setNull" ], "x-enum-name": "RelationMutate", - "x-enum-keys": [] + "x-enum-keys": [], + "x-nullable": true }, "newKey": { "type": "string", "description": "New Column Key.", - "x-example": null + "x-example": null, + "x-nullable": true } } } @@ -40076,7 +40256,8 @@ "x-example": "[\"read(\"any\")\"]", "items": { "type": "string" - } + }, + "x-nullable": true }, "rows": { "type": "array", @@ -40089,7 +40270,8 @@ "transactionId": { "type": "string", "description": "Transaction ID for staging the operation.", - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -40215,7 +40397,8 @@ "transactionId": { "type": "string", "description": "Transaction ID for staging the operation.", - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } }, "required": [ @@ -40320,7 +40503,8 @@ "transactionId": { "type": "string", "description": "Transaction ID for staging the operation.", - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -40417,7 +40601,8 @@ "transactionId": { "type": "string", "description": "Transaction ID for staging the operation.", - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -40667,12 +40852,14 @@ "x-example": "[\"read(\"any\")\"]", "items": { "type": "string" - } + }, + "x-nullable": true }, "transactionId": { "type": "string", "description": "Transaction ID for staging the operation.", - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -40781,12 +40968,14 @@ "x-example": "[\"read(\"any\")\"]", "items": { "type": "string" - } + }, + "x-nullable": true }, "transactionId": { "type": "string", "description": "Transaction ID for staging the operation.", - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -40880,7 +41069,8 @@ "transactionId": { "type": "string", "description": "Transaction ID for staging the operation.", - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -41094,12 +41284,14 @@ "min": { "type": "number", "description": "Minimum value for the column. If the current value is lesser than this value, an exception will be thrown.", - "x-example": null + "x-example": null, + "x-nullable": true }, "transactionId": { "type": "string", "description": "Transaction ID for staging the operation.", - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -41217,12 +41409,14 @@ "max": { "type": "number", "description": "Maximum value for the column. If the current value is greater than this value, an error will be thrown.", - "x-example": null + "x-example": null, + "x-nullable": true }, "transactionId": { "type": "string", "description": "Transaction ID for staging the operation.", - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -43091,12 +43285,14 @@ "email": { "type": "string", "description": "User email.", - "x-example": "email@example.com" + "x-example": "email@example.com", + "x-nullable": true }, "phone": { "type": "string", "description": "Phone number. Format this number with a leading '+' and a country code, e.g., +16175551212.", - "x-example": "+12065550100" + "x-example": "+12065550100", + "x-nullable": true }, "password": { "type": "string", diff --git a/app/config/specs/open-api3-1.8.x-server.json b/app/config/specs/open-api3-1.8.x-server.json index c3df6ef373..8fc49b1db5 100644 --- a/app/config/specs/open-api3-1.8.x-server.json +++ b/app/config/specs/open-api3-1.8.x-server.json @@ -6521,7 +6521,8 @@ "x-example": "[\"read(\"any\")\"]", "items": { "type": "string" - } + }, + "x-nullable": true }, "documentSecurity": { "type": "boolean", @@ -6709,7 +6710,8 @@ "x-example": "[\"read(\"any\")\"]", "items": { "type": "string" - } + }, + "x-nullable": true }, "documentSecurity": { "type": "boolean", @@ -6991,7 +6993,8 @@ "default": { "type": "boolean", "description": "Default value for attribute when not provided. Cannot be set when attribute is required.", - "x-example": false + "x-example": false, + "x-nullable": true }, "array": { "type": "boolean", @@ -7112,7 +7115,8 @@ "newKey": { "type": "string", "description": "New attribute key.", - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -7339,7 +7343,8 @@ "newKey": { "type": "string", "description": "New attribute key.", - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -7445,7 +7450,8 @@ "default": { "type": "string", "description": "Default value for attribute when not provided. Cannot be set when attribute is required.", - "x-example": "email@example.com" + "x-example": "email@example.com", + "x-nullable": true }, "array": { "type": "boolean", @@ -7566,7 +7572,8 @@ "newKey": { "type": "string", "description": "New Attribute Key.", - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -7680,7 +7687,8 @@ "default": { "type": "string", "description": "Default value for attribute when not provided. Cannot be set when attribute is required.", - "x-example": "<DEFAULT>" + "x-example": "<DEFAULT>", + "x-nullable": true }, "array": { "type": "boolean", @@ -7810,7 +7818,8 @@ "newKey": { "type": "string", "description": "New Attribute Key.", - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -7917,17 +7926,20 @@ "min": { "type": "number", "description": "Minimum value.", - "x-example": null + "x-example": null, + "x-nullable": true }, "max": { "type": "number", "description": "Maximum value.", - "x-example": null + "x-example": null, + "x-nullable": true }, "default": { "type": "number", "description": "Default value. Cannot be set when required.", - "x-example": null + "x-example": null, + "x-nullable": true }, "array": { "type": "boolean", @@ -8042,12 +8054,14 @@ "min": { "type": "number", "description": "Minimum value.", - "x-example": null + "x-example": null, + "x-nullable": true }, "max": { "type": "number", "description": "Maximum value.", - "x-example": null + "x-example": null, + "x-nullable": true }, "default": { "type": "number", @@ -8058,7 +8072,8 @@ "newKey": { "type": "string", "description": "New Attribute Key.", - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -8164,17 +8179,20 @@ "min": { "type": "integer", "description": "Minimum value", - "x-example": null + "x-example": null, + "x-nullable": true }, "max": { "type": "integer", "description": "Maximum value", - "x-example": null + "x-example": null, + "x-nullable": true }, "default": { "type": "integer", "description": "Default value. Cannot be set when attribute is required.", - "x-example": null + "x-example": null, + "x-nullable": true }, "array": { "type": "boolean", @@ -8289,12 +8307,14 @@ "min": { "type": "integer", "description": "Minimum value", - "x-example": null + "x-example": null, + "x-nullable": true }, "max": { "type": "integer", "description": "Maximum value", - "x-example": null + "x-example": null, + "x-nullable": true }, "default": { "type": "integer", @@ -8305,7 +8325,8 @@ "newKey": { "type": "string", "description": "New Attribute Key.", - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -8411,7 +8432,8 @@ "default": { "type": "string", "description": "Default value. Cannot be set when attribute is required.", - "x-example": null + "x-example": null, + "x-nullable": true }, "array": { "type": "boolean", @@ -8532,7 +8554,8 @@ "newKey": { "type": "string", "description": "New Attribute Key.", - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -8769,7 +8792,8 @@ "newKey": { "type": "string", "description": "New attribute key.", - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -9005,7 +9029,8 @@ "newKey": { "type": "string", "description": "New attribute key.", - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -9241,7 +9266,8 @@ "newKey": { "type": "string", "description": "New attribute key.", - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -9359,12 +9385,14 @@ "key": { "type": "string", "description": "Attribute Key.", - "x-example": null + "x-example": null, + "x-nullable": true }, "twoWayKey": { "type": "string", "description": "Two Way Attribute Key.", - "x-example": null + "x-example": null, + "x-nullable": true }, "onDelete": { "type": "string", @@ -9487,7 +9515,8 @@ "default": { "type": "string", "description": "Default value for attribute when not provided. Cannot be set when attribute is required.", - "x-example": "<DEFAULT>" + "x-example": "<DEFAULT>", + "x-nullable": true }, "array": { "type": "boolean", @@ -9614,12 +9643,14 @@ "size": { "type": "integer", "description": "Maximum size of the string attribute.", - "x-example": 1 + "x-example": 1, + "x-nullable": true }, "newKey": { "type": "string", "description": "New Attribute Key.", - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -9725,7 +9756,8 @@ "default": { "type": "string", "description": "Default value for attribute when not provided. Cannot be set when attribute is required.", - "x-example": "https:\/\/example.com" + "x-example": "https:\/\/example.com", + "x-nullable": true }, "array": { "type": "boolean", @@ -9846,7 +9878,8 @@ "newKey": { "type": "string", "description": "New Attribute Key.", - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -10150,12 +10183,14 @@ "setNull" ], "x-enum-name": "RelationMutate", - "x-enum-keys": [] + "x-enum-keys": [], + "x-nullable": true }, "newKey": { "type": "string", "description": "New Attribute Key.", - "x-example": null + "x-example": null, + "x-nullable": true } } } @@ -10445,7 +10480,8 @@ "x-example": "[\"read(\"any\")\"]", "items": { "type": "string" - } + }, + "x-nullable": true }, "documents": { "type": "array", @@ -10458,7 +10494,8 @@ "transactionId": { "type": "string", "description": "Transaction ID for staging the operation.", - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -10591,7 +10628,8 @@ "transactionId": { "type": "string", "description": "Transaction ID for staging the operation.", - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } }, "required": [ @@ -10698,7 +10736,8 @@ "transactionId": { "type": "string", "description": "Transaction ID for staging the operation.", - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -10797,7 +10836,8 @@ "transactionId": { "type": "string", "description": "Transaction ID for staging the operation.", - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -11059,12 +11099,14 @@ "x-example": "[\"read(\"any\")\"]", "items": { "type": "string" - } + }, + "x-nullable": true }, "transactionId": { "type": "string", "description": "Transaction ID for staging the operation.", - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } }, "required": [ @@ -11179,12 +11221,14 @@ "x-example": "[\"read(\"any\")\"]", "items": { "type": "string" - } + }, + "x-nullable": true }, "transactionId": { "type": "string", "description": "Transaction ID for staging the operation.", - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -11281,7 +11325,8 @@ "transactionId": { "type": "string", "description": "Transaction ID for staging the operation.", - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -11402,12 +11447,14 @@ "min": { "type": "number", "description": "Minimum value for the attribute. If the current value is lesser than this value, an exception will be thrown.", - "x-example": null + "x-example": null, + "x-nullable": true }, "transactionId": { "type": "string", "description": "Transaction ID for staging the operation.", - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -11528,12 +11575,14 @@ "max": { "type": "number", "description": "Maximum value for the attribute. If the current value is greater than this value, an error will be thrown.", - "x-example": null + "x-example": null, + "x-nullable": true }, "transactionId": { "type": "string", "description": "Transaction ID for staging the operation.", - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -12127,6 +12176,7 @@ "dart-3.3", "dart-3.5", "dart-3.8", + "dart-3.9", "dotnet-6.0", "dotnet-7.0", "dotnet-8.0", @@ -12153,7 +12203,8 @@ "flutter-3.24", "flutter-3.27", "flutter-3.29", - "flutter-3.32" + "flutter-3.32", + "flutter-3.35" ], "x-enum-name": null, "x-enum-keys": [] @@ -12529,6 +12580,7 @@ "dart-3.3", "dart-3.5", "dart-3.8", + "dart-3.9", "dotnet-6.0", "dotnet-7.0", "dotnet-8.0", @@ -12555,7 +12607,8 @@ "flutter-3.24", "flutter-3.27", "flutter-3.29", - "flutter-3.32" + "flutter-3.32", + "flutter-3.35" ], "x-enum-name": null, "x-enum-keys": [] @@ -12953,12 +13006,14 @@ "entrypoint": { "type": "string", "description": "Entrypoint File.", - "x-example": "<ENTRYPOINT>" + "x-example": "<ENTRYPOINT>", + "x-nullable": true }, "commands": { "type": "string", "description": "Build Commands.", - "x-example": "<COMMANDS>" + "x-example": "<COMMANDS>", + "x-nullable": true }, "code": { "type": "string", @@ -13757,7 +13812,8 @@ "scheduledAt": { "type": "string", "description": "Scheduled execution time in [ISO 8601](https:\/\/www.iso.org\/iso-8601-date-and-time-format.html) format. DateTime value must be in future with precision in minutes.", - "x-example": "<SCHEDULED_AT>" + "x-example": "<SCHEDULED_AT>", + "x-nullable": true } } } @@ -14211,12 +14267,14 @@ "value": { "type": "string", "description": "Variable value. Max length: 8192 chars.", - "x-example": "<VALUE>" + "x-example": "<VALUE>", + "x-nullable": true }, "secret": { "type": "boolean", "description": "Secret variables can be updated or deleted, but only functions can read them during build and runtime.", - "x-example": false + "x-example": false, + "x-nullable": true } }, "required": [ @@ -16367,7 +16425,8 @@ "scheduledAt": { "type": "string", "description": "Scheduled delivery time for message in [ISO 8601](https:\/\/www.iso.org\/iso-8601-date-and-time-format.html) format. DateTime value must be in future.", - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -16454,7 +16513,8 @@ "x-example": null, "items": { "type": "string" - } + }, + "x-nullable": true }, "users": { "type": "array", @@ -16462,7 +16522,8 @@ "x-example": null, "items": { "type": "string" - } + }, + "x-nullable": true }, "targets": { "type": "array", @@ -16470,27 +16531,32 @@ "x-example": null, "items": { "type": "string" - } + }, + "x-nullable": true }, "subject": { "type": "string", "description": "Email Subject.", - "x-example": "<SUBJECT>" + "x-example": "<SUBJECT>", + "x-nullable": true }, "content": { "type": "string", "description": "Email Content.", - "x-example": "<CONTENT>" + "x-example": "<CONTENT>", + "x-nullable": true }, "draft": { "type": "boolean", "description": "Is message a draft", - "x-example": false + "x-example": false, + "x-nullable": true }, "html": { "type": "boolean", "description": "Is content of type HTML", - "x-example": false + "x-example": false, + "x-nullable": true }, "cc": { "type": "array", @@ -16498,7 +16564,8 @@ "x-example": null, "items": { "type": "string" - } + }, + "x-nullable": true }, "bcc": { "type": "array", @@ -16506,12 +16573,14 @@ "x-example": null, "items": { "type": "string" - } + }, + "x-nullable": true }, "scheduledAt": { "type": "string", "description": "Scheduled delivery time for message in [ISO 8601](https:\/\/www.iso.org\/iso-8601-date-and-time-format.html) format. DateTime value must be in future.", - "x-example": null + "x-example": null, + "x-nullable": true }, "attachments": { "type": "array", @@ -16519,7 +16588,8 @@ "x-example": null, "items": { "type": "string" - } + }, + "x-nullable": true } } } @@ -16625,7 +16695,8 @@ "data": { "type": "object", "description": "Additional key-value pair data for push notification.", - "x-example": "{}" + "x-example": "{}", + "x-nullable": true }, "action": { "type": "string", @@ -16670,7 +16741,8 @@ "scheduledAt": { "type": "string", "description": "Scheduled delivery time for message in [ISO 8601](https:\/\/www.iso.org\/iso-8601-date-and-time-format.html) format. DateTime value must be in future.", - "x-example": null + "x-example": null, + "x-nullable": true }, "contentAvailable": { "type": "boolean", @@ -16776,7 +16848,8 @@ "x-example": null, "items": { "type": "string" - } + }, + "x-nullable": true }, "users": { "type": "array", @@ -16784,7 +16857,8 @@ "x-example": null, "items": { "type": "string" - } + }, + "x-nullable": true }, "targets": { "type": "array", @@ -16792,77 +16866,92 @@ "x-example": null, "items": { "type": "string" - } + }, + "x-nullable": true }, "title": { "type": "string", "description": "Title for push notification.", - "x-example": "<TITLE>" + "x-example": "<TITLE>", + "x-nullable": true }, "body": { "type": "string", "description": "Body for push notification.", - "x-example": "<BODY>" + "x-example": "<BODY>", + "x-nullable": true }, "data": { "type": "object", "description": "Additional Data for push notification.", - "x-example": "{}" + "x-example": "{}", + "x-nullable": true }, "action": { "type": "string", "description": "Action for push notification.", - "x-example": "<ACTION>" + "x-example": "<ACTION>", + "x-nullable": true }, "image": { "type": "string", "description": "Image for push notification. Must be a compound bucket ID to file ID of a jpeg, png, or bmp image in Appwrite Storage. It should be formatted as <BUCKET_ID>:<FILE_ID>.", - "x-example": "<ID1:ID2>" + "x-example": "<ID1:ID2>", + "x-nullable": true }, "icon": { "type": "string", "description": "Icon for push notification. Available only for Android and Web platforms.", - "x-example": "<ICON>" + "x-example": "<ICON>", + "x-nullable": true }, "sound": { "type": "string", "description": "Sound for push notification. Available only for Android and iOS platforms.", - "x-example": "<SOUND>" + "x-example": "<SOUND>", + "x-nullable": true }, "color": { "type": "string", "description": "Color for push notification. Available only for Android platforms.", - "x-example": "<COLOR>" + "x-example": "<COLOR>", + "x-nullable": true }, "tag": { "type": "string", "description": "Tag for push notification. Available only for Android platforms.", - "x-example": "<TAG>" + "x-example": "<TAG>", + "x-nullable": true }, "badge": { "type": "integer", "description": "Badge for push notification. Available only for iOS platforms.", - "x-example": null + "x-example": null, + "x-nullable": true }, "draft": { "type": "boolean", "description": "Is message a draft", - "x-example": false + "x-example": false, + "x-nullable": true }, "scheduledAt": { "type": "string", "description": "Scheduled delivery time for message in [ISO 8601](https:\/\/www.iso.org\/iso-8601-date-and-time-format.html) format. DateTime value must be in future.", - "x-example": null + "x-example": null, + "x-nullable": true }, "contentAvailable": { "type": "boolean", "description": "If set to true, the notification will be delivered in the background. Available only for iOS Platform.", - "x-example": false + "x-example": false, + "x-nullable": true }, "critical": { "type": "boolean", "description": "If set to true, the notification will be marked as critical. This requires the app to have the critical notification entitlement. Available only for iOS Platform.", - "x-example": false + "x-example": false, + "x-nullable": true }, "priority": { "type": "string", @@ -16873,7 +16962,8 @@ "high" ], "x-enum-name": "MessagePriority", - "x-enum-keys": [] + "x-enum-keys": [], + "x-nullable": true } } } @@ -17049,7 +17139,8 @@ "scheduledAt": { "type": "string", "description": "Scheduled delivery time for message in [ISO 8601](https:\/\/www.iso.org\/iso-8601-date-and-time-format.html) format. DateTime value must be in future.", - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -17203,7 +17294,8 @@ "x-example": null, "items": { "type": "string" - } + }, + "x-nullable": true }, "users": { "type": "array", @@ -17211,7 +17303,8 @@ "x-example": null, "items": { "type": "string" - } + }, + "x-nullable": true }, "targets": { "type": "array", @@ -17219,22 +17312,26 @@ "x-example": null, "items": { "type": "string" - } + }, + "x-nullable": true }, "content": { "type": "string", "description": "Email Content.", - "x-example": "<CONTENT>" + "x-example": "<CONTENT>", + "x-nullable": true }, "draft": { "type": "boolean", "description": "Is message a draft", - "x-example": false + "x-example": false, + "x-nullable": true }, "scheduledAt": { "type": "string", "description": "Scheduled delivery time for message in [ISO 8601](https:\/\/www.iso.org\/iso-8601-date-and-time-format.html) format. DateTime value must be in future.", - "x-example": null + "x-example": null, + "x-nullable": true } } } @@ -17787,7 +17884,8 @@ "enabled": { "type": "boolean", "description": "Set as enabled.", - "x-example": false + "x-example": false, + "x-nullable": true } }, "required": [ @@ -17945,7 +18043,8 @@ "enabled": { "type": "boolean", "description": "Set as enabled.", - "x-example": false + "x-example": false, + "x-nullable": true }, "authKey": { "type": "string", @@ -17970,7 +18069,8 @@ "sandbox": { "type": "boolean", "description": "Use APNS sandbox environment.", - "x-example": false + "x-example": false, + "x-nullable": true } } } @@ -18111,12 +18211,14 @@ "serviceAccountJSON": { "type": "object", "description": "FCM service account JSON.", - "x-example": "{}" + "x-example": "{}", + "x-nullable": true }, "enabled": { "type": "boolean", "description": "Set as enabled.", - "x-example": false + "x-example": false, + "x-nullable": true } }, "required": [ @@ -18266,12 +18368,14 @@ "enabled": { "type": "boolean", "description": "Set as enabled.", - "x-example": false + "x-example": false, + "x-nullable": true }, "serviceAccountJSON": { "type": "object", "description": "FCM service account JSON.", - "x-example": "{}" + "x-example": "{}", + "x-nullable": true } } } @@ -18358,7 +18462,8 @@ "isEuRegion": { "type": "boolean", "description": "Set as EU region.", - "x-example": false + "x-example": false, + "x-nullable": true }, "fromName": { "type": "string", @@ -18383,7 +18488,8 @@ "enabled": { "type": "boolean", "description": "Set as enabled.", - "x-example": false + "x-example": false, + "x-nullable": true } }, "required": [ @@ -18481,12 +18587,14 @@ "isEuRegion": { "type": "boolean", "description": "Set as EU region.", - "x-example": false + "x-example": false, + "x-nullable": true }, "enabled": { "type": "boolean", "description": "Set as enabled.", - "x-example": false + "x-example": false, + "x-nullable": true }, "fromName": { "type": "string", @@ -18598,7 +18706,8 @@ "enabled": { "type": "boolean", "description": "Set as enabled.", - "x-example": false + "x-example": false, + "x-nullable": true } }, "required": [ @@ -18686,7 +18795,8 @@ "enabled": { "type": "boolean", "description": "Set as enabled.", - "x-example": false + "x-example": false, + "x-nullable": true }, "templateId": { "type": "string", @@ -18803,7 +18913,8 @@ "enabled": { "type": "boolean", "description": "Set as enabled.", - "x-example": false + "x-example": false, + "x-nullable": true } }, "required": [ @@ -18891,7 +19002,8 @@ "enabled": { "type": "boolean", "description": "Set as enabled.", - "x-example": false + "x-example": false, + "x-nullable": true }, "apiKey": { "type": "string", @@ -19018,7 +19130,8 @@ "enabled": { "type": "boolean", "description": "Set as enabled.", - "x-example": false + "x-example": false, + "x-nullable": true } }, "required": [ @@ -19106,7 +19219,8 @@ "enabled": { "type": "boolean", "description": "Set as enabled.", - "x-example": false + "x-example": false, + "x-nullable": true }, "apiKey": { "type": "string", @@ -19356,7 +19470,8 @@ "enabled": { "type": "boolean", "description": "Set as enabled.", - "x-example": false + "x-example": false, + "x-nullable": true } }, "required": [ @@ -19532,7 +19647,8 @@ "port": { "type": "integer", "description": "SMTP port.", - "x-example": 1 + "x-example": 1, + "x-nullable": true }, "username": { "type": "string", @@ -19559,7 +19675,8 @@ "autoTLS": { "type": "boolean", "description": "Enable SMTP AutoTLS feature.", - "x-example": false + "x-example": false, + "x-nullable": true }, "mailer": { "type": "string", @@ -19589,7 +19706,8 @@ "enabled": { "type": "boolean", "description": "Set as enabled.", - "x-example": false + "x-example": false, + "x-nullable": true } } } @@ -19681,7 +19799,8 @@ "enabled": { "type": "boolean", "description": "Set as enabled.", - "x-example": false + "x-example": false, + "x-nullable": true } }, "required": [ @@ -19769,7 +19888,8 @@ "enabled": { "type": "boolean", "description": "Set as enabled.", - "x-example": false + "x-example": false, + "x-nullable": true }, "customerId": { "type": "string", @@ -19876,7 +19996,8 @@ "enabled": { "type": "boolean", "description": "Set as enabled.", - "x-example": false + "x-example": false, + "x-nullable": true } }, "required": [ @@ -19964,7 +20085,8 @@ "enabled": { "type": "boolean", "description": "Set as enabled.", - "x-example": false + "x-example": false, + "x-nullable": true }, "username": { "type": "string", @@ -20071,7 +20193,8 @@ "enabled": { "type": "boolean", "description": "Set as enabled.", - "x-example": false + "x-example": false, + "x-nullable": true } }, "required": [ @@ -20159,7 +20282,8 @@ "enabled": { "type": "boolean", "description": "Set as enabled.", - "x-example": false + "x-example": false, + "x-nullable": true }, "accountSid": { "type": "string", @@ -20266,7 +20390,8 @@ "enabled": { "type": "boolean", "description": "Set as enabled.", - "x-example": false + "x-example": false, + "x-nullable": true } }, "required": [ @@ -20354,7 +20479,8 @@ "enabled": { "type": "boolean", "description": "Set as enabled.", - "x-example": false + "x-example": false, + "x-nullable": true }, "apiKey": { "type": "string", @@ -20970,7 +21096,8 @@ "name": { "type": "string", "description": "Topic Name.", - "x-example": "<NAME>" + "x-example": "<NAME>", + "x-nullable": true }, "subscribe": { "type": "array", @@ -20978,7 +21105,8 @@ "x-example": "[\"any\"]", "items": { "type": "string" - } + }, + "x-nullable": true } } } @@ -21703,6 +21831,7 @@ "dart-3.3", "dart-3.5", "dart-3.8", + "dart-3.9", "dotnet-6.0", "dotnet-7.0", "dotnet-8.0", @@ -21729,7 +21858,8 @@ "flutter-3.24", "flutter-3.27", "flutter-3.29", - "flutter-3.32" + "flutter-3.32", + "flutter-3.35" ], "x-enum-name": null, "x-enum-keys": [] @@ -22122,6 +22252,7 @@ "dart-3.3", "dart-3.5", "dart-3.8", + "dart-3.9", "dotnet-6.0", "dotnet-7.0", "dotnet-8.0", @@ -22148,7 +22279,8 @@ "flutter-3.24", "flutter-3.27", "flutter-3.29", - "flutter-3.32" + "flutter-3.32", + "flutter-3.35" ], "x-enum-name": null, "x-enum-keys": [] @@ -22508,17 +22640,20 @@ "installCommand": { "type": "string", "description": "Install Commands.", - "x-example": "<INSTALL_COMMAND>" + "x-example": "<INSTALL_COMMAND>", + "x-nullable": true }, "buildCommand": { "type": "string", "description": "Build Commands.", - "x-example": "<BUILD_COMMAND>" + "x-example": "<BUILD_COMMAND>", + "x-nullable": true }, "outputDirectory": { "type": "string", "description": "Output Directory.", - "x-example": "<OUTPUT_DIRECTORY>" + "x-example": "<OUTPUT_DIRECTORY>", + "x-nullable": true }, "code": { "type": "string", @@ -23643,12 +23778,14 @@ "value": { "type": "string", "description": "Variable value. Max length: 8192 chars.", - "x-example": "<VALUE>" + "x-example": "<VALUE>", + "x-nullable": true }, "secret": { "type": "boolean", "description": "Secret variables can be updated or deleted, but only sites can read them during build and runtime.", - "x-example": false + "x-example": false, + "x-nullable": true } }, "required": [ @@ -23878,7 +24015,8 @@ "x-example": "[\"read(\"any\")\"]", "items": { "type": "string" - } + }, + "x-nullable": true }, "fileSecurity": { "type": "boolean", @@ -24073,7 +24211,8 @@ "x-example": "[\"read(\"any\")\"]", "items": { "type": "string" - } + }, + "x-nullable": true }, "fileSecurity": { "type": "boolean", @@ -24369,7 +24508,8 @@ "x-example": "[\"read(\"any\")\"]", "items": { "type": "string" - } + }, + "x-nullable": true } }, "required": [ @@ -24539,7 +24679,8 @@ "name": { "type": "string", "description": "Name of the file", - "x-example": "<NAME>" + "x-example": "<NAME>", + "x-nullable": true }, "permissions": { "type": "array", @@ -24547,7 +24688,8 @@ "x-example": "[\"read(\"any\")\"]", "items": { "type": "string" - } + }, + "x-nullable": true } } } @@ -26008,7 +26150,8 @@ "x-example": "[\"read(\"any\")\"]", "items": { "type": "string" - } + }, + "x-nullable": true }, "rowSecurity": { "type": "boolean", @@ -26194,7 +26337,8 @@ "x-example": "[\"read(\"any\")\"]", "items": { "type": "string" - } + }, + "x-nullable": true }, "rowSecurity": { "type": "boolean", @@ -26473,7 +26617,8 @@ "default": { "type": "boolean", "description": "Default value for column when not provided. Cannot be set when column is required.", - "x-example": false + "x-example": false, + "x-nullable": true }, "array": { "type": "boolean", @@ -26593,7 +26738,8 @@ "newKey": { "type": "string", "description": "New Column Key.", - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -26818,7 +26964,8 @@ "newKey": { "type": "string", "description": "New Column Key.", - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -26923,7 +27070,8 @@ "default": { "type": "string", "description": "Default value for column when not provided. Cannot be set when column is required.", - "x-example": "email@example.com" + "x-example": "email@example.com", + "x-nullable": true }, "array": { "type": "boolean", @@ -27043,7 +27191,8 @@ "newKey": { "type": "string", "description": "New Column Key.", - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -27156,7 +27305,8 @@ "default": { "type": "string", "description": "Default value for column when not provided. Cannot be set when column is required.", - "x-example": "<DEFAULT>" + "x-example": "<DEFAULT>", + "x-nullable": true }, "array": { "type": "boolean", @@ -27285,7 +27435,8 @@ "newKey": { "type": "string", "description": "New Column Key.", - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -27391,17 +27542,20 @@ "min": { "type": "number", "description": "Minimum value", - "x-example": null + "x-example": null, + "x-nullable": true }, "max": { "type": "number", "description": "Maximum value", - "x-example": null + "x-example": null, + "x-nullable": true }, "default": { "type": "number", "description": "Default value. Cannot be set when required.", - "x-example": null + "x-example": null, + "x-nullable": true }, "array": { "type": "boolean", @@ -27515,12 +27669,14 @@ "min": { "type": "number", "description": "Minimum value", - "x-example": null + "x-example": null, + "x-nullable": true }, "max": { "type": "number", "description": "Maximum value", - "x-example": null + "x-example": null, + "x-nullable": true }, "default": { "type": "number", @@ -27531,7 +27687,8 @@ "newKey": { "type": "string", "description": "New Column Key.", - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -27636,17 +27793,20 @@ "min": { "type": "integer", "description": "Minimum value", - "x-example": null + "x-example": null, + "x-nullable": true }, "max": { "type": "integer", "description": "Maximum value", - "x-example": null + "x-example": null, + "x-nullable": true }, "default": { "type": "integer", "description": "Default value. Cannot be set when column is required.", - "x-example": null + "x-example": null, + "x-nullable": true }, "array": { "type": "boolean", @@ -27760,12 +27920,14 @@ "min": { "type": "integer", "description": "Minimum value", - "x-example": null + "x-example": null, + "x-nullable": true }, "max": { "type": "integer", "description": "Maximum value", - "x-example": null + "x-example": null, + "x-nullable": true }, "default": { "type": "integer", @@ -27776,7 +27938,8 @@ "newKey": { "type": "string", "description": "New Column Key.", - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -27881,7 +28044,8 @@ "default": { "type": "string", "description": "Default value. Cannot be set when column is required.", - "x-example": null + "x-example": null, + "x-nullable": true }, "array": { "type": "boolean", @@ -28001,7 +28165,8 @@ "newKey": { "type": "string", "description": "New Column Key.", - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -28236,7 +28401,8 @@ "newKey": { "type": "string", "description": "New Column Key.", - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -28470,7 +28636,8 @@ "newKey": { "type": "string", "description": "New Column Key.", - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -28704,7 +28871,8 @@ "newKey": { "type": "string", "description": "New Column Key.", - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -28821,12 +28989,14 @@ "key": { "type": "string", "description": "Column Key.", - "x-example": null + "x-example": null, + "x-nullable": true }, "twoWayKey": { "type": "string", "description": "Two Way Column Key.", - "x-example": null + "x-example": null, + "x-nullable": true }, "onDelete": { "type": "string", @@ -28948,7 +29118,8 @@ "default": { "type": "string", "description": "Default value for column when not provided. Cannot be set when column is required.", - "x-example": "<DEFAULT>" + "x-example": "<DEFAULT>", + "x-nullable": true }, "array": { "type": "boolean", @@ -29074,12 +29245,14 @@ "size": { "type": "integer", "description": "Maximum size of the string column.", - "x-example": 1 + "x-example": 1, + "x-nullable": true }, "newKey": { "type": "string", "description": "New Column Key.", - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -29184,7 +29357,8 @@ "default": { "type": "string", "description": "Default value for column when not provided. Cannot be set when column is required.", - "x-example": "https:\/\/example.com" + "x-example": "https:\/\/example.com", + "x-nullable": true }, "array": { "type": "boolean", @@ -29304,7 +29478,8 @@ "newKey": { "type": "string", "description": "New Column Key.", - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -29605,12 +29780,14 @@ "setNull" ], "x-enum-name": "RelationMutate", - "x-enum-keys": [] + "x-enum-keys": [], + "x-nullable": true }, "newKey": { "type": "string", "description": "New Column Key.", - "x-example": null + "x-example": null, + "x-nullable": true } } } @@ -30279,7 +30456,8 @@ "x-example": "[\"read(\"any\")\"]", "items": { "type": "string" - } + }, + "x-nullable": true }, "rows": { "type": "array", @@ -30292,7 +30470,8 @@ "transactionId": { "type": "string", "description": "Transaction ID for staging the operation.", - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -30420,7 +30599,8 @@ "transactionId": { "type": "string", "description": "Transaction ID for staging the operation.", - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } }, "required": [ @@ -30526,7 +30706,8 @@ "transactionId": { "type": "string", "description": "Transaction ID for staging the operation.", - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -30624,7 +30805,8 @@ "transactionId": { "type": "string", "description": "Transaction ID for staging the operation.", - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -30879,12 +31061,14 @@ "x-example": "[\"read(\"any\")\"]", "items": { "type": "string" - } + }, + "x-nullable": true }, "transactionId": { "type": "string", "description": "Transaction ID for staging the operation.", - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -30995,12 +31179,14 @@ "x-example": "[\"read(\"any\")\"]", "items": { "type": "string" - } + }, + "x-nullable": true }, "transactionId": { "type": "string", "description": "Transaction ID for staging the operation.", - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -31096,7 +31282,8 @@ "transactionId": { "type": "string", "description": "Transaction ID for staging the operation.", - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -31216,12 +31403,14 @@ "min": { "type": "number", "description": "Minimum value for the column. If the current value is lesser than this value, an exception will be thrown.", - "x-example": null + "x-example": null, + "x-nullable": true }, "transactionId": { "type": "string", "description": "Transaction ID for staging the operation.", - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -31341,12 +31530,14 @@ "max": { "type": "number", "description": "Maximum value for the column. If the current value is greater than this value, an error will be thrown.", - "x-example": null + "x-example": null, + "x-nullable": true }, "transactionId": { "type": "string", "description": "Transaction ID for staging the operation.", - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -32959,12 +33150,14 @@ "email": { "type": "string", "description": "User email.", - "x-example": "email@example.com" + "x-example": "email@example.com", + "x-nullable": true }, "phone": { "type": "string", "description": "Phone number. Format this number with a leading '+' and a country code, e.g., +16175551212.", - "x-example": "+12065550100" + "x-example": "+12065550100", + "x-nullable": true }, "password": { "type": "string", diff --git a/app/config/specs/open-api3-latest-client.json b/app/config/specs/open-api3-latest-client.json index dde8390820..9adb21d960 100644 --- a/app/config/specs/open-api3-latest-client.json +++ b/app/config/specs/open-api3-latest-client.json @@ -6313,7 +6313,8 @@ "x-example": "[\"read(\"any\")\"]", "items": { "type": "string" - } + }, + "x-nullable": true }, "documents": { "type": "array", @@ -6326,7 +6327,8 @@ "transactionId": { "type": "string", "description": "Transaction ID for staging the operation.", - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -6583,12 +6585,14 @@ "x-example": "[\"read(\"any\")\"]", "items": { "type": "string" - } + }, + "x-nullable": true }, "transactionId": { "type": "string", "description": "Transaction ID for staging the operation.", - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } }, "required": [ @@ -6701,12 +6705,14 @@ "x-example": "[\"read(\"any\")\"]", "items": { "type": "string" - } + }, + "x-nullable": true }, "transactionId": { "type": "string", "description": "Transaction ID for staging the operation.", - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -6801,7 +6807,8 @@ "transactionId": { "type": "string", "description": "Transaction ID for staging the operation.", - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -6920,12 +6927,14 @@ "min": { "type": "number", "description": "Minimum value for the attribute. If the current value is lesser than this value, an exception will be thrown.", - "x-example": null + "x-example": null, + "x-nullable": true }, "transactionId": { "type": "string", "description": "Transaction ID for staging the operation.", - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -7044,12 +7053,14 @@ "max": { "type": "number", "description": "Maximum value for the attribute. If the current value is greater than this value, an error will be thrown.", - "x-example": null + "x-example": null, + "x-nullable": true }, "transactionId": { "type": "string", "description": "Transaction ID for staging the operation.", - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -7251,7 +7262,8 @@ "scheduledAt": { "type": "string", "description": "Scheduled execution time in [ISO 8601](https:\/\/www.iso.org\/iso-8601-date-and-time-format.html) format. DateTime value must be in future with precision in minutes.", - "x-example": "<SCHEDULED_AT>" + "x-example": "<SCHEDULED_AT>", + "x-nullable": true } } } @@ -8194,7 +8206,8 @@ "x-example": "[\"read(\"any\")\"]", "items": { "type": "string" - } + }, + "x-nullable": true } }, "required": [ @@ -8360,7 +8373,8 @@ "name": { "type": "string", "description": "Name of the file", - "x-example": "<NAME>" + "x-example": "<NAME>", + "x-nullable": true }, "permissions": { "type": "array", @@ -8368,7 +8382,8 @@ "x-example": "[\"read(\"any\")\"]", "items": { "type": "string" - } + }, + "x-nullable": true } } } @@ -9500,7 +9515,8 @@ "x-example": "[\"read(\"any\")\"]", "items": { "type": "string" - } + }, + "x-nullable": true }, "rows": { "type": "array", @@ -9513,7 +9529,8 @@ "transactionId": { "type": "string", "description": "Transaction ID for staging the operation.", - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -9763,12 +9780,14 @@ "x-example": "[\"read(\"any\")\"]", "items": { "type": "string" - } + }, + "x-nullable": true }, "transactionId": { "type": "string", "description": "Transaction ID for staging the operation.", - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -9877,12 +9896,14 @@ "x-example": "[\"read(\"any\")\"]", "items": { "type": "string" - } + }, + "x-nullable": true }, "transactionId": { "type": "string", "description": "Transaction ID for staging the operation.", - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -9976,7 +9997,8 @@ "transactionId": { "type": "string", "description": "Transaction ID for staging the operation.", - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -10094,12 +10116,14 @@ "min": { "type": "number", "description": "Minimum value for the column. If the current value is lesser than this value, an exception will be thrown.", - "x-example": null + "x-example": null, + "x-nullable": true }, "transactionId": { "type": "string", "description": "Transaction ID for staging the operation.", - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -10217,12 +10241,14 @@ "max": { "type": "number", "description": "Maximum value for the column. If the current value is greater than this value, an error will be thrown.", - "x-example": null + "x-example": null, + "x-nullable": true }, "transactionId": { "type": "string", "description": "Transaction ID for staging the operation.", - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } diff --git a/app/config/specs/open-api3-latest-console.json b/app/config/specs/open-api3-latest-console.json index aa1e81dbeb..12d2d30ab9 100644 --- a/app/config/specs/open-api3-latest-console.json +++ b/app/config/specs/open-api3-latest-console.json @@ -7059,7 +7059,8 @@ "x-example": "[\"read(\"any\")\"]", "items": { "type": "string" - } + }, + "x-nullable": true }, "documentSecurity": { "type": "boolean", @@ -7245,7 +7246,8 @@ "x-example": "[\"read(\"any\")\"]", "items": { "type": "string" - } + }, + "x-nullable": true }, "documentSecurity": { "type": "boolean", @@ -7524,7 +7526,8 @@ "default": { "type": "boolean", "description": "Default value for attribute when not provided. Cannot be set when attribute is required.", - "x-example": false + "x-example": false, + "x-nullable": true }, "array": { "type": "boolean", @@ -7644,7 +7647,8 @@ "newKey": { "type": "string", "description": "New attribute key.", - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -7869,7 +7873,8 @@ "newKey": { "type": "string", "description": "New attribute key.", - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -7974,7 +7979,8 @@ "default": { "type": "string", "description": "Default value for attribute when not provided. Cannot be set when attribute is required.", - "x-example": "email@example.com" + "x-example": "email@example.com", + "x-nullable": true }, "array": { "type": "boolean", @@ -8094,7 +8100,8 @@ "newKey": { "type": "string", "description": "New Attribute Key.", - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -8207,7 +8214,8 @@ "default": { "type": "string", "description": "Default value for attribute when not provided. Cannot be set when attribute is required.", - "x-example": "<DEFAULT>" + "x-example": "<DEFAULT>", + "x-nullable": true }, "array": { "type": "boolean", @@ -8336,7 +8344,8 @@ "newKey": { "type": "string", "description": "New Attribute Key.", - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -8442,17 +8451,20 @@ "min": { "type": "number", "description": "Minimum value.", - "x-example": null + "x-example": null, + "x-nullable": true }, "max": { "type": "number", "description": "Maximum value.", - "x-example": null + "x-example": null, + "x-nullable": true }, "default": { "type": "number", "description": "Default value. Cannot be set when required.", - "x-example": null + "x-example": null, + "x-nullable": true }, "array": { "type": "boolean", @@ -8566,12 +8578,14 @@ "min": { "type": "number", "description": "Minimum value.", - "x-example": null + "x-example": null, + "x-nullable": true }, "max": { "type": "number", "description": "Maximum value.", - "x-example": null + "x-example": null, + "x-nullable": true }, "default": { "type": "number", @@ -8582,7 +8596,8 @@ "newKey": { "type": "string", "description": "New Attribute Key.", - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -8687,17 +8702,20 @@ "min": { "type": "integer", "description": "Minimum value", - "x-example": null + "x-example": null, + "x-nullable": true }, "max": { "type": "integer", "description": "Maximum value", - "x-example": null + "x-example": null, + "x-nullable": true }, "default": { "type": "integer", "description": "Default value. Cannot be set when attribute is required.", - "x-example": null + "x-example": null, + "x-nullable": true }, "array": { "type": "boolean", @@ -8811,12 +8829,14 @@ "min": { "type": "integer", "description": "Minimum value", - "x-example": null + "x-example": null, + "x-nullable": true }, "max": { "type": "integer", "description": "Maximum value", - "x-example": null + "x-example": null, + "x-nullable": true }, "default": { "type": "integer", @@ -8827,7 +8847,8 @@ "newKey": { "type": "string", "description": "New Attribute Key.", - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -8932,7 +8953,8 @@ "default": { "type": "string", "description": "Default value. Cannot be set when attribute is required.", - "x-example": null + "x-example": null, + "x-nullable": true }, "array": { "type": "boolean", @@ -9052,7 +9074,8 @@ "newKey": { "type": "string", "description": "New Attribute Key.", - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -9287,7 +9310,8 @@ "newKey": { "type": "string", "description": "New attribute key.", - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -9521,7 +9545,8 @@ "newKey": { "type": "string", "description": "New attribute key.", - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -9755,7 +9780,8 @@ "newKey": { "type": "string", "description": "New attribute key.", - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -9872,12 +9898,14 @@ "key": { "type": "string", "description": "Attribute Key.", - "x-example": null + "x-example": null, + "x-nullable": true }, "twoWayKey": { "type": "string", "description": "Two Way Attribute Key.", - "x-example": null + "x-example": null, + "x-nullable": true }, "onDelete": { "type": "string", @@ -9999,7 +10027,8 @@ "default": { "type": "string", "description": "Default value for attribute when not provided. Cannot be set when attribute is required.", - "x-example": "<DEFAULT>" + "x-example": "<DEFAULT>", + "x-nullable": true }, "array": { "type": "boolean", @@ -10125,12 +10154,14 @@ "size": { "type": "integer", "description": "Maximum size of the string attribute.", - "x-example": 1 + "x-example": 1, + "x-nullable": true }, "newKey": { "type": "string", "description": "New Attribute Key.", - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -10235,7 +10266,8 @@ "default": { "type": "string", "description": "Default value for attribute when not provided. Cannot be set when attribute is required.", - "x-example": "https:\/\/example.com" + "x-example": "https:\/\/example.com", + "x-nullable": true }, "array": { "type": "boolean", @@ -10355,7 +10387,8 @@ "newKey": { "type": "string", "description": "New Attribute Key.", - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -10656,12 +10689,14 @@ "setNull" ], "x-enum-name": "RelationMutate", - "x-enum-keys": [] + "x-enum-keys": [], + "x-nullable": true }, "newKey": { "type": "string", "description": "New Attribute Key.", - "x-example": null + "x-example": null, + "x-nullable": true } } } @@ -10945,7 +10980,8 @@ "x-example": "[\"read(\"any\")\"]", "items": { "type": "string" - } + }, + "x-nullable": true }, "documents": { "type": "array", @@ -10958,7 +10994,8 @@ "transactionId": { "type": "string", "description": "Transaction ID for staging the operation.", - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -11089,7 +11126,8 @@ "transactionId": { "type": "string", "description": "Transaction ID for staging the operation.", - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } }, "required": [ @@ -11195,7 +11233,8 @@ "transactionId": { "type": "string", "description": "Transaction ID for staging the operation.", - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -11293,7 +11332,8 @@ "transactionId": { "type": "string", "description": "Transaction ID for staging the operation.", - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -11550,12 +11590,14 @@ "x-example": "[\"read(\"any\")\"]", "items": { "type": "string" - } + }, + "x-nullable": true }, "transactionId": { "type": "string", "description": "Transaction ID for staging the operation.", - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } }, "required": [ @@ -11668,12 +11710,14 @@ "x-example": "[\"read(\"any\")\"]", "items": { "type": "string" - } + }, + "x-nullable": true }, "transactionId": { "type": "string", "description": "Transaction ID for staging the operation.", - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -11768,7 +11812,8 @@ "transactionId": { "type": "string", "description": "Transaction ID for staging the operation.", - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -11984,12 +12029,14 @@ "min": { "type": "number", "description": "Minimum value for the attribute. If the current value is lesser than this value, an exception will be thrown.", - "x-example": null + "x-example": null, + "x-nullable": true }, "transactionId": { "type": "string", "description": "Transaction ID for staging the operation.", - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -12108,12 +12155,14 @@ "max": { "type": "number", "description": "Maximum value for the attribute. If the current value is greater than this value, an error will be thrown.", - "x-example": null + "x-example": null, + "x-nullable": true }, "transactionId": { "type": "string", "description": "Transaction ID for staging the operation.", - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -13105,6 +13154,7 @@ "dart-3.3", "dart-3.5", "dart-3.8", + "dart-3.9", "dotnet-6.0", "dotnet-7.0", "dotnet-8.0", @@ -13131,7 +13181,8 @@ "flutter-3.24", "flutter-3.27", "flutter-3.29", - "flutter-3.32" + "flutter-3.32", + "flutter-3.35" ], "x-enum-name": null, "x-enum-keys": [] @@ -13746,6 +13797,7 @@ "dart-3.3", "dart-3.5", "dart-3.8", + "dart-3.9", "dotnet-6.0", "dotnet-7.0", "dotnet-8.0", @@ -13772,7 +13824,8 @@ "flutter-3.24", "flutter-3.27", "flutter-3.29", - "flutter-3.32" + "flutter-3.32", + "flutter-3.35" ], "x-enum-name": null, "x-enum-keys": [] @@ -14166,12 +14219,14 @@ "entrypoint": { "type": "string", "description": "Entrypoint File.", - "x-example": "<ENTRYPOINT>" + "x-example": "<ENTRYPOINT>", + "x-nullable": true }, "commands": { "type": "string", "description": "Build Commands.", - "x-example": "<COMMANDS>" + "x-example": "<COMMANDS>", + "x-nullable": true }, "code": { "type": "string", @@ -14359,10 +14414,22 @@ "description": "Path to function code in the template repo.", "x-example": "<ROOT_DIRECTORY>" }, - "version": { + "type": { "type": "string", - "description": "Version (tag) for the repo linked to the function template.", - "x-example": "<VERSION>" + "description": "Type for the reference provided. Can be commit, branch, or tag", + "x-example": "commit", + "enum": [ + "commit", + "branch", + "tag" + ], + "x-enum-name": null, + "x-enum-keys": [] + }, + "reference": { + "type": "string", + "description": "Reference value, can be a commit hash, branch name, or release tag", + "x-example": "<REFERENCE>" }, "activate": { "type": "boolean", @@ -14374,7 +14441,8 @@ "repository", "owner", "rootDirectory", - "version" + "type", + "reference" ] } } @@ -14959,7 +15027,8 @@ "scheduledAt": { "type": "string", "description": "Scheduled execution time in [ISO 8601](https:\/\/www.iso.org\/iso-8601-date-and-time-format.html) format. DateTime value must be in future with precision in minutes.", - "x-example": "<SCHEDULED_AT>" + "x-example": "<SCHEDULED_AT>", + "x-nullable": true } } } @@ -15488,12 +15557,14 @@ "value": { "type": "string", "description": "Variable value. Max length: 8192 chars.", - "x-example": "<VALUE>" + "x-example": "<VALUE>", + "x-nullable": true }, "secret": { "type": "boolean", "description": "Secret variables can be updated or deleted, but only functions can read them during build and runtime.", - "x-example": false + "x-example": false, + "x-nullable": true } }, "required": [ @@ -17599,7 +17670,8 @@ "scheduledAt": { "type": "string", "description": "Scheduled delivery time for message in [ISO 8601](https:\/\/www.iso.org\/iso-8601-date-and-time-format.html) format. DateTime value must be in future.", - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -17685,7 +17757,8 @@ "x-example": null, "items": { "type": "string" - } + }, + "x-nullable": true }, "users": { "type": "array", @@ -17693,7 +17766,8 @@ "x-example": null, "items": { "type": "string" - } + }, + "x-nullable": true }, "targets": { "type": "array", @@ -17701,27 +17775,32 @@ "x-example": null, "items": { "type": "string" - } + }, + "x-nullable": true }, "subject": { "type": "string", "description": "Email Subject.", - "x-example": "<SUBJECT>" + "x-example": "<SUBJECT>", + "x-nullable": true }, "content": { "type": "string", "description": "Email Content.", - "x-example": "<CONTENT>" + "x-example": "<CONTENT>", + "x-nullable": true }, "draft": { "type": "boolean", "description": "Is message a draft", - "x-example": false + "x-example": false, + "x-nullable": true }, "html": { "type": "boolean", "description": "Is content of type HTML", - "x-example": false + "x-example": false, + "x-nullable": true }, "cc": { "type": "array", @@ -17729,7 +17808,8 @@ "x-example": null, "items": { "type": "string" - } + }, + "x-nullable": true }, "bcc": { "type": "array", @@ -17737,12 +17817,14 @@ "x-example": null, "items": { "type": "string" - } + }, + "x-nullable": true }, "scheduledAt": { "type": "string", "description": "Scheduled delivery time for message in [ISO 8601](https:\/\/www.iso.org\/iso-8601-date-and-time-format.html) format. DateTime value must be in future.", - "x-example": null + "x-example": null, + "x-nullable": true }, "attachments": { "type": "array", @@ -17750,7 +17832,8 @@ "x-example": null, "items": { "type": "string" - } + }, + "x-nullable": true } } } @@ -17855,7 +17938,8 @@ "data": { "type": "object", "description": "Additional key-value pair data for push notification.", - "x-example": "{}" + "x-example": "{}", + "x-nullable": true }, "action": { "type": "string", @@ -17900,7 +17984,8 @@ "scheduledAt": { "type": "string", "description": "Scheduled delivery time for message in [ISO 8601](https:\/\/www.iso.org\/iso-8601-date-and-time-format.html) format. DateTime value must be in future.", - "x-example": null + "x-example": null, + "x-nullable": true }, "contentAvailable": { "type": "boolean", @@ -18005,7 +18090,8 @@ "x-example": null, "items": { "type": "string" - } + }, + "x-nullable": true }, "users": { "type": "array", @@ -18013,7 +18099,8 @@ "x-example": null, "items": { "type": "string" - } + }, + "x-nullable": true }, "targets": { "type": "array", @@ -18021,77 +18108,92 @@ "x-example": null, "items": { "type": "string" - } + }, + "x-nullable": true }, "title": { "type": "string", "description": "Title for push notification.", - "x-example": "<TITLE>" + "x-example": "<TITLE>", + "x-nullable": true }, "body": { "type": "string", "description": "Body for push notification.", - "x-example": "<BODY>" + "x-example": "<BODY>", + "x-nullable": true }, "data": { "type": "object", "description": "Additional Data for push notification.", - "x-example": "{}" + "x-example": "{}", + "x-nullable": true }, "action": { "type": "string", "description": "Action for push notification.", - "x-example": "<ACTION>" + "x-example": "<ACTION>", + "x-nullable": true }, "image": { "type": "string", "description": "Image for push notification. Must be a compound bucket ID to file ID of a jpeg, png, or bmp image in Appwrite Storage. It should be formatted as <BUCKET_ID>:<FILE_ID>.", - "x-example": "<ID1:ID2>" + "x-example": "<ID1:ID2>", + "x-nullable": true }, "icon": { "type": "string", "description": "Icon for push notification. Available only for Android and Web platforms.", - "x-example": "<ICON>" + "x-example": "<ICON>", + "x-nullable": true }, "sound": { "type": "string", "description": "Sound for push notification. Available only for Android and iOS platforms.", - "x-example": "<SOUND>" + "x-example": "<SOUND>", + "x-nullable": true }, "color": { "type": "string", "description": "Color for push notification. Available only for Android platforms.", - "x-example": "<COLOR>" + "x-example": "<COLOR>", + "x-nullable": true }, "tag": { "type": "string", "description": "Tag for push notification. Available only for Android platforms.", - "x-example": "<TAG>" + "x-example": "<TAG>", + "x-nullable": true }, "badge": { "type": "integer", "description": "Badge for push notification. Available only for iOS platforms.", - "x-example": null + "x-example": null, + "x-nullable": true }, "draft": { "type": "boolean", "description": "Is message a draft", - "x-example": false + "x-example": false, + "x-nullable": true }, "scheduledAt": { "type": "string", "description": "Scheduled delivery time for message in [ISO 8601](https:\/\/www.iso.org\/iso-8601-date-and-time-format.html) format. DateTime value must be in future.", - "x-example": null + "x-example": null, + "x-nullable": true }, "contentAvailable": { "type": "boolean", "description": "If set to true, the notification will be delivered in the background. Available only for iOS Platform.", - "x-example": false + "x-example": false, + "x-nullable": true }, "critical": { "type": "boolean", "description": "If set to true, the notification will be marked as critical. This requires the app to have the critical notification entitlement. Available only for iOS Platform.", - "x-example": false + "x-example": false, + "x-nullable": true }, "priority": { "type": "string", @@ -18102,7 +18204,8 @@ "high" ], "x-enum-name": "MessagePriority", - "x-enum-keys": [] + "x-enum-keys": [], + "x-nullable": true } } } @@ -18275,7 +18378,8 @@ "scheduledAt": { "type": "string", "description": "Scheduled delivery time for message in [ISO 8601](https:\/\/www.iso.org\/iso-8601-date-and-time-format.html) format. DateTime value must be in future.", - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -18426,7 +18530,8 @@ "x-example": null, "items": { "type": "string" - } + }, + "x-nullable": true }, "users": { "type": "array", @@ -18434,7 +18539,8 @@ "x-example": null, "items": { "type": "string" - } + }, + "x-nullable": true }, "targets": { "type": "array", @@ -18442,22 +18548,26 @@ "x-example": null, "items": { "type": "string" - } + }, + "x-nullable": true }, "content": { "type": "string", "description": "Email Content.", - "x-example": "<CONTENT>" + "x-example": "<CONTENT>", + "x-nullable": true }, "draft": { "type": "boolean", "description": "Is message a draft", - "x-example": false + "x-example": false, + "x-nullable": true }, "scheduledAt": { "type": "string", "description": "Scheduled delivery time for message in [ISO 8601](https:\/\/www.iso.org\/iso-8601-date-and-time-format.html) format. DateTime value must be in future.", - "x-example": null + "x-example": null, + "x-nullable": true } } } @@ -19002,7 +19112,8 @@ "enabled": { "type": "boolean", "description": "Set as enabled.", - "x-example": false + "x-example": false, + "x-nullable": true } }, "required": [ @@ -19157,7 +19268,8 @@ "enabled": { "type": "boolean", "description": "Set as enabled.", - "x-example": false + "x-example": false, + "x-nullable": true }, "authKey": { "type": "string", @@ -19182,7 +19294,8 @@ "sandbox": { "type": "boolean", "description": "Use APNS sandbox environment.", - "x-example": false + "x-example": false, + "x-nullable": true } } } @@ -19320,12 +19433,14 @@ "serviceAccountJSON": { "type": "object", "description": "FCM service account JSON.", - "x-example": "{}" + "x-example": "{}", + "x-nullable": true }, "enabled": { "type": "boolean", "description": "Set as enabled.", - "x-example": false + "x-example": false, + "x-nullable": true } }, "required": [ @@ -19472,12 +19587,14 @@ "enabled": { "type": "boolean", "description": "Set as enabled.", - "x-example": false + "x-example": false, + "x-nullable": true }, "serviceAccountJSON": { "type": "object", "description": "FCM service account JSON.", - "x-example": "{}" + "x-example": "{}", + "x-nullable": true } } } @@ -19563,7 +19680,8 @@ "isEuRegion": { "type": "boolean", "description": "Set as EU region.", - "x-example": false + "x-example": false, + "x-nullable": true }, "fromName": { "type": "string", @@ -19588,7 +19706,8 @@ "enabled": { "type": "boolean", "description": "Set as enabled.", - "x-example": false + "x-example": false, + "x-nullable": true } }, "required": [ @@ -19685,12 +19804,14 @@ "isEuRegion": { "type": "boolean", "description": "Set as EU region.", - "x-example": false + "x-example": false, + "x-nullable": true }, "enabled": { "type": "boolean", "description": "Set as enabled.", - "x-example": false + "x-example": false, + "x-nullable": true }, "fromName": { "type": "string", @@ -19801,7 +19922,8 @@ "enabled": { "type": "boolean", "description": "Set as enabled.", - "x-example": false + "x-example": false, + "x-nullable": true } }, "required": [ @@ -19888,7 +20010,8 @@ "enabled": { "type": "boolean", "description": "Set as enabled.", - "x-example": false + "x-example": false, + "x-nullable": true }, "templateId": { "type": "string", @@ -20004,7 +20127,8 @@ "enabled": { "type": "boolean", "description": "Set as enabled.", - "x-example": false + "x-example": false, + "x-nullable": true } }, "required": [ @@ -20091,7 +20215,8 @@ "enabled": { "type": "boolean", "description": "Set as enabled.", - "x-example": false + "x-example": false, + "x-nullable": true }, "apiKey": { "type": "string", @@ -20217,7 +20342,8 @@ "enabled": { "type": "boolean", "description": "Set as enabled.", - "x-example": false + "x-example": false, + "x-nullable": true } }, "required": [ @@ -20304,7 +20430,8 @@ "enabled": { "type": "boolean", "description": "Set as enabled.", - "x-example": false + "x-example": false, + "x-nullable": true }, "apiKey": { "type": "string", @@ -20551,7 +20678,8 @@ "enabled": { "type": "boolean", "description": "Set as enabled.", - "x-example": false + "x-example": false, + "x-nullable": true } }, "required": [ @@ -20724,7 +20852,8 @@ "port": { "type": "integer", "description": "SMTP port.", - "x-example": 1 + "x-example": 1, + "x-nullable": true }, "username": { "type": "string", @@ -20751,7 +20880,8 @@ "autoTLS": { "type": "boolean", "description": "Enable SMTP AutoTLS feature.", - "x-example": false + "x-example": false, + "x-nullable": true }, "mailer": { "type": "string", @@ -20781,7 +20911,8 @@ "enabled": { "type": "boolean", "description": "Set as enabled.", - "x-example": false + "x-example": false, + "x-nullable": true } } } @@ -20872,7 +21003,8 @@ "enabled": { "type": "boolean", "description": "Set as enabled.", - "x-example": false + "x-example": false, + "x-nullable": true } }, "required": [ @@ -20959,7 +21091,8 @@ "enabled": { "type": "boolean", "description": "Set as enabled.", - "x-example": false + "x-example": false, + "x-nullable": true }, "customerId": { "type": "string", @@ -21065,7 +21198,8 @@ "enabled": { "type": "boolean", "description": "Set as enabled.", - "x-example": false + "x-example": false, + "x-nullable": true } }, "required": [ @@ -21152,7 +21286,8 @@ "enabled": { "type": "boolean", "description": "Set as enabled.", - "x-example": false + "x-example": false, + "x-nullable": true }, "username": { "type": "string", @@ -21258,7 +21393,8 @@ "enabled": { "type": "boolean", "description": "Set as enabled.", - "x-example": false + "x-example": false, + "x-nullable": true } }, "required": [ @@ -21345,7 +21481,8 @@ "enabled": { "type": "boolean", "description": "Set as enabled.", - "x-example": false + "x-example": false, + "x-nullable": true }, "accountSid": { "type": "string", @@ -21451,7 +21588,8 @@ "enabled": { "type": "boolean", "description": "Set as enabled.", - "x-example": false + "x-example": false, + "x-nullable": true } }, "required": [ @@ -21538,7 +21676,8 @@ "enabled": { "type": "boolean", "description": "Set as enabled.", - "x-example": false + "x-example": false, + "x-nullable": true }, "apiKey": { "type": "string", @@ -22146,7 +22285,8 @@ "name": { "type": "string", "description": "Topic Name.", - "x-example": "<NAME>" + "x-example": "<NAME>", + "x-nullable": true }, "subscribe": { "type": "array", @@ -22154,7 +22294,8 @@ "x-example": "[\"any\"]", "items": { "type": "string" - } + }, + "x-nullable": true } } } @@ -22898,7 +23039,7 @@ "tags": [ "migrations" ], - "description": "Export documents to a CSV file from your Appwrite database. This endpoint allows you to export documents to a CSV file stored in an Appwrite Storage bucket.", + "description": "Export documents to a CSV file from your Appwrite database. This endpoint allows you to export documents to a CSV file stored in a secure internal bucket. You'll receive an email with a download link when the export is complete.", "responses": { "202": { "description": "Migration", @@ -22948,11 +23089,6 @@ "description": "Composite ID in the format {databaseId:collectionId}, identifying a collection within a database to export.", "x-example": "<ID1:ID2>" }, - "bucketId": { - "type": "string", - "description": "Storage bucket unique ID where the exported CSV will be stored.", - "x-example": "<BUCKET_ID>" - }, "filename": { "type": "string", "description": "The name of the file to be created for the export, excluding the .csv extension.", @@ -23002,7 +23138,6 @@ }, "required": [ "resourceId", - "bucketId", "filename" ] } @@ -24227,12 +24362,14 @@ "value": { "type": "string", "description": "Variable value. Max length: 8192 chars.", - "x-example": "<VALUE>" + "x-example": "<VALUE>", + "x-nullable": true }, "secret": { "type": "boolean", "description": "Secret variables can be updated or deleted, but only projects can read them during build and runtime.", - "x-example": false + "x-example": false, + "x-nullable": true } }, "required": [ @@ -26552,12 +26689,14 @@ "x-example": null, "items": { "type": "string" - } + }, + "x-nullable": true }, "expire": { "type": "string", "description": "Expiration time in [ISO 8601](https:\/\/www.iso.org\/iso-8601-date-and-time-format.html) format. Use null for unlimited expiration.", - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -26723,12 +26862,14 @@ "x-example": null, "items": { "type": "string" - } + }, + "x-nullable": true }, "expire": { "type": "string", "description": "Expiration time in [ISO 8601](https:\/\/www.iso.org\/iso-8601-date-and-time-format.html) format. Use null for unlimited expiration.", - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -26918,17 +27059,20 @@ "appId": { "type": "string", "description": "Provider app ID. Max length: 256 chars.", - "x-example": "<APP_ID>" + "x-example": "<APP_ID>", + "x-nullable": true }, "secret": { "type": "string", "description": "Provider secret key. Max length: 512 chars.", - "x-example": "<SECRET>" + "x-example": "<SECRET>", + "x-nullable": true }, "enabled": { "type": "boolean", "description": "Provider status. Set to 'false' to disable new session creation.", - "x-example": false + "x-example": false, + "x-nullable": true } }, "required": [ @@ -30954,6 +31098,7 @@ "dart-3.3", "dart-3.5", "dart-3.8", + "dart-3.9", "dotnet-6.0", "dotnet-7.0", "dotnet-8.0", @@ -30980,7 +31125,8 @@ "flutter-3.24", "flutter-3.27", "flutter-3.29", - "flutter-3.32" + "flutter-3.32", + "flutter-3.35" ], "x-enum-name": null, "x-enum-keys": [] @@ -31601,6 +31747,7 @@ "dart-3.3", "dart-3.5", "dart-3.8", + "dart-3.9", "dotnet-6.0", "dotnet-7.0", "dotnet-8.0", @@ -31627,7 +31774,8 @@ "flutter-3.24", "flutter-3.27", "flutter-3.29", - "flutter-3.32" + "flutter-3.32", + "flutter-3.35" ], "x-enum-name": null, "x-enum-keys": [] @@ -31983,17 +32131,20 @@ "installCommand": { "type": "string", "description": "Install Commands.", - "x-example": "<INSTALL_COMMAND>" + "x-example": "<INSTALL_COMMAND>", + "x-nullable": true }, "buildCommand": { "type": "string", "description": "Build Commands.", - "x-example": "<BUILD_COMMAND>" + "x-example": "<BUILD_COMMAND>", + "x-nullable": true }, "outputDirectory": { "type": "string", "description": "Output Directory.", - "x-example": "<OUTPUT_DIRECTORY>" + "x-example": "<OUTPUT_DIRECTORY>", + "x-nullable": true }, "code": { "type": "string", @@ -32176,10 +32327,22 @@ "description": "Path to site code in the template repo.", "x-example": "<ROOT_DIRECTORY>" }, - "version": { + "type": { "type": "string", - "description": "Version (tag) for the repo linked to the site template.", - "x-example": "<VERSION>" + "description": "Type for the reference provided. Can be commit, branch, or tag", + "x-example": "branch", + "enum": [ + "branch", + "commit", + "tag" + ], + "x-enum-name": null, + "x-enum-keys": [] + }, + "reference": { + "type": "string", + "description": "Reference value, can be a commit hash, branch name, or release tag", + "x-example": "<REFERENCE>" }, "activate": { "type": "boolean", @@ -32191,7 +32354,8 @@ "repository", "owner", "rootDirectory", - "version" + "type", + "reference" ] } } @@ -33186,12 +33350,14 @@ "value": { "type": "string", "description": "Variable value. Max length: 8192 chars.", - "x-example": "<VALUE>" + "x-example": "<VALUE>", + "x-nullable": true }, "secret": { "type": "boolean", "description": "Secret variables can be updated or deleted, but only sites can read them during build and runtime.", - "x-example": false + "x-example": false, + "x-nullable": true } }, "required": [ @@ -33418,7 +33584,8 @@ "x-example": "[\"read(\"any\")\"]", "items": { "type": "string" - } + }, + "x-nullable": true }, "fileSecurity": { "type": "boolean", @@ -33611,7 +33778,8 @@ "x-example": "[\"read(\"any\")\"]", "items": { "type": "string" - } + }, + "x-nullable": true }, "fileSecurity": { "type": "boolean", @@ -33902,7 +34070,8 @@ "x-example": "[\"read(\"any\")\"]", "items": { "type": "string" - } + }, + "x-nullable": true } }, "required": [ @@ -34068,7 +34237,8 @@ "name": { "type": "string", "description": "Name of the file", - "x-example": "<NAME>" + "x-example": "<NAME>", + "x-nullable": true }, "permissions": { "type": "array", @@ -34076,7 +34246,8 @@ "x-example": "[\"read(\"any\")\"]", "items": { "type": "string" - } + }, + "x-nullable": true } } } @@ -35761,7 +35932,8 @@ "x-example": "[\"read(\"any\")\"]", "items": { "type": "string" - } + }, + "x-nullable": true }, "rowSecurity": { "type": "boolean", @@ -35945,7 +36117,8 @@ "x-example": "[\"read(\"any\")\"]", "items": { "type": "string" - } + }, + "x-nullable": true }, "rowSecurity": { "type": "boolean", @@ -36221,7 +36394,8 @@ "default": { "type": "boolean", "description": "Default value for column when not provided. Cannot be set when column is required.", - "x-example": false + "x-example": false, + "x-nullable": true }, "array": { "type": "boolean", @@ -36340,7 +36514,8 @@ "newKey": { "type": "string", "description": "New Column Key.", - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -36563,7 +36738,8 @@ "newKey": { "type": "string", "description": "New Column Key.", - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -36667,7 +36843,8 @@ "default": { "type": "string", "description": "Default value for column when not provided. Cannot be set when column is required.", - "x-example": "email@example.com" + "x-example": "email@example.com", + "x-nullable": true }, "array": { "type": "boolean", @@ -36786,7 +36963,8 @@ "newKey": { "type": "string", "description": "New Column Key.", - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -36898,7 +37076,8 @@ "default": { "type": "string", "description": "Default value for column when not provided. Cannot be set when column is required.", - "x-example": "<DEFAULT>" + "x-example": "<DEFAULT>", + "x-nullable": true }, "array": { "type": "boolean", @@ -37026,7 +37205,8 @@ "newKey": { "type": "string", "description": "New Column Key.", - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -37131,17 +37311,20 @@ "min": { "type": "number", "description": "Minimum value", - "x-example": null + "x-example": null, + "x-nullable": true }, "max": { "type": "number", "description": "Maximum value", - "x-example": null + "x-example": null, + "x-nullable": true }, "default": { "type": "number", "description": "Default value. Cannot be set when required.", - "x-example": null + "x-example": null, + "x-nullable": true }, "array": { "type": "boolean", @@ -37254,12 +37437,14 @@ "min": { "type": "number", "description": "Minimum value", - "x-example": null + "x-example": null, + "x-nullable": true }, "max": { "type": "number", "description": "Maximum value", - "x-example": null + "x-example": null, + "x-nullable": true }, "default": { "type": "number", @@ -37270,7 +37455,8 @@ "newKey": { "type": "string", "description": "New Column Key.", - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -37374,17 +37560,20 @@ "min": { "type": "integer", "description": "Minimum value", - "x-example": null + "x-example": null, + "x-nullable": true }, "max": { "type": "integer", "description": "Maximum value", - "x-example": null + "x-example": null, + "x-nullable": true }, "default": { "type": "integer", "description": "Default value. Cannot be set when column is required.", - "x-example": null + "x-example": null, + "x-nullable": true }, "array": { "type": "boolean", @@ -37497,12 +37686,14 @@ "min": { "type": "integer", "description": "Minimum value", - "x-example": null + "x-example": null, + "x-nullable": true }, "max": { "type": "integer", "description": "Maximum value", - "x-example": null + "x-example": null, + "x-nullable": true }, "default": { "type": "integer", @@ -37513,7 +37704,8 @@ "newKey": { "type": "string", "description": "New Column Key.", - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -37617,7 +37809,8 @@ "default": { "type": "string", "description": "Default value. Cannot be set when column is required.", - "x-example": null + "x-example": null, + "x-nullable": true }, "array": { "type": "boolean", @@ -37736,7 +37929,8 @@ "newKey": { "type": "string", "description": "New Column Key.", - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -37969,7 +38163,8 @@ "newKey": { "type": "string", "description": "New Column Key.", - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -38201,7 +38396,8 @@ "newKey": { "type": "string", "description": "New Column Key.", - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -38433,7 +38629,8 @@ "newKey": { "type": "string", "description": "New Column Key.", - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -38549,12 +38746,14 @@ "key": { "type": "string", "description": "Column Key.", - "x-example": null + "x-example": null, + "x-nullable": true }, "twoWayKey": { "type": "string", "description": "Two Way Column Key.", - "x-example": null + "x-example": null, + "x-nullable": true }, "onDelete": { "type": "string", @@ -38675,7 +38874,8 @@ "default": { "type": "string", "description": "Default value for column when not provided. Cannot be set when column is required.", - "x-example": "<DEFAULT>" + "x-example": "<DEFAULT>", + "x-nullable": true }, "array": { "type": "boolean", @@ -38800,12 +39000,14 @@ "size": { "type": "integer", "description": "Maximum size of the string column.", - "x-example": 1 + "x-example": 1, + "x-nullable": true }, "newKey": { "type": "string", "description": "New Column Key.", - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -38909,7 +39111,8 @@ "default": { "type": "string", "description": "Default value for column when not provided. Cannot be set when column is required.", - "x-example": "https:\/\/example.com" + "x-example": "https:\/\/example.com", + "x-nullable": true }, "array": { "type": "boolean", @@ -39028,7 +39231,8 @@ "newKey": { "type": "string", "description": "New Column Key.", - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -39326,12 +39530,14 @@ "setNull" ], "x-enum-name": "RelationMutate", - "x-enum-keys": [] + "x-enum-keys": [], + "x-nullable": true }, "newKey": { "type": "string", "description": "New Column Key.", - "x-example": null + "x-example": null, + "x-nullable": true } } } @@ -40076,7 +40282,8 @@ "x-example": "[\"read(\"any\")\"]", "items": { "type": "string" - } + }, + "x-nullable": true }, "rows": { "type": "array", @@ -40089,7 +40296,8 @@ "transactionId": { "type": "string", "description": "Transaction ID for staging the operation.", - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -40215,7 +40423,8 @@ "transactionId": { "type": "string", "description": "Transaction ID for staging the operation.", - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } }, "required": [ @@ -40320,7 +40529,8 @@ "transactionId": { "type": "string", "description": "Transaction ID for staging the operation.", - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -40417,7 +40627,8 @@ "transactionId": { "type": "string", "description": "Transaction ID for staging the operation.", - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -40667,12 +40878,14 @@ "x-example": "[\"read(\"any\")\"]", "items": { "type": "string" - } + }, + "x-nullable": true }, "transactionId": { "type": "string", "description": "Transaction ID for staging the operation.", - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -40781,12 +40994,14 @@ "x-example": "[\"read(\"any\")\"]", "items": { "type": "string" - } + }, + "x-nullable": true }, "transactionId": { "type": "string", "description": "Transaction ID for staging the operation.", - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -40880,7 +41095,8 @@ "transactionId": { "type": "string", "description": "Transaction ID for staging the operation.", - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -41094,12 +41310,14 @@ "min": { "type": "number", "description": "Minimum value for the column. If the current value is lesser than this value, an exception will be thrown.", - "x-example": null + "x-example": null, + "x-nullable": true }, "transactionId": { "type": "string", "description": "Transaction ID for staging the operation.", - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -41217,12 +41435,14 @@ "max": { "type": "number", "description": "Maximum value for the column. If the current value is greater than this value, an error will be thrown.", - "x-example": null + "x-example": null, + "x-nullable": true }, "transactionId": { "type": "string", "description": "Transaction ID for staging the operation.", - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -43091,12 +43311,14 @@ "email": { "type": "string", "description": "User email.", - "x-example": "email@example.com" + "x-example": "email@example.com", + "x-nullable": true }, "phone": { "type": "string", "description": "Phone number. Format this number with a leading '+' and a country code, e.g., +16175551212.", - "x-example": "+12065550100" + "x-example": "+12065550100", + "x-nullable": true }, "password": { "type": "string", diff --git a/app/config/specs/open-api3-latest-server.json b/app/config/specs/open-api3-latest-server.json index c3df6ef373..b11c69442b 100644 --- a/app/config/specs/open-api3-latest-server.json +++ b/app/config/specs/open-api3-latest-server.json @@ -6521,7 +6521,8 @@ "x-example": "[\"read(\"any\")\"]", "items": { "type": "string" - } + }, + "x-nullable": true }, "documentSecurity": { "type": "boolean", @@ -6709,7 +6710,8 @@ "x-example": "[\"read(\"any\")\"]", "items": { "type": "string" - } + }, + "x-nullable": true }, "documentSecurity": { "type": "boolean", @@ -6991,7 +6993,8 @@ "default": { "type": "boolean", "description": "Default value for attribute when not provided. Cannot be set when attribute is required.", - "x-example": false + "x-example": false, + "x-nullable": true }, "array": { "type": "boolean", @@ -7112,7 +7115,8 @@ "newKey": { "type": "string", "description": "New attribute key.", - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -7339,7 +7343,8 @@ "newKey": { "type": "string", "description": "New attribute key.", - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -7445,7 +7450,8 @@ "default": { "type": "string", "description": "Default value for attribute when not provided. Cannot be set when attribute is required.", - "x-example": "email@example.com" + "x-example": "email@example.com", + "x-nullable": true }, "array": { "type": "boolean", @@ -7566,7 +7572,8 @@ "newKey": { "type": "string", "description": "New Attribute Key.", - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -7680,7 +7687,8 @@ "default": { "type": "string", "description": "Default value for attribute when not provided. Cannot be set when attribute is required.", - "x-example": "<DEFAULT>" + "x-example": "<DEFAULT>", + "x-nullable": true }, "array": { "type": "boolean", @@ -7810,7 +7818,8 @@ "newKey": { "type": "string", "description": "New Attribute Key.", - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -7917,17 +7926,20 @@ "min": { "type": "number", "description": "Minimum value.", - "x-example": null + "x-example": null, + "x-nullable": true }, "max": { "type": "number", "description": "Maximum value.", - "x-example": null + "x-example": null, + "x-nullable": true }, "default": { "type": "number", "description": "Default value. Cannot be set when required.", - "x-example": null + "x-example": null, + "x-nullable": true }, "array": { "type": "boolean", @@ -8042,12 +8054,14 @@ "min": { "type": "number", "description": "Minimum value.", - "x-example": null + "x-example": null, + "x-nullable": true }, "max": { "type": "number", "description": "Maximum value.", - "x-example": null + "x-example": null, + "x-nullable": true }, "default": { "type": "number", @@ -8058,7 +8072,8 @@ "newKey": { "type": "string", "description": "New Attribute Key.", - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -8164,17 +8179,20 @@ "min": { "type": "integer", "description": "Minimum value", - "x-example": null + "x-example": null, + "x-nullable": true }, "max": { "type": "integer", "description": "Maximum value", - "x-example": null + "x-example": null, + "x-nullable": true }, "default": { "type": "integer", "description": "Default value. Cannot be set when attribute is required.", - "x-example": null + "x-example": null, + "x-nullable": true }, "array": { "type": "boolean", @@ -8289,12 +8307,14 @@ "min": { "type": "integer", "description": "Minimum value", - "x-example": null + "x-example": null, + "x-nullable": true }, "max": { "type": "integer", "description": "Maximum value", - "x-example": null + "x-example": null, + "x-nullable": true }, "default": { "type": "integer", @@ -8305,7 +8325,8 @@ "newKey": { "type": "string", "description": "New Attribute Key.", - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -8411,7 +8432,8 @@ "default": { "type": "string", "description": "Default value. Cannot be set when attribute is required.", - "x-example": null + "x-example": null, + "x-nullable": true }, "array": { "type": "boolean", @@ -8532,7 +8554,8 @@ "newKey": { "type": "string", "description": "New Attribute Key.", - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -8769,7 +8792,8 @@ "newKey": { "type": "string", "description": "New attribute key.", - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -9005,7 +9029,8 @@ "newKey": { "type": "string", "description": "New attribute key.", - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -9241,7 +9266,8 @@ "newKey": { "type": "string", "description": "New attribute key.", - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -9359,12 +9385,14 @@ "key": { "type": "string", "description": "Attribute Key.", - "x-example": null + "x-example": null, + "x-nullable": true }, "twoWayKey": { "type": "string", "description": "Two Way Attribute Key.", - "x-example": null + "x-example": null, + "x-nullable": true }, "onDelete": { "type": "string", @@ -9487,7 +9515,8 @@ "default": { "type": "string", "description": "Default value for attribute when not provided. Cannot be set when attribute is required.", - "x-example": "<DEFAULT>" + "x-example": "<DEFAULT>", + "x-nullable": true }, "array": { "type": "boolean", @@ -9614,12 +9643,14 @@ "size": { "type": "integer", "description": "Maximum size of the string attribute.", - "x-example": 1 + "x-example": 1, + "x-nullable": true }, "newKey": { "type": "string", "description": "New Attribute Key.", - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -9725,7 +9756,8 @@ "default": { "type": "string", "description": "Default value for attribute when not provided. Cannot be set when attribute is required.", - "x-example": "https:\/\/example.com" + "x-example": "https:\/\/example.com", + "x-nullable": true }, "array": { "type": "boolean", @@ -9846,7 +9878,8 @@ "newKey": { "type": "string", "description": "New Attribute Key.", - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -10150,12 +10183,14 @@ "setNull" ], "x-enum-name": "RelationMutate", - "x-enum-keys": [] + "x-enum-keys": [], + "x-nullable": true }, "newKey": { "type": "string", "description": "New Attribute Key.", - "x-example": null + "x-example": null, + "x-nullable": true } } } @@ -10445,7 +10480,8 @@ "x-example": "[\"read(\"any\")\"]", "items": { "type": "string" - } + }, + "x-nullable": true }, "documents": { "type": "array", @@ -10458,7 +10494,8 @@ "transactionId": { "type": "string", "description": "Transaction ID for staging the operation.", - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -10591,7 +10628,8 @@ "transactionId": { "type": "string", "description": "Transaction ID for staging the operation.", - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } }, "required": [ @@ -10698,7 +10736,8 @@ "transactionId": { "type": "string", "description": "Transaction ID for staging the operation.", - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -10797,7 +10836,8 @@ "transactionId": { "type": "string", "description": "Transaction ID for staging the operation.", - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -11059,12 +11099,14 @@ "x-example": "[\"read(\"any\")\"]", "items": { "type": "string" - } + }, + "x-nullable": true }, "transactionId": { "type": "string", "description": "Transaction ID for staging the operation.", - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } }, "required": [ @@ -11179,12 +11221,14 @@ "x-example": "[\"read(\"any\")\"]", "items": { "type": "string" - } + }, + "x-nullable": true }, "transactionId": { "type": "string", "description": "Transaction ID for staging the operation.", - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -11281,7 +11325,8 @@ "transactionId": { "type": "string", "description": "Transaction ID for staging the operation.", - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -11402,12 +11447,14 @@ "min": { "type": "number", "description": "Minimum value for the attribute. If the current value is lesser than this value, an exception will be thrown.", - "x-example": null + "x-example": null, + "x-nullable": true }, "transactionId": { "type": "string", "description": "Transaction ID for staging the operation.", - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -11528,12 +11575,14 @@ "max": { "type": "number", "description": "Maximum value for the attribute. If the current value is greater than this value, an error will be thrown.", - "x-example": null + "x-example": null, + "x-nullable": true }, "transactionId": { "type": "string", "description": "Transaction ID for staging the operation.", - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -12127,6 +12176,7 @@ "dart-3.3", "dart-3.5", "dart-3.8", + "dart-3.9", "dotnet-6.0", "dotnet-7.0", "dotnet-8.0", @@ -12153,7 +12203,8 @@ "flutter-3.24", "flutter-3.27", "flutter-3.29", - "flutter-3.32" + "flutter-3.32", + "flutter-3.35" ], "x-enum-name": null, "x-enum-keys": [] @@ -12529,6 +12580,7 @@ "dart-3.3", "dart-3.5", "dart-3.8", + "dart-3.9", "dotnet-6.0", "dotnet-7.0", "dotnet-8.0", @@ -12555,7 +12607,8 @@ "flutter-3.24", "flutter-3.27", "flutter-3.29", - "flutter-3.32" + "flutter-3.32", + "flutter-3.35" ], "x-enum-name": null, "x-enum-keys": [] @@ -12953,12 +13006,14 @@ "entrypoint": { "type": "string", "description": "Entrypoint File.", - "x-example": "<ENTRYPOINT>" + "x-example": "<ENTRYPOINT>", + "x-nullable": true }, "commands": { "type": "string", "description": "Build Commands.", - "x-example": "<COMMANDS>" + "x-example": "<COMMANDS>", + "x-nullable": true }, "code": { "type": "string", @@ -13148,10 +13203,22 @@ "description": "Path to function code in the template repo.", "x-example": "<ROOT_DIRECTORY>" }, - "version": { + "type": { "type": "string", - "description": "Version (tag) for the repo linked to the function template.", - "x-example": "<VERSION>" + "description": "Type for the reference provided. Can be commit, branch, or tag", + "x-example": "commit", + "enum": [ + "commit", + "branch", + "tag" + ], + "x-enum-name": null, + "x-enum-keys": [] + }, + "reference": { + "type": "string", + "description": "Reference value, can be a commit hash, branch name, or release tag", + "x-example": "<REFERENCE>" }, "activate": { "type": "boolean", @@ -13163,7 +13230,8 @@ "repository", "owner", "rootDirectory", - "version" + "type", + "reference" ] } } @@ -13757,7 +13825,8 @@ "scheduledAt": { "type": "string", "description": "Scheduled execution time in [ISO 8601](https:\/\/www.iso.org\/iso-8601-date-and-time-format.html) format. DateTime value must be in future with precision in minutes.", - "x-example": "<SCHEDULED_AT>" + "x-example": "<SCHEDULED_AT>", + "x-nullable": true } } } @@ -14211,12 +14280,14 @@ "value": { "type": "string", "description": "Variable value. Max length: 8192 chars.", - "x-example": "<VALUE>" + "x-example": "<VALUE>", + "x-nullable": true }, "secret": { "type": "boolean", "description": "Secret variables can be updated or deleted, but only functions can read them during build and runtime.", - "x-example": false + "x-example": false, + "x-nullable": true } }, "required": [ @@ -16367,7 +16438,8 @@ "scheduledAt": { "type": "string", "description": "Scheduled delivery time for message in [ISO 8601](https:\/\/www.iso.org\/iso-8601-date-and-time-format.html) format. DateTime value must be in future.", - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -16454,7 +16526,8 @@ "x-example": null, "items": { "type": "string" - } + }, + "x-nullable": true }, "users": { "type": "array", @@ -16462,7 +16535,8 @@ "x-example": null, "items": { "type": "string" - } + }, + "x-nullable": true }, "targets": { "type": "array", @@ -16470,27 +16544,32 @@ "x-example": null, "items": { "type": "string" - } + }, + "x-nullable": true }, "subject": { "type": "string", "description": "Email Subject.", - "x-example": "<SUBJECT>" + "x-example": "<SUBJECT>", + "x-nullable": true }, "content": { "type": "string", "description": "Email Content.", - "x-example": "<CONTENT>" + "x-example": "<CONTENT>", + "x-nullable": true }, "draft": { "type": "boolean", "description": "Is message a draft", - "x-example": false + "x-example": false, + "x-nullable": true }, "html": { "type": "boolean", "description": "Is content of type HTML", - "x-example": false + "x-example": false, + "x-nullable": true }, "cc": { "type": "array", @@ -16498,7 +16577,8 @@ "x-example": null, "items": { "type": "string" - } + }, + "x-nullable": true }, "bcc": { "type": "array", @@ -16506,12 +16586,14 @@ "x-example": null, "items": { "type": "string" - } + }, + "x-nullable": true }, "scheduledAt": { "type": "string", "description": "Scheduled delivery time for message in [ISO 8601](https:\/\/www.iso.org\/iso-8601-date-and-time-format.html) format. DateTime value must be in future.", - "x-example": null + "x-example": null, + "x-nullable": true }, "attachments": { "type": "array", @@ -16519,7 +16601,8 @@ "x-example": null, "items": { "type": "string" - } + }, + "x-nullable": true } } } @@ -16625,7 +16708,8 @@ "data": { "type": "object", "description": "Additional key-value pair data for push notification.", - "x-example": "{}" + "x-example": "{}", + "x-nullable": true }, "action": { "type": "string", @@ -16670,7 +16754,8 @@ "scheduledAt": { "type": "string", "description": "Scheduled delivery time for message in [ISO 8601](https:\/\/www.iso.org\/iso-8601-date-and-time-format.html) format. DateTime value must be in future.", - "x-example": null + "x-example": null, + "x-nullable": true }, "contentAvailable": { "type": "boolean", @@ -16776,7 +16861,8 @@ "x-example": null, "items": { "type": "string" - } + }, + "x-nullable": true }, "users": { "type": "array", @@ -16784,7 +16870,8 @@ "x-example": null, "items": { "type": "string" - } + }, + "x-nullable": true }, "targets": { "type": "array", @@ -16792,77 +16879,92 @@ "x-example": null, "items": { "type": "string" - } + }, + "x-nullable": true }, "title": { "type": "string", "description": "Title for push notification.", - "x-example": "<TITLE>" + "x-example": "<TITLE>", + "x-nullable": true }, "body": { "type": "string", "description": "Body for push notification.", - "x-example": "<BODY>" + "x-example": "<BODY>", + "x-nullable": true }, "data": { "type": "object", "description": "Additional Data for push notification.", - "x-example": "{}" + "x-example": "{}", + "x-nullable": true }, "action": { "type": "string", "description": "Action for push notification.", - "x-example": "<ACTION>" + "x-example": "<ACTION>", + "x-nullable": true }, "image": { "type": "string", "description": "Image for push notification. Must be a compound bucket ID to file ID of a jpeg, png, or bmp image in Appwrite Storage. It should be formatted as <BUCKET_ID>:<FILE_ID>.", - "x-example": "<ID1:ID2>" + "x-example": "<ID1:ID2>", + "x-nullable": true }, "icon": { "type": "string", "description": "Icon for push notification. Available only for Android and Web platforms.", - "x-example": "<ICON>" + "x-example": "<ICON>", + "x-nullable": true }, "sound": { "type": "string", "description": "Sound for push notification. Available only for Android and iOS platforms.", - "x-example": "<SOUND>" + "x-example": "<SOUND>", + "x-nullable": true }, "color": { "type": "string", "description": "Color for push notification. Available only for Android platforms.", - "x-example": "<COLOR>" + "x-example": "<COLOR>", + "x-nullable": true }, "tag": { "type": "string", "description": "Tag for push notification. Available only for Android platforms.", - "x-example": "<TAG>" + "x-example": "<TAG>", + "x-nullable": true }, "badge": { "type": "integer", "description": "Badge for push notification. Available only for iOS platforms.", - "x-example": null + "x-example": null, + "x-nullable": true }, "draft": { "type": "boolean", "description": "Is message a draft", - "x-example": false + "x-example": false, + "x-nullable": true }, "scheduledAt": { "type": "string", "description": "Scheduled delivery time for message in [ISO 8601](https:\/\/www.iso.org\/iso-8601-date-and-time-format.html) format. DateTime value must be in future.", - "x-example": null + "x-example": null, + "x-nullable": true }, "contentAvailable": { "type": "boolean", "description": "If set to true, the notification will be delivered in the background. Available only for iOS Platform.", - "x-example": false + "x-example": false, + "x-nullable": true }, "critical": { "type": "boolean", "description": "If set to true, the notification will be marked as critical. This requires the app to have the critical notification entitlement. Available only for iOS Platform.", - "x-example": false + "x-example": false, + "x-nullable": true }, "priority": { "type": "string", @@ -16873,7 +16975,8 @@ "high" ], "x-enum-name": "MessagePriority", - "x-enum-keys": [] + "x-enum-keys": [], + "x-nullable": true } } } @@ -17049,7 +17152,8 @@ "scheduledAt": { "type": "string", "description": "Scheduled delivery time for message in [ISO 8601](https:\/\/www.iso.org\/iso-8601-date-and-time-format.html) format. DateTime value must be in future.", - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -17203,7 +17307,8 @@ "x-example": null, "items": { "type": "string" - } + }, + "x-nullable": true }, "users": { "type": "array", @@ -17211,7 +17316,8 @@ "x-example": null, "items": { "type": "string" - } + }, + "x-nullable": true }, "targets": { "type": "array", @@ -17219,22 +17325,26 @@ "x-example": null, "items": { "type": "string" - } + }, + "x-nullable": true }, "content": { "type": "string", "description": "Email Content.", - "x-example": "<CONTENT>" + "x-example": "<CONTENT>", + "x-nullable": true }, "draft": { "type": "boolean", "description": "Is message a draft", - "x-example": false + "x-example": false, + "x-nullable": true }, "scheduledAt": { "type": "string", "description": "Scheduled delivery time for message in [ISO 8601](https:\/\/www.iso.org\/iso-8601-date-and-time-format.html) format. DateTime value must be in future.", - "x-example": null + "x-example": null, + "x-nullable": true } } } @@ -17787,7 +17897,8 @@ "enabled": { "type": "boolean", "description": "Set as enabled.", - "x-example": false + "x-example": false, + "x-nullable": true } }, "required": [ @@ -17945,7 +18056,8 @@ "enabled": { "type": "boolean", "description": "Set as enabled.", - "x-example": false + "x-example": false, + "x-nullable": true }, "authKey": { "type": "string", @@ -17970,7 +18082,8 @@ "sandbox": { "type": "boolean", "description": "Use APNS sandbox environment.", - "x-example": false + "x-example": false, + "x-nullable": true } } } @@ -18111,12 +18224,14 @@ "serviceAccountJSON": { "type": "object", "description": "FCM service account JSON.", - "x-example": "{}" + "x-example": "{}", + "x-nullable": true }, "enabled": { "type": "boolean", "description": "Set as enabled.", - "x-example": false + "x-example": false, + "x-nullable": true } }, "required": [ @@ -18266,12 +18381,14 @@ "enabled": { "type": "boolean", "description": "Set as enabled.", - "x-example": false + "x-example": false, + "x-nullable": true }, "serviceAccountJSON": { "type": "object", "description": "FCM service account JSON.", - "x-example": "{}" + "x-example": "{}", + "x-nullable": true } } } @@ -18358,7 +18475,8 @@ "isEuRegion": { "type": "boolean", "description": "Set as EU region.", - "x-example": false + "x-example": false, + "x-nullable": true }, "fromName": { "type": "string", @@ -18383,7 +18501,8 @@ "enabled": { "type": "boolean", "description": "Set as enabled.", - "x-example": false + "x-example": false, + "x-nullable": true } }, "required": [ @@ -18481,12 +18600,14 @@ "isEuRegion": { "type": "boolean", "description": "Set as EU region.", - "x-example": false + "x-example": false, + "x-nullable": true }, "enabled": { "type": "boolean", "description": "Set as enabled.", - "x-example": false + "x-example": false, + "x-nullable": true }, "fromName": { "type": "string", @@ -18598,7 +18719,8 @@ "enabled": { "type": "boolean", "description": "Set as enabled.", - "x-example": false + "x-example": false, + "x-nullable": true } }, "required": [ @@ -18686,7 +18808,8 @@ "enabled": { "type": "boolean", "description": "Set as enabled.", - "x-example": false + "x-example": false, + "x-nullable": true }, "templateId": { "type": "string", @@ -18803,7 +18926,8 @@ "enabled": { "type": "boolean", "description": "Set as enabled.", - "x-example": false + "x-example": false, + "x-nullable": true } }, "required": [ @@ -18891,7 +19015,8 @@ "enabled": { "type": "boolean", "description": "Set as enabled.", - "x-example": false + "x-example": false, + "x-nullable": true }, "apiKey": { "type": "string", @@ -19018,7 +19143,8 @@ "enabled": { "type": "boolean", "description": "Set as enabled.", - "x-example": false + "x-example": false, + "x-nullable": true } }, "required": [ @@ -19106,7 +19232,8 @@ "enabled": { "type": "boolean", "description": "Set as enabled.", - "x-example": false + "x-example": false, + "x-nullable": true }, "apiKey": { "type": "string", @@ -19356,7 +19483,8 @@ "enabled": { "type": "boolean", "description": "Set as enabled.", - "x-example": false + "x-example": false, + "x-nullable": true } }, "required": [ @@ -19532,7 +19660,8 @@ "port": { "type": "integer", "description": "SMTP port.", - "x-example": 1 + "x-example": 1, + "x-nullable": true }, "username": { "type": "string", @@ -19559,7 +19688,8 @@ "autoTLS": { "type": "boolean", "description": "Enable SMTP AutoTLS feature.", - "x-example": false + "x-example": false, + "x-nullable": true }, "mailer": { "type": "string", @@ -19589,7 +19719,8 @@ "enabled": { "type": "boolean", "description": "Set as enabled.", - "x-example": false + "x-example": false, + "x-nullable": true } } } @@ -19681,7 +19812,8 @@ "enabled": { "type": "boolean", "description": "Set as enabled.", - "x-example": false + "x-example": false, + "x-nullable": true } }, "required": [ @@ -19769,7 +19901,8 @@ "enabled": { "type": "boolean", "description": "Set as enabled.", - "x-example": false + "x-example": false, + "x-nullable": true }, "customerId": { "type": "string", @@ -19876,7 +20009,8 @@ "enabled": { "type": "boolean", "description": "Set as enabled.", - "x-example": false + "x-example": false, + "x-nullable": true } }, "required": [ @@ -19964,7 +20098,8 @@ "enabled": { "type": "boolean", "description": "Set as enabled.", - "x-example": false + "x-example": false, + "x-nullable": true }, "username": { "type": "string", @@ -20071,7 +20206,8 @@ "enabled": { "type": "boolean", "description": "Set as enabled.", - "x-example": false + "x-example": false, + "x-nullable": true } }, "required": [ @@ -20159,7 +20295,8 @@ "enabled": { "type": "boolean", "description": "Set as enabled.", - "x-example": false + "x-example": false, + "x-nullable": true }, "accountSid": { "type": "string", @@ -20266,7 +20403,8 @@ "enabled": { "type": "boolean", "description": "Set as enabled.", - "x-example": false + "x-example": false, + "x-nullable": true } }, "required": [ @@ -20354,7 +20492,8 @@ "enabled": { "type": "boolean", "description": "Set as enabled.", - "x-example": false + "x-example": false, + "x-nullable": true }, "apiKey": { "type": "string", @@ -20970,7 +21109,8 @@ "name": { "type": "string", "description": "Topic Name.", - "x-example": "<NAME>" + "x-example": "<NAME>", + "x-nullable": true }, "subscribe": { "type": "array", @@ -20978,7 +21118,8 @@ "x-example": "[\"any\"]", "items": { "type": "string" - } + }, + "x-nullable": true } } } @@ -21703,6 +21844,7 @@ "dart-3.3", "dart-3.5", "dart-3.8", + "dart-3.9", "dotnet-6.0", "dotnet-7.0", "dotnet-8.0", @@ -21729,7 +21871,8 @@ "flutter-3.24", "flutter-3.27", "flutter-3.29", - "flutter-3.32" + "flutter-3.32", + "flutter-3.35" ], "x-enum-name": null, "x-enum-keys": [] @@ -22122,6 +22265,7 @@ "dart-3.3", "dart-3.5", "dart-3.8", + "dart-3.9", "dotnet-6.0", "dotnet-7.0", "dotnet-8.0", @@ -22148,7 +22292,8 @@ "flutter-3.24", "flutter-3.27", "flutter-3.29", - "flutter-3.32" + "flutter-3.32", + "flutter-3.35" ], "x-enum-name": null, "x-enum-keys": [] @@ -22508,17 +22653,20 @@ "installCommand": { "type": "string", "description": "Install Commands.", - "x-example": "<INSTALL_COMMAND>" + "x-example": "<INSTALL_COMMAND>", + "x-nullable": true }, "buildCommand": { "type": "string", "description": "Build Commands.", - "x-example": "<BUILD_COMMAND>" + "x-example": "<BUILD_COMMAND>", + "x-nullable": true }, "outputDirectory": { "type": "string", "description": "Output Directory.", - "x-example": "<OUTPUT_DIRECTORY>" + "x-example": "<OUTPUT_DIRECTORY>", + "x-nullable": true }, "code": { "type": "string", @@ -22703,10 +22851,22 @@ "description": "Path to site code in the template repo.", "x-example": "<ROOT_DIRECTORY>" }, - "version": { + "type": { "type": "string", - "description": "Version (tag) for the repo linked to the site template.", - "x-example": "<VERSION>" + "description": "Type for the reference provided. Can be commit, branch, or tag", + "x-example": "branch", + "enum": [ + "branch", + "commit", + "tag" + ], + "x-enum-name": null, + "x-enum-keys": [] + }, + "reference": { + "type": "string", + "description": "Reference value, can be a commit hash, branch name, or release tag", + "x-example": "<REFERENCE>" }, "activate": { "type": "boolean", @@ -22718,7 +22878,8 @@ "repository", "owner", "rootDirectory", - "version" + "type", + "reference" ] } } @@ -23643,12 +23804,14 @@ "value": { "type": "string", "description": "Variable value. Max length: 8192 chars.", - "x-example": "<VALUE>" + "x-example": "<VALUE>", + "x-nullable": true }, "secret": { "type": "boolean", "description": "Secret variables can be updated or deleted, but only sites can read them during build and runtime.", - "x-example": false + "x-example": false, + "x-nullable": true } }, "required": [ @@ -23878,7 +24041,8 @@ "x-example": "[\"read(\"any\")\"]", "items": { "type": "string" - } + }, + "x-nullable": true }, "fileSecurity": { "type": "boolean", @@ -24073,7 +24237,8 @@ "x-example": "[\"read(\"any\")\"]", "items": { "type": "string" - } + }, + "x-nullable": true }, "fileSecurity": { "type": "boolean", @@ -24369,7 +24534,8 @@ "x-example": "[\"read(\"any\")\"]", "items": { "type": "string" - } + }, + "x-nullable": true } }, "required": [ @@ -24539,7 +24705,8 @@ "name": { "type": "string", "description": "Name of the file", - "x-example": "<NAME>" + "x-example": "<NAME>", + "x-nullable": true }, "permissions": { "type": "array", @@ -24547,7 +24714,8 @@ "x-example": "[\"read(\"any\")\"]", "items": { "type": "string" - } + }, + "x-nullable": true } } } @@ -26008,7 +26176,8 @@ "x-example": "[\"read(\"any\")\"]", "items": { "type": "string" - } + }, + "x-nullable": true }, "rowSecurity": { "type": "boolean", @@ -26194,7 +26363,8 @@ "x-example": "[\"read(\"any\")\"]", "items": { "type": "string" - } + }, + "x-nullable": true }, "rowSecurity": { "type": "boolean", @@ -26473,7 +26643,8 @@ "default": { "type": "boolean", "description": "Default value for column when not provided. Cannot be set when column is required.", - "x-example": false + "x-example": false, + "x-nullable": true }, "array": { "type": "boolean", @@ -26593,7 +26764,8 @@ "newKey": { "type": "string", "description": "New Column Key.", - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -26818,7 +26990,8 @@ "newKey": { "type": "string", "description": "New Column Key.", - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -26923,7 +27096,8 @@ "default": { "type": "string", "description": "Default value for column when not provided. Cannot be set when column is required.", - "x-example": "email@example.com" + "x-example": "email@example.com", + "x-nullable": true }, "array": { "type": "boolean", @@ -27043,7 +27217,8 @@ "newKey": { "type": "string", "description": "New Column Key.", - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -27156,7 +27331,8 @@ "default": { "type": "string", "description": "Default value for column when not provided. Cannot be set when column is required.", - "x-example": "<DEFAULT>" + "x-example": "<DEFAULT>", + "x-nullable": true }, "array": { "type": "boolean", @@ -27285,7 +27461,8 @@ "newKey": { "type": "string", "description": "New Column Key.", - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -27391,17 +27568,20 @@ "min": { "type": "number", "description": "Minimum value", - "x-example": null + "x-example": null, + "x-nullable": true }, "max": { "type": "number", "description": "Maximum value", - "x-example": null + "x-example": null, + "x-nullable": true }, "default": { "type": "number", "description": "Default value. Cannot be set when required.", - "x-example": null + "x-example": null, + "x-nullable": true }, "array": { "type": "boolean", @@ -27515,12 +27695,14 @@ "min": { "type": "number", "description": "Minimum value", - "x-example": null + "x-example": null, + "x-nullable": true }, "max": { "type": "number", "description": "Maximum value", - "x-example": null + "x-example": null, + "x-nullable": true }, "default": { "type": "number", @@ -27531,7 +27713,8 @@ "newKey": { "type": "string", "description": "New Column Key.", - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -27636,17 +27819,20 @@ "min": { "type": "integer", "description": "Minimum value", - "x-example": null + "x-example": null, + "x-nullable": true }, "max": { "type": "integer", "description": "Maximum value", - "x-example": null + "x-example": null, + "x-nullable": true }, "default": { "type": "integer", "description": "Default value. Cannot be set when column is required.", - "x-example": null + "x-example": null, + "x-nullable": true }, "array": { "type": "boolean", @@ -27760,12 +27946,14 @@ "min": { "type": "integer", "description": "Minimum value", - "x-example": null + "x-example": null, + "x-nullable": true }, "max": { "type": "integer", "description": "Maximum value", - "x-example": null + "x-example": null, + "x-nullable": true }, "default": { "type": "integer", @@ -27776,7 +27964,8 @@ "newKey": { "type": "string", "description": "New Column Key.", - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -27881,7 +28070,8 @@ "default": { "type": "string", "description": "Default value. Cannot be set when column is required.", - "x-example": null + "x-example": null, + "x-nullable": true }, "array": { "type": "boolean", @@ -28001,7 +28191,8 @@ "newKey": { "type": "string", "description": "New Column Key.", - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -28236,7 +28427,8 @@ "newKey": { "type": "string", "description": "New Column Key.", - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -28470,7 +28662,8 @@ "newKey": { "type": "string", "description": "New Column Key.", - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -28704,7 +28897,8 @@ "newKey": { "type": "string", "description": "New Column Key.", - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -28821,12 +29015,14 @@ "key": { "type": "string", "description": "Column Key.", - "x-example": null + "x-example": null, + "x-nullable": true }, "twoWayKey": { "type": "string", "description": "Two Way Column Key.", - "x-example": null + "x-example": null, + "x-nullable": true }, "onDelete": { "type": "string", @@ -28948,7 +29144,8 @@ "default": { "type": "string", "description": "Default value for column when not provided. Cannot be set when column is required.", - "x-example": "<DEFAULT>" + "x-example": "<DEFAULT>", + "x-nullable": true }, "array": { "type": "boolean", @@ -29074,12 +29271,14 @@ "size": { "type": "integer", "description": "Maximum size of the string column.", - "x-example": 1 + "x-example": 1, + "x-nullable": true }, "newKey": { "type": "string", "description": "New Column Key.", - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -29184,7 +29383,8 @@ "default": { "type": "string", "description": "Default value for column when not provided. Cannot be set when column is required.", - "x-example": "https:\/\/example.com" + "x-example": "https:\/\/example.com", + "x-nullable": true }, "array": { "type": "boolean", @@ -29304,7 +29504,8 @@ "newKey": { "type": "string", "description": "New Column Key.", - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -29605,12 +29806,14 @@ "setNull" ], "x-enum-name": "RelationMutate", - "x-enum-keys": [] + "x-enum-keys": [], + "x-nullable": true }, "newKey": { "type": "string", "description": "New Column Key.", - "x-example": null + "x-example": null, + "x-nullable": true } } } @@ -30279,7 +30482,8 @@ "x-example": "[\"read(\"any\")\"]", "items": { "type": "string" - } + }, + "x-nullable": true }, "rows": { "type": "array", @@ -30292,7 +30496,8 @@ "transactionId": { "type": "string", "description": "Transaction ID for staging the operation.", - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -30420,7 +30625,8 @@ "transactionId": { "type": "string", "description": "Transaction ID for staging the operation.", - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } }, "required": [ @@ -30526,7 +30732,8 @@ "transactionId": { "type": "string", "description": "Transaction ID for staging the operation.", - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -30624,7 +30831,8 @@ "transactionId": { "type": "string", "description": "Transaction ID for staging the operation.", - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -30879,12 +31087,14 @@ "x-example": "[\"read(\"any\")\"]", "items": { "type": "string" - } + }, + "x-nullable": true }, "transactionId": { "type": "string", "description": "Transaction ID for staging the operation.", - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -30995,12 +31205,14 @@ "x-example": "[\"read(\"any\")\"]", "items": { "type": "string" - } + }, + "x-nullable": true }, "transactionId": { "type": "string", "description": "Transaction ID for staging the operation.", - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -31096,7 +31308,8 @@ "transactionId": { "type": "string", "description": "Transaction ID for staging the operation.", - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -31216,12 +31429,14 @@ "min": { "type": "number", "description": "Minimum value for the column. If the current value is lesser than this value, an exception will be thrown.", - "x-example": null + "x-example": null, + "x-nullable": true }, "transactionId": { "type": "string", "description": "Transaction ID for staging the operation.", - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -31341,12 +31556,14 @@ "max": { "type": "number", "description": "Maximum value for the column. If the current value is greater than this value, an error will be thrown.", - "x-example": null + "x-example": null, + "x-nullable": true }, "transactionId": { "type": "string", "description": "Transaction ID for staging the operation.", - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -32959,12 +33176,14 @@ "email": { "type": "string", "description": "User email.", - "x-example": "email@example.com" + "x-example": "email@example.com", + "x-nullable": true }, "phone": { "type": "string", "description": "Phone number. Format this number with a leading '+' and a country code, e.g., +16175551212.", - "x-example": "+12065550100" + "x-example": "+12065550100", + "x-nullable": true }, "password": { "type": "string", diff --git a/app/config/specs/swagger2-1.8.x-client.json b/app/config/specs/swagger2-1.8.x-client.json index a304a7cb1a..1594f816ef 100644 --- a/app/config/specs/swagger2-1.8.x-client.json +++ b/app/config/specs/swagger2-1.8.x-client.json @@ -6400,6 +6400,7 @@ "description": "An array of permissions strings. By default, only the current user is granted all permissions. [Learn more about permissions](https:\/\/appwrite.io\/docs\/permissions).", "default": null, "x-example": "[\"read(\"any\")\"]", + "x-nullable": true, "items": { "type": "string" } @@ -6417,7 +6418,8 @@ "type": "string", "description": "Transaction ID for staging the operation.", "default": null, - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -6657,6 +6659,7 @@ "description": "An array of permissions strings. By default, the current permissions are inherited. [Learn more about permissions](https:\/\/appwrite.io\/docs\/permissions).", "default": null, "x-example": "[\"read(\"any\")\"]", + "x-nullable": true, "items": { "type": "string" } @@ -6665,7 +6668,8 @@ "type": "string", "description": "Transaction ID for staging the operation.", "default": null, - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } }, "required": [ @@ -6771,6 +6775,7 @@ "description": "An array of permissions strings. By default, the current permissions are inherited. [Learn more about permissions](https:\/\/appwrite.io\/docs\/permissions).", "default": null, "x-example": "[\"read(\"any\")\"]", + "x-nullable": true, "items": { "type": "string" } @@ -6779,7 +6784,8 @@ "type": "string", "description": "Transaction ID for staging the operation.", "default": null, - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -6870,7 +6876,8 @@ "type": "string", "description": "Transaction ID for staging the operation.", "default": null, - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -6982,13 +6989,15 @@ "type": "number", "description": "Minimum value for the attribute. If the current value is lesser than this value, an exception will be thrown.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "transactionId": { "type": "string", "description": "Transaction ID for staging the operation.", "default": null, - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -7100,13 +7109,15 @@ "type": "number", "description": "Maximum value for the attribute. If the current value is greater than this value, an error will be thrown.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "transactionId": { "type": "string", "description": "Transaction ID for staging the operation.", "default": null, - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -7305,7 +7316,8 @@ "type": "string", "description": "Scheduled execution time in [ISO 8601](https:\/\/www.iso.org\/iso-8601-date-and-time-format.html) format. DateTime value must be in future with precision in minutes.", "default": null, - "x-example": "<SCHEDULED_AT>" + "x-example": "<SCHEDULED_AT>", + "x-nullable": true } } } @@ -8417,13 +8429,15 @@ "type": "string", "description": "Name of the file", "default": null, - "x-example": "<NAME>" + "x-example": "<NAME>", + "x-nullable": true }, "permissions": { "type": "array", "description": "An array of permission string. By default, the current permissions are inherited. [Learn more about permissions](https:\/\/appwrite.io\/docs\/permissions).", "default": null, "x-example": "[\"read(\"any\")\"]", + "x-nullable": true, "items": { "type": "string" } @@ -9516,6 +9530,7 @@ "description": "An array of permissions strings. By default, only the current user is granted all permissions. [Learn more about permissions](https:\/\/appwrite.io\/docs\/permissions).", "default": null, "x-example": "[\"read(\"any\")\"]", + "x-nullable": true, "items": { "type": "string" } @@ -9533,7 +9548,8 @@ "type": "string", "description": "Transaction ID for staging the operation.", "default": null, - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -9766,6 +9782,7 @@ "description": "An array of permissions strings. By default, the current permissions are inherited. [Learn more about permissions](https:\/\/appwrite.io\/docs\/permissions).", "default": null, "x-example": "[\"read(\"any\")\"]", + "x-nullable": true, "items": { "type": "string" } @@ -9774,7 +9791,8 @@ "type": "string", "description": "Transaction ID for staging the operation.", "default": null, - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -9876,6 +9894,7 @@ "description": "An array of permissions strings. By default, the current permissions are inherited. [Learn more about permissions](https:\/\/appwrite.io\/docs\/permissions).", "default": null, "x-example": "[\"read(\"any\")\"]", + "x-nullable": true, "items": { "type": "string" } @@ -9884,7 +9903,8 @@ "type": "string", "description": "Transaction ID for staging the operation.", "default": null, - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -9974,7 +9994,8 @@ "type": "string", "description": "Transaction ID for staging the operation.", "default": null, - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -10085,13 +10106,15 @@ "type": "number", "description": "Minimum value for the column. If the current value is lesser than this value, an exception will be thrown.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "transactionId": { "type": "string", "description": "Transaction ID for staging the operation.", "default": null, - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -10202,13 +10225,15 @@ "type": "number", "description": "Maximum value for the column. If the current value is greater than this value, an error will be thrown.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "transactionId": { "type": "string", "description": "Transaction ID for staging the operation.", "default": null, - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } diff --git a/app/config/specs/swagger2-1.8.x-console.json b/app/config/specs/swagger2-1.8.x-console.json index 158b308f87..384011f2fd 100644 --- a/app/config/specs/swagger2-1.8.x-console.json +++ b/app/config/specs/swagger2-1.8.x-console.json @@ -7168,6 +7168,7 @@ "description": "An array of permissions strings. By default, no user is granted with any permissions. [Learn more about permissions](https:\/\/appwrite.io\/docs\/permissions).", "default": null, "x-example": "[\"read(\"any\")\"]", + "x-nullable": true, "items": { "type": "string" } @@ -7350,6 +7351,7 @@ "description": "An array of permission strings. By default, the current permissions are inherited. [Learn more about permissions](https:\/\/appwrite.io\/docs\/permissions).", "default": null, "x-example": "[\"read(\"any\")\"]", + "x-nullable": true, "items": { "type": "string" } @@ -7625,7 +7627,8 @@ "type": "boolean", "description": "Default value for attribute when not provided. Cannot be set when attribute is required.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true }, "array": { "type": "boolean", @@ -7743,7 +7746,8 @@ "type": "string", "description": "New attribute key.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -7965,7 +7969,8 @@ "type": "string", "description": "New attribute key.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -8069,7 +8074,8 @@ "type": "string", "description": "Default value for attribute when not provided. Cannot be set when attribute is required.", "default": null, - "x-example": "email@example.com" + "x-example": "email@example.com", + "x-nullable": true }, "array": { "type": "boolean", @@ -8187,7 +8193,8 @@ "type": "string", "description": "New Attribute Key.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -8300,7 +8307,8 @@ "type": "string", "description": "Default value for attribute when not provided. Cannot be set when attribute is required.", "default": null, - "x-example": "<DEFAULT>" + "x-example": "<DEFAULT>", + "x-nullable": true }, "array": { "type": "boolean", @@ -8428,7 +8436,8 @@ "type": "string", "description": "New Attribute Key.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -8533,19 +8542,22 @@ "type": "number", "description": "Minimum value.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "max": { "type": "number", "description": "Maximum value.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "default": { "type": "number", "description": "Default value. Cannot be set when required.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "array": { "type": "boolean", @@ -8656,13 +8668,15 @@ "type": "number", "description": "Minimum value.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "max": { "type": "number", "description": "Maximum value.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "default": { "type": "number", @@ -8675,7 +8689,8 @@ "type": "string", "description": "New Attribute Key.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -8779,19 +8794,22 @@ "type": "integer", "description": "Minimum value", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "max": { "type": "integer", "description": "Maximum value", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "default": { "type": "integer", "description": "Default value. Cannot be set when attribute is required.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "array": { "type": "boolean", @@ -8902,13 +8920,15 @@ "type": "integer", "description": "Minimum value", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "max": { "type": "integer", "description": "Maximum value", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "default": { "type": "integer", @@ -8921,7 +8941,8 @@ "type": "string", "description": "New Attribute Key.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -9025,7 +9046,8 @@ "type": "string", "description": "Default value. Cannot be set when attribute is required.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "array": { "type": "boolean", @@ -9143,7 +9165,8 @@ "type": "string", "description": "New Attribute Key.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -9360,7 +9383,8 @@ "type": "string", "description": "New attribute key.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -9576,7 +9600,8 @@ "type": "string", "description": "New attribute key.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -9792,7 +9817,8 @@ "type": "string", "description": "New attribute key.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -9909,13 +9935,15 @@ "type": "string", "description": "Attribute Key.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "twoWayKey": { "type": "string", "description": "Two Way Attribute Key.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "onDelete": { "type": "string", @@ -10038,7 +10066,8 @@ "type": "string", "description": "Default value for attribute when not provided. Cannot be set when attribute is required.", "default": null, - "x-example": "<DEFAULT>" + "x-example": "<DEFAULT>", + "x-nullable": true }, "array": { "type": "boolean", @@ -10163,13 +10192,15 @@ "type": "integer", "description": "Maximum size of the string attribute.", "default": null, - "x-example": 1 + "x-example": 1, + "x-nullable": true }, "newKey": { "type": "string", "description": "New Attribute Key.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -10273,7 +10304,8 @@ "type": "string", "description": "Default value for attribute when not provided. Cannot be set when attribute is required.", "default": null, - "x-example": "https:\/\/example.com" + "x-example": "https:\/\/example.com", + "x-nullable": true }, "array": { "type": "boolean", @@ -10391,7 +10423,8 @@ "type": "string", "description": "New Attribute Key.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -10679,13 +10712,15 @@ "setNull" ], "x-enum-name": "RelationMutate", - "x-enum-keys": [] + "x-enum-keys": [], + "x-nullable": true }, "newKey": { "type": "string", "description": "New Attribute Key.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true } } } @@ -10955,6 +10990,7 @@ "description": "An array of permissions strings. By default, only the current user is granted all permissions. [Learn more about permissions](https:\/\/appwrite.io\/docs\/permissions).", "default": null, "x-example": "[\"read(\"any\")\"]", + "x-nullable": true, "items": { "type": "string" } @@ -10972,7 +11008,8 @@ "type": "string", "description": "Transaction ID for staging the operation.", "default": null, - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -11101,7 +11138,8 @@ "type": "string", "description": "Transaction ID for staging the operation.", "default": null, - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } }, "required": [ @@ -11206,7 +11244,8 @@ "type": "string", "description": "Transaction ID for staging the operation.", "default": null, - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -11302,7 +11341,8 @@ "type": "string", "description": "Transaction ID for staging the operation.", "default": null, - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -11542,6 +11582,7 @@ "description": "An array of permissions strings. By default, the current permissions are inherited. [Learn more about permissions](https:\/\/appwrite.io\/docs\/permissions).", "default": null, "x-example": "[\"read(\"any\")\"]", + "x-nullable": true, "items": { "type": "string" } @@ -11550,7 +11591,8 @@ "type": "string", "description": "Transaction ID for staging the operation.", "default": null, - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } }, "required": [ @@ -11656,6 +11698,7 @@ "description": "An array of permissions strings. By default, the current permissions are inherited. [Learn more about permissions](https:\/\/appwrite.io\/docs\/permissions).", "default": null, "x-example": "[\"read(\"any\")\"]", + "x-nullable": true, "items": { "type": "string" } @@ -11664,7 +11707,8 @@ "type": "string", "description": "Transaction ID for staging the operation.", "default": null, - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -11755,7 +11799,8 @@ "type": "string", "description": "Transaction ID for staging the operation.", "default": null, - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -11957,13 +12002,15 @@ "type": "number", "description": "Minimum value for the attribute. If the current value is lesser than this value, an exception will be thrown.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "transactionId": { "type": "string", "description": "Transaction ID for staging the operation.", "default": null, - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -12075,13 +12122,15 @@ "type": "number", "description": "Maximum value for the attribute. If the current value is greater than this value, an error will be thrown.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "transactionId": { "type": "string", "description": "Transaction ID for staging the operation.", "default": null, - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -13041,6 +13090,7 @@ "dart-3.3", "dart-3.5", "dart-3.8", + "dart-3.9", "dotnet-6.0", "dotnet-7.0", "dotnet-8.0", @@ -13067,7 +13117,8 @@ "flutter-3.24", "flutter-3.27", "flutter-3.29", - "flutter-3.32" + "flutter-3.32", + "flutter-3.35" ], "x-enum-name": null, "x-enum-keys": [] @@ -13683,6 +13734,7 @@ "dart-3.3", "dart-3.5", "dart-3.8", + "dart-3.9", "dotnet-6.0", "dotnet-7.0", "dotnet-8.0", @@ -13709,7 +13761,8 @@ "flutter-3.24", "flutter-3.27", "flutter-3.29", - "flutter-3.32" + "flutter-3.32", + "flutter-3.35" ], "x-enum-name": null, "x-enum-keys": [] @@ -14896,7 +14949,8 @@ "type": "string", "description": "Scheduled execution time in [ISO 8601](https:\/\/www.iso.org\/iso-8601-date-and-time-format.html) format. DateTime value must be in future with precision in minutes.", "default": null, - "x-example": "<SCHEDULED_AT>" + "x-example": "<SCHEDULED_AT>", + "x-nullable": true } } } @@ -15409,13 +15463,15 @@ "type": "string", "description": "Variable value. Max length: 8192 chars.", "default": null, - "x-example": "<VALUE>" + "x-example": "<VALUE>", + "x-nullable": true }, "secret": { "type": "boolean", "description": "Secret variables can be updated or deleted, but only functions can read them during build and runtime.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true } }, "required": [ @@ -17532,7 +17588,8 @@ "type": "string", "description": "Scheduled delivery time for message in [ISO 8601](https:\/\/www.iso.org\/iso-8601-date-and-time-format.html) format. DateTime value must be in future.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -17615,6 +17672,7 @@ "description": "List of Topic IDs.", "default": null, "x-example": null, + "x-nullable": true, "items": { "type": "string" } @@ -17624,6 +17682,7 @@ "description": "List of User IDs.", "default": null, "x-example": null, + "x-nullable": true, "items": { "type": "string" } @@ -17633,6 +17692,7 @@ "description": "List of Targets IDs.", "default": null, "x-example": null, + "x-nullable": true, "items": { "type": "string" } @@ -17641,31 +17701,36 @@ "type": "string", "description": "Email Subject.", "default": null, - "x-example": "<SUBJECT>" + "x-example": "<SUBJECT>", + "x-nullable": true }, "content": { "type": "string", "description": "Email Content.", "default": null, - "x-example": "<CONTENT>" + "x-example": "<CONTENT>", + "x-nullable": true }, "draft": { "type": "boolean", "description": "Is message a draft", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true }, "html": { "type": "boolean", "description": "Is content of type HTML", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true }, "cc": { "type": "array", "description": "Array of target IDs to be added as CC.", "default": null, "x-example": null, + "x-nullable": true, "items": { "type": "string" } @@ -17675,6 +17740,7 @@ "description": "Array of target IDs to be added as BCC.", "default": null, "x-example": null, + "x-nullable": true, "items": { "type": "string" } @@ -17683,13 +17749,15 @@ "type": "string", "description": "Scheduled delivery time for message in [ISO 8601](https:\/\/www.iso.org\/iso-8601-date-and-time-format.html) format. DateTime value must be in future.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "attachments": { "type": "array", "description": "Array of compound ID strings of bucket IDs and file IDs to be attached to the email. They should be formatted as <BUCKET_ID>:<FILE_ID>.", "default": null, "x-example": null, + "x-nullable": true, "items": { "type": "string" } @@ -17806,7 +17874,8 @@ "type": "object", "description": "Additional key-value pair data for push notification.", "default": {}, - "x-example": "{}" + "x-example": "{}", + "x-nullable": true }, "action": { "type": "string", @@ -17860,7 +17929,8 @@ "type": "string", "description": "Scheduled delivery time for message in [ISO 8601](https:\/\/www.iso.org\/iso-8601-date-and-time-format.html) format. DateTime value must be in future.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "contentAvailable": { "type": "boolean", @@ -17965,6 +18035,7 @@ "description": "List of Topic IDs.", "default": null, "x-example": null, + "x-nullable": true, "items": { "type": "string" } @@ -17974,6 +18045,7 @@ "description": "List of User IDs.", "default": null, "x-example": null, + "x-nullable": true, "items": { "type": "string" } @@ -17983,6 +18055,7 @@ "description": "List of Targets IDs.", "default": null, "x-example": null, + "x-nullable": true, "items": { "type": "string" } @@ -17991,85 +18064,99 @@ "type": "string", "description": "Title for push notification.", "default": null, - "x-example": "<TITLE>" + "x-example": "<TITLE>", + "x-nullable": true }, "body": { "type": "string", "description": "Body for push notification.", "default": null, - "x-example": "<BODY>" + "x-example": "<BODY>", + "x-nullable": true }, "data": { "type": "object", "description": "Additional Data for push notification.", "default": {}, - "x-example": "{}" + "x-example": "{}", + "x-nullable": true }, "action": { "type": "string", "description": "Action for push notification.", "default": null, - "x-example": "<ACTION>" + "x-example": "<ACTION>", + "x-nullable": true }, "image": { "type": "string", "description": "Image for push notification. Must be a compound bucket ID to file ID of a jpeg, png, or bmp image in Appwrite Storage. It should be formatted as <BUCKET_ID>:<FILE_ID>.", "default": null, - "x-example": "<ID1:ID2>" + "x-example": "<ID1:ID2>", + "x-nullable": true }, "icon": { "type": "string", "description": "Icon for push notification. Available only for Android and Web platforms.", "default": null, - "x-example": "<ICON>" + "x-example": "<ICON>", + "x-nullable": true }, "sound": { "type": "string", "description": "Sound for push notification. Available only for Android and iOS platforms.", "default": null, - "x-example": "<SOUND>" + "x-example": "<SOUND>", + "x-nullable": true }, "color": { "type": "string", "description": "Color for push notification. Available only for Android platforms.", "default": null, - "x-example": "<COLOR>" + "x-example": "<COLOR>", + "x-nullable": true }, "tag": { "type": "string", "description": "Tag for push notification. Available only for Android platforms.", "default": null, - "x-example": "<TAG>" + "x-example": "<TAG>", + "x-nullable": true }, "badge": { "type": "integer", "description": "Badge for push notification. Available only for iOS platforms.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "draft": { "type": "boolean", "description": "Is message a draft", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true }, "scheduledAt": { "type": "string", "description": "Scheduled delivery time for message in [ISO 8601](https:\/\/www.iso.org\/iso-8601-date-and-time-format.html) format. DateTime value must be in future.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "contentAvailable": { "type": "boolean", "description": "If set to true, the notification will be delivered in the background. Available only for iOS Platform.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true }, "critical": { "type": "boolean", "description": "If set to true, the notification will be marked as critical. This requires the app to have the critical notification entitlement. Available only for iOS Platform.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true }, "priority": { "type": "string", @@ -18081,7 +18168,8 @@ "high" ], "x-enum-name": "MessagePriority", - "x-enum-keys": [] + "x-enum-keys": [], + "x-nullable": true } } } @@ -18263,7 +18351,8 @@ "type": "string", "description": "Scheduled delivery time for message in [ISO 8601](https:\/\/www.iso.org\/iso-8601-date-and-time-format.html) format. DateTime value must be in future.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -18411,6 +18500,7 @@ "description": "List of Topic IDs.", "default": null, "x-example": null, + "x-nullable": true, "items": { "type": "string" } @@ -18420,6 +18510,7 @@ "description": "List of User IDs.", "default": null, "x-example": null, + "x-nullable": true, "items": { "type": "string" } @@ -18429,6 +18520,7 @@ "description": "List of Targets IDs.", "default": null, "x-example": null, + "x-nullable": true, "items": { "type": "string" } @@ -18437,19 +18529,22 @@ "type": "string", "description": "Email Content.", "default": null, - "x-example": "<CONTENT>" + "x-example": "<CONTENT>", + "x-nullable": true }, "draft": { "type": "boolean", "description": "Is message a draft", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true }, "scheduledAt": { "type": "string", "description": "Scheduled delivery time for message in [ISO 8601](https:\/\/www.iso.org\/iso-8601-date-and-time-format.html) format. DateTime value must be in future.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true } } } @@ -18989,7 +19084,8 @@ "type": "boolean", "description": "Set as enabled.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true } }, "required": [ @@ -19144,7 +19240,8 @@ "type": "boolean", "description": "Set as enabled.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true }, "authKey": { "type": "string", @@ -19174,7 +19271,8 @@ "type": "boolean", "description": "Use APNS sandbox environment.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true } } } @@ -19317,13 +19415,15 @@ "type": "object", "description": "FCM service account JSON.", "default": {}, - "x-example": "{}" + "x-example": "{}", + "x-nullable": true }, "enabled": { "type": "boolean", "description": "Set as enabled.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true } }, "required": [ @@ -19470,13 +19570,15 @@ "type": "boolean", "description": "Set as enabled.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true }, "serviceAccountJSON": { "type": "object", "description": "FCM service account JSON.", "default": {}, - "x-example": "{}" + "x-example": "{}", + "x-nullable": true } } } @@ -19569,7 +19671,8 @@ "type": "boolean", "description": "Set as EU region.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true }, "fromName": { "type": "string", @@ -19599,7 +19702,8 @@ "type": "boolean", "description": "Set as enabled.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true } }, "required": [ @@ -19698,13 +19802,15 @@ "type": "boolean", "description": "Set as EU region.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true }, "enabled": { "type": "boolean", "description": "Set as enabled.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true }, "fromName": { "type": "string", @@ -19827,7 +19933,8 @@ "type": "boolean", "description": "Set as enabled.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true } }, "required": [ @@ -19914,7 +20021,8 @@ "type": "boolean", "description": "Set as enabled.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true }, "templateId": { "type": "string", @@ -20043,7 +20151,8 @@ "type": "boolean", "description": "Set as enabled.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true } }, "required": [ @@ -20130,7 +20239,8 @@ "type": "boolean", "description": "Set as enabled.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true }, "apiKey": { "type": "string", @@ -20271,7 +20381,8 @@ "type": "boolean", "description": "Set as enabled.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true } }, "required": [ @@ -20358,7 +20469,8 @@ "type": "boolean", "description": "Set as enabled.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true }, "apiKey": { "type": "string", @@ -20626,7 +20738,8 @@ "type": "boolean", "description": "Set as enabled.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true } }, "required": [ @@ -20800,7 +20913,8 @@ "type": "integer", "description": "SMTP port.", "default": null, - "x-example": 1 + "x-example": 1, + "x-nullable": true }, "username": { "type": "string", @@ -20831,7 +20945,8 @@ "type": "boolean", "description": "Enable SMTP AutoTLS feature.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true }, "mailer": { "type": "string", @@ -20867,7 +20982,8 @@ "type": "boolean", "description": "Set as enabled.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true } } } @@ -20966,7 +21082,8 @@ "type": "boolean", "description": "Set as enabled.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true } }, "required": [ @@ -21053,7 +21170,8 @@ "type": "boolean", "description": "Set as enabled.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true }, "customerId": { "type": "string", @@ -21170,7 +21288,8 @@ "type": "boolean", "description": "Set as enabled.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true } }, "required": [ @@ -21257,7 +21376,8 @@ "type": "boolean", "description": "Set as enabled.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true }, "username": { "type": "string", @@ -21374,7 +21494,8 @@ "type": "boolean", "description": "Set as enabled.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true } }, "required": [ @@ -21461,7 +21582,8 @@ "type": "boolean", "description": "Set as enabled.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true }, "accountSid": { "type": "string", @@ -21578,7 +21700,8 @@ "type": "boolean", "description": "Set as enabled.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true } }, "required": [ @@ -21665,7 +21788,8 @@ "type": "boolean", "description": "Set as enabled.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true }, "apiKey": { "type": "string", @@ -22265,13 +22389,15 @@ "type": "string", "description": "Topic Name.", "default": null, - "x-example": "<NAME>" + "x-example": "<NAME>", + "x-nullable": true }, "subscribe": { "type": "array", "description": "An array of role strings with subscribe permission. By default all users are granted with any subscribe permission. [learn more about roles](https:\/\/appwrite.io\/docs\/permissions#permission-roles). Maximum of 100 roles are allowed, each 64 characters long.", "default": null, "x-example": "[\"any\"]", + "x-nullable": true, "items": { "type": "string" } @@ -23001,7 +23127,7 @@ "tags": [ "migrations" ], - "description": "Export documents to a CSV file from your Appwrite database. This endpoint allows you to export documents to a CSV file stored in an Appwrite Storage bucket.", + "description": "Export documents to a CSV file from your Appwrite database. This endpoint allows you to export documents to a CSV file stored in a secure internal bucket. You'll receive an email with a download link when the export is complete.", "responses": { "202": { "description": "Migration", @@ -23049,12 +23175,6 @@ "default": null, "x-example": "<ID1:ID2>" }, - "bucketId": { - "type": "string", - "description": "Storage bucket unique ID where the exported CSV will be stored.", - "default": null, - "x-example": "<BUCKET_ID>" - }, "filename": { "type": "string", "description": "The name of the file to be created for the export, excluding the .csv extension.", @@ -23112,7 +23232,6 @@ }, "required": [ "resourceId", - "bucketId", "filename" ] } @@ -24332,13 +24451,15 @@ "type": "string", "description": "Variable value. Max length: 8192 chars.", "default": null, - "x-example": "<VALUE>" + "x-example": "<VALUE>", + "x-nullable": true }, "secret": { "type": "boolean", "description": "Secret variables can be updated or deleted, but only projects can read them during build and runtime.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true } }, "required": [ @@ -26651,6 +26772,7 @@ "description": "Key scopes list. Maximum of 100 scopes are allowed.", "default": null, "x-example": null, + "x-nullable": true, "items": { "type": "string" } @@ -26659,7 +26781,8 @@ "type": "string", "description": "Expiration time in [ISO 8601](https:\/\/www.iso.org\/iso-8601-date-and-time-format.html) format. Use null for unlimited expiration.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -26817,6 +26940,7 @@ "description": "Key scopes list. Maximum of 100 events are allowed.", "default": null, "x-example": null, + "x-nullable": true, "items": { "type": "string" } @@ -26825,7 +26949,8 @@ "type": "string", "description": "Expiration time in [ISO 8601](https:\/\/www.iso.org\/iso-8601-date-and-time-format.html) format. Use null for unlimited expiration.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -27015,19 +27140,22 @@ "type": "string", "description": "Provider app ID. Max length: 256 chars.", "default": null, - "x-example": "<APP_ID>" + "x-example": "<APP_ID>", + "x-nullable": true }, "secret": { "type": "string", "description": "Provider secret key. Max length: 512 chars.", "default": null, - "x-example": "<SECRET>" + "x-example": "<SECRET>", + "x-nullable": true }, "enabled": { "type": "boolean", "description": "Provider status. Set to 'false' to disable new session creation.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true } }, "required": [ @@ -31058,6 +31186,7 @@ "dart-3.3", "dart-3.5", "dart-3.8", + "dart-3.9", "dotnet-6.0", "dotnet-7.0", "dotnet-8.0", @@ -31084,7 +31213,8 @@ "flutter-3.24", "flutter-3.27", "flutter-3.29", - "flutter-3.32" + "flutter-3.32", + "flutter-3.35" ], "x-enum-name": null, "x-enum-keys": [] @@ -31708,6 +31838,7 @@ "dart-3.3", "dart-3.5", "dart-3.8", + "dart-3.9", "dotnet-6.0", "dotnet-7.0", "dotnet-8.0", @@ -31734,7 +31865,8 @@ "flutter-3.24", "flutter-3.27", "flutter-3.29", - "flutter-3.32" + "flutter-3.32", + "flutter-3.35" ], "x-enum-name": null, "x-enum-keys": [] @@ -33273,13 +33405,15 @@ "type": "string", "description": "Variable value. Max length: 8192 chars.", "default": null, - "x-example": "<VALUE>" + "x-example": "<VALUE>", + "x-nullable": true }, "secret": { "type": "boolean", "description": "Secret variables can be updated or deleted, but only sites can read them during build and runtime.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true } }, "required": [ @@ -33504,6 +33638,7 @@ "description": "An array of permission strings. By default, no user is granted with any permissions. [Learn more about permissions](https:\/\/appwrite.io\/docs\/permissions).", "default": null, "x-example": "[\"read(\"any\")\"]", + "x-nullable": true, "items": { "type": "string" } @@ -33702,6 +33837,7 @@ "description": "An array of permission strings. By default, the current permissions are inherited. [Learn more about permissions](https:\/\/appwrite.io\/docs\/permissions).", "default": null, "x-example": "[\"read(\"any\")\"]", + "x-nullable": true, "items": { "type": "string" } @@ -34146,13 +34282,15 @@ "type": "string", "description": "Name of the file", "default": null, - "x-example": "<NAME>" + "x-example": "<NAME>", + "x-nullable": true }, "permissions": { "type": "array", "description": "An array of permission string. By default, the current permissions are inherited. [Learn more about permissions](https:\/\/appwrite.io\/docs\/permissions).", "default": null, "x-example": "[\"read(\"any\")\"]", + "x-nullable": true, "items": { "type": "string" } @@ -35796,6 +35934,7 @@ "description": "An array of permissions strings. By default, no user is granted with any permissions. [Learn more about permissions](https:\/\/appwrite.io\/docs\/permissions).", "default": null, "x-example": "[\"read(\"any\")\"]", + "x-nullable": true, "items": { "type": "string" } @@ -35976,6 +36115,7 @@ "description": "An array of permission strings. By default, the current permissions are inherited. [Learn more about permissions](https:\/\/appwrite.io\/docs\/permissions).", "default": null, "x-example": "[\"read(\"any\")\"]", + "x-nullable": true, "items": { "type": "string" } @@ -36248,7 +36388,8 @@ "type": "boolean", "description": "Default value for column when not provided. Cannot be set when column is required.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true }, "array": { "type": "boolean", @@ -36365,7 +36506,8 @@ "type": "string", "description": "New Column Key.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -36585,7 +36727,8 @@ "type": "string", "description": "New Column Key.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -36688,7 +36831,8 @@ "type": "string", "description": "Default value for column when not provided. Cannot be set when column is required.", "default": null, - "x-example": "email@example.com" + "x-example": "email@example.com", + "x-nullable": true }, "array": { "type": "boolean", @@ -36805,7 +36949,8 @@ "type": "string", "description": "New Column Key.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -36917,7 +37062,8 @@ "type": "string", "description": "Default value for column when not provided. Cannot be set when column is required.", "default": null, - "x-example": "<DEFAULT>" + "x-example": "<DEFAULT>", + "x-nullable": true }, "array": { "type": "boolean", @@ -37044,7 +37190,8 @@ "type": "string", "description": "New Column Key.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -37148,19 +37295,22 @@ "type": "number", "description": "Minimum value", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "max": { "type": "number", "description": "Maximum value", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "default": { "type": "number", "description": "Default value. Cannot be set when required.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "array": { "type": "boolean", @@ -37270,13 +37420,15 @@ "type": "number", "description": "Minimum value", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "max": { "type": "number", "description": "Maximum value", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "default": { "type": "number", @@ -37289,7 +37441,8 @@ "type": "string", "description": "New Column Key.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -37392,19 +37545,22 @@ "type": "integer", "description": "Minimum value", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "max": { "type": "integer", "description": "Maximum value", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "default": { "type": "integer", "description": "Default value. Cannot be set when column is required.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "array": { "type": "boolean", @@ -37514,13 +37670,15 @@ "type": "integer", "description": "Minimum value", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "max": { "type": "integer", "description": "Maximum value", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "default": { "type": "integer", @@ -37533,7 +37691,8 @@ "type": "string", "description": "New Column Key.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -37636,7 +37795,8 @@ "type": "string", "description": "Default value. Cannot be set when column is required.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "array": { "type": "boolean", @@ -37753,7 +37913,8 @@ "type": "string", "description": "New Column Key.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -37968,7 +38129,8 @@ "type": "string", "description": "New Column Key.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -38182,7 +38344,8 @@ "type": "string", "description": "New Column Key.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -38396,7 +38559,8 @@ "type": "string", "description": "New Column Key.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -38512,13 +38676,15 @@ "type": "string", "description": "Column Key.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "twoWayKey": { "type": "string", "description": "Two Way Column Key.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "onDelete": { "type": "string", @@ -38640,7 +38806,8 @@ "type": "string", "description": "Default value for column when not provided. Cannot be set when column is required.", "default": null, - "x-example": "<DEFAULT>" + "x-example": "<DEFAULT>", + "x-nullable": true }, "array": { "type": "boolean", @@ -38764,13 +38931,15 @@ "type": "integer", "description": "Maximum size of the string column.", "default": null, - "x-example": 1 + "x-example": 1, + "x-nullable": true }, "newKey": { "type": "string", "description": "New Column Key.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -38873,7 +39042,8 @@ "type": "string", "description": "Default value for column when not provided. Cannot be set when column is required.", "default": null, - "x-example": "https:\/\/example.com" + "x-example": "https:\/\/example.com", + "x-nullable": true }, "array": { "type": "boolean", @@ -38990,7 +39160,8 @@ "type": "string", "description": "New Column Key.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -39275,13 +39446,15 @@ "setNull" ], "x-enum-name": "RelationMutate", - "x-enum-keys": [] + "x-enum-keys": [], + "x-nullable": true }, "newKey": { "type": "string", "description": "New Column Key.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true } } } @@ -39993,6 +40166,7 @@ "description": "An array of permissions strings. By default, only the current user is granted all permissions. [Learn more about permissions](https:\/\/appwrite.io\/docs\/permissions).", "default": null, "x-example": "[\"read(\"any\")\"]", + "x-nullable": true, "items": { "type": "string" } @@ -40010,7 +40184,8 @@ "type": "string", "description": "Transaction ID for staging the operation.", "default": null, - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -40134,7 +40309,8 @@ "type": "string", "description": "Transaction ID for staging the operation.", "default": null, - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } }, "required": [ @@ -40238,7 +40414,8 @@ "type": "string", "description": "Transaction ID for staging the operation.", "default": null, - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -40333,7 +40510,8 @@ "type": "string", "description": "Transaction ID for staging the operation.", "default": null, - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -40566,6 +40744,7 @@ "description": "An array of permissions strings. By default, the current permissions are inherited. [Learn more about permissions](https:\/\/appwrite.io\/docs\/permissions).", "default": null, "x-example": "[\"read(\"any\")\"]", + "x-nullable": true, "items": { "type": "string" } @@ -40574,7 +40753,8 @@ "type": "string", "description": "Transaction ID for staging the operation.", "default": null, - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -40676,6 +40856,7 @@ "description": "An array of permissions strings. By default, the current permissions are inherited. [Learn more about permissions](https:\/\/appwrite.io\/docs\/permissions).", "default": null, "x-example": "[\"read(\"any\")\"]", + "x-nullable": true, "items": { "type": "string" } @@ -40684,7 +40865,8 @@ "type": "string", "description": "Transaction ID for staging the operation.", "default": null, - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -40774,7 +40956,8 @@ "type": "string", "description": "Transaction ID for staging the operation.", "default": null, - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -40974,13 +41157,15 @@ "type": "number", "description": "Minimum value for the column. If the current value is lesser than this value, an exception will be thrown.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "transactionId": { "type": "string", "description": "Transaction ID for staging the operation.", "default": null, - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -41091,13 +41276,15 @@ "type": "number", "description": "Maximum value for the column. If the current value is greater than this value, an error will be thrown.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "transactionId": { "type": "string", "description": "Transaction ID for staging the operation.", "default": null, - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -42915,13 +43102,15 @@ "type": "string", "description": "User email.", "default": null, - "x-example": "email@example.com" + "x-example": "email@example.com", + "x-nullable": true }, "phone": { "type": "string", "description": "Phone number. Format this number with a leading '+' and a country code, e.g., +16175551212.", "default": null, - "x-example": "+12065550100" + "x-example": "+12065550100", + "x-nullable": true }, "password": { "type": "string", diff --git a/app/config/specs/swagger2-1.8.x-server.json b/app/config/specs/swagger2-1.8.x-server.json index 8b972be590..af417d5788 100644 --- a/app/config/specs/swagger2-1.8.x-server.json +++ b/app/config/specs/swagger2-1.8.x-server.json @@ -6620,6 +6620,7 @@ "description": "An array of permissions strings. By default, no user is granted with any permissions. [Learn more about permissions](https:\/\/appwrite.io\/docs\/permissions).", "default": null, "x-example": "[\"read(\"any\")\"]", + "x-nullable": true, "items": { "type": "string" } @@ -6804,6 +6805,7 @@ "description": "An array of permission strings. By default, the current permissions are inherited. [Learn more about permissions](https:\/\/appwrite.io\/docs\/permissions).", "default": null, "x-example": "[\"read(\"any\")\"]", + "x-nullable": true, "items": { "type": "string" } @@ -7082,7 +7084,8 @@ "type": "boolean", "description": "Default value for attribute when not provided. Cannot be set when attribute is required.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true }, "array": { "type": "boolean", @@ -7201,7 +7204,8 @@ "type": "string", "description": "New attribute key.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -7425,7 +7429,8 @@ "type": "string", "description": "New attribute key.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -7530,7 +7535,8 @@ "type": "string", "description": "Default value for attribute when not provided. Cannot be set when attribute is required.", "default": null, - "x-example": "email@example.com" + "x-example": "email@example.com", + "x-nullable": true }, "array": { "type": "boolean", @@ -7649,7 +7655,8 @@ "type": "string", "description": "New Attribute Key.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -7763,7 +7770,8 @@ "type": "string", "description": "Default value for attribute when not provided. Cannot be set when attribute is required.", "default": null, - "x-example": "<DEFAULT>" + "x-example": "<DEFAULT>", + "x-nullable": true }, "array": { "type": "boolean", @@ -7892,7 +7900,8 @@ "type": "string", "description": "New Attribute Key.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -7998,19 +8007,22 @@ "type": "number", "description": "Minimum value.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "max": { "type": "number", "description": "Maximum value.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "default": { "type": "number", "description": "Default value. Cannot be set when required.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "array": { "type": "boolean", @@ -8122,13 +8134,15 @@ "type": "number", "description": "Minimum value.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "max": { "type": "number", "description": "Maximum value.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "default": { "type": "number", @@ -8141,7 +8155,8 @@ "type": "string", "description": "New Attribute Key.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -8246,19 +8261,22 @@ "type": "integer", "description": "Minimum value", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "max": { "type": "integer", "description": "Maximum value", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "default": { "type": "integer", "description": "Default value. Cannot be set when attribute is required.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "array": { "type": "boolean", @@ -8370,13 +8388,15 @@ "type": "integer", "description": "Minimum value", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "max": { "type": "integer", "description": "Maximum value", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "default": { "type": "integer", @@ -8389,7 +8409,8 @@ "type": "string", "description": "New Attribute Key.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -8494,7 +8515,8 @@ "type": "string", "description": "Default value. Cannot be set when attribute is required.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "array": { "type": "boolean", @@ -8613,7 +8635,8 @@ "type": "string", "description": "New Attribute Key.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -8832,7 +8855,8 @@ "type": "string", "description": "New attribute key.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -9050,7 +9074,8 @@ "type": "string", "description": "New attribute key.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -9268,7 +9293,8 @@ "type": "string", "description": "New attribute key.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -9386,13 +9412,15 @@ "type": "string", "description": "Attribute Key.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "twoWayKey": { "type": "string", "description": "Two Way Attribute Key.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "onDelete": { "type": "string", @@ -9516,7 +9544,8 @@ "type": "string", "description": "Default value for attribute when not provided. Cannot be set when attribute is required.", "default": null, - "x-example": "<DEFAULT>" + "x-example": "<DEFAULT>", + "x-nullable": true }, "array": { "type": "boolean", @@ -9642,13 +9671,15 @@ "type": "integer", "description": "Maximum size of the string attribute.", "default": null, - "x-example": 1 + "x-example": 1, + "x-nullable": true }, "newKey": { "type": "string", "description": "New Attribute Key.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -9753,7 +9784,8 @@ "type": "string", "description": "Default value for attribute when not provided. Cannot be set when attribute is required.", "default": null, - "x-example": "https:\/\/example.com" + "x-example": "https:\/\/example.com", + "x-nullable": true }, "array": { "type": "boolean", @@ -9872,7 +9904,8 @@ "type": "string", "description": "New Attribute Key.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -10163,13 +10196,15 @@ "setNull" ], "x-enum-name": "RelationMutate", - "x-enum-keys": [] + "x-enum-keys": [], + "x-nullable": true }, "newKey": { "type": "string", "description": "New Attribute Key.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true } } } @@ -10445,6 +10480,7 @@ "description": "An array of permissions strings. By default, only the current user is granted all permissions. [Learn more about permissions](https:\/\/appwrite.io\/docs\/permissions).", "default": null, "x-example": "[\"read(\"any\")\"]", + "x-nullable": true, "items": { "type": "string" } @@ -10462,7 +10498,8 @@ "type": "string", "description": "Transaction ID for staging the operation.", "default": null, - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -10593,7 +10630,8 @@ "type": "string", "description": "Transaction ID for staging the operation.", "default": null, - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } }, "required": [ @@ -10699,7 +10737,8 @@ "type": "string", "description": "Transaction ID for staging the operation.", "default": null, - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -10796,7 +10835,8 @@ "type": "string", "description": "Transaction ID for staging the operation.", "default": null, - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -11041,6 +11081,7 @@ "description": "An array of permissions strings. By default, the current permissions are inherited. [Learn more about permissions](https:\/\/appwrite.io\/docs\/permissions).", "default": null, "x-example": "[\"read(\"any\")\"]", + "x-nullable": true, "items": { "type": "string" } @@ -11049,7 +11090,8 @@ "type": "string", "description": "Transaction ID for staging the operation.", "default": null, - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } }, "required": [ @@ -11157,6 +11199,7 @@ "description": "An array of permissions strings. By default, the current permissions are inherited. [Learn more about permissions](https:\/\/appwrite.io\/docs\/permissions).", "default": null, "x-example": "[\"read(\"any\")\"]", + "x-nullable": true, "items": { "type": "string" } @@ -11165,7 +11208,8 @@ "type": "string", "description": "Transaction ID for staging the operation.", "default": null, - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -11258,7 +11302,8 @@ "type": "string", "description": "Transaction ID for staging the operation.", "default": null, - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -11372,13 +11417,15 @@ "type": "number", "description": "Minimum value for the attribute. If the current value is lesser than this value, an exception will be thrown.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "transactionId": { "type": "string", "description": "Transaction ID for staging the operation.", "default": null, - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -11492,13 +11539,15 @@ "type": "number", "description": "Maximum value for the attribute. If the current value is greater than this value, an error will be thrown.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "transactionId": { "type": "string", "description": "Transaction ID for staging the operation.", "default": null, - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -12078,6 +12127,7 @@ "dart-3.3", "dart-3.5", "dart-3.8", + "dart-3.9", "dotnet-6.0", "dotnet-7.0", "dotnet-8.0", @@ -12104,7 +12154,8 @@ "flutter-3.24", "flutter-3.27", "flutter-3.29", - "flutter-3.32" + "flutter-3.32", + "flutter-3.35" ], "x-enum-name": null, "x-enum-keys": [] @@ -12493,6 +12544,7 @@ "dart-3.3", "dart-3.5", "dart-3.8", + "dart-3.9", "dotnet-6.0", "dotnet-7.0", "dotnet-8.0", @@ -12519,7 +12571,8 @@ "flutter-3.24", "flutter-3.27", "flutter-3.29", - "flutter-3.32" + "flutter-3.32", + "flutter-3.35" ], "x-enum-name": null, "x-enum-keys": [] @@ -13721,7 +13774,8 @@ "type": "string", "description": "Scheduled execution time in [ISO 8601](https:\/\/www.iso.org\/iso-8601-date-and-time-format.html) format. DateTime value must be in future with precision in minutes.", "default": null, - "x-example": "<SCHEDULED_AT>" + "x-example": "<SCHEDULED_AT>", + "x-nullable": true } } } @@ -14163,13 +14217,15 @@ "type": "string", "description": "Variable value. Max length: 8192 chars.", "default": null, - "x-example": "<VALUE>" + "x-example": "<VALUE>", + "x-nullable": true }, "secret": { "type": "boolean", "description": "Secret variables can be updated or deleted, but only functions can read them during build and runtime.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true } }, "required": [ @@ -16331,7 +16387,8 @@ "type": "string", "description": "Scheduled delivery time for message in [ISO 8601](https:\/\/www.iso.org\/iso-8601-date-and-time-format.html) format. DateTime value must be in future.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -16415,6 +16472,7 @@ "description": "List of Topic IDs.", "default": null, "x-example": null, + "x-nullable": true, "items": { "type": "string" } @@ -16424,6 +16482,7 @@ "description": "List of User IDs.", "default": null, "x-example": null, + "x-nullable": true, "items": { "type": "string" } @@ -16433,6 +16492,7 @@ "description": "List of Targets IDs.", "default": null, "x-example": null, + "x-nullable": true, "items": { "type": "string" } @@ -16441,31 +16501,36 @@ "type": "string", "description": "Email Subject.", "default": null, - "x-example": "<SUBJECT>" + "x-example": "<SUBJECT>", + "x-nullable": true }, "content": { "type": "string", "description": "Email Content.", "default": null, - "x-example": "<CONTENT>" + "x-example": "<CONTENT>", + "x-nullable": true }, "draft": { "type": "boolean", "description": "Is message a draft", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true }, "html": { "type": "boolean", "description": "Is content of type HTML", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true }, "cc": { "type": "array", "description": "Array of target IDs to be added as CC.", "default": null, "x-example": null, + "x-nullable": true, "items": { "type": "string" } @@ -16475,6 +16540,7 @@ "description": "Array of target IDs to be added as BCC.", "default": null, "x-example": null, + "x-nullable": true, "items": { "type": "string" } @@ -16483,13 +16549,15 @@ "type": "string", "description": "Scheduled delivery time for message in [ISO 8601](https:\/\/www.iso.org\/iso-8601-date-and-time-format.html) format. DateTime value must be in future.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "attachments": { "type": "array", "description": "Array of compound ID strings of bucket IDs and file IDs to be attached to the email. They should be formatted as <BUCKET_ID>:<FILE_ID>.", "default": null, "x-example": null, + "x-nullable": true, "items": { "type": "string" } @@ -16607,7 +16675,8 @@ "type": "object", "description": "Additional key-value pair data for push notification.", "default": {}, - "x-example": "{}" + "x-example": "{}", + "x-nullable": true }, "action": { "type": "string", @@ -16661,7 +16730,8 @@ "type": "string", "description": "Scheduled delivery time for message in [ISO 8601](https:\/\/www.iso.org\/iso-8601-date-and-time-format.html) format. DateTime value must be in future.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "contentAvailable": { "type": "boolean", @@ -16767,6 +16837,7 @@ "description": "List of Topic IDs.", "default": null, "x-example": null, + "x-nullable": true, "items": { "type": "string" } @@ -16776,6 +16847,7 @@ "description": "List of User IDs.", "default": null, "x-example": null, + "x-nullable": true, "items": { "type": "string" } @@ -16785,6 +16857,7 @@ "description": "List of Targets IDs.", "default": null, "x-example": null, + "x-nullable": true, "items": { "type": "string" } @@ -16793,85 +16866,99 @@ "type": "string", "description": "Title for push notification.", "default": null, - "x-example": "<TITLE>" + "x-example": "<TITLE>", + "x-nullable": true }, "body": { "type": "string", "description": "Body for push notification.", "default": null, - "x-example": "<BODY>" + "x-example": "<BODY>", + "x-nullable": true }, "data": { "type": "object", "description": "Additional Data for push notification.", "default": {}, - "x-example": "{}" + "x-example": "{}", + "x-nullable": true }, "action": { "type": "string", "description": "Action for push notification.", "default": null, - "x-example": "<ACTION>" + "x-example": "<ACTION>", + "x-nullable": true }, "image": { "type": "string", "description": "Image for push notification. Must be a compound bucket ID to file ID of a jpeg, png, or bmp image in Appwrite Storage. It should be formatted as <BUCKET_ID>:<FILE_ID>.", "default": null, - "x-example": "<ID1:ID2>" + "x-example": "<ID1:ID2>", + "x-nullable": true }, "icon": { "type": "string", "description": "Icon for push notification. Available only for Android and Web platforms.", "default": null, - "x-example": "<ICON>" + "x-example": "<ICON>", + "x-nullable": true }, "sound": { "type": "string", "description": "Sound for push notification. Available only for Android and iOS platforms.", "default": null, - "x-example": "<SOUND>" + "x-example": "<SOUND>", + "x-nullable": true }, "color": { "type": "string", "description": "Color for push notification. Available only for Android platforms.", "default": null, - "x-example": "<COLOR>" + "x-example": "<COLOR>", + "x-nullable": true }, "tag": { "type": "string", "description": "Tag for push notification. Available only for Android platforms.", "default": null, - "x-example": "<TAG>" + "x-example": "<TAG>", + "x-nullable": true }, "badge": { "type": "integer", "description": "Badge for push notification. Available only for iOS platforms.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "draft": { "type": "boolean", "description": "Is message a draft", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true }, "scheduledAt": { "type": "string", "description": "Scheduled delivery time for message in [ISO 8601](https:\/\/www.iso.org\/iso-8601-date-and-time-format.html) format. DateTime value must be in future.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "contentAvailable": { "type": "boolean", "description": "If set to true, the notification will be delivered in the background. Available only for iOS Platform.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true }, "critical": { "type": "boolean", "description": "If set to true, the notification will be marked as critical. This requires the app to have the critical notification entitlement. Available only for iOS Platform.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true }, "priority": { "type": "string", @@ -16883,7 +16970,8 @@ "high" ], "x-enum-name": "MessagePriority", - "x-enum-keys": [] + "x-enum-keys": [], + "x-nullable": true } } } @@ -17068,7 +17156,8 @@ "type": "string", "description": "Scheduled delivery time for message in [ISO 8601](https:\/\/www.iso.org\/iso-8601-date-and-time-format.html) format. DateTime value must be in future.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -17219,6 +17308,7 @@ "description": "List of Topic IDs.", "default": null, "x-example": null, + "x-nullable": true, "items": { "type": "string" } @@ -17228,6 +17318,7 @@ "description": "List of User IDs.", "default": null, "x-example": null, + "x-nullable": true, "items": { "type": "string" } @@ -17237,6 +17328,7 @@ "description": "List of Targets IDs.", "default": null, "x-example": null, + "x-nullable": true, "items": { "type": "string" } @@ -17245,19 +17337,22 @@ "type": "string", "description": "Email Content.", "default": null, - "x-example": "<CONTENT>" + "x-example": "<CONTENT>", + "x-nullable": true }, "draft": { "type": "boolean", "description": "Is message a draft", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true }, "scheduledAt": { "type": "string", "description": "Scheduled delivery time for message in [ISO 8601](https:\/\/www.iso.org\/iso-8601-date-and-time-format.html) format. DateTime value must be in future.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true } } } @@ -17805,7 +17900,8 @@ "type": "boolean", "description": "Set as enabled.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true } }, "required": [ @@ -17963,7 +18059,8 @@ "type": "boolean", "description": "Set as enabled.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true }, "authKey": { "type": "string", @@ -17993,7 +18090,8 @@ "type": "boolean", "description": "Use APNS sandbox environment.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true } } } @@ -18139,13 +18237,15 @@ "type": "object", "description": "FCM service account JSON.", "default": {}, - "x-example": "{}" + "x-example": "{}", + "x-nullable": true }, "enabled": { "type": "boolean", "description": "Set as enabled.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true } }, "required": [ @@ -18295,13 +18395,15 @@ "type": "boolean", "description": "Set as enabled.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true }, "serviceAccountJSON": { "type": "object", "description": "FCM service account JSON.", "default": {}, - "x-example": "{}" + "x-example": "{}", + "x-nullable": true } } } @@ -18395,7 +18497,8 @@ "type": "boolean", "description": "Set as EU region.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true }, "fromName": { "type": "string", @@ -18425,7 +18528,8 @@ "type": "boolean", "description": "Set as enabled.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true } }, "required": [ @@ -18525,13 +18629,15 @@ "type": "boolean", "description": "Set as EU region.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true }, "enabled": { "type": "boolean", "description": "Set as enabled.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true }, "fromName": { "type": "string", @@ -18655,7 +18761,8 @@ "type": "boolean", "description": "Set as enabled.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true } }, "required": [ @@ -18743,7 +18850,8 @@ "type": "boolean", "description": "Set as enabled.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true }, "templateId": { "type": "string", @@ -18873,7 +18981,8 @@ "type": "boolean", "description": "Set as enabled.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true } }, "required": [ @@ -18961,7 +19070,8 @@ "type": "boolean", "description": "Set as enabled.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true }, "apiKey": { "type": "string", @@ -19103,7 +19213,8 @@ "type": "boolean", "description": "Set as enabled.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true } }, "required": [ @@ -19191,7 +19302,8 @@ "type": "boolean", "description": "Set as enabled.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true }, "apiKey": { "type": "string", @@ -19462,7 +19574,8 @@ "type": "boolean", "description": "Set as enabled.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true } }, "required": [ @@ -19639,7 +19752,8 @@ "type": "integer", "description": "SMTP port.", "default": null, - "x-example": 1 + "x-example": 1, + "x-nullable": true }, "username": { "type": "string", @@ -19670,7 +19784,8 @@ "type": "boolean", "description": "Enable SMTP AutoTLS feature.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true }, "mailer": { "type": "string", @@ -19706,7 +19821,8 @@ "type": "boolean", "description": "Set as enabled.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true } } } @@ -19806,7 +19922,8 @@ "type": "boolean", "description": "Set as enabled.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true } }, "required": [ @@ -19894,7 +20011,8 @@ "type": "boolean", "description": "Set as enabled.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true }, "customerId": { "type": "string", @@ -20012,7 +20130,8 @@ "type": "boolean", "description": "Set as enabled.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true } }, "required": [ @@ -20100,7 +20219,8 @@ "type": "boolean", "description": "Set as enabled.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true }, "username": { "type": "string", @@ -20218,7 +20338,8 @@ "type": "boolean", "description": "Set as enabled.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true } }, "required": [ @@ -20306,7 +20427,8 @@ "type": "boolean", "description": "Set as enabled.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true }, "accountSid": { "type": "string", @@ -20424,7 +20546,8 @@ "type": "boolean", "description": "Set as enabled.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true } }, "required": [ @@ -20512,7 +20635,8 @@ "type": "boolean", "description": "Set as enabled.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true }, "apiKey": { "type": "string", @@ -21120,13 +21244,15 @@ "type": "string", "description": "Topic Name.", "default": null, - "x-example": "<NAME>" + "x-example": "<NAME>", + "x-nullable": true }, "subscribe": { "type": "array", "description": "An array of role strings with subscribe permission. By default all users are granted with any subscribe permission. [learn more about roles](https:\/\/appwrite.io\/docs\/permissions#permission-roles). Maximum of 100 roles are allowed, each 64 characters long.", "default": null, "x-example": "[\"any\"]", + "x-nullable": true, "items": { "type": "string" } @@ -21845,6 +21971,7 @@ "dart-3.3", "dart-3.5", "dart-3.8", + "dart-3.9", "dotnet-6.0", "dotnet-7.0", "dotnet-8.0", @@ -21871,7 +21998,8 @@ "flutter-3.24", "flutter-3.27", "flutter-3.29", - "flutter-3.32" + "flutter-3.32", + "flutter-3.35" ], "x-enum-name": null, "x-enum-keys": [] @@ -22277,6 +22405,7 @@ "dart-3.3", "dart-3.5", "dart-3.8", + "dart-3.9", "dotnet-6.0", "dotnet-7.0", "dotnet-8.0", @@ -22303,7 +22432,8 @@ "flutter-3.24", "flutter-3.27", "flutter-3.29", - "flutter-3.32" + "flutter-3.32", + "flutter-3.35" ], "x-enum-name": null, "x-enum-keys": [] @@ -23782,13 +23912,15 @@ "type": "string", "description": "Variable value. Max length: 8192 chars.", "default": null, - "x-example": "<VALUE>" + "x-example": "<VALUE>", + "x-nullable": true }, "secret": { "type": "boolean", "description": "Secret variables can be updated or deleted, but only sites can read them during build and runtime.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true } }, "required": [ @@ -24016,6 +24148,7 @@ "description": "An array of permission strings. By default, no user is granted with any permissions. [Learn more about permissions](https:\/\/appwrite.io\/docs\/permissions).", "default": null, "x-example": "[\"read(\"any\")\"]", + "x-nullable": true, "items": { "type": "string" } @@ -24216,6 +24349,7 @@ "description": "An array of permission strings. By default, the current permissions are inherited. [Learn more about permissions](https:\/\/appwrite.io\/docs\/permissions).", "default": null, "x-example": "[\"read(\"any\")\"]", + "x-nullable": true, "items": { "type": "string" } @@ -24669,13 +24803,15 @@ "type": "string", "description": "Name of the file", "default": null, - "x-example": "<NAME>" + "x-example": "<NAME>", + "x-nullable": true }, "permissions": { "type": "array", "description": "An array of permission string. By default, the current permissions are inherited. [Learn more about permissions](https:\/\/appwrite.io\/docs\/permissions).", "default": null, "x-example": "[\"read(\"any\")\"]", + "x-nullable": true, "items": { "type": "string" } @@ -26103,6 +26239,7 @@ "description": "An array of permissions strings. By default, no user is granted with any permissions. [Learn more about permissions](https:\/\/appwrite.io\/docs\/permissions).", "default": null, "x-example": "[\"read(\"any\")\"]", + "x-nullable": true, "items": { "type": "string" } @@ -26285,6 +26422,7 @@ "description": "An array of permission strings. By default, the current permissions are inherited. [Learn more about permissions](https:\/\/appwrite.io\/docs\/permissions).", "default": null, "x-example": "[\"read(\"any\")\"]", + "x-nullable": true, "items": { "type": "string" } @@ -26560,7 +26698,8 @@ "type": "boolean", "description": "Default value for column when not provided. Cannot be set when column is required.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true }, "array": { "type": "boolean", @@ -26678,7 +26817,8 @@ "type": "string", "description": "New Column Key.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -26900,7 +27040,8 @@ "type": "string", "description": "New Column Key.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -27004,7 +27145,8 @@ "type": "string", "description": "Default value for column when not provided. Cannot be set when column is required.", "default": null, - "x-example": "email@example.com" + "x-example": "email@example.com", + "x-nullable": true }, "array": { "type": "boolean", @@ -27122,7 +27264,8 @@ "type": "string", "description": "New Column Key.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -27235,7 +27378,8 @@ "type": "string", "description": "Default value for column when not provided. Cannot be set when column is required.", "default": null, - "x-example": "<DEFAULT>" + "x-example": "<DEFAULT>", + "x-nullable": true }, "array": { "type": "boolean", @@ -27363,7 +27507,8 @@ "type": "string", "description": "New Column Key.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -27468,19 +27613,22 @@ "type": "number", "description": "Minimum value", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "max": { "type": "number", "description": "Maximum value", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "default": { "type": "number", "description": "Default value. Cannot be set when required.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "array": { "type": "boolean", @@ -27591,13 +27739,15 @@ "type": "number", "description": "Minimum value", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "max": { "type": "number", "description": "Maximum value", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "default": { "type": "number", @@ -27610,7 +27760,8 @@ "type": "string", "description": "New Column Key.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -27714,19 +27865,22 @@ "type": "integer", "description": "Minimum value", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "max": { "type": "integer", "description": "Maximum value", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "default": { "type": "integer", "description": "Default value. Cannot be set when column is required.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "array": { "type": "boolean", @@ -27837,13 +27991,15 @@ "type": "integer", "description": "Minimum value", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "max": { "type": "integer", "description": "Maximum value", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "default": { "type": "integer", @@ -27856,7 +28012,8 @@ "type": "string", "description": "New Column Key.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -27960,7 +28117,8 @@ "type": "string", "description": "Default value. Cannot be set when column is required.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "array": { "type": "boolean", @@ -28078,7 +28236,8 @@ "type": "string", "description": "New Column Key.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -28295,7 +28454,8 @@ "type": "string", "description": "New Column Key.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -28511,7 +28671,8 @@ "type": "string", "description": "New Column Key.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -28727,7 +28888,8 @@ "type": "string", "description": "New Column Key.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -28844,13 +29006,15 @@ "type": "string", "description": "Column Key.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "twoWayKey": { "type": "string", "description": "Two Way Column Key.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "onDelete": { "type": "string", @@ -28973,7 +29137,8 @@ "type": "string", "description": "Default value for column when not provided. Cannot be set when column is required.", "default": null, - "x-example": "<DEFAULT>" + "x-example": "<DEFAULT>", + "x-nullable": true }, "array": { "type": "boolean", @@ -29098,13 +29263,15 @@ "type": "integer", "description": "Maximum size of the string column.", "default": null, - "x-example": 1 + "x-example": 1, + "x-nullable": true }, "newKey": { "type": "string", "description": "New Column Key.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -29208,7 +29375,8 @@ "type": "string", "description": "Default value for column when not provided. Cannot be set when column is required.", "default": null, - "x-example": "https:\/\/example.com" + "x-example": "https:\/\/example.com", + "x-nullable": true }, "array": { "type": "boolean", @@ -29326,7 +29494,8 @@ "type": "string", "description": "New Column Key.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -29614,13 +29783,15 @@ "setNull" ], "x-enum-name": "RelationMutate", - "x-enum-keys": [] + "x-enum-keys": [], + "x-nullable": true }, "newKey": { "type": "string", "description": "New Column Key.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true } } } @@ -30261,6 +30432,7 @@ "description": "An array of permissions strings. By default, only the current user is granted all permissions. [Learn more about permissions](https:\/\/appwrite.io\/docs\/permissions).", "default": null, "x-example": "[\"read(\"any\")\"]", + "x-nullable": true, "items": { "type": "string" } @@ -30278,7 +30450,8 @@ "type": "string", "description": "Transaction ID for staging the operation.", "default": null, - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -30404,7 +30577,8 @@ "type": "string", "description": "Transaction ID for staging the operation.", "default": null, - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } }, "required": [ @@ -30509,7 +30683,8 @@ "type": "string", "description": "Transaction ID for staging the operation.", "default": null, - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -30605,7 +30780,8 @@ "type": "string", "description": "Transaction ID for staging the operation.", "default": null, - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -30843,6 +31019,7 @@ "description": "An array of permissions strings. By default, the current permissions are inherited. [Learn more about permissions](https:\/\/appwrite.io\/docs\/permissions).", "default": null, "x-example": "[\"read(\"any\")\"]", + "x-nullable": true, "items": { "type": "string" } @@ -30851,7 +31028,8 @@ "type": "string", "description": "Transaction ID for staging the operation.", "default": null, - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -30955,6 +31133,7 @@ "description": "An array of permissions strings. By default, the current permissions are inherited. [Learn more about permissions](https:\/\/appwrite.io\/docs\/permissions).", "default": null, "x-example": "[\"read(\"any\")\"]", + "x-nullable": true, "items": { "type": "string" } @@ -30963,7 +31142,8 @@ "type": "string", "description": "Transaction ID for staging the operation.", "default": null, - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -31055,7 +31235,8 @@ "type": "string", "description": "Transaction ID for staging the operation.", "default": null, - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -31168,13 +31349,15 @@ "type": "number", "description": "Minimum value for the column. If the current value is lesser than this value, an exception will be thrown.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "transactionId": { "type": "string", "description": "Transaction ID for staging the operation.", "default": null, - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -31287,13 +31470,15 @@ "type": "number", "description": "Maximum value for the column. If the current value is greater than this value, an error will be thrown.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "transactionId": { "type": "string", "description": "Transaction ID for staging the operation.", "default": null, - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -32870,13 +33055,15 @@ "type": "string", "description": "User email.", "default": null, - "x-example": "email@example.com" + "x-example": "email@example.com", + "x-nullable": true }, "phone": { "type": "string", "description": "Phone number. Format this number with a leading '+' and a country code, e.g., +16175551212.", "default": null, - "x-example": "+12065550100" + "x-example": "+12065550100", + "x-nullable": true }, "password": { "type": "string", diff --git a/app/config/specs/swagger2-latest-client.json b/app/config/specs/swagger2-latest-client.json index a304a7cb1a..1594f816ef 100644 --- a/app/config/specs/swagger2-latest-client.json +++ b/app/config/specs/swagger2-latest-client.json @@ -6400,6 +6400,7 @@ "description": "An array of permissions strings. By default, only the current user is granted all permissions. [Learn more about permissions](https:\/\/appwrite.io\/docs\/permissions).", "default": null, "x-example": "[\"read(\"any\")\"]", + "x-nullable": true, "items": { "type": "string" } @@ -6417,7 +6418,8 @@ "type": "string", "description": "Transaction ID for staging the operation.", "default": null, - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -6657,6 +6659,7 @@ "description": "An array of permissions strings. By default, the current permissions are inherited. [Learn more about permissions](https:\/\/appwrite.io\/docs\/permissions).", "default": null, "x-example": "[\"read(\"any\")\"]", + "x-nullable": true, "items": { "type": "string" } @@ -6665,7 +6668,8 @@ "type": "string", "description": "Transaction ID for staging the operation.", "default": null, - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } }, "required": [ @@ -6771,6 +6775,7 @@ "description": "An array of permissions strings. By default, the current permissions are inherited. [Learn more about permissions](https:\/\/appwrite.io\/docs\/permissions).", "default": null, "x-example": "[\"read(\"any\")\"]", + "x-nullable": true, "items": { "type": "string" } @@ -6779,7 +6784,8 @@ "type": "string", "description": "Transaction ID for staging the operation.", "default": null, - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -6870,7 +6876,8 @@ "type": "string", "description": "Transaction ID for staging the operation.", "default": null, - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -6982,13 +6989,15 @@ "type": "number", "description": "Minimum value for the attribute. If the current value is lesser than this value, an exception will be thrown.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "transactionId": { "type": "string", "description": "Transaction ID for staging the operation.", "default": null, - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -7100,13 +7109,15 @@ "type": "number", "description": "Maximum value for the attribute. If the current value is greater than this value, an error will be thrown.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "transactionId": { "type": "string", "description": "Transaction ID for staging the operation.", "default": null, - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -7305,7 +7316,8 @@ "type": "string", "description": "Scheduled execution time in [ISO 8601](https:\/\/www.iso.org\/iso-8601-date-and-time-format.html) format. DateTime value must be in future with precision in minutes.", "default": null, - "x-example": "<SCHEDULED_AT>" + "x-example": "<SCHEDULED_AT>", + "x-nullable": true } } } @@ -8417,13 +8429,15 @@ "type": "string", "description": "Name of the file", "default": null, - "x-example": "<NAME>" + "x-example": "<NAME>", + "x-nullable": true }, "permissions": { "type": "array", "description": "An array of permission string. By default, the current permissions are inherited. [Learn more about permissions](https:\/\/appwrite.io\/docs\/permissions).", "default": null, "x-example": "[\"read(\"any\")\"]", + "x-nullable": true, "items": { "type": "string" } @@ -9516,6 +9530,7 @@ "description": "An array of permissions strings. By default, only the current user is granted all permissions. [Learn more about permissions](https:\/\/appwrite.io\/docs\/permissions).", "default": null, "x-example": "[\"read(\"any\")\"]", + "x-nullable": true, "items": { "type": "string" } @@ -9533,7 +9548,8 @@ "type": "string", "description": "Transaction ID for staging the operation.", "default": null, - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -9766,6 +9782,7 @@ "description": "An array of permissions strings. By default, the current permissions are inherited. [Learn more about permissions](https:\/\/appwrite.io\/docs\/permissions).", "default": null, "x-example": "[\"read(\"any\")\"]", + "x-nullable": true, "items": { "type": "string" } @@ -9774,7 +9791,8 @@ "type": "string", "description": "Transaction ID for staging the operation.", "default": null, - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -9876,6 +9894,7 @@ "description": "An array of permissions strings. By default, the current permissions are inherited. [Learn more about permissions](https:\/\/appwrite.io\/docs\/permissions).", "default": null, "x-example": "[\"read(\"any\")\"]", + "x-nullable": true, "items": { "type": "string" } @@ -9884,7 +9903,8 @@ "type": "string", "description": "Transaction ID for staging the operation.", "default": null, - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -9974,7 +9994,8 @@ "type": "string", "description": "Transaction ID for staging the operation.", "default": null, - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -10085,13 +10106,15 @@ "type": "number", "description": "Minimum value for the column. If the current value is lesser than this value, an exception will be thrown.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "transactionId": { "type": "string", "description": "Transaction ID for staging the operation.", "default": null, - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -10202,13 +10225,15 @@ "type": "number", "description": "Maximum value for the column. If the current value is greater than this value, an error will be thrown.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "transactionId": { "type": "string", "description": "Transaction ID for staging the operation.", "default": null, - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } diff --git a/app/config/specs/swagger2-latest-console.json b/app/config/specs/swagger2-latest-console.json index 158b308f87..efa5d9bdee 100644 --- a/app/config/specs/swagger2-latest-console.json +++ b/app/config/specs/swagger2-latest-console.json @@ -7168,6 +7168,7 @@ "description": "An array of permissions strings. By default, no user is granted with any permissions. [Learn more about permissions](https:\/\/appwrite.io\/docs\/permissions).", "default": null, "x-example": "[\"read(\"any\")\"]", + "x-nullable": true, "items": { "type": "string" } @@ -7350,6 +7351,7 @@ "description": "An array of permission strings. By default, the current permissions are inherited. [Learn more about permissions](https:\/\/appwrite.io\/docs\/permissions).", "default": null, "x-example": "[\"read(\"any\")\"]", + "x-nullable": true, "items": { "type": "string" } @@ -7625,7 +7627,8 @@ "type": "boolean", "description": "Default value for attribute when not provided. Cannot be set when attribute is required.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true }, "array": { "type": "boolean", @@ -7743,7 +7746,8 @@ "type": "string", "description": "New attribute key.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -7965,7 +7969,8 @@ "type": "string", "description": "New attribute key.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -8069,7 +8074,8 @@ "type": "string", "description": "Default value for attribute when not provided. Cannot be set when attribute is required.", "default": null, - "x-example": "email@example.com" + "x-example": "email@example.com", + "x-nullable": true }, "array": { "type": "boolean", @@ -8187,7 +8193,8 @@ "type": "string", "description": "New Attribute Key.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -8300,7 +8307,8 @@ "type": "string", "description": "Default value for attribute when not provided. Cannot be set when attribute is required.", "default": null, - "x-example": "<DEFAULT>" + "x-example": "<DEFAULT>", + "x-nullable": true }, "array": { "type": "boolean", @@ -8428,7 +8436,8 @@ "type": "string", "description": "New Attribute Key.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -8533,19 +8542,22 @@ "type": "number", "description": "Minimum value.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "max": { "type": "number", "description": "Maximum value.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "default": { "type": "number", "description": "Default value. Cannot be set when required.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "array": { "type": "boolean", @@ -8656,13 +8668,15 @@ "type": "number", "description": "Minimum value.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "max": { "type": "number", "description": "Maximum value.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "default": { "type": "number", @@ -8675,7 +8689,8 @@ "type": "string", "description": "New Attribute Key.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -8779,19 +8794,22 @@ "type": "integer", "description": "Minimum value", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "max": { "type": "integer", "description": "Maximum value", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "default": { "type": "integer", "description": "Default value. Cannot be set when attribute is required.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "array": { "type": "boolean", @@ -8902,13 +8920,15 @@ "type": "integer", "description": "Minimum value", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "max": { "type": "integer", "description": "Maximum value", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "default": { "type": "integer", @@ -8921,7 +8941,8 @@ "type": "string", "description": "New Attribute Key.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -9025,7 +9046,8 @@ "type": "string", "description": "Default value. Cannot be set when attribute is required.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "array": { "type": "boolean", @@ -9143,7 +9165,8 @@ "type": "string", "description": "New Attribute Key.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -9360,7 +9383,8 @@ "type": "string", "description": "New attribute key.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -9576,7 +9600,8 @@ "type": "string", "description": "New attribute key.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -9792,7 +9817,8 @@ "type": "string", "description": "New attribute key.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -9909,13 +9935,15 @@ "type": "string", "description": "Attribute Key.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "twoWayKey": { "type": "string", "description": "Two Way Attribute Key.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "onDelete": { "type": "string", @@ -10038,7 +10066,8 @@ "type": "string", "description": "Default value for attribute when not provided. Cannot be set when attribute is required.", "default": null, - "x-example": "<DEFAULT>" + "x-example": "<DEFAULT>", + "x-nullable": true }, "array": { "type": "boolean", @@ -10163,13 +10192,15 @@ "type": "integer", "description": "Maximum size of the string attribute.", "default": null, - "x-example": 1 + "x-example": 1, + "x-nullable": true }, "newKey": { "type": "string", "description": "New Attribute Key.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -10273,7 +10304,8 @@ "type": "string", "description": "Default value for attribute when not provided. Cannot be set when attribute is required.", "default": null, - "x-example": "https:\/\/example.com" + "x-example": "https:\/\/example.com", + "x-nullable": true }, "array": { "type": "boolean", @@ -10391,7 +10423,8 @@ "type": "string", "description": "New Attribute Key.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -10679,13 +10712,15 @@ "setNull" ], "x-enum-name": "RelationMutate", - "x-enum-keys": [] + "x-enum-keys": [], + "x-nullable": true }, "newKey": { "type": "string", "description": "New Attribute Key.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true } } } @@ -10955,6 +10990,7 @@ "description": "An array of permissions strings. By default, only the current user is granted all permissions. [Learn more about permissions](https:\/\/appwrite.io\/docs\/permissions).", "default": null, "x-example": "[\"read(\"any\")\"]", + "x-nullable": true, "items": { "type": "string" } @@ -10972,7 +11008,8 @@ "type": "string", "description": "Transaction ID for staging the operation.", "default": null, - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -11101,7 +11138,8 @@ "type": "string", "description": "Transaction ID for staging the operation.", "default": null, - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } }, "required": [ @@ -11206,7 +11244,8 @@ "type": "string", "description": "Transaction ID for staging the operation.", "default": null, - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -11302,7 +11341,8 @@ "type": "string", "description": "Transaction ID for staging the operation.", "default": null, - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -11542,6 +11582,7 @@ "description": "An array of permissions strings. By default, the current permissions are inherited. [Learn more about permissions](https:\/\/appwrite.io\/docs\/permissions).", "default": null, "x-example": "[\"read(\"any\")\"]", + "x-nullable": true, "items": { "type": "string" } @@ -11550,7 +11591,8 @@ "type": "string", "description": "Transaction ID for staging the operation.", "default": null, - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } }, "required": [ @@ -11656,6 +11698,7 @@ "description": "An array of permissions strings. By default, the current permissions are inherited. [Learn more about permissions](https:\/\/appwrite.io\/docs\/permissions).", "default": null, "x-example": "[\"read(\"any\")\"]", + "x-nullable": true, "items": { "type": "string" } @@ -11664,7 +11707,8 @@ "type": "string", "description": "Transaction ID for staging the operation.", "default": null, - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -11755,7 +11799,8 @@ "type": "string", "description": "Transaction ID for staging the operation.", "default": null, - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -11957,13 +12002,15 @@ "type": "number", "description": "Minimum value for the attribute. If the current value is lesser than this value, an exception will be thrown.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "transactionId": { "type": "string", "description": "Transaction ID for staging the operation.", "default": null, - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -12075,13 +12122,15 @@ "type": "number", "description": "Maximum value for the attribute. If the current value is greater than this value, an error will be thrown.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "transactionId": { "type": "string", "description": "Transaction ID for staging the operation.", "default": null, - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -13041,6 +13090,7 @@ "dart-3.3", "dart-3.5", "dart-3.8", + "dart-3.9", "dotnet-6.0", "dotnet-7.0", "dotnet-8.0", @@ -13067,7 +13117,8 @@ "flutter-3.24", "flutter-3.27", "flutter-3.29", - "flutter-3.32" + "flutter-3.32", + "flutter-3.35" ], "x-enum-name": null, "x-enum-keys": [] @@ -13683,6 +13734,7 @@ "dart-3.3", "dart-3.5", "dart-3.8", + "dart-3.9", "dotnet-6.0", "dotnet-7.0", "dotnet-8.0", @@ -13709,7 +13761,8 @@ "flutter-3.24", "flutter-3.27", "flutter-3.29", - "flutter-3.32" + "flutter-3.32", + "flutter-3.35" ], "x-enum-name": null, "x-enum-keys": [] @@ -14302,11 +14355,24 @@ "default": null, "x-example": "<ROOT_DIRECTORY>" }, - "version": { + "type": { "type": "string", - "description": "Version (tag) for the repo linked to the function template.", + "description": "Type for the reference provided. Can be commit, branch, or tag", "default": null, - "x-example": "<VERSION>" + "x-example": "commit", + "enum": [ + "commit", + "branch", + "tag" + ], + "x-enum-name": null, + "x-enum-keys": [] + }, + "reference": { + "type": "string", + "description": "Reference value, can be a commit hash, branch name, or release tag", + "default": null, + "x-example": "<REFERENCE>" }, "activate": { "type": "boolean", @@ -14319,7 +14385,8 @@ "repository", "owner", "rootDirectory", - "version" + "type", + "reference" ] } } @@ -14896,7 +14963,8 @@ "type": "string", "description": "Scheduled execution time in [ISO 8601](https:\/\/www.iso.org\/iso-8601-date-and-time-format.html) format. DateTime value must be in future with precision in minutes.", "default": null, - "x-example": "<SCHEDULED_AT>" + "x-example": "<SCHEDULED_AT>", + "x-nullable": true } } } @@ -15409,13 +15477,15 @@ "type": "string", "description": "Variable value. Max length: 8192 chars.", "default": null, - "x-example": "<VALUE>" + "x-example": "<VALUE>", + "x-nullable": true }, "secret": { "type": "boolean", "description": "Secret variables can be updated or deleted, but only functions can read them during build and runtime.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true } }, "required": [ @@ -17532,7 +17602,8 @@ "type": "string", "description": "Scheduled delivery time for message in [ISO 8601](https:\/\/www.iso.org\/iso-8601-date-and-time-format.html) format. DateTime value must be in future.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -17615,6 +17686,7 @@ "description": "List of Topic IDs.", "default": null, "x-example": null, + "x-nullable": true, "items": { "type": "string" } @@ -17624,6 +17696,7 @@ "description": "List of User IDs.", "default": null, "x-example": null, + "x-nullable": true, "items": { "type": "string" } @@ -17633,6 +17706,7 @@ "description": "List of Targets IDs.", "default": null, "x-example": null, + "x-nullable": true, "items": { "type": "string" } @@ -17641,31 +17715,36 @@ "type": "string", "description": "Email Subject.", "default": null, - "x-example": "<SUBJECT>" + "x-example": "<SUBJECT>", + "x-nullable": true }, "content": { "type": "string", "description": "Email Content.", "default": null, - "x-example": "<CONTENT>" + "x-example": "<CONTENT>", + "x-nullable": true }, "draft": { "type": "boolean", "description": "Is message a draft", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true }, "html": { "type": "boolean", "description": "Is content of type HTML", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true }, "cc": { "type": "array", "description": "Array of target IDs to be added as CC.", "default": null, "x-example": null, + "x-nullable": true, "items": { "type": "string" } @@ -17675,6 +17754,7 @@ "description": "Array of target IDs to be added as BCC.", "default": null, "x-example": null, + "x-nullable": true, "items": { "type": "string" } @@ -17683,13 +17763,15 @@ "type": "string", "description": "Scheduled delivery time for message in [ISO 8601](https:\/\/www.iso.org\/iso-8601-date-and-time-format.html) format. DateTime value must be in future.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "attachments": { "type": "array", "description": "Array of compound ID strings of bucket IDs and file IDs to be attached to the email. They should be formatted as <BUCKET_ID>:<FILE_ID>.", "default": null, "x-example": null, + "x-nullable": true, "items": { "type": "string" } @@ -17806,7 +17888,8 @@ "type": "object", "description": "Additional key-value pair data for push notification.", "default": {}, - "x-example": "{}" + "x-example": "{}", + "x-nullable": true }, "action": { "type": "string", @@ -17860,7 +17943,8 @@ "type": "string", "description": "Scheduled delivery time for message in [ISO 8601](https:\/\/www.iso.org\/iso-8601-date-and-time-format.html) format. DateTime value must be in future.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "contentAvailable": { "type": "boolean", @@ -17965,6 +18049,7 @@ "description": "List of Topic IDs.", "default": null, "x-example": null, + "x-nullable": true, "items": { "type": "string" } @@ -17974,6 +18059,7 @@ "description": "List of User IDs.", "default": null, "x-example": null, + "x-nullable": true, "items": { "type": "string" } @@ -17983,6 +18069,7 @@ "description": "List of Targets IDs.", "default": null, "x-example": null, + "x-nullable": true, "items": { "type": "string" } @@ -17991,85 +18078,99 @@ "type": "string", "description": "Title for push notification.", "default": null, - "x-example": "<TITLE>" + "x-example": "<TITLE>", + "x-nullable": true }, "body": { "type": "string", "description": "Body for push notification.", "default": null, - "x-example": "<BODY>" + "x-example": "<BODY>", + "x-nullable": true }, "data": { "type": "object", "description": "Additional Data for push notification.", "default": {}, - "x-example": "{}" + "x-example": "{}", + "x-nullable": true }, "action": { "type": "string", "description": "Action for push notification.", "default": null, - "x-example": "<ACTION>" + "x-example": "<ACTION>", + "x-nullable": true }, "image": { "type": "string", "description": "Image for push notification. Must be a compound bucket ID to file ID of a jpeg, png, or bmp image in Appwrite Storage. It should be formatted as <BUCKET_ID>:<FILE_ID>.", "default": null, - "x-example": "<ID1:ID2>" + "x-example": "<ID1:ID2>", + "x-nullable": true }, "icon": { "type": "string", "description": "Icon for push notification. Available only for Android and Web platforms.", "default": null, - "x-example": "<ICON>" + "x-example": "<ICON>", + "x-nullable": true }, "sound": { "type": "string", "description": "Sound for push notification. Available only for Android and iOS platforms.", "default": null, - "x-example": "<SOUND>" + "x-example": "<SOUND>", + "x-nullable": true }, "color": { "type": "string", "description": "Color for push notification. Available only for Android platforms.", "default": null, - "x-example": "<COLOR>" + "x-example": "<COLOR>", + "x-nullable": true }, "tag": { "type": "string", "description": "Tag for push notification. Available only for Android platforms.", "default": null, - "x-example": "<TAG>" + "x-example": "<TAG>", + "x-nullable": true }, "badge": { "type": "integer", "description": "Badge for push notification. Available only for iOS platforms.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "draft": { "type": "boolean", "description": "Is message a draft", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true }, "scheduledAt": { "type": "string", "description": "Scheduled delivery time for message in [ISO 8601](https:\/\/www.iso.org\/iso-8601-date-and-time-format.html) format. DateTime value must be in future.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "contentAvailable": { "type": "boolean", "description": "If set to true, the notification will be delivered in the background. Available only for iOS Platform.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true }, "critical": { "type": "boolean", "description": "If set to true, the notification will be marked as critical. This requires the app to have the critical notification entitlement. Available only for iOS Platform.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true }, "priority": { "type": "string", @@ -18081,7 +18182,8 @@ "high" ], "x-enum-name": "MessagePriority", - "x-enum-keys": [] + "x-enum-keys": [], + "x-nullable": true } } } @@ -18263,7 +18365,8 @@ "type": "string", "description": "Scheduled delivery time for message in [ISO 8601](https:\/\/www.iso.org\/iso-8601-date-and-time-format.html) format. DateTime value must be in future.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -18411,6 +18514,7 @@ "description": "List of Topic IDs.", "default": null, "x-example": null, + "x-nullable": true, "items": { "type": "string" } @@ -18420,6 +18524,7 @@ "description": "List of User IDs.", "default": null, "x-example": null, + "x-nullable": true, "items": { "type": "string" } @@ -18429,6 +18534,7 @@ "description": "List of Targets IDs.", "default": null, "x-example": null, + "x-nullable": true, "items": { "type": "string" } @@ -18437,19 +18543,22 @@ "type": "string", "description": "Email Content.", "default": null, - "x-example": "<CONTENT>" + "x-example": "<CONTENT>", + "x-nullable": true }, "draft": { "type": "boolean", "description": "Is message a draft", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true }, "scheduledAt": { "type": "string", "description": "Scheduled delivery time for message in [ISO 8601](https:\/\/www.iso.org\/iso-8601-date-and-time-format.html) format. DateTime value must be in future.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true } } } @@ -18989,7 +19098,8 @@ "type": "boolean", "description": "Set as enabled.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true } }, "required": [ @@ -19144,7 +19254,8 @@ "type": "boolean", "description": "Set as enabled.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true }, "authKey": { "type": "string", @@ -19174,7 +19285,8 @@ "type": "boolean", "description": "Use APNS sandbox environment.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true } } } @@ -19317,13 +19429,15 @@ "type": "object", "description": "FCM service account JSON.", "default": {}, - "x-example": "{}" + "x-example": "{}", + "x-nullable": true }, "enabled": { "type": "boolean", "description": "Set as enabled.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true } }, "required": [ @@ -19470,13 +19584,15 @@ "type": "boolean", "description": "Set as enabled.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true }, "serviceAccountJSON": { "type": "object", "description": "FCM service account JSON.", "default": {}, - "x-example": "{}" + "x-example": "{}", + "x-nullable": true } } } @@ -19569,7 +19685,8 @@ "type": "boolean", "description": "Set as EU region.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true }, "fromName": { "type": "string", @@ -19599,7 +19716,8 @@ "type": "boolean", "description": "Set as enabled.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true } }, "required": [ @@ -19698,13 +19816,15 @@ "type": "boolean", "description": "Set as EU region.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true }, "enabled": { "type": "boolean", "description": "Set as enabled.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true }, "fromName": { "type": "string", @@ -19827,7 +19947,8 @@ "type": "boolean", "description": "Set as enabled.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true } }, "required": [ @@ -19914,7 +20035,8 @@ "type": "boolean", "description": "Set as enabled.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true }, "templateId": { "type": "string", @@ -20043,7 +20165,8 @@ "type": "boolean", "description": "Set as enabled.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true } }, "required": [ @@ -20130,7 +20253,8 @@ "type": "boolean", "description": "Set as enabled.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true }, "apiKey": { "type": "string", @@ -20271,7 +20395,8 @@ "type": "boolean", "description": "Set as enabled.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true } }, "required": [ @@ -20358,7 +20483,8 @@ "type": "boolean", "description": "Set as enabled.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true }, "apiKey": { "type": "string", @@ -20626,7 +20752,8 @@ "type": "boolean", "description": "Set as enabled.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true } }, "required": [ @@ -20800,7 +20927,8 @@ "type": "integer", "description": "SMTP port.", "default": null, - "x-example": 1 + "x-example": 1, + "x-nullable": true }, "username": { "type": "string", @@ -20831,7 +20959,8 @@ "type": "boolean", "description": "Enable SMTP AutoTLS feature.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true }, "mailer": { "type": "string", @@ -20867,7 +20996,8 @@ "type": "boolean", "description": "Set as enabled.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true } } } @@ -20966,7 +21096,8 @@ "type": "boolean", "description": "Set as enabled.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true } }, "required": [ @@ -21053,7 +21184,8 @@ "type": "boolean", "description": "Set as enabled.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true }, "customerId": { "type": "string", @@ -21170,7 +21302,8 @@ "type": "boolean", "description": "Set as enabled.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true } }, "required": [ @@ -21257,7 +21390,8 @@ "type": "boolean", "description": "Set as enabled.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true }, "username": { "type": "string", @@ -21374,7 +21508,8 @@ "type": "boolean", "description": "Set as enabled.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true } }, "required": [ @@ -21461,7 +21596,8 @@ "type": "boolean", "description": "Set as enabled.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true }, "accountSid": { "type": "string", @@ -21578,7 +21714,8 @@ "type": "boolean", "description": "Set as enabled.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true } }, "required": [ @@ -21665,7 +21802,8 @@ "type": "boolean", "description": "Set as enabled.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true }, "apiKey": { "type": "string", @@ -22265,13 +22403,15 @@ "type": "string", "description": "Topic Name.", "default": null, - "x-example": "<NAME>" + "x-example": "<NAME>", + "x-nullable": true }, "subscribe": { "type": "array", "description": "An array of role strings with subscribe permission. By default all users are granted with any subscribe permission. [learn more about roles](https:\/\/appwrite.io\/docs\/permissions#permission-roles). Maximum of 100 roles are allowed, each 64 characters long.", "default": null, "x-example": "[\"any\"]", + "x-nullable": true, "items": { "type": "string" } @@ -23001,7 +23141,7 @@ "tags": [ "migrations" ], - "description": "Export documents to a CSV file from your Appwrite database. This endpoint allows you to export documents to a CSV file stored in an Appwrite Storage bucket.", + "description": "Export documents to a CSV file from your Appwrite database. This endpoint allows you to export documents to a CSV file stored in a secure internal bucket. You'll receive an email with a download link when the export is complete.", "responses": { "202": { "description": "Migration", @@ -23049,12 +23189,6 @@ "default": null, "x-example": "<ID1:ID2>" }, - "bucketId": { - "type": "string", - "description": "Storage bucket unique ID where the exported CSV will be stored.", - "default": null, - "x-example": "<BUCKET_ID>" - }, "filename": { "type": "string", "description": "The name of the file to be created for the export, excluding the .csv extension.", @@ -23112,7 +23246,6 @@ }, "required": [ "resourceId", - "bucketId", "filename" ] } @@ -24332,13 +24465,15 @@ "type": "string", "description": "Variable value. Max length: 8192 chars.", "default": null, - "x-example": "<VALUE>" + "x-example": "<VALUE>", + "x-nullable": true }, "secret": { "type": "boolean", "description": "Secret variables can be updated or deleted, but only projects can read them during build and runtime.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true } }, "required": [ @@ -26651,6 +26786,7 @@ "description": "Key scopes list. Maximum of 100 scopes are allowed.", "default": null, "x-example": null, + "x-nullable": true, "items": { "type": "string" } @@ -26659,7 +26795,8 @@ "type": "string", "description": "Expiration time in [ISO 8601](https:\/\/www.iso.org\/iso-8601-date-and-time-format.html) format. Use null for unlimited expiration.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -26817,6 +26954,7 @@ "description": "Key scopes list. Maximum of 100 events are allowed.", "default": null, "x-example": null, + "x-nullable": true, "items": { "type": "string" } @@ -26825,7 +26963,8 @@ "type": "string", "description": "Expiration time in [ISO 8601](https:\/\/www.iso.org\/iso-8601-date-and-time-format.html) format. Use null for unlimited expiration.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -27015,19 +27154,22 @@ "type": "string", "description": "Provider app ID. Max length: 256 chars.", "default": null, - "x-example": "<APP_ID>" + "x-example": "<APP_ID>", + "x-nullable": true }, "secret": { "type": "string", "description": "Provider secret key. Max length: 512 chars.", "default": null, - "x-example": "<SECRET>" + "x-example": "<SECRET>", + "x-nullable": true }, "enabled": { "type": "boolean", "description": "Provider status. Set to 'false' to disable new session creation.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true } }, "required": [ @@ -31058,6 +31200,7 @@ "dart-3.3", "dart-3.5", "dart-3.8", + "dart-3.9", "dotnet-6.0", "dotnet-7.0", "dotnet-8.0", @@ -31084,7 +31227,8 @@ "flutter-3.24", "flutter-3.27", "flutter-3.29", - "flutter-3.32" + "flutter-3.32", + "flutter-3.35" ], "x-enum-name": null, "x-enum-keys": [] @@ -31708,6 +31852,7 @@ "dart-3.3", "dart-3.5", "dart-3.8", + "dart-3.9", "dotnet-6.0", "dotnet-7.0", "dotnet-8.0", @@ -31734,7 +31879,8 @@ "flutter-3.24", "flutter-3.27", "flutter-3.29", - "flutter-3.32" + "flutter-3.32", + "flutter-3.35" ], "x-enum-name": null, "x-enum-keys": [] @@ -32284,11 +32430,24 @@ "default": null, "x-example": "<ROOT_DIRECTORY>" }, - "version": { + "type": { "type": "string", - "description": "Version (tag) for the repo linked to the site template.", + "description": "Type for the reference provided. Can be commit, branch, or tag", "default": null, - "x-example": "<VERSION>" + "x-example": "branch", + "enum": [ + "branch", + "commit", + "tag" + ], + "x-enum-name": null, + "x-enum-keys": [] + }, + "reference": { + "type": "string", + "description": "Reference value, can be a commit hash, branch name, or release tag", + "default": null, + "x-example": "<REFERENCE>" }, "activate": { "type": "boolean", @@ -32301,7 +32460,8 @@ "repository", "owner", "rootDirectory", - "version" + "type", + "reference" ] } } @@ -33273,13 +33433,15 @@ "type": "string", "description": "Variable value. Max length: 8192 chars.", "default": null, - "x-example": "<VALUE>" + "x-example": "<VALUE>", + "x-nullable": true }, "secret": { "type": "boolean", "description": "Secret variables can be updated or deleted, but only sites can read them during build and runtime.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true } }, "required": [ @@ -33504,6 +33666,7 @@ "description": "An array of permission strings. By default, no user is granted with any permissions. [Learn more about permissions](https:\/\/appwrite.io\/docs\/permissions).", "default": null, "x-example": "[\"read(\"any\")\"]", + "x-nullable": true, "items": { "type": "string" } @@ -33702,6 +33865,7 @@ "description": "An array of permission strings. By default, the current permissions are inherited. [Learn more about permissions](https:\/\/appwrite.io\/docs\/permissions).", "default": null, "x-example": "[\"read(\"any\")\"]", + "x-nullable": true, "items": { "type": "string" } @@ -34146,13 +34310,15 @@ "type": "string", "description": "Name of the file", "default": null, - "x-example": "<NAME>" + "x-example": "<NAME>", + "x-nullable": true }, "permissions": { "type": "array", "description": "An array of permission string. By default, the current permissions are inherited. [Learn more about permissions](https:\/\/appwrite.io\/docs\/permissions).", "default": null, "x-example": "[\"read(\"any\")\"]", + "x-nullable": true, "items": { "type": "string" } @@ -35796,6 +35962,7 @@ "description": "An array of permissions strings. By default, no user is granted with any permissions. [Learn more about permissions](https:\/\/appwrite.io\/docs\/permissions).", "default": null, "x-example": "[\"read(\"any\")\"]", + "x-nullable": true, "items": { "type": "string" } @@ -35976,6 +36143,7 @@ "description": "An array of permission strings. By default, the current permissions are inherited. [Learn more about permissions](https:\/\/appwrite.io\/docs\/permissions).", "default": null, "x-example": "[\"read(\"any\")\"]", + "x-nullable": true, "items": { "type": "string" } @@ -36248,7 +36416,8 @@ "type": "boolean", "description": "Default value for column when not provided. Cannot be set when column is required.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true }, "array": { "type": "boolean", @@ -36365,7 +36534,8 @@ "type": "string", "description": "New Column Key.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -36585,7 +36755,8 @@ "type": "string", "description": "New Column Key.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -36688,7 +36859,8 @@ "type": "string", "description": "Default value for column when not provided. Cannot be set when column is required.", "default": null, - "x-example": "email@example.com" + "x-example": "email@example.com", + "x-nullable": true }, "array": { "type": "boolean", @@ -36805,7 +36977,8 @@ "type": "string", "description": "New Column Key.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -36917,7 +37090,8 @@ "type": "string", "description": "Default value for column when not provided. Cannot be set when column is required.", "default": null, - "x-example": "<DEFAULT>" + "x-example": "<DEFAULT>", + "x-nullable": true }, "array": { "type": "boolean", @@ -37044,7 +37218,8 @@ "type": "string", "description": "New Column Key.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -37148,19 +37323,22 @@ "type": "number", "description": "Minimum value", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "max": { "type": "number", "description": "Maximum value", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "default": { "type": "number", "description": "Default value. Cannot be set when required.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "array": { "type": "boolean", @@ -37270,13 +37448,15 @@ "type": "number", "description": "Minimum value", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "max": { "type": "number", "description": "Maximum value", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "default": { "type": "number", @@ -37289,7 +37469,8 @@ "type": "string", "description": "New Column Key.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -37392,19 +37573,22 @@ "type": "integer", "description": "Minimum value", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "max": { "type": "integer", "description": "Maximum value", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "default": { "type": "integer", "description": "Default value. Cannot be set when column is required.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "array": { "type": "boolean", @@ -37514,13 +37698,15 @@ "type": "integer", "description": "Minimum value", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "max": { "type": "integer", "description": "Maximum value", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "default": { "type": "integer", @@ -37533,7 +37719,8 @@ "type": "string", "description": "New Column Key.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -37636,7 +37823,8 @@ "type": "string", "description": "Default value. Cannot be set when column is required.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "array": { "type": "boolean", @@ -37753,7 +37941,8 @@ "type": "string", "description": "New Column Key.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -37968,7 +38157,8 @@ "type": "string", "description": "New Column Key.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -38182,7 +38372,8 @@ "type": "string", "description": "New Column Key.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -38396,7 +38587,8 @@ "type": "string", "description": "New Column Key.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -38512,13 +38704,15 @@ "type": "string", "description": "Column Key.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "twoWayKey": { "type": "string", "description": "Two Way Column Key.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "onDelete": { "type": "string", @@ -38640,7 +38834,8 @@ "type": "string", "description": "Default value for column when not provided. Cannot be set when column is required.", "default": null, - "x-example": "<DEFAULT>" + "x-example": "<DEFAULT>", + "x-nullable": true }, "array": { "type": "boolean", @@ -38764,13 +38959,15 @@ "type": "integer", "description": "Maximum size of the string column.", "default": null, - "x-example": 1 + "x-example": 1, + "x-nullable": true }, "newKey": { "type": "string", "description": "New Column Key.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -38873,7 +39070,8 @@ "type": "string", "description": "Default value for column when not provided. Cannot be set when column is required.", "default": null, - "x-example": "https:\/\/example.com" + "x-example": "https:\/\/example.com", + "x-nullable": true }, "array": { "type": "boolean", @@ -38990,7 +39188,8 @@ "type": "string", "description": "New Column Key.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -39275,13 +39474,15 @@ "setNull" ], "x-enum-name": "RelationMutate", - "x-enum-keys": [] + "x-enum-keys": [], + "x-nullable": true }, "newKey": { "type": "string", "description": "New Column Key.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true } } } @@ -39993,6 +40194,7 @@ "description": "An array of permissions strings. By default, only the current user is granted all permissions. [Learn more about permissions](https:\/\/appwrite.io\/docs\/permissions).", "default": null, "x-example": "[\"read(\"any\")\"]", + "x-nullable": true, "items": { "type": "string" } @@ -40010,7 +40212,8 @@ "type": "string", "description": "Transaction ID for staging the operation.", "default": null, - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -40134,7 +40337,8 @@ "type": "string", "description": "Transaction ID for staging the operation.", "default": null, - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } }, "required": [ @@ -40238,7 +40442,8 @@ "type": "string", "description": "Transaction ID for staging the operation.", "default": null, - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -40333,7 +40538,8 @@ "type": "string", "description": "Transaction ID for staging the operation.", "default": null, - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -40566,6 +40772,7 @@ "description": "An array of permissions strings. By default, the current permissions are inherited. [Learn more about permissions](https:\/\/appwrite.io\/docs\/permissions).", "default": null, "x-example": "[\"read(\"any\")\"]", + "x-nullable": true, "items": { "type": "string" } @@ -40574,7 +40781,8 @@ "type": "string", "description": "Transaction ID for staging the operation.", "default": null, - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -40676,6 +40884,7 @@ "description": "An array of permissions strings. By default, the current permissions are inherited. [Learn more about permissions](https:\/\/appwrite.io\/docs\/permissions).", "default": null, "x-example": "[\"read(\"any\")\"]", + "x-nullable": true, "items": { "type": "string" } @@ -40684,7 +40893,8 @@ "type": "string", "description": "Transaction ID for staging the operation.", "default": null, - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -40774,7 +40984,8 @@ "type": "string", "description": "Transaction ID for staging the operation.", "default": null, - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -40974,13 +41185,15 @@ "type": "number", "description": "Minimum value for the column. If the current value is lesser than this value, an exception will be thrown.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "transactionId": { "type": "string", "description": "Transaction ID for staging the operation.", "default": null, - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -41091,13 +41304,15 @@ "type": "number", "description": "Maximum value for the column. If the current value is greater than this value, an error will be thrown.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "transactionId": { "type": "string", "description": "Transaction ID for staging the operation.", "default": null, - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -42915,13 +43130,15 @@ "type": "string", "description": "User email.", "default": null, - "x-example": "email@example.com" + "x-example": "email@example.com", + "x-nullable": true }, "phone": { "type": "string", "description": "Phone number. Format this number with a leading '+' and a country code, e.g., +16175551212.", "default": null, - "x-example": "+12065550100" + "x-example": "+12065550100", + "x-nullable": true }, "password": { "type": "string", diff --git a/app/config/specs/swagger2-latest-server.json b/app/config/specs/swagger2-latest-server.json index 8b972be590..e48f00475a 100644 --- a/app/config/specs/swagger2-latest-server.json +++ b/app/config/specs/swagger2-latest-server.json @@ -6620,6 +6620,7 @@ "description": "An array of permissions strings. By default, no user is granted with any permissions. [Learn more about permissions](https:\/\/appwrite.io\/docs\/permissions).", "default": null, "x-example": "[\"read(\"any\")\"]", + "x-nullable": true, "items": { "type": "string" } @@ -6804,6 +6805,7 @@ "description": "An array of permission strings. By default, the current permissions are inherited. [Learn more about permissions](https:\/\/appwrite.io\/docs\/permissions).", "default": null, "x-example": "[\"read(\"any\")\"]", + "x-nullable": true, "items": { "type": "string" } @@ -7082,7 +7084,8 @@ "type": "boolean", "description": "Default value for attribute when not provided. Cannot be set when attribute is required.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true }, "array": { "type": "boolean", @@ -7201,7 +7204,8 @@ "type": "string", "description": "New attribute key.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -7425,7 +7429,8 @@ "type": "string", "description": "New attribute key.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -7530,7 +7535,8 @@ "type": "string", "description": "Default value for attribute when not provided. Cannot be set when attribute is required.", "default": null, - "x-example": "email@example.com" + "x-example": "email@example.com", + "x-nullable": true }, "array": { "type": "boolean", @@ -7649,7 +7655,8 @@ "type": "string", "description": "New Attribute Key.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -7763,7 +7770,8 @@ "type": "string", "description": "Default value for attribute when not provided. Cannot be set when attribute is required.", "default": null, - "x-example": "<DEFAULT>" + "x-example": "<DEFAULT>", + "x-nullable": true }, "array": { "type": "boolean", @@ -7892,7 +7900,8 @@ "type": "string", "description": "New Attribute Key.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -7998,19 +8007,22 @@ "type": "number", "description": "Minimum value.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "max": { "type": "number", "description": "Maximum value.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "default": { "type": "number", "description": "Default value. Cannot be set when required.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "array": { "type": "boolean", @@ -8122,13 +8134,15 @@ "type": "number", "description": "Minimum value.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "max": { "type": "number", "description": "Maximum value.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "default": { "type": "number", @@ -8141,7 +8155,8 @@ "type": "string", "description": "New Attribute Key.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -8246,19 +8261,22 @@ "type": "integer", "description": "Minimum value", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "max": { "type": "integer", "description": "Maximum value", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "default": { "type": "integer", "description": "Default value. Cannot be set when attribute is required.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "array": { "type": "boolean", @@ -8370,13 +8388,15 @@ "type": "integer", "description": "Minimum value", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "max": { "type": "integer", "description": "Maximum value", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "default": { "type": "integer", @@ -8389,7 +8409,8 @@ "type": "string", "description": "New Attribute Key.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -8494,7 +8515,8 @@ "type": "string", "description": "Default value. Cannot be set when attribute is required.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "array": { "type": "boolean", @@ -8613,7 +8635,8 @@ "type": "string", "description": "New Attribute Key.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -8832,7 +8855,8 @@ "type": "string", "description": "New attribute key.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -9050,7 +9074,8 @@ "type": "string", "description": "New attribute key.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -9268,7 +9293,8 @@ "type": "string", "description": "New attribute key.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -9386,13 +9412,15 @@ "type": "string", "description": "Attribute Key.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "twoWayKey": { "type": "string", "description": "Two Way Attribute Key.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "onDelete": { "type": "string", @@ -9516,7 +9544,8 @@ "type": "string", "description": "Default value for attribute when not provided. Cannot be set when attribute is required.", "default": null, - "x-example": "<DEFAULT>" + "x-example": "<DEFAULT>", + "x-nullable": true }, "array": { "type": "boolean", @@ -9642,13 +9671,15 @@ "type": "integer", "description": "Maximum size of the string attribute.", "default": null, - "x-example": 1 + "x-example": 1, + "x-nullable": true }, "newKey": { "type": "string", "description": "New Attribute Key.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -9753,7 +9784,8 @@ "type": "string", "description": "Default value for attribute when not provided. Cannot be set when attribute is required.", "default": null, - "x-example": "https:\/\/example.com" + "x-example": "https:\/\/example.com", + "x-nullable": true }, "array": { "type": "boolean", @@ -9872,7 +9904,8 @@ "type": "string", "description": "New Attribute Key.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -10163,13 +10196,15 @@ "setNull" ], "x-enum-name": "RelationMutate", - "x-enum-keys": [] + "x-enum-keys": [], + "x-nullable": true }, "newKey": { "type": "string", "description": "New Attribute Key.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true } } } @@ -10445,6 +10480,7 @@ "description": "An array of permissions strings. By default, only the current user is granted all permissions. [Learn more about permissions](https:\/\/appwrite.io\/docs\/permissions).", "default": null, "x-example": "[\"read(\"any\")\"]", + "x-nullable": true, "items": { "type": "string" } @@ -10462,7 +10498,8 @@ "type": "string", "description": "Transaction ID for staging the operation.", "default": null, - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -10593,7 +10630,8 @@ "type": "string", "description": "Transaction ID for staging the operation.", "default": null, - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } }, "required": [ @@ -10699,7 +10737,8 @@ "type": "string", "description": "Transaction ID for staging the operation.", "default": null, - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -10796,7 +10835,8 @@ "type": "string", "description": "Transaction ID for staging the operation.", "default": null, - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -11041,6 +11081,7 @@ "description": "An array of permissions strings. By default, the current permissions are inherited. [Learn more about permissions](https:\/\/appwrite.io\/docs\/permissions).", "default": null, "x-example": "[\"read(\"any\")\"]", + "x-nullable": true, "items": { "type": "string" } @@ -11049,7 +11090,8 @@ "type": "string", "description": "Transaction ID for staging the operation.", "default": null, - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } }, "required": [ @@ -11157,6 +11199,7 @@ "description": "An array of permissions strings. By default, the current permissions are inherited. [Learn more about permissions](https:\/\/appwrite.io\/docs\/permissions).", "default": null, "x-example": "[\"read(\"any\")\"]", + "x-nullable": true, "items": { "type": "string" } @@ -11165,7 +11208,8 @@ "type": "string", "description": "Transaction ID for staging the operation.", "default": null, - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -11258,7 +11302,8 @@ "type": "string", "description": "Transaction ID for staging the operation.", "default": null, - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -11372,13 +11417,15 @@ "type": "number", "description": "Minimum value for the attribute. If the current value is lesser than this value, an exception will be thrown.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "transactionId": { "type": "string", "description": "Transaction ID for staging the operation.", "default": null, - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -11492,13 +11539,15 @@ "type": "number", "description": "Maximum value for the attribute. If the current value is greater than this value, an error will be thrown.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "transactionId": { "type": "string", "description": "Transaction ID for staging the operation.", "default": null, - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -12078,6 +12127,7 @@ "dart-3.3", "dart-3.5", "dart-3.8", + "dart-3.9", "dotnet-6.0", "dotnet-7.0", "dotnet-8.0", @@ -12104,7 +12154,8 @@ "flutter-3.24", "flutter-3.27", "flutter-3.29", - "flutter-3.32" + "flutter-3.32", + "flutter-3.35" ], "x-enum-name": null, "x-enum-keys": [] @@ -12493,6 +12544,7 @@ "dart-3.3", "dart-3.5", "dart-3.8", + "dart-3.9", "dotnet-6.0", "dotnet-7.0", "dotnet-8.0", @@ -12519,7 +12571,8 @@ "flutter-3.24", "flutter-3.27", "flutter-3.29", - "flutter-3.32" + "flutter-3.32", + "flutter-3.35" ], "x-enum-name": null, "x-enum-keys": [] @@ -13118,11 +13171,24 @@ "default": null, "x-example": "<ROOT_DIRECTORY>" }, - "version": { + "type": { "type": "string", - "description": "Version (tag) for the repo linked to the function template.", + "description": "Type for the reference provided. Can be commit, branch, or tag", "default": null, - "x-example": "<VERSION>" + "x-example": "commit", + "enum": [ + "commit", + "branch", + "tag" + ], + "x-enum-name": null, + "x-enum-keys": [] + }, + "reference": { + "type": "string", + "description": "Reference value, can be a commit hash, branch name, or release tag", + "default": null, + "x-example": "<REFERENCE>" }, "activate": { "type": "boolean", @@ -13135,7 +13201,8 @@ "repository", "owner", "rootDirectory", - "version" + "type", + "reference" ] } } @@ -13721,7 +13788,8 @@ "type": "string", "description": "Scheduled execution time in [ISO 8601](https:\/\/www.iso.org\/iso-8601-date-and-time-format.html) format. DateTime value must be in future with precision in minutes.", "default": null, - "x-example": "<SCHEDULED_AT>" + "x-example": "<SCHEDULED_AT>", + "x-nullable": true } } } @@ -14163,13 +14231,15 @@ "type": "string", "description": "Variable value. Max length: 8192 chars.", "default": null, - "x-example": "<VALUE>" + "x-example": "<VALUE>", + "x-nullable": true }, "secret": { "type": "boolean", "description": "Secret variables can be updated or deleted, but only functions can read them during build and runtime.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true } }, "required": [ @@ -16331,7 +16401,8 @@ "type": "string", "description": "Scheduled delivery time for message in [ISO 8601](https:\/\/www.iso.org\/iso-8601-date-and-time-format.html) format. DateTime value must be in future.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -16415,6 +16486,7 @@ "description": "List of Topic IDs.", "default": null, "x-example": null, + "x-nullable": true, "items": { "type": "string" } @@ -16424,6 +16496,7 @@ "description": "List of User IDs.", "default": null, "x-example": null, + "x-nullable": true, "items": { "type": "string" } @@ -16433,6 +16506,7 @@ "description": "List of Targets IDs.", "default": null, "x-example": null, + "x-nullable": true, "items": { "type": "string" } @@ -16441,31 +16515,36 @@ "type": "string", "description": "Email Subject.", "default": null, - "x-example": "<SUBJECT>" + "x-example": "<SUBJECT>", + "x-nullable": true }, "content": { "type": "string", "description": "Email Content.", "default": null, - "x-example": "<CONTENT>" + "x-example": "<CONTENT>", + "x-nullable": true }, "draft": { "type": "boolean", "description": "Is message a draft", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true }, "html": { "type": "boolean", "description": "Is content of type HTML", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true }, "cc": { "type": "array", "description": "Array of target IDs to be added as CC.", "default": null, "x-example": null, + "x-nullable": true, "items": { "type": "string" } @@ -16475,6 +16554,7 @@ "description": "Array of target IDs to be added as BCC.", "default": null, "x-example": null, + "x-nullable": true, "items": { "type": "string" } @@ -16483,13 +16563,15 @@ "type": "string", "description": "Scheduled delivery time for message in [ISO 8601](https:\/\/www.iso.org\/iso-8601-date-and-time-format.html) format. DateTime value must be in future.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "attachments": { "type": "array", "description": "Array of compound ID strings of bucket IDs and file IDs to be attached to the email. They should be formatted as <BUCKET_ID>:<FILE_ID>.", "default": null, "x-example": null, + "x-nullable": true, "items": { "type": "string" } @@ -16607,7 +16689,8 @@ "type": "object", "description": "Additional key-value pair data for push notification.", "default": {}, - "x-example": "{}" + "x-example": "{}", + "x-nullable": true }, "action": { "type": "string", @@ -16661,7 +16744,8 @@ "type": "string", "description": "Scheduled delivery time for message in [ISO 8601](https:\/\/www.iso.org\/iso-8601-date-and-time-format.html) format. DateTime value must be in future.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "contentAvailable": { "type": "boolean", @@ -16767,6 +16851,7 @@ "description": "List of Topic IDs.", "default": null, "x-example": null, + "x-nullable": true, "items": { "type": "string" } @@ -16776,6 +16861,7 @@ "description": "List of User IDs.", "default": null, "x-example": null, + "x-nullable": true, "items": { "type": "string" } @@ -16785,6 +16871,7 @@ "description": "List of Targets IDs.", "default": null, "x-example": null, + "x-nullable": true, "items": { "type": "string" } @@ -16793,85 +16880,99 @@ "type": "string", "description": "Title for push notification.", "default": null, - "x-example": "<TITLE>" + "x-example": "<TITLE>", + "x-nullable": true }, "body": { "type": "string", "description": "Body for push notification.", "default": null, - "x-example": "<BODY>" + "x-example": "<BODY>", + "x-nullable": true }, "data": { "type": "object", "description": "Additional Data for push notification.", "default": {}, - "x-example": "{}" + "x-example": "{}", + "x-nullable": true }, "action": { "type": "string", "description": "Action for push notification.", "default": null, - "x-example": "<ACTION>" + "x-example": "<ACTION>", + "x-nullable": true }, "image": { "type": "string", "description": "Image for push notification. Must be a compound bucket ID to file ID of a jpeg, png, or bmp image in Appwrite Storage. It should be formatted as <BUCKET_ID>:<FILE_ID>.", "default": null, - "x-example": "<ID1:ID2>" + "x-example": "<ID1:ID2>", + "x-nullable": true }, "icon": { "type": "string", "description": "Icon for push notification. Available only for Android and Web platforms.", "default": null, - "x-example": "<ICON>" + "x-example": "<ICON>", + "x-nullable": true }, "sound": { "type": "string", "description": "Sound for push notification. Available only for Android and iOS platforms.", "default": null, - "x-example": "<SOUND>" + "x-example": "<SOUND>", + "x-nullable": true }, "color": { "type": "string", "description": "Color for push notification. Available only for Android platforms.", "default": null, - "x-example": "<COLOR>" + "x-example": "<COLOR>", + "x-nullable": true }, "tag": { "type": "string", "description": "Tag for push notification. Available only for Android platforms.", "default": null, - "x-example": "<TAG>" + "x-example": "<TAG>", + "x-nullable": true }, "badge": { "type": "integer", "description": "Badge for push notification. Available only for iOS platforms.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "draft": { "type": "boolean", "description": "Is message a draft", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true }, "scheduledAt": { "type": "string", "description": "Scheduled delivery time for message in [ISO 8601](https:\/\/www.iso.org\/iso-8601-date-and-time-format.html) format. DateTime value must be in future.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "contentAvailable": { "type": "boolean", "description": "If set to true, the notification will be delivered in the background. Available only for iOS Platform.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true }, "critical": { "type": "boolean", "description": "If set to true, the notification will be marked as critical. This requires the app to have the critical notification entitlement. Available only for iOS Platform.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true }, "priority": { "type": "string", @@ -16883,7 +16984,8 @@ "high" ], "x-enum-name": "MessagePriority", - "x-enum-keys": [] + "x-enum-keys": [], + "x-nullable": true } } } @@ -17068,7 +17170,8 @@ "type": "string", "description": "Scheduled delivery time for message in [ISO 8601](https:\/\/www.iso.org\/iso-8601-date-and-time-format.html) format. DateTime value must be in future.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -17219,6 +17322,7 @@ "description": "List of Topic IDs.", "default": null, "x-example": null, + "x-nullable": true, "items": { "type": "string" } @@ -17228,6 +17332,7 @@ "description": "List of User IDs.", "default": null, "x-example": null, + "x-nullable": true, "items": { "type": "string" } @@ -17237,6 +17342,7 @@ "description": "List of Targets IDs.", "default": null, "x-example": null, + "x-nullable": true, "items": { "type": "string" } @@ -17245,19 +17351,22 @@ "type": "string", "description": "Email Content.", "default": null, - "x-example": "<CONTENT>" + "x-example": "<CONTENT>", + "x-nullable": true }, "draft": { "type": "boolean", "description": "Is message a draft", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true }, "scheduledAt": { "type": "string", "description": "Scheduled delivery time for message in [ISO 8601](https:\/\/www.iso.org\/iso-8601-date-and-time-format.html) format. DateTime value must be in future.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true } } } @@ -17805,7 +17914,8 @@ "type": "boolean", "description": "Set as enabled.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true } }, "required": [ @@ -17963,7 +18073,8 @@ "type": "boolean", "description": "Set as enabled.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true }, "authKey": { "type": "string", @@ -17993,7 +18104,8 @@ "type": "boolean", "description": "Use APNS sandbox environment.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true } } } @@ -18139,13 +18251,15 @@ "type": "object", "description": "FCM service account JSON.", "default": {}, - "x-example": "{}" + "x-example": "{}", + "x-nullable": true }, "enabled": { "type": "boolean", "description": "Set as enabled.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true } }, "required": [ @@ -18295,13 +18409,15 @@ "type": "boolean", "description": "Set as enabled.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true }, "serviceAccountJSON": { "type": "object", "description": "FCM service account JSON.", "default": {}, - "x-example": "{}" + "x-example": "{}", + "x-nullable": true } } } @@ -18395,7 +18511,8 @@ "type": "boolean", "description": "Set as EU region.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true }, "fromName": { "type": "string", @@ -18425,7 +18542,8 @@ "type": "boolean", "description": "Set as enabled.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true } }, "required": [ @@ -18525,13 +18643,15 @@ "type": "boolean", "description": "Set as EU region.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true }, "enabled": { "type": "boolean", "description": "Set as enabled.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true }, "fromName": { "type": "string", @@ -18655,7 +18775,8 @@ "type": "boolean", "description": "Set as enabled.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true } }, "required": [ @@ -18743,7 +18864,8 @@ "type": "boolean", "description": "Set as enabled.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true }, "templateId": { "type": "string", @@ -18873,7 +18995,8 @@ "type": "boolean", "description": "Set as enabled.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true } }, "required": [ @@ -18961,7 +19084,8 @@ "type": "boolean", "description": "Set as enabled.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true }, "apiKey": { "type": "string", @@ -19103,7 +19227,8 @@ "type": "boolean", "description": "Set as enabled.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true } }, "required": [ @@ -19191,7 +19316,8 @@ "type": "boolean", "description": "Set as enabled.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true }, "apiKey": { "type": "string", @@ -19462,7 +19588,8 @@ "type": "boolean", "description": "Set as enabled.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true } }, "required": [ @@ -19639,7 +19766,8 @@ "type": "integer", "description": "SMTP port.", "default": null, - "x-example": 1 + "x-example": 1, + "x-nullable": true }, "username": { "type": "string", @@ -19670,7 +19798,8 @@ "type": "boolean", "description": "Enable SMTP AutoTLS feature.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true }, "mailer": { "type": "string", @@ -19706,7 +19835,8 @@ "type": "boolean", "description": "Set as enabled.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true } } } @@ -19806,7 +19936,8 @@ "type": "boolean", "description": "Set as enabled.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true } }, "required": [ @@ -19894,7 +20025,8 @@ "type": "boolean", "description": "Set as enabled.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true }, "customerId": { "type": "string", @@ -20012,7 +20144,8 @@ "type": "boolean", "description": "Set as enabled.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true } }, "required": [ @@ -20100,7 +20233,8 @@ "type": "boolean", "description": "Set as enabled.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true }, "username": { "type": "string", @@ -20218,7 +20352,8 @@ "type": "boolean", "description": "Set as enabled.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true } }, "required": [ @@ -20306,7 +20441,8 @@ "type": "boolean", "description": "Set as enabled.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true }, "accountSid": { "type": "string", @@ -20424,7 +20560,8 @@ "type": "boolean", "description": "Set as enabled.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true } }, "required": [ @@ -20512,7 +20649,8 @@ "type": "boolean", "description": "Set as enabled.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true }, "apiKey": { "type": "string", @@ -21120,13 +21258,15 @@ "type": "string", "description": "Topic Name.", "default": null, - "x-example": "<NAME>" + "x-example": "<NAME>", + "x-nullable": true }, "subscribe": { "type": "array", "description": "An array of role strings with subscribe permission. By default all users are granted with any subscribe permission. [learn more about roles](https:\/\/appwrite.io\/docs\/permissions#permission-roles). Maximum of 100 roles are allowed, each 64 characters long.", "default": null, "x-example": "[\"any\"]", + "x-nullable": true, "items": { "type": "string" } @@ -21845,6 +21985,7 @@ "dart-3.3", "dart-3.5", "dart-3.8", + "dart-3.9", "dotnet-6.0", "dotnet-7.0", "dotnet-8.0", @@ -21871,7 +22012,8 @@ "flutter-3.24", "flutter-3.27", "flutter-3.29", - "flutter-3.32" + "flutter-3.32", + "flutter-3.35" ], "x-enum-name": null, "x-enum-keys": [] @@ -22277,6 +22419,7 @@ "dart-3.3", "dart-3.5", "dart-3.8", + "dart-3.9", "dotnet-6.0", "dotnet-7.0", "dotnet-8.0", @@ -22303,7 +22446,8 @@ "flutter-3.24", "flutter-3.27", "flutter-3.29", - "flutter-3.32" + "flutter-3.32", + "flutter-3.35" ], "x-enum-name": null, "x-enum-keys": [] @@ -22859,11 +23003,24 @@ "default": null, "x-example": "<ROOT_DIRECTORY>" }, - "version": { + "type": { "type": "string", - "description": "Version (tag) for the repo linked to the site template.", + "description": "Type for the reference provided. Can be commit, branch, or tag", "default": null, - "x-example": "<VERSION>" + "x-example": "branch", + "enum": [ + "branch", + "commit", + "tag" + ], + "x-enum-name": null, + "x-enum-keys": [] + }, + "reference": { + "type": "string", + "description": "Reference value, can be a commit hash, branch name, or release tag", + "default": null, + "x-example": "<REFERENCE>" }, "activate": { "type": "boolean", @@ -22876,7 +23033,8 @@ "repository", "owner", "rootDirectory", - "version" + "type", + "reference" ] } } @@ -23782,13 +23940,15 @@ "type": "string", "description": "Variable value. Max length: 8192 chars.", "default": null, - "x-example": "<VALUE>" + "x-example": "<VALUE>", + "x-nullable": true }, "secret": { "type": "boolean", "description": "Secret variables can be updated or deleted, but only sites can read them during build and runtime.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true } }, "required": [ @@ -24016,6 +24176,7 @@ "description": "An array of permission strings. By default, no user is granted with any permissions. [Learn more about permissions](https:\/\/appwrite.io\/docs\/permissions).", "default": null, "x-example": "[\"read(\"any\")\"]", + "x-nullable": true, "items": { "type": "string" } @@ -24216,6 +24377,7 @@ "description": "An array of permission strings. By default, the current permissions are inherited. [Learn more about permissions](https:\/\/appwrite.io\/docs\/permissions).", "default": null, "x-example": "[\"read(\"any\")\"]", + "x-nullable": true, "items": { "type": "string" } @@ -24669,13 +24831,15 @@ "type": "string", "description": "Name of the file", "default": null, - "x-example": "<NAME>" + "x-example": "<NAME>", + "x-nullable": true }, "permissions": { "type": "array", "description": "An array of permission string. By default, the current permissions are inherited. [Learn more about permissions](https:\/\/appwrite.io\/docs\/permissions).", "default": null, "x-example": "[\"read(\"any\")\"]", + "x-nullable": true, "items": { "type": "string" } @@ -26103,6 +26267,7 @@ "description": "An array of permissions strings. By default, no user is granted with any permissions. [Learn more about permissions](https:\/\/appwrite.io\/docs\/permissions).", "default": null, "x-example": "[\"read(\"any\")\"]", + "x-nullable": true, "items": { "type": "string" } @@ -26285,6 +26450,7 @@ "description": "An array of permission strings. By default, the current permissions are inherited. [Learn more about permissions](https:\/\/appwrite.io\/docs\/permissions).", "default": null, "x-example": "[\"read(\"any\")\"]", + "x-nullable": true, "items": { "type": "string" } @@ -26560,7 +26726,8 @@ "type": "boolean", "description": "Default value for column when not provided. Cannot be set when column is required.", "default": null, - "x-example": false + "x-example": false, + "x-nullable": true }, "array": { "type": "boolean", @@ -26678,7 +26845,8 @@ "type": "string", "description": "New Column Key.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -26900,7 +27068,8 @@ "type": "string", "description": "New Column Key.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -27004,7 +27173,8 @@ "type": "string", "description": "Default value for column when not provided. Cannot be set when column is required.", "default": null, - "x-example": "email@example.com" + "x-example": "email@example.com", + "x-nullable": true }, "array": { "type": "boolean", @@ -27122,7 +27292,8 @@ "type": "string", "description": "New Column Key.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -27235,7 +27406,8 @@ "type": "string", "description": "Default value for column when not provided. Cannot be set when column is required.", "default": null, - "x-example": "<DEFAULT>" + "x-example": "<DEFAULT>", + "x-nullable": true }, "array": { "type": "boolean", @@ -27363,7 +27535,8 @@ "type": "string", "description": "New Column Key.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -27468,19 +27641,22 @@ "type": "number", "description": "Minimum value", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "max": { "type": "number", "description": "Maximum value", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "default": { "type": "number", "description": "Default value. Cannot be set when required.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "array": { "type": "boolean", @@ -27591,13 +27767,15 @@ "type": "number", "description": "Minimum value", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "max": { "type": "number", "description": "Maximum value", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "default": { "type": "number", @@ -27610,7 +27788,8 @@ "type": "string", "description": "New Column Key.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -27714,19 +27893,22 @@ "type": "integer", "description": "Minimum value", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "max": { "type": "integer", "description": "Maximum value", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "default": { "type": "integer", "description": "Default value. Cannot be set when column is required.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "array": { "type": "boolean", @@ -27837,13 +28019,15 @@ "type": "integer", "description": "Minimum value", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "max": { "type": "integer", "description": "Maximum value", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "default": { "type": "integer", @@ -27856,7 +28040,8 @@ "type": "string", "description": "New Column Key.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -27960,7 +28145,8 @@ "type": "string", "description": "Default value. Cannot be set when column is required.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "array": { "type": "boolean", @@ -28078,7 +28264,8 @@ "type": "string", "description": "New Column Key.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -28295,7 +28482,8 @@ "type": "string", "description": "New Column Key.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -28511,7 +28699,8 @@ "type": "string", "description": "New Column Key.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -28727,7 +28916,8 @@ "type": "string", "description": "New Column Key.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -28844,13 +29034,15 @@ "type": "string", "description": "Column Key.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "twoWayKey": { "type": "string", "description": "Two Way Column Key.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "onDelete": { "type": "string", @@ -28973,7 +29165,8 @@ "type": "string", "description": "Default value for column when not provided. Cannot be set when column is required.", "default": null, - "x-example": "<DEFAULT>" + "x-example": "<DEFAULT>", + "x-nullable": true }, "array": { "type": "boolean", @@ -29098,13 +29291,15 @@ "type": "integer", "description": "Maximum size of the string column.", "default": null, - "x-example": 1 + "x-example": 1, + "x-nullable": true }, "newKey": { "type": "string", "description": "New Column Key.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -29208,7 +29403,8 @@ "type": "string", "description": "Default value for column when not provided. Cannot be set when column is required.", "default": null, - "x-example": "https:\/\/example.com" + "x-example": "https:\/\/example.com", + "x-nullable": true }, "array": { "type": "boolean", @@ -29326,7 +29522,8 @@ "type": "string", "description": "New Column Key.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true } }, "required": [ @@ -29614,13 +29811,15 @@ "setNull" ], "x-enum-name": "RelationMutate", - "x-enum-keys": [] + "x-enum-keys": [], + "x-nullable": true }, "newKey": { "type": "string", "description": "New Column Key.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true } } } @@ -30261,6 +30460,7 @@ "description": "An array of permissions strings. By default, only the current user is granted all permissions. [Learn more about permissions](https:\/\/appwrite.io\/docs\/permissions).", "default": null, "x-example": "[\"read(\"any\")\"]", + "x-nullable": true, "items": { "type": "string" } @@ -30278,7 +30478,8 @@ "type": "string", "description": "Transaction ID for staging the operation.", "default": null, - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -30404,7 +30605,8 @@ "type": "string", "description": "Transaction ID for staging the operation.", "default": null, - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } }, "required": [ @@ -30509,7 +30711,8 @@ "type": "string", "description": "Transaction ID for staging the operation.", "default": null, - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -30605,7 +30808,8 @@ "type": "string", "description": "Transaction ID for staging the operation.", "default": null, - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -30843,6 +31047,7 @@ "description": "An array of permissions strings. By default, the current permissions are inherited. [Learn more about permissions](https:\/\/appwrite.io\/docs\/permissions).", "default": null, "x-example": "[\"read(\"any\")\"]", + "x-nullable": true, "items": { "type": "string" } @@ -30851,7 +31056,8 @@ "type": "string", "description": "Transaction ID for staging the operation.", "default": null, - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -30955,6 +31161,7 @@ "description": "An array of permissions strings. By default, the current permissions are inherited. [Learn more about permissions](https:\/\/appwrite.io\/docs\/permissions).", "default": null, "x-example": "[\"read(\"any\")\"]", + "x-nullable": true, "items": { "type": "string" } @@ -30963,7 +31170,8 @@ "type": "string", "description": "Transaction ID for staging the operation.", "default": null, - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -31055,7 +31263,8 @@ "type": "string", "description": "Transaction ID for staging the operation.", "default": null, - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -31168,13 +31377,15 @@ "type": "number", "description": "Minimum value for the column. If the current value is lesser than this value, an exception will be thrown.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "transactionId": { "type": "string", "description": "Transaction ID for staging the operation.", "default": null, - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -31287,13 +31498,15 @@ "type": "number", "description": "Maximum value for the column. If the current value is greater than this value, an error will be thrown.", "default": null, - "x-example": null + "x-example": null, + "x-nullable": true }, "transactionId": { "type": "string", "description": "Transaction ID for staging the operation.", "default": null, - "x-example": "<TRANSACTION_ID>" + "x-example": "<TRANSACTION_ID>", + "x-nullable": true } } } @@ -32870,13 +33083,15 @@ "type": "string", "description": "User email.", "default": null, - "x-example": "email@example.com" + "x-example": "email@example.com", + "x-nullable": true }, "phone": { "type": "string", "description": "Phone number. Format this number with a leading '+' and a country code, e.g., +16175551212.", "default": null, - "x-example": "+12065550100" + "x-example": "+12065550100", + "x-nullable": true }, "password": { "type": "string", diff --git a/app/config/template-runtimes.php b/app/config/template-runtimes.php index d1bb1a5b6a..04eaba2c44 100644 --- a/app/config/template-runtimes.php +++ b/app/config/template-runtimes.php @@ -14,7 +14,7 @@ return [ ], 'DART' => [ 'name' => 'dart', - 'versions' => ['3.8', '3.5', '3.3', '3.1', '3.0', '2.19', '2.18', '2.17', '2.16'] + 'versions' => ['3.9', '3.8', '3.5', '3.3', '3.1', '3.0', '2.19', '2.18', '2.17', '2.16'] ], 'GO' => [ 'name' => 'go', @@ -38,6 +38,6 @@ return [ ], 'FLUTTER' => [ 'name' => 'flutter', - 'versions' => ['3.32', '3.24'] + 'versions' => ['3.35', '3.32', '3.24'] ], ]; diff --git a/app/config/templates/site.php b/app/config/templates/site.php index e552a6b9ac..c8bb019123 100644 --- a/app/config/templates/site.php +++ b/app/config/templates/site.php @@ -24,6 +24,7 @@ class UseCases public const ECOMMERCE = 'ecommerce'; public const DOCUMENTATION = 'documentation'; public const BLOG = 'blog'; + public const AI = 'artificial intelligence'; } const TEMPLATE_FRAMEWORKS = [ @@ -83,7 +84,7 @@ const TEMPLATE_FRAMEWORKS = [ 'installCommand' => '', 'buildCommand' => 'flutter build web', 'outputDirectory' => './build/web', - 'buildRuntime' => 'flutter-3.29', + 'buildRuntime' => 'flutter-3.35', 'adapter' => 'static', 'fallbackFile' => '', ], @@ -970,7 +971,7 @@ return [ 'name' => 'TanStack Start starter', 'useCases' => [UseCases::STARTER], 'tagline' => 'Simple TanStack Start application integrated with Appwrite SDK.', - 'score' => 6, // 0 to 10 based on looks of screenshot (avoid 1,2,3,8,9,10 if possible) + 'score' => 9, // 0 to 10 based on looks of screenshot (avoid 1,2,3,8,9,10 if possible) 'screenshotDark' => $url . '/images/sites/templates/starter-for-tanstack-start-dark.png', 'screenshotLight' => $url . '/images/sites/templates/starter-for-tanstack-start-light.png', 'frameworks' => [ @@ -1443,4 +1444,32 @@ return [ 'providerVersion' => '0.3.*', 'variables' => [] ], + [ + 'key' => 'text-to-speech', + 'name' => 'Text-to-speech with ElevenLabs', + 'tagline' => 'Next.js app that transforms text into natural, human-like speech using ElevenLabs', + 'score' => 10, // 0 to 10 based on looks of screenshot (avoid 1,2,3,8,9,10 if possible) + 'useCases' => [UseCases::AI], + 'screenshotDark' => $url . '/images/sites/templates/text-to-speech-dark.png', + 'screenshotLight' => $url . '/images/sites/templates/text-to-speech-light.png', + 'frameworks' => [ + getFramework('NEXTJS', [ + 'providerRootDirectory' => './nextjs/text-to-speech', + ]), + ], + 'vcsProvider' => 'github', + 'providerRepositoryId' => 'templates-for-sites', + 'providerOwner' => 'appwrite', + 'providerVersion' => '0.6.*', + 'variables' => [ + [ + 'name' => 'ELEVENLABS_API_KEY', + 'description' => 'Your ElevenLabs API key', + 'value' => '', + 'placeholder' => 'sk_.....', + 'required' => true, + 'type' => 'password' + ], + ] + ], ]; diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php index 9d1987591e..b7959bb6a9 100644 --- a/app/controllers/api/account.php +++ b/app/controllers/api/account.php @@ -20,7 +20,7 @@ use Appwrite\Event\Messaging; use Appwrite\Event\StatsUsage; use Appwrite\Extend\Exception; use Appwrite\Hooks\Hooks; -use Appwrite\Network\Validator\Email; +use Appwrite\Network\Validator\Email as EmailValidator; use Appwrite\Network\Validator\Redirect; use Appwrite\OpenSSL\OpenSSL; use Appwrite\SDK\AuthType; @@ -57,6 +57,7 @@ use Utopia\Database\Validator\Query\Cursor; use Utopia\Database\Validator\Query\Limit; use Utopia\Database\Validator\Query\Offset; use Utopia\Database\Validator\UID; +use Utopia\Emails\Email; use Utopia\Locale\Locale; use Utopia\Storage\Validator\FileName; use Utopia\System\System; @@ -337,7 +338,7 @@ App::post('/v1/account') )) ->label('abuse-limit', 10) ->param('userId', '', new CustomId(), 'User ID. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.') - ->param('email', '', new Email(), 'User email.') + ->param('email', '', new EmailValidator(), 'User email.') ->param('password', '', fn ($project, $passwordsDictionary) => new PasswordDictionary($passwordsDictionary, $project->getAttribute('auths', [])['passwordDictionary'] ?? false), 'New user password. Must be between 8 and 256 chars.', false, ['project', 'passwordsDictionary']) ->param('name', '', new Text(128), 'User name. Max length: 128 chars.', true) ->inject('request') @@ -394,6 +395,13 @@ App::post('/v1/account') $passwordHistory = $project->getAttribute('auths', [])['passwordHistory'] ?? 0; $password = Auth::passwordHash($password, Auth::DEFAULT_ALGO, Auth::DEFAULT_ALGO_OPTIONS); + + try { + $emailCanonical = new Email($email); + } catch (Throwable) { + $emailCanonical = null; + } + try { $userId = $userId == 'unique()' ? ID::unique() : $userId; $user->setAttributes([ @@ -422,7 +430,13 @@ App::post('/v1/account') 'authenticators' => null, 'search' => implode(' ', [$userId, $email, $name]), 'accessedAt' => DateTime::now(), + 'emailCanonical' => $emailCanonical?->getCanonical(), + 'emailIsCanonical' => $emailCanonical?->isCanonicalSupported(), + 'emailIsCorporate' => $emailCanonical?->isCorporate(), + 'emailIsDisposable' => $emailCanonical?->isDisposable(), + 'emailIsFree' => $emailCanonical?->isFree(), ]); + $user->removeAttribute('$sequence'); $user = Authorization::skip(fn () => $dbForProject->createDocument('users', $user)); try { @@ -903,7 +917,7 @@ App::post('/v1/account/sessions/email') )) ->label('abuse-limit', 10) ->label('abuse-key', 'url:{url},email:{param-email}') - ->param('email', '', new Email(), 'User email.') + ->param('email', '', new EmailValidator(), 'User email.') ->param('password', '', new Password(), 'User password. Must be at least 8 chars.') ->inject('request') ->inject('response') @@ -1598,6 +1612,12 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect') $failureRedirect(Exception::GENERAL_BAD_REQUEST); /** Return a generic bad request to prevent exposing existing accounts */ } + try { + $emailCanonical = new Email($email); + } catch (Throwable) { + $emailCanonical = null; + } + try { $userId = ID::unique(); $user->setAttributes([ @@ -1625,7 +1645,13 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect') 'authenticators' => null, 'search' => implode(' ', [$userId, $email, $name]), 'accessedAt' => DateTime::now(), + 'emailCanonical' => $emailCanonical?->getCanonical(), + 'emailIsCanonical' => $emailCanonical?->isCanonicalSupported(), + 'emailIsCorporate' => $emailCanonical?->isCorporate(), + 'emailIsDisposable' => $emailCanonical?->isDisposable(), + 'emailIsFree' => $emailCanonical?->isFree(), ]); + $user->removeAttribute('$sequence'); $userDoc = Authorization::skip(fn () => $dbForProject->createDocument('users', $user)); $dbForProject->createDocument('targets', new Document([ @@ -1696,6 +1722,18 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect') if (empty($user->getAttribute('email'))) { $user->setAttribute('email', $oauth2->getUserEmail($accessToken)); + + try { + $emailCanonical = new Email($user->getAttribute('email')); + } catch (Throwable) { + $emailCanonical = null; + } + + $user->setAttribute('emailCanonical', $emailCanonical?->getCanonical()); + $user->setAttribute('emailIsCanonical', $emailCanonical?->isCanonicalSupported()); + $user->setAttribute('emailIsCorporate', $emailCanonical?->isCorporate()); + $user->setAttribute('emailIsDisposable', $emailCanonical?->isDisposable()); + $user->setAttribute('emailIsFree', $emailCanonical?->isFree()); } if (empty($user->getAttribute('name'))) { @@ -1944,7 +1982,7 @@ App::post('/v1/account/tokens/magic-url') ->label('abuse-limit', 60) ->label('abuse-key', ['url:{url},email:{param-email}', 'url:{url},ip:{ip}']) ->param('userId', '', new CustomId(), 'Unique Id. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars. If the email address has never been used, a new account is created using the provided userId. Otherwise, if the email address is already attached to an account, the user ID is ignored.') - ->param('email', '', new Email(), 'User email.') + ->param('email', '', new EmailValidator(), 'User email.') ->param('url', '', fn ($platforms, $devKey) => $devKey->isEmpty() ? new Redirect($platforms) : new URL(), 'URL to redirect the user back to your app from the magic URL login. Only URLs from hostnames in your project platform list are allowed. This requirement helps to prevent an [open redirect](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.', true, ['platforms', 'devKey']) ->param('phrase', false, new Boolean(), 'Toggle for security phrase. If enabled, email will be send with a randomly generated phrase and the phrase will also be included in the response. Confirming phrases match increases the security of your authentication flow.', true) ->inject('request') @@ -1990,6 +2028,12 @@ App::post('/v1/account/tokens/magic-url') $userId = $userId === 'unique()' ? ID::unique() : $userId; + try { + $emailCanonical = new Email($email); + } catch (Throwable) { + $emailCanonical = null; + } + $user->setAttributes([ '$id' => $userId, '$permissions' => [ @@ -2014,6 +2058,11 @@ App::post('/v1/account/tokens/magic-url') 'authenticators' => null, 'search' => implode(' ', [$userId, $email]), 'accessedAt' => DateTime::now(), + 'emailCanonical' => $emailCanonical?->getCanonical(), + 'emailIsCanonical' => $emailCanonical?->isCanonicalSupported(), + 'emailIsCorporate' => $emailCanonical?->isCorporate(), + 'emailIsDisposable' => $emailCanonical?->isDisposable(), + 'emailIsFree' => $emailCanonical?->isFree(), ]); $user->removeAttribute('$sequence'); @@ -2197,7 +2246,7 @@ App::post('/v1/account/tokens/email') ->label('abuse-limit', 10) ->label('abuse-key', ['url:{url},email:{param-email}', 'url:{url},ip:{ip}']) ->param('userId', '', new CustomId(), 'User ID. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars. If the email address has never been used, a new account is created using the provided userId. Otherwise, if the email address is already attached to an account, the user ID is ignored.') - ->param('email', '', new Email(), 'User email.') + ->param('email', '', new EmailValidator(), 'User email.') ->param('phrase', false, new Boolean(), 'Toggle for security phrase. If enabled, email will be send with a randomly generated phrase and the phrase will also be included in the response. Confirming phrases match increases the security of your authentication flow.', true) ->inject('request') ->inject('response') @@ -2240,6 +2289,12 @@ App::post('/v1/account/tokens/email') $userId = $userId === 'unique()' ? ID::unique() : $userId; + try { + $emailCanonical = new Email($email); + } catch (Throwable) { + $emailCanonical = null; + } + $user->setAttributes([ '$id' => $userId, '$permissions' => [ @@ -2262,6 +2317,11 @@ App::post('/v1/account/tokens/email') 'memberships' => null, 'search' => implode(' ', [$userId, $email]), 'accessedAt' => DateTime::now(), + 'emailCanonical' => $emailCanonical?->getCanonical(), + 'emailIsCanonical' => $emailCanonical?->isCanonicalSupported(), + 'emailIsCorporate' => $emailCanonical?->isCorporate(), + 'emailIsDisposable' => $emailCanonical?->isDisposable(), + 'emailIsFree' => $emailCanonical?->isFree(), ]); $user->removeAttribute('$sequence'); @@ -2609,6 +2669,11 @@ App::post('/v1/account/tokens/phone') 'memberships' => null, 'search' => implode(' ', [$userId, $phone]), 'accessedAt' => DateTime::now(), + 'emailCanonical' => null, + 'emailIsCanonical' => null, + 'emailIsCorporate' => null, + 'emailIsDisposable' => null, + 'emailIsFree' => null, ]); $user->removeAttribute('$sequence'); @@ -3037,7 +3102,7 @@ App::patch('/v1/account/email') ], contentType: ContentType::JSON )) - ->param('email', '', new Email(), 'User email.') + ->param('email', '', new EmailValidator(), 'User email.') ->param('password', '', new Password(), 'User password. Must be at least 8 chars.') ->inject('requestTimestamp') ->inject('response') @@ -3072,9 +3137,20 @@ App::patch('/v1/account/email') throw new Exception(Exception::GENERAL_BAD_REQUEST); /** Return a generic bad request to prevent exposing existing accounts */ } + try { + $emailCanonical = new Email($email); + } catch (Throwable) { + $emailCanonical = null; + } + $user ->setAttribute('email', $email) ->setAttribute('emailVerification', false) // After this user needs to confirm mail again + ->setAttribute('emailCanonical', $emailCanonical?->getCanonical()) + ->setAttribute('emailIsCanonical', $emailCanonical?->isCanonicalSupported()) + ->setAttribute('emailIsCorporate', $emailCanonical?->isCorporate()) + ->setAttribute('emailIsDisposable', $emailCanonical?->isDisposable()) + ->setAttribute('emailIsFree', $emailCanonical?->isFree()) ; if (empty($passwordUpdate)) { @@ -3311,7 +3387,7 @@ App::post('/v1/account/recovery') )) ->label('abuse-limit', 10) ->label('abuse-key', ['url:{url},email:{param-email}', 'url:{url},ip:{ip}']) - ->param('email', '', new Email(), 'User email.') + ->param('email', '', new EmailValidator(), 'User email.') ->param('url', '', fn ($platforms, $devKey) => $devKey->isEmpty() ? new Redirect($platforms) : new URL(), 'URL to redirect the user back to your app from the recovery email. Only URLs from hostnames in your project platform list are allowed. This requirement helps to prevent an [open redirect](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.', false, ['platforms', 'devKey']) ->inject('request') ->inject('response') diff --git a/app/controllers/api/messaging.php b/app/controllers/api/messaging.php index 5193d0b30f..58a0ffe42f 100644 --- a/app/controllers/api/messaging.php +++ b/app/controllers/api/messaging.php @@ -49,6 +49,7 @@ use Utopia\Validator\ArrayList; use Utopia\Validator\Boolean; use Utopia\Validator\Integer; use Utopia\Validator\JSON; +use Utopia\Validator\Nullable; use Utopia\Validator\Range; use Utopia\Validator\Text; use Utopia\Validator\WhiteList; @@ -80,12 +81,12 @@ App::post('/v1/messaging/providers/mailgun') ->param('name', '', new Text(128), 'Provider name.') ->param('apiKey', '', new Text(0), 'Mailgun API Key.', true) ->param('domain', '', new Text(0), 'Mailgun Domain.', true) - ->param('isEuRegion', null, new Boolean(), 'Set as EU region.', true) + ->param('isEuRegion', null, new Nullable(new Boolean()), 'Set as EU region.', true) ->param('fromName', '', new Text(128, 0), 'Sender Name.', true) ->param('fromEmail', '', new Email(), 'Sender email address.', true) ->param('replyToName', '', new Text(128, 0), 'Name set in the reply to field for the mail. Default value is sender name. Reply to name must have reply to email as well.', true) ->param('replyToEmail', '', new Email(), 'Email set in the reply to field for the mail. Default value is sender email. Reply to email must have reply to name as well.', true) - ->param('enabled', null, new Boolean(), 'Set as enabled.', true) + ->param('enabled', null, new Nullable(new Boolean()), 'Set as enabled.', true) ->inject('queueForEvents') ->inject('dbForProject') ->inject('response') @@ -177,7 +178,7 @@ App::post('/v1/messaging/providers/sendgrid') ->param('fromEmail', '', new Email(), 'Sender email address.', true) ->param('replyToName', '', new Text(128, 0), 'Name set in the reply to field for the mail. Default value is sender name.', true) ->param('replyToEmail', '', new Email(), 'Email set in the reply to field for the mail. Default value is sender email.', true) - ->param('enabled', null, new Boolean(), 'Set as enabled.', true) + ->param('enabled', null, new Nullable(new Boolean()), 'Set as enabled.', true) ->inject('queueForEvents') ->inject('dbForProject') ->inject('response') @@ -259,7 +260,7 @@ App::post('/v1/messaging/providers/resend') ->param('fromEmail', '', new Email(), 'Sender email address.', true) ->param('replyToName', '', new Text(128, 0), 'Name set in the reply to field for the mail. Default value is sender name.', true) ->param('replyToEmail', '', new Email(), 'Email set in the reply to field for the mail. Default value is sender email.', true) - ->param('enabled', null, new Boolean(), 'Set as enabled.', true) + ->param('enabled', null, new Nullable(new Boolean()), 'Set as enabled.', true) ->inject('queueForEvents') ->inject('dbForProject') ->inject('response') @@ -366,7 +367,7 @@ App::post('/v1/messaging/providers/smtp') ->param('fromEmail', '', new Email(), 'Sender email address.', true) ->param('replyToName', '', new Text(128, 0), 'Name set in the reply to field for the mail. Default value is sender name.', true) ->param('replyToEmail', '', new Email(), 'Email set in the reply to field for the mail. Default value is sender email.', true) - ->param('enabled', null, new Boolean(), 'Set as enabled.', true) + ->param('enabled', null, new Nullable(new Boolean()), 'Set as enabled.', true) ->inject('queueForEvents') ->inject('dbForProject') ->inject('response') @@ -453,7 +454,7 @@ App::post('/v1/messaging/providers/msg91') ->param('templateId', '', new Text(0), 'Msg91 template ID', true) ->param('senderId', '', new Text(0), 'Msg91 sender ID.', true) ->param('authKey', '', new Text(0), 'Msg91 auth key.', true) - ->param('enabled', null, new Boolean(), 'Set as enabled.', true) + ->param('enabled', null, new Nullable(new Boolean()), 'Set as enabled.', true) ->inject('queueForEvents') ->inject('dbForProject') ->inject('response') @@ -536,7 +537,7 @@ App::post('/v1/messaging/providers/telesign') ->param('from', '', new Phone(), 'Sender Phone number. Format this number with a leading \'+\' and a country code, e.g., +16175551212.', true) ->param('customerId', '', new Text(0), 'Telesign customer ID.', true) ->param('apiKey', '', new Text(0), 'Telesign API key.', true) - ->param('enabled', null, new Boolean(), 'Set as enabled.', true) + ->param('enabled', null, new Nullable(new Boolean()), 'Set as enabled.', true) ->inject('queueForEvents') ->inject('dbForProject') ->inject('response') @@ -620,7 +621,7 @@ App::post('/v1/messaging/providers/textmagic') ->param('from', '', new Phone(), 'Sender Phone number. Format this number with a leading \'+\' and a country code, e.g., +16175551212.', true) ->param('username', '', new Text(0), 'Textmagic username.', true) ->param('apiKey', '', new Text(0), 'Textmagic apiKey.', true) - ->param('enabled', null, new Boolean(), 'Set as enabled.', true) + ->param('enabled', null, new Nullable(new Boolean()), 'Set as enabled.', true) ->inject('queueForEvents') ->inject('dbForProject') ->inject('response') @@ -704,7 +705,7 @@ App::post('/v1/messaging/providers/twilio') ->param('from', '', new Phone(), 'Sender Phone number. Format this number with a leading \'+\' and a country code, e.g., +16175551212.', true) ->param('accountSid', '', new Text(0), 'Twilio account secret ID.', true) ->param('authToken', '', new Text(0), 'Twilio authentication token.', true) - ->param('enabled', null, new Boolean(), 'Set as enabled.', true) + ->param('enabled', null, new Nullable(new Boolean()), 'Set as enabled.', true) ->inject('queueForEvents') ->inject('dbForProject') ->inject('response') @@ -788,7 +789,7 @@ App::post('/v1/messaging/providers/vonage') ->param('from', '', new Phone(), 'Sender Phone number. Format this number with a leading \'+\' and a country code, e.g., +16175551212.', true) ->param('apiKey', '', new Text(0), 'Vonage API key.', true) ->param('apiSecret', '', new Text(0), 'Vonage API secret.', true) - ->param('enabled', null, new Boolean(), 'Set as enabled.', true) + ->param('enabled', null, new Nullable(new Boolean()), 'Set as enabled.', true) ->inject('queueForEvents') ->inject('dbForProject') ->inject('response') @@ -888,8 +889,8 @@ App::post('/v1/messaging/providers/fcm') ]) ->param('providerId', '', new CustomId(), 'Provider ID. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.') ->param('name', '', new Text(128), 'Provider name.') - ->param('serviceAccountJSON', null, new JSON(), 'FCM service account JSON.', true) - ->param('enabled', null, new Boolean(), 'Set as enabled.', true) + ->param('serviceAccountJSON', null, new Nullable(new JSON()), 'FCM service account JSON.', true) + ->param('enabled', null, new Nullable(new Boolean()), 'Set as enabled.', true) ->inject('queueForEvents') ->inject('dbForProject') ->inject('response') @@ -982,7 +983,7 @@ App::post('/v1/messaging/providers/apns') ->param('teamId', '', new Text(0), 'APNS team ID.', true) ->param('bundleId', '', new Text(0), 'APNS bundle ID.', true) ->param('sandbox', false, new Boolean(), 'Use APNS sandbox environment.', true) - ->param('enabled', null, new Boolean(), 'Set as enabled.', true) + ->param('enabled', null, new Nullable(new Boolean()), 'Set as enabled.', true) ->inject('queueForEvents') ->inject('dbForProject') ->inject('response') @@ -1270,8 +1271,8 @@ App::patch('/v1/messaging/providers/mailgun/:providerId') ->param('name', '', new Text(128), 'Provider name.', true) ->param('apiKey', '', new Text(0), 'Mailgun API Key.', true) ->param('domain', '', new Text(0), 'Mailgun Domain.', true) - ->param('isEuRegion', null, new Boolean(), 'Set as EU region.', true) - ->param('enabled', null, new Boolean(), 'Set as enabled.', true) + ->param('isEuRegion', null, new Nullable(new Boolean()), 'Set as EU region.', true) + ->param('enabled', null, new Nullable(new Boolean()), 'Set as enabled.', true) ->param('fromName', '', new Text(128), 'Sender Name.', true) ->param('fromEmail', '', new Email(), 'Sender email address.', true) ->param('replyToName', '', new Text(128), 'Name set in the reply to field for the mail. Default value is sender name.', true) @@ -1381,7 +1382,7 @@ App::patch('/v1/messaging/providers/sendgrid/:providerId') )) ->param('providerId', '', new UID(), 'Provider ID.') ->param('name', '', new Text(128), 'Provider name.', true) - ->param('enabled', null, new Boolean(), 'Set as enabled.', true) + ->param('enabled', null, new Nullable(new Boolean()), 'Set as enabled.', true) ->param('apiKey', '', new Text(0), 'Sendgrid API key.', true) ->param('fromName', '', new Text(128), 'Sender Name.', true) ->param('fromEmail', '', new Email(), 'Sender email address.', true) @@ -1479,7 +1480,7 @@ App::patch('/v1/messaging/providers/resend/:providerId') )) ->param('providerId', '', new UID(), 'Provider ID.') ->param('name', '', new Text(128), 'Provider name.', true) - ->param('enabled', null, new Boolean(), 'Set as enabled.', true) + ->param('enabled', null, new Nullable(new Boolean()), 'Set as enabled.', true) ->param('apiKey', '', new Text(0), 'Resend API key.', true) ->param('fromName', '', new Text(128), 'Sender Name.', true) ->param('fromEmail', '', new Email(), 'Sender email address.', true) @@ -1597,17 +1598,17 @@ App::patch('/v1/messaging/providers/smtp/:providerId') ->param('providerId', '', new UID(), 'Provider ID.') ->param('name', '', new Text(128), 'Provider name.', true) ->param('host', '', new Text(0), 'SMTP hosts. Either a single hostname or multiple semicolon-delimited hostnames. You can also specify a different port for each host such as `smtp1.example.com:25;smtp2.example.com`. You can also specify encryption type, for example: `tls://smtp1.example.com:587;ssl://smtp2.example.com:465"`. Hosts will be tried in order.', true) - ->param('port', null, new Range(1, 65535), 'SMTP port.', true) + ->param('port', null, new Nullable(new Range(1, 65535)), 'SMTP port.', true) ->param('username', '', new Text(0), 'Authentication username.', true) ->param('password', '', new Text(0), 'Authentication password.', true) ->param('encryption', '', new WhiteList(['none', 'ssl', 'tls']), 'Encryption type. Can be \'ssl\' or \'tls\'', true) - ->param('autoTLS', null, new Boolean(), 'Enable SMTP AutoTLS feature.', true) + ->param('autoTLS', null, new Nullable(new Boolean()), 'Enable SMTP AutoTLS feature.', true) ->param('mailer', '', new Text(0), 'The value to use for the X-Mailer header.', true) ->param('fromName', '', new Text(128), 'Sender Name.', true) ->param('fromEmail', '', new Email(), 'Sender email address.', true) ->param('replyToName', '', new Text(128), 'Name set in the Reply To field for the mail. Default value is Sender Name.', true) ->param('replyToEmail', '', new Text(128), 'Email set in the Reply To field for the mail. Default value is Sender Email.', true) - ->param('enabled', null, new Boolean(), 'Set as enabled.', true) + ->param('enabled', null, new Nullable(new Boolean()), 'Set as enabled.', true) ->inject('queueForEvents') ->inject('dbForProject') ->inject('response') @@ -1725,7 +1726,7 @@ App::patch('/v1/messaging/providers/msg91/:providerId') )) ->param('providerId', '', new UID(), 'Provider ID.') ->param('name', '', new Text(128), 'Provider name.', true) - ->param('enabled', null, new Boolean(), 'Set as enabled.', true) + ->param('enabled', null, new Nullable(new Boolean()), 'Set as enabled.', true) ->param('templateId', '', new Text(0), 'Msg91 template ID.', true) ->param('senderId', '', new Text(0), 'Msg91 sender ID.', true) ->param('authKey', '', new Text(0), 'Msg91 auth key.', true) @@ -1812,7 +1813,7 @@ App::patch('/v1/messaging/providers/telesign/:providerId') )) ->param('providerId', '', new UID(), 'Provider ID.') ->param('name', '', new Text(128), 'Provider name.', true) - ->param('enabled', null, new Boolean(), 'Set as enabled.', true) + ->param('enabled', null, new Nullable(new Boolean()), 'Set as enabled.', true) ->param('customerId', '', new Text(0), 'Telesign customer ID.', true) ->param('apiKey', '', new Text(0), 'Telesign API key.', true) ->param('from', '', new Text(256), 'Sender number.', true) @@ -1901,7 +1902,7 @@ App::patch('/v1/messaging/providers/textmagic/:providerId') )) ->param('providerId', '', new UID(), 'Provider ID.') ->param('name', '', new Text(128), 'Provider name.', true) - ->param('enabled', null, new Boolean(), 'Set as enabled.', true) + ->param('enabled', null, new Nullable(new Boolean()), 'Set as enabled.', true) ->param('username', '', new Text(0), 'Textmagic username.', true) ->param('apiKey', '', new Text(0), 'Textmagic apiKey.', true) ->param('from', '', new Text(256), 'Sender number.', true) @@ -1990,7 +1991,7 @@ App::patch('/v1/messaging/providers/twilio/:providerId') )) ->param('providerId', '', new UID(), 'Provider ID.') ->param('name', '', new Text(128), 'Provider name.', true) - ->param('enabled', null, new Boolean(), 'Set as enabled.', true) + ->param('enabled', null, new Nullable(new Boolean()), 'Set as enabled.', true) ->param('accountSid', '', new Text(0), 'Twilio account secret ID.', true) ->param('authToken', '', new Text(0), 'Twilio authentication token.', true) ->param('from', '', new Text(256), 'Sender number.', true) @@ -2079,7 +2080,7 @@ App::patch('/v1/messaging/providers/vonage/:providerId') )) ->param('providerId', '', new UID(), 'Provider ID.') ->param('name', '', new Text(128), 'Provider name.', true) - ->param('enabled', null, new Boolean(), 'Set as enabled.', true) + ->param('enabled', null, new Nullable(new Boolean()), 'Set as enabled.', true) ->param('apiKey', '', new Text(0), 'Vonage API key.', true) ->param('apiSecret', '', new Text(0), 'Vonage API secret.', true) ->param('from', '', new Text(256), 'Sender number.', true) @@ -2187,8 +2188,8 @@ App::patch('/v1/messaging/providers/fcm/:providerId') ]) ->param('providerId', '', new UID(), 'Provider ID.') ->param('name', '', new Text(128), 'Provider name.', true) - ->param('enabled', null, new Boolean(), 'Set as enabled.', true) - ->param('serviceAccountJSON', null, new JSON(), 'FCM service account JSON.', true) + ->param('enabled', null, new Nullable(new Boolean()), 'Set as enabled.', true) + ->param('serviceAccountJSON', null, new Nullable(new JSON()), 'FCM service account JSON.', true) ->inject('queueForEvents') ->inject('dbForProject') ->inject('response') @@ -2282,12 +2283,12 @@ App::patch('/v1/messaging/providers/apns/:providerId') ]) ->param('providerId', '', new UID(), 'Provider ID.') ->param('name', '', new Text(128), 'Provider name.', true) - ->param('enabled', null, new Boolean(), 'Set as enabled.', true) + ->param('enabled', null, new Nullable(new Boolean()), 'Set as enabled.', true) ->param('authKey', '', new Text(0), 'APNS authentication key.', true) ->param('authKeyId', '', new Text(0), 'APNS authentication key ID.', true) ->param('teamId', '', new Text(0), 'APNS team ID.', true) ->param('bundleId', '', new Text(0), 'APNS bundle ID.', true) - ->param('sandbox', null, new Boolean(), 'Use APNS sandbox environment.', true) + ->param('sandbox', null, new Nullable(new Boolean()), 'Use APNS sandbox environment.', true) ->inject('queueForEvents') ->inject('dbForProject') ->inject('response') @@ -2676,8 +2677,8 @@ App::patch('/v1/messaging/topics/:topicId') ] )) ->param('topicId', '', new UID(), 'Topic ID.') - ->param('name', null, new Text(128), 'Topic Name.', true) - ->param('subscribe', null, new Roles(APP_LIMIT_ARRAY_PARAMS_SIZE), 'An array of role strings with subscribe permission. By default all users are granted with any subscribe permission. [learn more about roles](https://appwrite.io/docs/permissions#permission-roles). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' roles are allowed, each 64 characters long.', true) + ->param('name', null, new Nullable(new Text(128)), 'Topic Name.', true) + ->param('subscribe', null, new Nullable(new Roles(APP_LIMIT_ARRAY_PARAMS_SIZE)), 'An array of role strings with subscribe permission. By default all users are granted with any subscribe permission. [learn more about roles](https://appwrite.io/docs/permissions#permission-roles). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' roles are allowed, each 64 characters long.', true) ->inject('queueForEvents') ->inject('dbForProject') ->inject('response') @@ -3190,7 +3191,7 @@ App::post('/v1/messaging/messages/email') ->param('attachments', [], new ArrayList(new CompoundUID()), 'Array of compound ID strings of bucket IDs and file IDs to be attached to the email. They should be formatted as <BUCKET_ID>:<FILE_ID>.', true) ->param('draft', false, new Boolean(), 'Is message a draft', true) ->param('html', false, new Boolean(), 'Is content of type HTML', true) - ->param('scheduledAt', null, new DatetimeValidator(requireDateInFuture: true), 'Scheduled delivery time for message in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. DateTime value must be in future.', true) + ->param('scheduledAt', null, new Nullable(new DatetimeValidator(requireDateInFuture: true)), 'Scheduled delivery time for message in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. DateTime value must be in future.', true) ->inject('queueForEvents') ->inject('dbForProject') ->inject('dbForPlatform') @@ -3363,7 +3364,7 @@ App::post('/v1/messaging/messages/sms') ->param('users', [], new ArrayList(new UID()), 'List of User IDs.', true) ->param('targets', [], new ArrayList(new UID()), 'List of Targets IDs.', true) ->param('draft', false, new Boolean(), 'Is message a draft', true) - ->param('scheduledAt', null, new DatetimeValidator(requireDateInFuture: true), 'Scheduled delivery time for message in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. DateTime value must be in future.', true) + ->param('scheduledAt', null, new Nullable(new DatetimeValidator(requireDateInFuture: true)), 'Scheduled delivery time for message in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. DateTime value must be in future.', true) ->inject('queueForEvents') ->inject('dbForProject') ->inject('dbForPlatform') @@ -3486,7 +3487,7 @@ App::post('/v1/messaging/messages/push') ->param('topics', [], new ArrayList(new UID()), 'List of Topic IDs.', true) ->param('users', [], new ArrayList(new UID()), 'List of User IDs.', true) ->param('targets', [], new ArrayList(new UID()), 'List of Targets IDs.', true) - ->param('data', null, new JSON(), 'Additional key-value pair data for push notification.', true) + ->param('data', null, new Nullable(new JSON()), 'Additional key-value pair data for push notification.', true) ->param('action', '', new Text(256), 'Action for push notification.', true) ->param('image', '', new CompoundUID(), 'Image for push notification. Must be a compound bucket ID to file ID of a jpeg, png, or bmp image in Appwrite Storage. It should be formatted as <BUCKET_ID>:<FILE_ID>.', true) ->param('icon', '', new Text(256), 'Icon for push notification. Available only for Android and Web Platform.', true) @@ -3495,7 +3496,7 @@ App::post('/v1/messaging/messages/push') ->param('tag', '', new Text(256), 'Tag for push notification. Available only for Android Platform.', true) ->param('badge', -1, new Integer(), 'Badge for push notification. Available only for iOS Platform.', true) ->param('draft', false, new Boolean(), 'Is message a draft', true) - ->param('scheduledAt', null, new DatetimeValidator(requireDateInFuture: true), 'Scheduled delivery time for message in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. DateTime value must be in future.', true) + ->param('scheduledAt', null, new Nullable(new DatetimeValidator(requireDateInFuture: true)), 'Scheduled delivery time for message in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. DateTime value must be in future.', true) ->param('contentAvailable', false, new Boolean(), 'If set to true, the notification will be delivered in the background. Available only for iOS Platform.', true) ->param('critical', false, new Boolean(), 'If set to true, the notification will be marked as critical. This requires the app to have the critical notification entitlement. Available only for iOS Platform.', true) ->param('priority', 'high', new WhiteList(['normal', 'high']), 'Set the notification priority. "normal" will consider device state and may not deliver notifications immediately. "high" will always attempt to immediately deliver the notification.', true) @@ -3981,17 +3982,17 @@ App::patch('/v1/messaging/messages/email/:messageId') ] )) ->param('messageId', '', new UID(), 'Message ID.') - ->param('topics', null, new ArrayList(new UID()), 'List of Topic IDs.', true) - ->param('users', null, new ArrayList(new UID()), 'List of User IDs.', true) - ->param('targets', null, new ArrayList(new UID()), 'List of Targets IDs.', true) - ->param('subject', null, new Text(998), 'Email Subject.', true) - ->param('content', null, new Text(64230), 'Email Content.', true) - ->param('draft', null, new Boolean(), 'Is message a draft', true) - ->param('html', null, new Boolean(), 'Is content of type HTML', true) - ->param('cc', null, new ArrayList(new UID()), 'Array of target IDs to be added as CC.', true) - ->param('bcc', null, new ArrayList(new UID()), 'Array of target IDs to be added as BCC.', true) - ->param('scheduledAt', null, new DatetimeValidator(requireDateInFuture: true), 'Scheduled delivery time for message in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. DateTime value must be in future.', true) - ->param('attachments', null, new ArrayList(new CompoundUID()), 'Array of compound ID strings of bucket IDs and file IDs to be attached to the email. They should be formatted as <BUCKET_ID>:<FILE_ID>.', true) + ->param('topics', null, new Nullable(new ArrayList(new UID())), 'List of Topic IDs.', true) + ->param('users', null, new Nullable(new ArrayList(new UID())), 'List of User IDs.', true) + ->param('targets', null, new Nullable(new ArrayList(new UID())), 'List of Targets IDs.', true) + ->param('subject', null, new Nullable(new Text(998)), 'Email Subject.', true) + ->param('content', null, new Nullable(new Text(64230)), 'Email Content.', true) + ->param('draft', null, new Nullable(new Boolean()), 'Is message a draft', true) + ->param('html', null, new Nullable(new Boolean()), 'Is content of type HTML', true) + ->param('cc', null, new Nullable(new ArrayList(new UID())), 'Array of target IDs to be added as CC.', true) + ->param('bcc', null, new Nullable(new ArrayList(new UID())), 'Array of target IDs to be added as BCC.', true) + ->param('scheduledAt', null, new Nullable(new DatetimeValidator(requireDateInFuture: true)), 'Scheduled delivery time for message in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. DateTime value must be in future.', true) + ->param('attachments', null, new Nullable(new ArrayList(new CompoundUID())), 'Array of compound ID strings of bucket IDs and file IDs to be attached to the email. They should be formatted as <BUCKET_ID>:<FILE_ID>.', true) ->inject('queueForEvents') ->inject('dbForProject') ->inject('dbForPlatform') @@ -4207,12 +4208,12 @@ App::patch('/v1/messaging/messages/sms/:messageId') ) ]) ->param('messageId', '', new UID(), 'Message ID.') - ->param('topics', null, new ArrayList(new UID()), 'List of Topic IDs.', true) - ->param('users', null, new ArrayList(new UID()), 'List of User IDs.', true) - ->param('targets', null, new ArrayList(new UID()), 'List of Targets IDs.', true) - ->param('content', null, new Text(64230), 'Email Content.', true) - ->param('draft', null, new Boolean(), 'Is message a draft', true) - ->param('scheduledAt', null, new DatetimeValidator(requireDateInFuture: true), 'Scheduled delivery time for message in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. DateTime value must be in future.', true) + ->param('topics', null, new Nullable(new ArrayList(new UID())), 'List of Topic IDs.', true) + ->param('users', null, new Nullable(new ArrayList(new UID())), 'List of User IDs.', true) + ->param('targets', null, new Nullable(new ArrayList(new UID())), 'List of Targets IDs.', true) + ->param('content', null, new Nullable(new Text(64230)), 'Email Content.', true) + ->param('draft', null, new Nullable(new Boolean()), 'Is message a draft', true) + ->param('scheduledAt', null, new Nullable(new DatetimeValidator(requireDateInFuture: true)), 'Scheduled delivery time for message in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. DateTime value must be in future.', true) ->inject('queueForEvents') ->inject('dbForProject') ->inject('dbForPlatform') @@ -4369,24 +4370,24 @@ App::patch('/v1/messaging/messages/push/:messageId') ] )) ->param('messageId', '', new UID(), 'Message ID.') - ->param('topics', null, new ArrayList(new UID()), 'List of Topic IDs.', true) - ->param('users', null, new ArrayList(new UID()), 'List of User IDs.', true) - ->param('targets', null, new ArrayList(new UID()), 'List of Targets IDs.', true) - ->param('title', null, new Text(256), 'Title for push notification.', true) - ->param('body', null, new Text(64230), 'Body for push notification.', true) - ->param('data', null, new JSON(), 'Additional Data for push notification.', true) - ->param('action', null, new Text(256), 'Action for push notification.', true) - ->param('image', null, new CompoundUID(), 'Image for push notification. Must be a compound bucket ID to file ID of a jpeg, png, or bmp image in Appwrite Storage. It should be formatted as <BUCKET_ID>:<FILE_ID>.', true) - ->param('icon', null, new Text(256), 'Icon for push notification. Available only for Android and Web platforms.', true) - ->param('sound', null, new Text(256), 'Sound for push notification. Available only for Android and iOS platforms.', true) - ->param('color', null, new Text(256), 'Color for push notification. Available only for Android platforms.', true) - ->param('tag', null, new Text(256), 'Tag for push notification. Available only for Android platforms.', true) - ->param('badge', null, new Integer(), 'Badge for push notification. Available only for iOS platforms.', true) - ->param('draft', null, new Boolean(), 'Is message a draft', true) - ->param('scheduledAt', null, new DatetimeValidator(requireDateInFuture: true), 'Scheduled delivery time for message in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. DateTime value must be in future.', true) - ->param('contentAvailable', null, new Boolean(), 'If set to true, the notification will be delivered in the background. Available only for iOS Platform.', true) - ->param('critical', null, new Boolean(), 'If set to true, the notification will be marked as critical. This requires the app to have the critical notification entitlement. Available only for iOS Platform.', true) - ->param('priority', null, new WhiteList(['normal', 'high']), 'Set the notification priority. "normal" will consider device battery state and may send notifications later. "high" will always attempt to immediately deliver the notification.', true) + ->param('topics', null, new Nullable(new ArrayList(new UID())), 'List of Topic IDs.', true) + ->param('users', null, new Nullable(new ArrayList(new UID())), 'List of User IDs.', true) + ->param('targets', null, new Nullable(new ArrayList(new UID())), 'List of Targets IDs.', true) + ->param('title', null, new Nullable(new Text(256)), 'Title for push notification.', true) + ->param('body', null, new Nullable(new Text(64230)), 'Body for push notification.', true) + ->param('data', null, new Nullable(new JSON()), 'Additional Data for push notification.', true) + ->param('action', null, new Nullable(new Text(256)), 'Action for push notification.', true) + ->param('image', null, new Nullable(new CompoundUID()), 'Image for push notification. Must be a compound bucket ID to file ID of a jpeg, png, or bmp image in Appwrite Storage. It should be formatted as <BUCKET_ID>:<FILE_ID>.', true) + ->param('icon', null, new Nullable(new Text(256)), 'Icon for push notification. Available only for Android and Web platforms.', true) + ->param('sound', null, new Nullable(new Text(256)), 'Sound for push notification. Available only for Android and iOS platforms.', true) + ->param('color', null, new Nullable(new Text(256)), 'Color for push notification. Available only for Android platforms.', true) + ->param('tag', null, new Nullable(new Text(256)), 'Tag for push notification. Available only for Android platforms.', true) + ->param('badge', null, new Nullable(new Integer()), 'Badge for push notification. Available only for iOS platforms.', true) + ->param('draft', null, new Nullable(new Boolean()), 'Is message a draft', true) + ->param('scheduledAt', null, new Nullable(new DatetimeValidator(requireDateInFuture: true)), 'Scheduled delivery time for message in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. DateTime value must be in future.', true) + ->param('contentAvailable', null, new Nullable(new Boolean()), 'If set to true, the notification will be delivered in the background. Available only for iOS Platform.', true) + ->param('critical', null, new Nullable(new Boolean()), 'If set to true, the notification will be marked as critical. This requires the app to have the critical notification entitlement. Available only for iOS Platform.', true) + ->param('priority', null, new Nullable(new WhiteList(['normal', 'high'])), 'Set the notification priority. "normal" will consider device battery state and may send notifications later. "high" will always attempt to immediately deliver the notification.', true) ->inject('queueForEvents') ->inject('dbForProject') ->inject('dbForPlatform') diff --git a/app/controllers/api/migrations.php b/app/controllers/api/migrations.php index 1d1e6e999c..41b98ab333 100644 --- a/app/controllers/api/migrations.php +++ b/app/controllers/api/migrations.php @@ -468,7 +468,6 @@ App::post('/v1/migrations/csv/exports') ] )) ->param('resourceId', null, new CompoundUID(), 'Composite ID in the format {databaseId:collectionId}, identifying a collection within a database to export.') - ->param('bucketId', '', new UID(), 'Storage bucket unique ID where the exported CSV will be stored.') ->param('filename', '', new Text(255), 'The name of the file to be created for the export, excluding the .csv extension.') ->param('columns', [], new ArrayList(new Text(Database::LENGTH_KEY)), 'List of attributes to export. If empty, all attributes will be exported. You can use the `*` wildcard to export all attributes from the collection.', true) ->param('queries', [], new ArrayList(new Text(0)), 'Array of query strings generated using the Query class provided by the SDK to filter documents to export. [Learn more about queries](https://appwrite.io/docs/databases#querying-documents). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long.', true) @@ -480,12 +479,12 @@ App::post('/v1/migrations/csv/exports') ->inject('user') ->inject('response') ->inject('dbForProject') + ->inject('dbForPlatform') ->inject('project') ->inject('queueForEvents') ->inject('queueForMigrations') ->action(function ( string $resourceId, - string $bucketId, string $filename, array $columns, array $queries, @@ -497,6 +496,7 @@ App::post('/v1/migrations/csv/exports') Document $user, Response $response, Database $dbForProject, + Database $dbForPlatform, Document $project, Event $queueForEvents, Migration $queueForMigrations @@ -507,7 +507,7 @@ App::post('/v1/migrations/csv/exports') throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage()); } - $bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); + $bucket = Authorization::skip(fn () => $dbForPlatform->getDocument('buckets', 'default')); if ($bucket->isEmpty()) { throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); } @@ -553,7 +553,7 @@ App::post('/v1/migrations/csv/exports') 'resourceData' => '{}', 'errors' => [], 'options' => [ - 'bucketId' => $bucketId, + 'bucketId' => 'default', // Always use internal bucket 'filename' => $filename, 'columns' => $columns, 'queries' => $queries, diff --git a/app/controllers/api/project.php b/app/controllers/api/project.php index 390e88637a..a57675d3e8 100644 --- a/app/controllers/api/project.php +++ b/app/controllers/api/project.php @@ -18,6 +18,7 @@ use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\Datetime as DateTimeValidator; use Utopia\Database\Validator\UID; use Utopia\Validator\Boolean; +use Utopia\Validator\Nullable; use Utopia\Validator\Text; use Utopia\Validator\WhiteList; @@ -526,8 +527,8 @@ App::put('/v1/project/variables/:variableId') )) ->param('variableId', '', new UID(), 'Variable unique ID.', false) ->param('key', null, new Text(255), 'Variable key. Max length: 255 chars.', false) - ->param('value', null, new Text(8192, 0), 'Variable value. Max length: 8192 chars.', true) - ->param('secret', null, new Boolean(), 'Secret variables can be updated or deleted, but only projects can read them during build and runtime.', true) + ->param('value', null, new Nullable(new Text(8192, 0)), 'Variable value. Max length: 8192 chars.', true) + ->param('secret', null, new Nullable(new Boolean()), 'Secret variables can be updated or deleted, but only projects can read them during build and runtime.', true) ->inject('project') ->inject('response') ->inject('dbForProject') diff --git a/app/controllers/api/projects.php b/app/controllers/api/projects.php index 2b30b1cd08..760a4f23a6 100644 --- a/app/controllers/api/projects.php +++ b/app/controllers/api/projects.php @@ -46,6 +46,7 @@ use Utopia\Validator\Boolean; use Utopia\Validator\Hostname; use Utopia\Validator\Integer; use Utopia\Validator\Multiple; +use Utopia\Validator\Nullable; use Utopia\Validator\Range; use Utopia\Validator\Text; use Utopia\Validator\URL; @@ -678,9 +679,9 @@ App::patch('/v1/projects/:projectId/oauth2') )) ->param('projectId', '', new UID(), 'Project unique ID.') ->param('provider', '', new WhiteList(\array_keys(Config::getParam('oAuthProviders')), true), 'Provider Name') - ->param('appId', null, new Text(256), 'Provider app ID. Max length: 256 chars.', true) - ->param('secret', null, new text(512), 'Provider secret key. Max length: 512 chars.', true) - ->param('enabled', null, new Boolean(), 'Provider status. Set to \'false\' to disable new session creation.', true) + ->param('appId', null, new Nullable(new Text(256)), 'Provider app ID. Max length: 256 chars.', true) + ->param('secret', null, new Nullable(new text(512)), 'Provider secret key. Max length: 512 chars.', true) + ->param('enabled', null, new Nullable(new Boolean()), 'Provider status. Set to \'false\' to disable new session creation.', true) ->inject('response') ->inject('dbForPlatform') ->action(function (string $projectId, string $provider, ?string $appId, ?string $secret, ?bool $enabled, Response $response, Database $dbForPlatform) { @@ -1476,8 +1477,8 @@ App::post('/v1/projects/:projectId/keys') )) ->param('projectId', '', new UID(), 'Project unique ID.') ->param('name', null, new Text(128), 'Key name. Max length: 128 chars.') - ->param('scopes', null, new ArrayList(new WhiteList(array_keys(Config::getParam('scopes')), true), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Key scopes list. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' scopes are allowed.') - ->param('expire', null, new DatetimeValidator(), 'Expiration time in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. Use null for unlimited expiration.', true) + ->param('scopes', null, new Nullable(new ArrayList(new WhiteList(array_keys(Config::getParam('scopes')), true), APP_LIMIT_ARRAY_PARAMS_SIZE)), 'Key scopes list. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' scopes are allowed.') + ->param('expire', null, new Nullable(new DatetimeValidator()), 'Expiration time in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. Use null for unlimited expiration.', true) ->inject('response') ->inject('dbForPlatform') ->action(function (string $projectId, string $name, array $scopes, ?string $expire, Response $response, Database $dbForPlatform) { @@ -1615,8 +1616,8 @@ App::put('/v1/projects/:projectId/keys/:keyId') ->param('projectId', '', new UID(), 'Project unique ID.') ->param('keyId', '', new UID(), 'Key unique ID.') ->param('name', null, new Text(128), 'Key name. Max length: 128 chars.') - ->param('scopes', null, new ArrayList(new WhiteList(array_keys(Config::getParam('scopes')), true), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Key scopes list. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' events are allowed.') - ->param('expire', null, new DatetimeValidator(), 'Expiration time in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. Use null for unlimited expiration.', true) + ->param('scopes', null, new Nullable(new ArrayList(new WhiteList(array_keys(Config::getParam('scopes')), true), APP_LIMIT_ARRAY_PARAMS_SIZE)), 'Key scopes list. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' events are allowed.') + ->param('expire', null, new Nullable(new DatetimeValidator()), 'Expiration time in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. Use null for unlimited expiration.', true) ->inject('response') ->inject('dbForPlatform') ->action(function (string $projectId, string $keyId, string $name, array $scopes, ?string $expire, Response $response, Database $dbForPlatform) { diff --git a/app/controllers/api/storage.php b/app/controllers/api/storage.php index 72acd7b4a2..13234513c0 100644 --- a/app/controllers/api/storage.php +++ b/app/controllers/api/storage.php @@ -50,6 +50,7 @@ use Utopia\System\System; use Utopia\Validator\ArrayList; use Utopia\Validator\Boolean; use Utopia\Validator\HexColor; +use Utopia\Validator\Nullable; use Utopia\Validator\Range; use Utopia\Validator\Text; use Utopia\Validator\WhiteList; @@ -77,7 +78,7 @@ App::post('/v1/storage/buckets') )) ->param('bucketId', '', new CustomId(), 'Unique Id. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.') ->param('name', '', new Text(128), 'Bucket name') - ->param('permissions', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE), 'An array of permission strings. By default, no user is granted with any permissions. [Learn more about permissions](https://appwrite.io/docs/permissions).', true) + ->param('permissions', null, new Nullable(new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE)), 'An array of permission strings. By default, no user is granted with any permissions. [Learn more about permissions](https://appwrite.io/docs/permissions).', true) ->param('fileSecurity', false, new Boolean(true), 'Enables configuring permissions for individual file. A user needs one of file or bucket level permissions to access a file. [Learn more about permissions](https://appwrite.io/docs/permissions).', true) ->param('enabled', true, new Boolean(true), 'Is bucket enabled? When set to \'disabled\', users cannot access the files in this bucket but Server SDKs with and API key can still access the bucket. No files are lost when this is toggled.', true) ->param('maximumFileSize', fn (array $plan) => empty($plan['fileSize']) ? (int) System::getEnv('_APP_STORAGE_LIMIT', 0) : $plan['fileSize'] * 1000 * 1000, fn (array $plan) => new Range(1, empty($plan['fileSize']) ? (int) System::getEnv('_APP_STORAGE_LIMIT', 0) : $plan['fileSize'] * 1000 * 1000), 'Maximum file size allowed in bytes. Maximum allowed value is ' . Storage::human(System::getEnv('_APP_STORAGE_LIMIT', 0), 0) . '.', true, ['plan']) @@ -290,7 +291,7 @@ App::put('/v1/storage/buckets/:bucketId') )) ->param('bucketId', '', new UID(), 'Bucket unique ID.') ->param('name', null, new Text(128), 'Bucket name', false) - ->param('permissions', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE), 'An array of permission strings. By default, the current permissions are inherited. [Learn more about permissions](https://appwrite.io/docs/permissions).', true) + ->param('permissions', null, new Nullable(new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE)), 'An array of permission strings. By default, the current permissions are inherited. [Learn more about permissions](https://appwrite.io/docs/permissions).', true) ->param('fileSecurity', false, new Boolean(true), 'Enables configuring permissions for individual file. A user needs one of file or bucket level permissions to access a file. [Learn more about permissions](https://appwrite.io/docs/permissions).', true) ->param('enabled', true, new Boolean(true), 'Is bucket enabled? When set to \'disabled\', users cannot access the files in this bucket but Server SDKs with and API key can still access the bucket. No files are lost when this is toggled.', true) ->param('maximumFileSize', fn (array $plan) => empty($plan['fileSize']) ? (int) System::getEnv('_APP_STORAGE_LIMIT', 0) : $plan['fileSize'] * 1000 * 1000, fn (array $plan) => new Range(1, empty($plan['fileSize']) ? (int) System::getEnv('_APP_STORAGE_LIMIT', 0) : $plan['fileSize'] * 1000 * 1000), 'Maximum file size allowed in bytes. Maximum allowed value is ' . Storage::human(System::getEnv('_APP_STORAGE_LIMIT', 0), 0) . '.', true, ['plan']) @@ -418,7 +419,7 @@ App::post('/v1/storage/buckets/:bucketId/files') ->param('bucketId', '', new UID(), 'Storage bucket unique ID. You can create a new storage bucket using the Storage service [server integration](https://appwrite.io/docs/server/storage#createBucket).') ->param('fileId', '', new CustomId(), 'File ID. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.') ->param('file', [], new File(), 'Binary file. Appwrite SDKs provide helpers to handle file input. [Learn about file input](https://appwrite.io/docs/products/storage/upload-download#input-file).', skipValidation: true) - ->param('permissions', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE, [Database::PERMISSION_READ, Database::PERMISSION_UPDATE, Database::PERMISSION_DELETE, Database::PERMISSION_WRITE]), 'An array of permission strings. By default, only the current user is granted all permissions. [Learn more about permissions](https://appwrite.io/docs/permissions).', true) + ->param('permissions', null, new Nullable(new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE, [Database::PERMISSION_READ, Database::PERMISSION_UPDATE, Database::PERMISSION_DELETE, Database::PERMISSION_WRITE])), 'An array of permission strings. By default, only the current user is granted all permissions. [Learn more about permissions](https://appwrite.io/docs/permissions).', true) ->inject('request') ->inject('response') ->inject('dbForProject') @@ -1477,12 +1478,11 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/push') ->inject('response') ->inject('request') ->inject('dbForProject') + ->inject('dbForPlatform') ->inject('project') ->inject('mode') ->inject('deviceForFiles') - ->action(function (string $bucketId, string $fileId, string $jwt, Response $response, Request $request, Database $dbForProject, Document $project, string $mode, Device $deviceForFiles) { - $bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); - + ->action(function (string $bucketId, string $fileId, string $jwt, Response $response, Request $request, Database $dbForProject, Database $dbForPlatform, Document $project, string $mode, Device $deviceForFiles) { $decoder = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', 3600, 0); try { @@ -1499,15 +1499,18 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/push') throw new Exception(Exception::USER_UNAUTHORIZED); } + $isInternal = $decoded['internal'] ?? false; + $dbForProject = $isInternal ? $dbForPlatform : $dbForProject; + $isAPIKey = Auth::isAppUser(Authorization::getRoles()); $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); + $bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) { throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); } $file = Authorization::skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId)); - if ($file->isEmpty()) { throw new Exception(Exception::STORAGE_FILE_NOT_FOUND); } @@ -1645,8 +1648,8 @@ App::put('/v1/storage/buckets/:bucketId/files/:fileId') )) ->param('bucketId', '', new UID(), 'Storage bucket unique ID. You can create a new storage bucket using the Storage service [server integration](https://appwrite.io/docs/server/storage#createBucket).') ->param('fileId', '', new UID(), 'File unique ID.') - ->param('name', null, new Text(255), 'Name of the file', true) - ->param('permissions', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE, [Database::PERMISSION_READ, Database::PERMISSION_UPDATE, Database::PERMISSION_DELETE, Database::PERMISSION_WRITE]), 'An array of permission string. By default, the current permissions are inherited. [Learn more about permissions](https://appwrite.io/docs/permissions).', true) + ->param('name', null, new Nullable(new Text(255)), 'Name of the file', true) + ->param('permissions', null, new Nullable(new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE, [Database::PERMISSION_READ, Database::PERMISSION_UPDATE, Database::PERMISSION_DELETE, Database::PERMISSION_WRITE])), 'An array of permission string. By default, the current permissions are inherited. [Learn more about permissions](https://appwrite.io/docs/permissions).', true) ->inject('response') ->inject('dbForProject') ->inject('user') diff --git a/app/controllers/api/teams.php b/app/controllers/api/teams.php index 9fb5db0c5b..554ef6f4fe 100644 --- a/app/controllers/api/teams.php +++ b/app/controllers/api/teams.php @@ -10,7 +10,7 @@ use Appwrite\Event\Mail; use Appwrite\Event\Messaging; use Appwrite\Event\StatsUsage; use Appwrite\Extend\Exception; -use Appwrite\Network\Validator\Email; +use Appwrite\Network\Validator\Email as EmailValidator; use Appwrite\Network\Validator\Redirect; use Appwrite\Platform\Workers\Deletes; use Appwrite\SDK\AuthType; @@ -48,6 +48,7 @@ use Utopia\Database\Validator\Query\Cursor; use Utopia\Database\Validator\Query\Limit; use Utopia\Database\Validator\Query\Offset; use Utopia\Database\Validator\UID; +use Utopia\Emails\Email; use Utopia\Locale\Locale; use Utopia\System\System; use Utopia\Validator\ArrayList; @@ -468,7 +469,7 @@ App::post('/v1/teams/:teamId/memberships') )) ->label('abuse-limit', 10) ->param('teamId', '', new UID(), 'Team ID.') - ->param('email', '', new Email(), 'Email of the new team member.', true) + ->param('email', '', new EmailValidator(), 'Email of the new team member.', true) ->param('userId', '', new UID(), 'ID of the user to be added to a team.', true) ->param('phone', '', new Phone(), 'Phone number. Format this number with a leading \'+\' and a country code, e.g., +16175551212.', true) ->param('roles', [], function (Document $project) { @@ -567,38 +568,52 @@ App::post('/v1/teams/:teamId/memberships') } try { - $userId = ID::unique(); - $invitee = Authorization::skip(fn () => $dbForProject->createDocument('users', new Document([ - '$id' => $userId, - '$permissions' => [ - Permission::read(Role::any()), - Permission::read(Role::user($userId)), - Permission::update(Role::user($userId)), - Permission::delete(Role::user($userId)), - ], - 'email' => empty($email) ? null : $email, - 'phone' => empty($phone) ? null : $phone, - 'emailVerification' => false, - 'status' => true, - // TODO: Set password empty? - 'password' => Auth::passwordHash(Auth::passwordGenerator(), Auth::DEFAULT_ALGO, Auth::DEFAULT_ALGO_OPTIONS), - 'hash' => Auth::DEFAULT_ALGO, - 'hashOptions' => Auth::DEFAULT_ALGO_OPTIONS, - /** - * Set the password update time to 0 for users created using - * team invite and OAuth to allow password updates without an - * old password - */ - 'passwordUpdate' => null, - 'registration' => DateTime::now(), - 'reset' => false, - 'name' => $name, - 'prefs' => new \stdClass(), - 'sessions' => null, - 'tokens' => null, - 'memberships' => null, - 'search' => implode(' ', [$userId, $email, $name]), - ]))); + $emailCanonical = new Email($email); + } catch (Throwable) { + $emailCanonical = null; + } + + $userId = ID::unique(); + + $userDocument = new Document([ + '$id' => $userId, + '$permissions' => [ + Permission::read(Role::any()), + Permission::read(Role::user($userId)), + Permission::update(Role::user($userId)), + Permission::delete(Role::user($userId)), + ], + 'email' => empty($email) ? null : $email, + 'phone' => empty($phone) ? null : $phone, + 'emailVerification' => false, + 'status' => true, + // TODO: Set password empty? + 'password' => Auth::passwordHash(Auth::passwordGenerator(), Auth::DEFAULT_ALGO, Auth::DEFAULT_ALGO_OPTIONS), + 'hash' => Auth::DEFAULT_ALGO, + 'hashOptions' => Auth::DEFAULT_ALGO_OPTIONS, + /** + * Set the password update time to 0 for users created using + * team invite and OAuth to allow password updates without an + * old password + */ + 'passwordUpdate' => null, + 'registration' => DateTime::now(), + 'reset' => false, + 'name' => $name, + 'prefs' => new \stdClass(), + 'sessions' => null, + 'tokens' => null, + 'memberships' => null, + 'search' => implode(' ', [$userId, $email, $name]), + 'emailCanonical' => $emailCanonical?->getCanonical(), + 'emailIsCanonical' => $emailCanonical?->isCanonicalSupported(), + 'emailIsCorporate' => $emailCanonical?->isCorporate(), + 'emailIsDisposable' => $emailCanonical?->isDisposable(), + 'emailIsFree' => $emailCanonical?->isFree(), + ]); + + try { + $invitee = Authorization::skip(fn () => $dbForProject->createDocument('users', $userDocument)); } catch (Duplicate $th) { throw new Exception(Exception::USER_ALREADY_EXISTS); } diff --git a/app/controllers/api/users.php b/app/controllers/api/users.php index 591a22705d..a8570c3079 100644 --- a/app/controllers/api/users.php +++ b/app/controllers/api/users.php @@ -16,7 +16,7 @@ use Appwrite\Event\Delete; use Appwrite\Event\Event; use Appwrite\Extend\Exception; use Appwrite\Hooks\Hooks; -use Appwrite\Network\Validator\Email; +use Appwrite\Network\Validator\Email as EmailValidator; use Appwrite\SDK\AuthType; use Appwrite\SDK\ContentType; use Appwrite\SDK\Deprecated; @@ -49,12 +49,14 @@ use Utopia\Database\Validator\Query\Cursor; use Utopia\Database\Validator\Query\Limit; use Utopia\Database\Validator\Query\Offset; use Utopia\Database\Validator\UID; +use Utopia\Emails\Email; use Utopia\Locale\Locale; use Utopia\System\System; use Utopia\Validator\ArrayList; use Utopia\Validator\Assoc; use Utopia\Validator\Boolean; use Utopia\Validator\Integer; +use Utopia\Validator\Nullable; use Utopia\Validator\Range; use Utopia\Validator\Text; use Utopia\Validator\WhiteList; @@ -97,6 +99,12 @@ function createUser(string $hash, mixed $hashOptions, string $userId, ?string $e } } + try { + $emailCanonical = new Email($email); + } catch (Throwable) { + $emailCanonical = null; + } + $password = (!empty($password)) ? ($hash === 'plaintext' ? Auth::passwordHash($password, $hash, $hashOptionsObject) : $password) : null; $user = new Document([ '$id' => $userId, @@ -124,6 +132,11 @@ function createUser(string $hash, mixed $hashOptions, string $userId, ?string $e 'tokens' => null, 'memberships' => null, 'search' => implode(' ', [$userId, $email, $phone, $name]), + 'emailCanonical' => $emailCanonical?->getCanonical(), + 'emailIsCanonical' => $emailCanonical?->isCanonicalSupported(), + 'emailIsCorporate' => $emailCanonical?->isCorporate(), + 'emailIsDisposable' => $emailCanonical?->isDisposable(), + 'emailIsFree' => $emailCanonical?->isFree(), ]); if ($hash === 'plaintext') { @@ -208,8 +221,8 @@ App::post('/v1/users') ] )) ->param('userId', '', new CustomId(), 'User ID. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.') - ->param('email', null, new Email(), 'User email.', true) - ->param('phone', null, new Phone(), 'Phone number. Format this number with a leading \'+\' and a country code, e.g., +16175551212.', true) + ->param('email', null, new Nullable(new EmailValidator()), 'User email.', true) + ->param('phone', null, new Nullable(new Phone()), 'Phone number. Format this number with a leading \'+\' and a country code, e.g., +16175551212.', true) ->param('password', '', fn ($project, $passwordsDictionary) => new PasswordDictionary($passwordsDictionary, $project->getAttribute('auths', [])['passwordDictionary'] ?? false), 'Plain text user password. Must be at least 8 chars.', true, ['project', 'passwordsDictionary']) ->param('name', '', new Text(128), 'User name. Max length: 128 chars.', true) ->inject('response') @@ -243,7 +256,7 @@ App::post('/v1/users/bcrypt') ] )) ->param('userId', '', new CustomId(), 'User ID. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.') - ->param('email', '', new Email(), 'User email.') + ->param('email', '', new EmailValidator(), 'User email.') ->param('password', '', new Password(), 'User password hashed using Bcrypt.') ->param('name', '', new Text(128), 'User name. Max length: 128 chars.', true) ->inject('response') @@ -278,7 +291,7 @@ App::post('/v1/users/md5') ] )) ->param('userId', '', new CustomId(), 'User ID. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.') - ->param('email', '', new Email(), 'User email.') + ->param('email', '', new EmailValidator(), 'User email.') ->param('password', '', new Password(), 'User password hashed using MD5.') ->param('name', '', new Text(128), 'User name. Max length: 128 chars.', true) ->inject('response') @@ -313,7 +326,7 @@ App::post('/v1/users/argon2') ] )) ->param('userId', '', new CustomId(), 'User ID. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.') - ->param('email', '', new Email(), 'User email.') + ->param('email', '', new EmailValidator(), 'User email.') ->param('password', '', new Password(), 'User password hashed using Argon2.') ->param('name', '', new Text(128), 'User name. Max length: 128 chars.', true) ->inject('response') @@ -348,7 +361,7 @@ App::post('/v1/users/sha') ] )) ->param('userId', '', new CustomId(), 'User ID. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.') - ->param('email', '', new Email(), 'User email.') + ->param('email', '', new EmailValidator(), 'User email.') ->param('password', '', new Password(), 'User password hashed using SHA.') ->param('passwordVersion', '', new WhiteList(['sha1', 'sha224', 'sha256', 'sha384', 'sha512/224', 'sha512/256', 'sha512', 'sha3-224', 'sha3-256', 'sha3-384', 'sha3-512']), "Optional SHA version used to hash password. Allowed values are: 'sha1', 'sha224', 'sha256', 'sha384', 'sha512/224', 'sha512/256', 'sha512', 'sha3-224', 'sha3-256', 'sha3-384', 'sha3-512'", true) ->param('name', '', new Text(128), 'User name. Max length: 128 chars.', true) @@ -390,7 +403,7 @@ App::post('/v1/users/phpass') ] )) ->param('userId', '', new CustomId(), 'User ID. Choose a custom ID or pass the string `ID.unique()`to auto generate it. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.') - ->param('email', '', new Email(), 'User email.') + ->param('email', '', new EmailValidator(), 'User email.') ->param('password', '', new Password(), 'User password hashed using PHPass.') ->param('name', '', new Text(128), 'User name. Max length: 128 chars.', true) ->inject('response') @@ -425,7 +438,7 @@ App::post('/v1/users/scrypt') ] )) ->param('userId', '', new CustomId(), 'User ID. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.') - ->param('email', '', new Email(), 'User email.') + ->param('email', '', new EmailValidator(), 'User email.') ->param('password', '', new Password(), 'User password hashed using Scrypt.') ->param('passwordSalt', '', new Text(128), 'Optional salt used to hash password.') ->param('passwordCpu', 8, new Integer(), 'Optional CPU cost used to hash password.') @@ -473,7 +486,7 @@ App::post('/v1/users/scrypt-modified') ] )) ->param('userId', '', new CustomId(), 'User ID. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.') - ->param('email', '', new Email(), 'User email.') + ->param('email', '', new EmailValidator(), 'User email.') ->param('password', '', new Password(), 'User password hashed using Scrypt Modified.') ->param('passwordSalt', '', new Text(128), 'Salt used to hash password.') ->param('passwordSaltSeparator', '', new Text(128), 'Salt separator used to hash password.') @@ -527,7 +540,7 @@ App::post('/v1/users/:userId/targets') switch ($providerType) { case 'email': - $validator = new Email(); + $validator = new EmailValidator(); if (!$validator->isValid($identifier)) { throw new Exception(Exception::GENERAL_INVALID_EMAIL); } @@ -1402,7 +1415,7 @@ App::patch('/v1/users/:userId/email') ] )) ->param('userId', '', new UID(), 'User ID.') - ->param('email', '', new Email(allowEmpty: true), 'User email.') + ->param('email', '', new EmailValidator(allowEmpty: true), 'User email.') ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') @@ -1437,9 +1450,20 @@ App::patch('/v1/users/:userId/email') $oldEmail = $user->getAttribute('email'); + try { + $emailCanonical = new Email($email); + } catch (Throwable) { + $emailCanonical = null; + } + $user ->setAttribute('email', $email) ->setAttribute('emailVerification', false) + ->setAttribute('emailCanonical', $emailCanonical?->getCanonical()) + ->setAttribute('emailIsCanonical', $emailCanonical?->isCanonicalSupported()) + ->setAttribute('emailIsCorporate', $emailCanonical?->isCorporate()) + ->setAttribute('emailIsDisposable', $emailCanonical?->isDisposable()) + ->setAttribute('emailIsFree', $emailCanonical?->isFree()) ; try { @@ -1700,7 +1724,7 @@ App::patch('/v1/users/:userId/targets/:targetId') switch ($providerType) { case 'email': - $validator = new Email(); + $validator = new EmailValidator(); if (!$validator->isValid($identifier)) { throw new Exception(Exception::GENERAL_INVALID_EMAIL); } diff --git a/app/controllers/general.php b/app/controllers/general.php index 07de95a38f..e0435cd499 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -23,6 +23,7 @@ use Appwrite\Utopia\Request\Filters\V17 as RequestV17; use Appwrite\Utopia\Request\Filters\V18 as RequestV18; use Appwrite\Utopia\Request\Filters\V19 as RequestV19; use Appwrite\Utopia\Request\Filters\V20 as RequestV20; +use Appwrite\Utopia\Request\Filters\V21 as RequestV21; use Appwrite\Utopia\Response; use Appwrite\Utopia\Response\Filters\V16 as ResponseV16; use Appwrite\Utopia\Response\Filters\V17 as ResponseV17; @@ -906,6 +907,9 @@ App::init() $dbForProject = $getProjectDB($project); $request->addFilter(new RequestV20($dbForProject, $route->getPathValues($request))); } + if (version_compare($requestFormat, '1.9.0', '<')) { + $request->addFilter(new RequestV21()); + } } $domain = $request->getHostname(); diff --git a/app/controllers/shared/api.php b/app/controllers/shared/api.php index 122139d48b..2d32c906bf 100644 --- a/app/controllers/shared/api.php +++ b/app/controllers/shared/api.php @@ -234,7 +234,9 @@ App::init() ->inject('apiKey') ->action(function (App $utopia, Request $request, Database $dbForPlatform, Database $dbForProject, Audit $queueForAudits, Document $project, Document $user, ?Document $session, array $servers, string $mode, Document $team, ?Key $apiKey) { $route = $utopia->getRoute(); - + if (System::getEnv('_APP_EDITION', 'self-hosted') === 'self-hosted' && str_starts_with($route->getPath(), '/v1/backups')) { + throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Database Backups are available on Appwrite Cloud'); + } if ($project->isEmpty()) { throw new Exception(Exception::PROJECT_NOT_FOUND); } diff --git a/app/init/constants.php b/app/init/constants.php index 3b81785690..e11fdf9a54 100644 --- a/app/init/constants.php +++ b/app/init/constants.php @@ -138,6 +138,7 @@ const DELETE_TYPE_TOPIC = 'topic'; const DELETE_TYPE_TARGET = 'target'; const DELETE_TYPE_EXPIRED_TARGETS = 'invalid_targets'; const DELETE_TYPE_SESSION_TARGETS = 'session_targets'; +const DELETE_TYPE_CSV_EXPORTS = 'csv_exports'; const DELETE_TYPE_MAINTENANCE = 'maintenance'; // Message types diff --git a/docs/examples/1.8.x/client-android/java/avatars/get-screenshot.md b/docs/examples/1.8.x/client-android/java/avatars/get-screenshot.md new file mode 100644 index 0000000000..077716f523 --- /dev/null +++ b/docs/examples/1.8.x/client-android/java/avatars/get-screenshot.md @@ -0,0 +1,41 @@ +import io.appwrite.Client; +import io.appwrite.coroutines.CoroutineCallback; +import io.appwrite.services.Avatars; + +Client client = new Client(context) + .setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint + .setProject("<YOUR_PROJECT_ID>"); // Your project ID + +Avatars avatars = new Avatars(client); + +avatars.getScreenshot( + "https://example.com", // url + mapOf( "a" to "b" ), // headers (optional) + 1, // viewportWidth (optional) + 1, // viewportHeight (optional) + 0.1, // scale (optional) + theme.LIGHT, // theme (optional) + "<USER_AGENT>", // userAgent (optional) + false, // fullpage (optional) + "<LOCALE>", // locale (optional) + timezone.AFRICA_ABIDJAN, // timezone (optional) + -90, // latitude (optional) + -180, // longitude (optional) + 0, // accuracy (optional) + false, // touch (optional) + listOf(), // permissions (optional) + 0, // sleep (optional) + 0, // width (optional) + 0, // height (optional) + -1, // quality (optional) + output.JPG, // output (optional) + new CoroutineCallback<>((result, error) -> { + if (error != null) { + error.printStackTrace(); + return; + } + + Log.d("Appwrite", result.toString()); + }) +); + diff --git a/docs/examples/1.8.x/client-android/kotlin/avatars/get-screenshot.md b/docs/examples/1.8.x/client-android/kotlin/avatars/get-screenshot.md new file mode 100644 index 0000000000..014ca90fd8 --- /dev/null +++ b/docs/examples/1.8.x/client-android/kotlin/avatars/get-screenshot.md @@ -0,0 +1,32 @@ +import io.appwrite.Client +import io.appwrite.coroutines.CoroutineCallback +import io.appwrite.services.Avatars + +val client = Client(context) + .setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint + .setProject("<YOUR_PROJECT_ID>") // Your project ID + +val avatars = Avatars(client) + +val result = avatars.getScreenshot( + url = "https://example.com", + headers = mapOf( "a" to "b" ), // (optional) + viewportWidth = 1, // (optional) + viewportHeight = 1, // (optional) + scale = 0.1, // (optional) + theme = theme.LIGHT, // (optional) + userAgent = "<USER_AGENT>", // (optional) + fullpage = false, // (optional) + locale = "<LOCALE>", // (optional) + timezone = timezone.AFRICA_ABIDJAN, // (optional) + latitude = -90, // (optional) + longitude = -180, // (optional) + accuracy = 0, // (optional) + touch = false, // (optional) + permissions = listOf(), // (optional) + sleep = 0, // (optional) + width = 0, // (optional) + height = 0, // (optional) + quality = -1, // (optional) + output = output.JPG, // (optional) +) \ No newline at end of file diff --git a/docs/examples/1.8.x/client-apple/examples/avatars/get-screenshot.md b/docs/examples/1.8.x/client-apple/examples/avatars/get-screenshot.md new file mode 100644 index 0000000000..7f4ef5da5c --- /dev/null +++ b/docs/examples/1.8.x/client-apple/examples/avatars/get-screenshot.md @@ -0,0 +1,32 @@ +import Appwrite +import AppwriteEnums + +let client = Client() + .setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint + .setProject("<YOUR_PROJECT_ID>") // Your project ID + +let avatars = Avatars(client) + +let bytes = try await avatars.getScreenshot( + url: "https://example.com", + headers: [:], // optional + viewportWidth: 1, // optional + viewportHeight: 1, // optional + scale: 0.1, // optional + theme: .light, // optional + userAgent: "<USER_AGENT>", // optional + fullpage: false, // optional + locale: "<LOCALE>", // optional + timezone: .africaAbidjan, // optional + latitude: -90, // optional + longitude: -180, // optional + accuracy: 0, // optional + touch: false, // optional + permissions: [], // optional + sleep: 0, // optional + width: 0, // optional + height: 0, // optional + quality: -1, // optional + output: .jpg // optional +) + diff --git a/docs/examples/1.8.x/client-flutter/examples/avatars/get-screenshot.md b/docs/examples/1.8.x/client-flutter/examples/avatars/get-screenshot.md new file mode 100644 index 0000000000..768cb8f271 --- /dev/null +++ b/docs/examples/1.8.x/client-flutter/examples/avatars/get-screenshot.md @@ -0,0 +1,65 @@ +import 'package:appwrite/appwrite.dart'; + +Client client = Client() + .setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint + .setProject('<YOUR_PROJECT_ID>'); // Your project ID + +Avatars avatars = Avatars(client); + +// Downloading file +UInt8List bytes = await avatars.getScreenshot( + url: 'https://example.com', + headers: {}, // optional + viewportWidth: 1, // optional + viewportHeight: 1, // optional + scale: 0.1, // optional + theme: .light, // optional + userAgent: '<USER_AGENT>', // optional + fullpage: false, // optional + locale: '<LOCALE>', // optional + timezone: .africaAbidjan, // optional + latitude: -90, // optional + longitude: -180, // optional + accuracy: 0, // optional + touch: false, // optional + permissions: [], // optional + sleep: 0, // optional + width: 0, // optional + height: 0, // optional + quality: -1, // optional + output: .jpg, // optional +) + +final file = File('path_to_file/filename.ext'); +file.writeAsBytesSync(bytes); + +// Displaying image preview +FutureBuilder( + future: avatars.getScreenshot( + url:'https://example.com' , + headers:{} , // optional + viewportWidth:1 , // optional + viewportHeight:1 , // optional + scale:0.1 , // optional + theme: .light, // optional + userAgent:'<USER_AGENT>' , // optional + fullpage:false , // optional + locale:'<LOCALE>' , // optional + timezone: .africaAbidjan, // optional + latitude:-90 , // optional + longitude:-180 , // optional + accuracy:0 , // optional + touch:false , // optional + permissions:[] , // optional + sleep:0 , // optional + width:0 , // optional + height:0 , // optional + quality:-1 , // optional + output: .jpg, // optional +), // Works for both public file and private file, for private files you need to be logged in + builder: (context, snapshot) { + return snapshot.hasData && snapshot.data != null + ? Image.memory(snapshot.data) + : CircularProgressIndicator(); + } +); diff --git a/docs/examples/1.8.x/client-graphql/examples/avatars/get-screenshot.md b/docs/examples/1.8.x/client-graphql/examples/avatars/get-screenshot.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/docs/examples/1.8.x/client-react-native/examples/avatars/get-screenshot.md b/docs/examples/1.8.x/client-react-native/examples/avatars/get-screenshot.md new file mode 100644 index 0000000000..7482b4cf0e --- /dev/null +++ b/docs/examples/1.8.x/client-react-native/examples/avatars/get-screenshot.md @@ -0,0 +1,32 @@ +import { Client, Avatars, , , } from "react-native-appwrite"; + +const client = new Client() + .setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint + .setProject('<YOUR_PROJECT_ID>'); // Your project ID + +const avatars = new Avatars(client); + +const result = avatars.getScreenshot({ + url: 'https://example.com', + headers: {}, // optional + viewportWidth: 1, // optional + viewportHeight: 1, // optional + scale: 0.1, // optional + theme: .Light, // optional + userAgent: '<USER_AGENT>', // optional + fullpage: false, // optional + locale: '<LOCALE>', // optional + timezone: .AfricaAbidjan, // optional + latitude: -90, // optional + longitude: -180, // optional + accuracy: 0, // optional + touch: false, // optional + permissions: [], // optional + sleep: 0, // optional + width: 0, // optional + height: 0, // optional + quality: -1, // optional + output: .Jpg // optional +}); + +console.log(result); diff --git a/docs/examples/1.8.x/client-rest/examples/avatars/get-screenshot.md b/docs/examples/1.8.x/client-rest/examples/avatars/get-screenshot.md new file mode 100644 index 0000000000..b4c31ca100 --- /dev/null +++ b/docs/examples/1.8.x/client-rest/examples/avatars/get-screenshot.md @@ -0,0 +1,6 @@ +GET /v1/avatars/screenshots HTTP/1.1 +Host: cloud.appwrite.io +X-Appwrite-Response-Format: 1.8.0 +X-Appwrite-Project: <YOUR_PROJECT_ID> +X-Appwrite-Session: +X-Appwrite-JWT: <YOUR_JWT> diff --git a/docs/examples/1.8.x/client-web/examples/avatars/get-screenshot.md b/docs/examples/1.8.x/client-web/examples/avatars/get-screenshot.md new file mode 100644 index 0000000000..c4722be633 --- /dev/null +++ b/docs/examples/1.8.x/client-web/examples/avatars/get-screenshot.md @@ -0,0 +1,32 @@ +import { Client, Avatars, , , } from "appwrite"; + +const client = new Client() + .setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint + .setProject('<YOUR_PROJECT_ID>'); // Your project ID + +const avatars = new Avatars(client); + +const result = avatars.getScreenshot({ + url: 'https://example.com', + headers: {}, // optional + viewportWidth: 1, // optional + viewportHeight: 1, // optional + scale: 0.1, // optional + theme: .Light, // optional + userAgent: '<USER_AGENT>', // optional + fullpage: false, // optional + locale: '<LOCALE>', // optional + timezone: .AfricaAbidjan, // optional + latitude: -90, // optional + longitude: -180, // optional + accuracy: 0, // optional + touch: false, // optional + permissions: [], // optional + sleep: 0, // optional + width: 0, // optional + height: 0, // optional + quality: -1, // optional + output: .Jpg // optional +}); + +console.log(result); diff --git a/docs/examples/1.8.x/console-cli/examples/migrations/create-csv-export.md b/docs/examples/1.8.x/console-cli/examples/migrations/create-csv-export.md index e56afae786..61eceabcd8 100644 --- a/docs/examples/1.8.x/console-cli/examples/migrations/create-csv-export.md +++ b/docs/examples/1.8.x/console-cli/examples/migrations/create-csv-export.md @@ -1,4 +1,3 @@ appwrite migrations create-csv-export \ --resource-id <ID1:ID2> \ - --bucket-id <BUCKET_ID> \ --filename <FILENAME> diff --git a/docs/examples/1.8.x/console-web/examples/avatars/get-screenshot.md b/docs/examples/1.8.x/console-web/examples/avatars/get-screenshot.md new file mode 100644 index 0000000000..3a9437515d --- /dev/null +++ b/docs/examples/1.8.x/console-web/examples/avatars/get-screenshot.md @@ -0,0 +1,32 @@ +import { Client, Avatars, , , } from "@appwrite.io/console"; + +const client = new Client() + .setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint + .setProject('<YOUR_PROJECT_ID>'); // Your project ID + +const avatars = new Avatars(client); + +const result = avatars.getScreenshot({ + url: 'https://example.com', + headers: {}, // optional + viewportWidth: 1, // optional + viewportHeight: 1, // optional + scale: 0.1, // optional + theme: .Light, // optional + userAgent: '<USER_AGENT>', // optional + fullpage: false, // optional + locale: '<LOCALE>', // optional + timezone: .AfricaAbidjan, // optional + latitude: -90, // optional + longitude: -180, // optional + accuracy: 0, // optional + touch: false, // optional + permissions: [], // optional + sleep: 0, // optional + width: 0, // optional + height: 0, // optional + quality: -1, // optional + output: .Jpg // optional +}); + +console.log(result); diff --git a/docs/examples/1.8.x/console-web/examples/migrations/create-csv-export.md b/docs/examples/1.8.x/console-web/examples/migrations/create-csv-export.md index e1b909a852..89f779fc4c 100644 --- a/docs/examples/1.8.x/console-web/examples/migrations/create-csv-export.md +++ b/docs/examples/1.8.x/console-web/examples/migrations/create-csv-export.md @@ -8,7 +8,6 @@ const migrations = new Migrations(client); const result = await migrations.createCSVExport({ resourceId: '<ID1:ID2>', - bucketId: '<BUCKET_ID>', filename: '<FILENAME>', columns: [], // optional queries: [], // optional diff --git a/docs/examples/1.8.x/server-dart/examples/avatars/get-screenshot.md b/docs/examples/1.8.x/server-dart/examples/avatars/get-screenshot.md new file mode 100644 index 0000000000..7630648f98 --- /dev/null +++ b/docs/examples/1.8.x/server-dart/examples/avatars/get-screenshot.md @@ -0,0 +1,31 @@ +import 'package:dart_appwrite/dart_appwrite.dart'; + +Client client = Client() + .setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint + .setProject('<YOUR_PROJECT_ID>') // Your project ID + .setSession(''); // The user session to authenticate with + +Avatars avatars = Avatars(client); + +UInt8List result = await avatars.getScreenshot( + url: 'https://example.com', + headers: {}, // (optional) + viewportWidth: 1, // (optional) + viewportHeight: 1, // (optional) + scale: 0.1, // (optional) + theme: .light, // (optional) + userAgent: '<USER_AGENT>', // (optional) + fullpage: false, // (optional) + locale: '<LOCALE>', // (optional) + timezone: .africaAbidjan, // (optional) + latitude: -90, // (optional) + longitude: -180, // (optional) + accuracy: 0, // (optional) + touch: false, // (optional) + permissions: [], // (optional) + sleep: 0, // (optional) + width: 0, // (optional) + height: 0, // (optional) + quality: -1, // (optional) + output: .jpg, // (optional) +); diff --git a/docs/examples/1.8.x/server-dotnet/examples/avatars/get-screenshot.md b/docs/examples/1.8.x/server-dotnet/examples/avatars/get-screenshot.md new file mode 100644 index 0000000000..f5c3542a97 --- /dev/null +++ b/docs/examples/1.8.x/server-dotnet/examples/avatars/get-screenshot.md @@ -0,0 +1,34 @@ +using Appwrite; +using Appwrite.Enums; +using Appwrite.Models; +using Appwrite.Services; + +Client client = new Client() + .SetEndPoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint + .SetProject("<YOUR_PROJECT_ID>") // Your project ID + .SetSession(""); // The user session to authenticate with + +Avatars avatars = new Avatars(client); + +byte[] result = await avatars.GetScreenshot( + url: "https://example.com", + headers: [object], // optional + viewportWidth: 1, // optional + viewportHeight: 1, // optional + scale: 0.1, // optional + theme: .Light, // optional + userAgent: "<USER_AGENT>", // optional + fullpage: false, // optional + locale: "<LOCALE>", // optional + timezone: .AfricaAbidjan, // optional + latitude: -90, // optional + longitude: -180, // optional + accuracy: 0, // optional + touch: false, // optional + permissions: new List<string>(), // optional + sleep: 0, // optional + width: 0, // optional + height: 0, // optional + quality: -1, // optional + output: .Jpg // optional +); \ No newline at end of file diff --git a/docs/examples/1.8.x/server-go/examples/avatars/get-screenshot.md b/docs/examples/1.8.x/server-go/examples/avatars/get-screenshot.md new file mode 100644 index 0000000000..ac425fbc4f --- /dev/null +++ b/docs/examples/1.8.x/server-go/examples/avatars/get-screenshot.md @@ -0,0 +1,38 @@ +package main + +import ( + "fmt" + "github.com/appwrite/sdk-for-go/client" + "github.com/appwrite/sdk-for-go/avatars" +) + +client := client.New( + client.WithEndpoint("https://<REGION>.cloud.appwrite.io/v1") + client.WithProject("<YOUR_PROJECT_ID>") + client.WithSession("") +) + +service := avatars.New(client) + +response, error := service.GetScreenshot( + "https://example.com", + avatars.WithGetScreenshotHeaders(map[string]interface{}{}), + avatars.WithGetScreenshotViewportWidth(1), + avatars.WithGetScreenshotViewportHeight(1), + avatars.WithGetScreenshotScale(0.1), + avatars.WithGetScreenshotTheme("light"), + avatars.WithGetScreenshotUserAgent("<USER_AGENT>"), + avatars.WithGetScreenshotFullpage(false), + avatars.WithGetScreenshotLocale("<LOCALE>"), + avatars.WithGetScreenshotTimezone("africa/abidjan"), + avatars.WithGetScreenshotLatitude(-90), + avatars.WithGetScreenshotLongitude(-180), + avatars.WithGetScreenshotAccuracy(0), + avatars.WithGetScreenshotTouch(false), + avatars.WithGetScreenshotPermissions([]interface{}{}), + avatars.WithGetScreenshotSleep(0), + avatars.WithGetScreenshotWidth(0), + avatars.WithGetScreenshotHeight(0), + avatars.WithGetScreenshotQuality(-1), + avatars.WithGetScreenshotOutput("jpg"), +) diff --git a/docs/examples/1.8.x/server-graphql/examples/avatars/get-screenshot.md b/docs/examples/1.8.x/server-graphql/examples/avatars/get-screenshot.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/docs/examples/1.8.x/server-kotlin/java/avatars/get-screenshot.md b/docs/examples/1.8.x/server-kotlin/java/avatars/get-screenshot.md new file mode 100644 index 0000000000..cf734af3b2 --- /dev/null +++ b/docs/examples/1.8.x/server-kotlin/java/avatars/get-screenshot.md @@ -0,0 +1,42 @@ +import io.appwrite.Client; +import io.appwrite.coroutines.CoroutineCallback; +import io.appwrite.services.Avatars; + +Client client = new Client() + .setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint + .setProject("<YOUR_PROJECT_ID>") // Your project ID + .setSession(""); // The user session to authenticate with + +Avatars avatars = new Avatars(client); + +avatars.getScreenshot( + "https://example.com", // url + mapOf( "a" to "b" ), // headers (optional) + 1, // viewportWidth (optional) + 1, // viewportHeight (optional) + 0.1, // scale (optional) + .LIGHT, // theme (optional) + "<USER_AGENT>", // userAgent (optional) + false, // fullpage (optional) + "<LOCALE>", // locale (optional) + .AFRICA_ABIDJAN, // timezone (optional) + -90, // latitude (optional) + -180, // longitude (optional) + 0, // accuracy (optional) + false, // touch (optional) + listOf(), // permissions (optional) + 0, // sleep (optional) + 0, // width (optional) + 0, // height (optional) + -1, // quality (optional) + .JPG, // output (optional) + new CoroutineCallback<>((result, error) -> { + if (error != null) { + error.printStackTrace(); + return; + } + + System.out.println(result); + }) +); + diff --git a/docs/examples/1.8.x/server-kotlin/kotlin/avatars/get-screenshot.md b/docs/examples/1.8.x/server-kotlin/kotlin/avatars/get-screenshot.md new file mode 100644 index 0000000000..96032bb8a4 --- /dev/null +++ b/docs/examples/1.8.x/server-kotlin/kotlin/avatars/get-screenshot.md @@ -0,0 +1,33 @@ +import io.appwrite.Client +import io.appwrite.coroutines.CoroutineCallback +import io.appwrite.services.Avatars + +val client = Client() + .setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint + .setProject("<YOUR_PROJECT_ID>") // Your project ID + .setSession("") // The user session to authenticate with + +val avatars = Avatars(client) + +val result = avatars.getScreenshot( + url = "https://example.com", + headers = mapOf( "a" to "b" ), // optional + viewportWidth = 1, // optional + viewportHeight = 1, // optional + scale = 0.1, // optional + theme = "light", // optional + userAgent = "<USER_AGENT>", // optional + fullpage = false, // optional + locale = "<LOCALE>", // optional + timezone = "africa/abidjan", // optional + latitude = -90, // optional + longitude = -180, // optional + accuracy = 0, // optional + touch = false, // optional + permissions = listOf(), // optional + sleep = 0, // optional + width = 0, // optional + height = 0, // optional + quality = -1, // optional + output = "jpg" // optional +) diff --git a/docs/examples/1.8.x/server-nodejs/examples/avatars/get-screenshot.md b/docs/examples/1.8.x/server-nodejs/examples/avatars/get-screenshot.md new file mode 100644 index 0000000000..5f7b40cece --- /dev/null +++ b/docs/examples/1.8.x/server-nodejs/examples/avatars/get-screenshot.md @@ -0,0 +1,31 @@ +const sdk = require('node-appwrite'); + +const client = new sdk.Client() + .setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint + .setProject('<YOUR_PROJECT_ID>') // Your project ID + .setSession(''); // The user session to authenticate with + +const avatars = new sdk.Avatars(client); + +const result = await avatars.getScreenshot({ + url: 'https://example.com', + headers: {}, // optional + viewportWidth: 1, // optional + viewportHeight: 1, // optional + scale: 0.1, // optional + theme: sdk..Light, // optional + userAgent: '<USER_AGENT>', // optional + fullpage: false, // optional + locale: '<LOCALE>', // optional + timezone: sdk..AfricaAbidjan, // optional + latitude: -90, // optional + longitude: -180, // optional + accuracy: 0, // optional + touch: false, // optional + permissions: [], // optional + sleep: 0, // optional + width: 0, // optional + height: 0, // optional + quality: -1, // optional + output: sdk..Jpg // optional +}); diff --git a/docs/examples/1.8.x/server-php/examples/avatars/get-screenshot.md b/docs/examples/1.8.x/server-php/examples/avatars/get-screenshot.md new file mode 100644 index 0000000000..b9dfd23862 --- /dev/null +++ b/docs/examples/1.8.x/server-php/examples/avatars/get-screenshot.md @@ -0,0 +1,37 @@ +<?php + +use Appwrite\Client; +use Appwrite\Services\Avatars; +use Appwrite\Enums\Theme; +use Appwrite\Enums\Timezone; +use Appwrite\Enums\Output; + +$client = (new Client()) + ->setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint + ->setProject('<YOUR_PROJECT_ID>') // Your project ID + ->setSession(''); // The user session to authenticate with + +$avatars = new Avatars($client); + +$result = $avatars->getScreenshot( + url: 'https://example.com', + headers: [], // optional + viewportWidth: 1, // optional + viewportHeight: 1, // optional + scale: 0.1, // optional + theme: Theme::LIGHT(), // optional + userAgent: '<USER_AGENT>', // optional + fullpage: false, // optional + locale: '<LOCALE>', // optional + timezone: Timezone::AFRICAABIDJAN(), // optional + latitude: -90, // optional + longitude: -180, // optional + accuracy: 0, // optional + touch: false, // optional + permissions: [], // optional + sleep: 0, // optional + width: 0, // optional + height: 0, // optional + quality: -1, // optional + output: Output::JPG() // optional +); \ No newline at end of file diff --git a/docs/examples/1.8.x/server-php/examples/databases/create-relationship-attribute.md b/docs/examples/1.8.x/server-php/examples/databases/create-relationship-attribute.md index caccd36031..551fe17a9d 100644 --- a/docs/examples/1.8.x/server-php/examples/databases/create-relationship-attribute.md +++ b/docs/examples/1.8.x/server-php/examples/databases/create-relationship-attribute.md @@ -3,6 +3,7 @@ use Appwrite\Client; use Appwrite\Services\Databases; use Appwrite\Enums\RelationshipType; +use Appwrite\Enums\RelationMutate; $client = (new Client()) ->setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint diff --git a/docs/examples/1.8.x/server-php/examples/databases/update-relationship-attribute.md b/docs/examples/1.8.x/server-php/examples/databases/update-relationship-attribute.md index 01783cf3bf..a4d6888711 100644 --- a/docs/examples/1.8.x/server-php/examples/databases/update-relationship-attribute.md +++ b/docs/examples/1.8.x/server-php/examples/databases/update-relationship-attribute.md @@ -2,6 +2,7 @@ use Appwrite\Client; use Appwrite\Services\Databases; +use Appwrite\Enums\RelationMutate; $client = (new Client()) ->setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint diff --git a/docs/examples/1.8.x/server-php/examples/functions/create-execution.md b/docs/examples/1.8.x/server-php/examples/functions/create-execution.md index cd11b5ea6e..9c12e87374 100644 --- a/docs/examples/1.8.x/server-php/examples/functions/create-execution.md +++ b/docs/examples/1.8.x/server-php/examples/functions/create-execution.md @@ -2,6 +2,7 @@ use Appwrite\Client; use Appwrite\Services\Functions; +use Appwrite\Enums\ExecutionMethod; $client = (new Client()) ->setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint diff --git a/docs/examples/1.8.x/server-php/examples/functions/create.md b/docs/examples/1.8.x/server-php/examples/functions/create.md index 3d37b8068e..f7176871bd 100644 --- a/docs/examples/1.8.x/server-php/examples/functions/create.md +++ b/docs/examples/1.8.x/server-php/examples/functions/create.md @@ -2,7 +2,7 @@ use Appwrite\Client; use Appwrite\Services\Functions; -use Appwrite\Enums\; +use Appwrite\Enums\Runtime; $client = (new Client()) ->setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint @@ -14,7 +14,7 @@ $functions = new Functions($client); $result = $functions->create( functionId: '<FUNCTION_ID>', name: '<NAME>', - runtime: ::NODE145(), + runtime: Runtime::NODE145(), execute: ["any"], // optional events: [], // optional schedule: '', // optional diff --git a/docs/examples/1.8.x/server-php/examples/functions/get-deployment-download.md b/docs/examples/1.8.x/server-php/examples/functions/get-deployment-download.md index 7b3e18751e..a06f97b662 100644 --- a/docs/examples/1.8.x/server-php/examples/functions/get-deployment-download.md +++ b/docs/examples/1.8.x/server-php/examples/functions/get-deployment-download.md @@ -2,6 +2,7 @@ use Appwrite\Client; use Appwrite\Services\Functions; +use Appwrite\Enums\DeploymentDownloadType; $client = (new Client()) ->setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint diff --git a/docs/examples/1.8.x/server-php/examples/functions/update.md b/docs/examples/1.8.x/server-php/examples/functions/update.md index ea8d863ae5..da5ee88931 100644 --- a/docs/examples/1.8.x/server-php/examples/functions/update.md +++ b/docs/examples/1.8.x/server-php/examples/functions/update.md @@ -2,6 +2,7 @@ use Appwrite\Client; use Appwrite\Services\Functions; +use Appwrite\Enums\Runtime; $client = (new Client()) ->setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint @@ -13,7 +14,7 @@ $functions = new Functions($client); $result = $functions->update( functionId: '<FUNCTION_ID>', name: '<NAME>', - runtime: ::NODE145(), // optional + runtime: Runtime::NODE145(), // optional execute: ["any"], // optional events: [], // optional schedule: '', // optional diff --git a/docs/examples/1.8.x/server-php/examples/health/get-failed-jobs.md b/docs/examples/1.8.x/server-php/examples/health/get-failed-jobs.md index 02959db3b5..63bc1c83f2 100644 --- a/docs/examples/1.8.x/server-php/examples/health/get-failed-jobs.md +++ b/docs/examples/1.8.x/server-php/examples/health/get-failed-jobs.md @@ -2,7 +2,7 @@ use Appwrite\Client; use Appwrite\Services\Health; -use Appwrite\Enums\; +use Appwrite\Enums\Name; $client = (new Client()) ->setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint @@ -12,6 +12,6 @@ $client = (new Client()) $health = new Health($client); $result = $health->getFailedJobs( - name: ::V1DATABASE(), + name: Name::V1DATABASE(), threshold: null // optional ); \ No newline at end of file diff --git a/docs/examples/1.8.x/server-php/examples/messaging/create-push.md b/docs/examples/1.8.x/server-php/examples/messaging/create-push.md index 51fc0d0a92..614c758c80 100644 --- a/docs/examples/1.8.x/server-php/examples/messaging/create-push.md +++ b/docs/examples/1.8.x/server-php/examples/messaging/create-push.md @@ -2,6 +2,7 @@ use Appwrite\Client; use Appwrite\Services\Messaging; +use Appwrite\Enums\MessagePriority; $client = (new Client()) ->setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint diff --git a/docs/examples/1.8.x/server-php/examples/messaging/create-smtp-provider.md b/docs/examples/1.8.x/server-php/examples/messaging/create-smtp-provider.md index 017f20cc15..953bbcf44f 100644 --- a/docs/examples/1.8.x/server-php/examples/messaging/create-smtp-provider.md +++ b/docs/examples/1.8.x/server-php/examples/messaging/create-smtp-provider.md @@ -2,6 +2,7 @@ use Appwrite\Client; use Appwrite\Services\Messaging; +use Appwrite\Enums\SmtpEncryption; $client = (new Client()) ->setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint diff --git a/docs/examples/1.8.x/server-php/examples/messaging/update-push.md b/docs/examples/1.8.x/server-php/examples/messaging/update-push.md index 05a51783c9..0fea9a135f 100644 --- a/docs/examples/1.8.x/server-php/examples/messaging/update-push.md +++ b/docs/examples/1.8.x/server-php/examples/messaging/update-push.md @@ -2,6 +2,7 @@ use Appwrite\Client; use Appwrite\Services\Messaging; +use Appwrite\Enums\MessagePriority; $client = (new Client()) ->setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint diff --git a/docs/examples/1.8.x/server-php/examples/messaging/update-smtp-provider.md b/docs/examples/1.8.x/server-php/examples/messaging/update-smtp-provider.md index 3bc80d2789..495f332131 100644 --- a/docs/examples/1.8.x/server-php/examples/messaging/update-smtp-provider.md +++ b/docs/examples/1.8.x/server-php/examples/messaging/update-smtp-provider.md @@ -2,6 +2,7 @@ use Appwrite\Client; use Appwrite\Services\Messaging; +use Appwrite\Enums\SmtpEncryption; $client = (new Client()) ->setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint diff --git a/docs/examples/1.8.x/server-php/examples/sites/create.md b/docs/examples/1.8.x/server-php/examples/sites/create.md index 4a1c3a4fcb..6f1fc5ac27 100644 --- a/docs/examples/1.8.x/server-php/examples/sites/create.md +++ b/docs/examples/1.8.x/server-php/examples/sites/create.md @@ -2,8 +2,9 @@ use Appwrite\Client; use Appwrite\Services\Sites; -use Appwrite\Enums\; -use Appwrite\Enums\; +use Appwrite\Enums\Framework; +use Appwrite\Enums\BuildRuntime; +use Appwrite\Enums\Adapter; $client = (new Client()) ->setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint @@ -15,15 +16,15 @@ $sites = new Sites($client); $result = $sites->create( siteId: '<SITE_ID>', name: '<NAME>', - framework: ::ANALOG(), - buildRuntime: ::NODE145(), + framework: Framework::ANALOG(), + buildRuntime: BuildRuntime::NODE145(), enabled: false, // optional logging: false, // optional timeout: 1, // optional installCommand: '<INSTALL_COMMAND>', // optional buildCommand: '<BUILD_COMMAND>', // optional outputDirectory: '<OUTPUT_DIRECTORY>', // optional - adapter: ::STATIC(), // optional + adapter: Adapter::STATIC(), // optional installationId: '<INSTALLATION_ID>', // optional fallbackFile: '<FALLBACK_FILE>', // optional providerRepositoryId: '<PROVIDER_REPOSITORY_ID>', // optional diff --git a/docs/examples/1.8.x/server-php/examples/sites/get-deployment-download.md b/docs/examples/1.8.x/server-php/examples/sites/get-deployment-download.md index 91c6b6e52a..61fad0bd74 100644 --- a/docs/examples/1.8.x/server-php/examples/sites/get-deployment-download.md +++ b/docs/examples/1.8.x/server-php/examples/sites/get-deployment-download.md @@ -2,6 +2,7 @@ use Appwrite\Client; use Appwrite\Services\Sites; +use Appwrite\Enums\DeploymentDownloadType; $client = (new Client()) ->setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint diff --git a/docs/examples/1.8.x/server-php/examples/sites/update.md b/docs/examples/1.8.x/server-php/examples/sites/update.md index f2ca54a987..d2a6c9d256 100644 --- a/docs/examples/1.8.x/server-php/examples/sites/update.md +++ b/docs/examples/1.8.x/server-php/examples/sites/update.md @@ -2,7 +2,9 @@ use Appwrite\Client; use Appwrite\Services\Sites; -use Appwrite\Enums\; +use Appwrite\Enums\Framework; +use Appwrite\Enums\BuildRuntime; +use Appwrite\Enums\Adapter; $client = (new Client()) ->setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint @@ -14,15 +16,15 @@ $sites = new Sites($client); $result = $sites->update( siteId: '<SITE_ID>', name: '<NAME>', - framework: ::ANALOG(), + framework: Framework::ANALOG(), enabled: false, // optional logging: false, // optional timeout: 1, // optional installCommand: '<INSTALL_COMMAND>', // optional buildCommand: '<BUILD_COMMAND>', // optional outputDirectory: '<OUTPUT_DIRECTORY>', // optional - buildRuntime: ::NODE145(), // optional - adapter: ::STATIC(), // optional + buildRuntime: BuildRuntime::NODE145(), // optional + adapter: Adapter::STATIC(), // optional fallbackFile: '<FALLBACK_FILE>', // optional installationId: '<INSTALLATION_ID>', // optional providerRepositoryId: '<PROVIDER_REPOSITORY_ID>', // optional diff --git a/docs/examples/1.8.x/server-php/examples/storage/create-bucket.md b/docs/examples/1.8.x/server-php/examples/storage/create-bucket.md index 2e7cc1d15c..3d4f717e4d 100644 --- a/docs/examples/1.8.x/server-php/examples/storage/create-bucket.md +++ b/docs/examples/1.8.x/server-php/examples/storage/create-bucket.md @@ -2,6 +2,7 @@ use Appwrite\Client; use Appwrite\Services\Storage; +use Appwrite\Enums\Compression; use Appwrite\Permission; use Appwrite\Role; @@ -20,7 +21,7 @@ $result = $storage->createBucket( enabled: false, // optional maximumFileSize: 1, // optional allowedFileExtensions: [], // optional - compression: ::NONE(), // optional + compression: Compression::NONE(), // optional encryption: false, // optional antivirus: false // optional ); \ No newline at end of file diff --git a/docs/examples/1.8.x/server-php/examples/storage/get-file-preview.md b/docs/examples/1.8.x/server-php/examples/storage/get-file-preview.md index 0b65fc326a..aaa15a22fb 100644 --- a/docs/examples/1.8.x/server-php/examples/storage/get-file-preview.md +++ b/docs/examples/1.8.x/server-php/examples/storage/get-file-preview.md @@ -2,6 +2,8 @@ use Appwrite\Client; use Appwrite\Services\Storage; +use Appwrite\Enums\ImageGravity; +use Appwrite\Enums\ImageFormat; $client = (new Client()) ->setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint diff --git a/docs/examples/1.8.x/server-php/examples/storage/update-bucket.md b/docs/examples/1.8.x/server-php/examples/storage/update-bucket.md index 819798cb95..77f4262c2d 100644 --- a/docs/examples/1.8.x/server-php/examples/storage/update-bucket.md +++ b/docs/examples/1.8.x/server-php/examples/storage/update-bucket.md @@ -2,6 +2,7 @@ use Appwrite\Client; use Appwrite\Services\Storage; +use Appwrite\Enums\Compression; use Appwrite\Permission; use Appwrite\Role; @@ -20,7 +21,7 @@ $result = $storage->updateBucket( enabled: false, // optional maximumFileSize: 1, // optional allowedFileExtensions: [], // optional - compression: ::NONE(), // optional + compression: Compression::NONE(), // optional encryption: false, // optional antivirus: false // optional ); \ No newline at end of file diff --git a/docs/examples/1.8.x/server-php/examples/tablesdb/create-relationship-column.md b/docs/examples/1.8.x/server-php/examples/tablesdb/create-relationship-column.md index 031d1fd1aa..7f9a06cc03 100644 --- a/docs/examples/1.8.x/server-php/examples/tablesdb/create-relationship-column.md +++ b/docs/examples/1.8.x/server-php/examples/tablesdb/create-relationship-column.md @@ -3,6 +3,7 @@ use Appwrite\Client; use Appwrite\Services\TablesDB; use Appwrite\Enums\RelationshipType; +use Appwrite\Enums\RelationMutate; $client = (new Client()) ->setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint diff --git a/docs/examples/1.8.x/server-php/examples/tablesdb/update-relationship-column.md b/docs/examples/1.8.x/server-php/examples/tablesdb/update-relationship-column.md index 834dc18cee..cc2e2ccaef 100644 --- a/docs/examples/1.8.x/server-php/examples/tablesdb/update-relationship-column.md +++ b/docs/examples/1.8.x/server-php/examples/tablesdb/update-relationship-column.md @@ -2,6 +2,7 @@ use Appwrite\Client; use Appwrite\Services\TablesDB; +use Appwrite\Enums\RelationMutate; $client = (new Client()) ->setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint diff --git a/docs/examples/1.8.x/server-php/examples/users/create-sha-user.md b/docs/examples/1.8.x/server-php/examples/users/create-sha-user.md index 0b9a27ed8e..812bcd5eb5 100644 --- a/docs/examples/1.8.x/server-php/examples/users/create-sha-user.md +++ b/docs/examples/1.8.x/server-php/examples/users/create-sha-user.md @@ -2,6 +2,7 @@ use Appwrite\Client; use Appwrite\Services\Users; +use Appwrite\Enums\PasswordHash; $client = (new Client()) ->setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint diff --git a/docs/examples/1.8.x/server-python/examples/avatars/get-screenshot.md b/docs/examples/1.8.x/server-python/examples/avatars/get-screenshot.md new file mode 100644 index 0000000000..34bdf8ac7a --- /dev/null +++ b/docs/examples/1.8.x/server-python/examples/avatars/get-screenshot.md @@ -0,0 +1,32 @@ +from appwrite.client import Client +from appwrite.services.avatars import Avatars + +client = Client() +client.set_endpoint('https://<REGION>.cloud.appwrite.io/v1') # Your API Endpoint +client.set_project('<YOUR_PROJECT_ID>') # Your project ID +client.set_session('') # The user session to authenticate with + +avatars = Avatars(client) + +result = avatars.get_screenshot( + url = 'https://example.com', + headers = {}, # optional + viewport_width = 1, # optional + viewport_height = 1, # optional + scale = 0.1, # optional + theme = .LIGHT, # optional + user_agent = '<USER_AGENT>', # optional + fullpage = False, # optional + locale = '<LOCALE>', # optional + timezone = .AFRICA_ABIDJAN, # optional + latitude = -90, # optional + longitude = -180, # optional + accuracy = 0, # optional + touch = False, # optional + permissions = [], # optional + sleep = 0, # optional + width = 0, # optional + height = 0, # optional + quality = -1, # optional + output = .JPG # optional +) diff --git a/docs/examples/1.8.x/server-rest/examples/avatars/get-screenshot.md b/docs/examples/1.8.x/server-rest/examples/avatars/get-screenshot.md new file mode 100644 index 0000000000..0ab16b59e6 --- /dev/null +++ b/docs/examples/1.8.x/server-rest/examples/avatars/get-screenshot.md @@ -0,0 +1,7 @@ +GET /v1/avatars/screenshots HTTP/1.1 +Host: cloud.appwrite.io +X-Appwrite-Response-Format: 1.8.0 +X-Appwrite-Project: <YOUR_PROJECT_ID> +X-Appwrite-Session: +X-Appwrite-Key: <YOUR_API_KEY> +X-Appwrite-JWT: <YOUR_JWT> diff --git a/docs/examples/1.8.x/server-ruby/examples/avatars/get-screenshot.md b/docs/examples/1.8.x/server-ruby/examples/avatars/get-screenshot.md new file mode 100644 index 0000000000..f2af537fe8 --- /dev/null +++ b/docs/examples/1.8.x/server-ruby/examples/avatars/get-screenshot.md @@ -0,0 +1,33 @@ +require 'appwrite' + +include Appwrite + +client = Client.new + .set_endpoint('https://<REGION>.cloud.appwrite.io/v1') # Your API Endpoint + .set_project('<YOUR_PROJECT_ID>') # Your project ID + .set_session('') # The user session to authenticate with + +avatars = Avatars.new(client) + +result = avatars.get_screenshot( + url: 'https://example.com', + headers: {}, # optional + viewport_width: 1, # optional + viewport_height: 1, # optional + scale: 0.1, # optional + theme: ::LIGHT, # optional + user_agent: '<USER_AGENT>', # optional + fullpage: false, # optional + locale: '<LOCALE>', # optional + timezone: ::AFRICA_ABIDJAN, # optional + latitude: -90, # optional + longitude: -180, # optional + accuracy: 0, # optional + touch: false, # optional + permissions: [], # optional + sleep: 0, # optional + width: 0, # optional + height: 0, # optional + quality: -1, # optional + output: ::JPG # optional +) diff --git a/docs/examples/1.8.x/server-swift/examples/avatars/get-screenshot.md b/docs/examples/1.8.x/server-swift/examples/avatars/get-screenshot.md new file mode 100644 index 0000000000..3aa1661093 --- /dev/null +++ b/docs/examples/1.8.x/server-swift/examples/avatars/get-screenshot.md @@ -0,0 +1,33 @@ +import Appwrite +import AppwriteEnums + +let client = Client() + .setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint + .setProject("<YOUR_PROJECT_ID>") // Your project ID + .setSession("") // The user session to authenticate with + +let avatars = Avatars(client) + +let bytes = try await avatars.getScreenshot( + url: "https://example.com", + headers: [:], // optional + viewportWidth: 1, // optional + viewportHeight: 1, // optional + scale: 0.1, // optional + theme: .light, // optional + userAgent: "<USER_AGENT>", // optional + fullpage: false, // optional + locale: "<LOCALE>", // optional + timezone: .africaAbidjan, // optional + latitude: -90, // optional + longitude: -180, // optional + accuracy: 0, // optional + touch: false, // optional + permissions: [], // optional + sleep: 0, // optional + width: 0, // optional + height: 0, // optional + quality: -1, // optional + output: .jpg // optional +) + diff --git a/docs/references/migrations/migration-csv-export.md b/docs/references/migrations/migration-csv-export.md index 866faed2d2..069dda895e 100644 --- a/docs/references/migrations/migration-csv-export.md +++ b/docs/references/migrations/migration-csv-export.md @@ -1 +1 @@ -Export documents to a CSV file from your Appwrite database. This endpoint allows you to export documents to a CSV file stored in an Appwrite Storage bucket. \ No newline at end of file +Export documents to a CSV file from your Appwrite database. This endpoint allows you to export documents to a CSV file stored in a secure internal bucket. You'll receive an email with a download link when the export is complete. \ No newline at end of file diff --git a/docs/sdks/cli/CHANGELOG.md b/docs/sdks/cli/CHANGELOG.md index ac1624401c..8e50441769 100644 --- a/docs/sdks/cli/CHANGELOG.md +++ b/docs/sdks/cli/CHANGELOG.md @@ -1,5 +1,9 @@ # Change Log +## 11.1.1 + +* Fix duplicate `enums` during type generation by prefixing them with table name. For example, `enum MyEnum` will now be generated as `enum MyTableMyEnum` to avoid conflicts. + ## 11.1.0 * Add `total` parameter to list queries allowing skipping counting rows in a table for improved performance diff --git a/docs/sdks/dart/CHANGELOG.md b/docs/sdks/dart/CHANGELOG.md index 1a2cd6a5be..7fd7227f15 100644 --- a/docs/sdks/dart/CHANGELOG.md +++ b/docs/sdks/dart/CHANGELOG.md @@ -1,5 +1,12 @@ # Change Log +## 19.4.0 + +* Add `getScreenshot` method to `Avatars` service +* Add enums `Theme`, `Output` and `Timezone` +* Update runtime enums to add support for `dart39` and `flutter335` runtimes +* Fix passing of `null` values and stripping only non-nullable optional parameters from the request body + ## 19.3.0 * Add `total` parameter to list queries allowing skipping counting rows in a table for improved performance diff --git a/docs/sdks/flutter/CHANGELOG.md b/docs/sdks/flutter/CHANGELOG.md index 5ab7d3269a..2f26f34edd 100644 --- a/docs/sdks/flutter/CHANGELOG.md +++ b/docs/sdks/flutter/CHANGELOG.md @@ -1,5 +1,9 @@ # Change Log +## 20.3.1 + +* Fix passing of `null` values and stripping only non-nullable optional parameters from the request body + ## 20.3.0 * Add `total` parameter to list queries allowing skipping counting rows in a table for improved performance diff --git a/docs/sdks/php/CHANGELOG.md b/docs/sdks/php/CHANGELOG.md index 6e8d4d7545..14a26e441d 100644 --- a/docs/sdks/php/CHANGELOG.md +++ b/docs/sdks/php/CHANGELOG.md @@ -1,5 +1,15 @@ # Change Log +## 18.0.1 + +* Fix `TablesDB` service to use correct file name + +## 18.0.0 + +* Fix duplicate methods issue (e.g., `updateMFA` and `updateMfa`) causing build and runtime errors +* Add support for `getScreenshot` method to `Avatars` service +* Add `Output`, `Theme` and `Timezone` enums + ## 17.5.0 * Add `total` parameter to list queries allowing skipping counting rows in a table for improved performance diff --git a/docs/tutorials/release-sdks.md b/docs/tutorials/release-sdks.md index 0e03beca71..99c0fa4fd3 100644 --- a/docs/tutorials/release-sdks.md +++ b/docs/tutorials/release-sdks.md @@ -26,27 +26,34 @@ Before releasing SDKs, you need to: To enable SDK releases via GitHub, you need to mount SSH keys and configure GitHub authentication in your Docker environment. -#### Update docker-compose.override.yml +#### Update Dockerfile -Update `docker-compose.override.yml` to mount SSH keys and set environment variables for the `appwrite` service: +Add the following configuration to your `Dockerfile`: + +```dockerfile +ARG GH_TOKEN +ENV GH_TOKEN=your_github_token_here +RUN git config --global user.email "your-email@example.com" +RUN apk add --update --no-cache openssh-client github-cli +``` + +Replace: +- `your_github_token_here` with your GitHub personal access token (with appropriate permissions) +- `your-email@example.com` with your Git email address + +#### Update docker-compose.yml + +Add the SSH key volume mount to the `appwrite` service in `docker-compose.yml`: ```yaml services: appwrite: volumes: - ~/.ssh:/root/.ssh - environment: - - GH_TOKEN=your_github_token_here - - GIT_EMAIL=your-email@example.com + # ... other volumes ``` -Uncomment the volumes section. - -Replace: -- `your_github_token_here` with your GitHub personal access token (with appropriate permissions) -- `your-email@example.com` with your Git email address - -This mounts your SSH keys from the host machine and sets the GitHub token and email as environment variables, allowing the container to authenticate with GitHub. The git configuration is handled automatically at runtime. +This mounts your SSH keys from the host machine, allowing the container to authenticate with GitHub. ### Updating Specs diff --git a/phpunit.xml b/phpunit.xml index 4c4e55ea4e..a8578995c1 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -31,6 +31,7 @@ <directory>./tests/e2e/Services/Locale</directory> <directory>./tests/e2e/Services/Projects</directory> <directory>./tests/e2e/Services/Storage</directory> + <directory>./tests/e2e/Services/Tokens</directory> <directory>./tests/e2e/Services/Webhooks</directory> <directory>./tests/e2e/Services/Messaging</directory> <directory>./tests/e2e/Services/Migrations</directory> diff --git a/public/images/sites/templates/text-to-speech-dark.png b/public/images/sites/templates/text-to-speech-dark.png new file mode 100644 index 0000000000..afa68c4227 Binary files /dev/null and b/public/images/sites/templates/text-to-speech-dark.png differ diff --git a/public/images/sites/templates/text-to-speech-light.png b/public/images/sites/templates/text-to-speech-light.png new file mode 100644 index 0000000000..e10148fe17 Binary files /dev/null and b/public/images/sites/templates/text-to-speech-light.png differ diff --git a/src/Appwrite/Migration/Version/V23.php b/src/Appwrite/Migration/Version/V23.php index 7a6d58d59f..a4027b506e 100644 --- a/src/Appwrite/Migration/Version/V23.php +++ b/src/Appwrite/Migration/Version/V23.php @@ -6,6 +6,7 @@ use Appwrite\Migration\Migration; use Exception; use Throwable; use Utopia\CLI\Console; +use Utopia\Config\Config; use Utopia\Database\Database; use Utopia\Database\Document; use Utopia\Database\Exception\Conflict; @@ -132,6 +133,13 @@ class V23 extends Migration } $this->dbForProject->purgeCachedCollection($id); break; + case 'migrations': + try { + $this->updateMigrateErrorSize(); + } catch (\Throwable $th) { + Console::warning("Failed to migration error attribute size in collection {$id}: {$th->getMessage()}"); + } + default: break; } @@ -201,4 +209,46 @@ class V23 extends Migration } return $document; } + + /** + * Update migration attribute size + * @return void + */ + private function updateMigrateErrorSize(): void + { + + if ($this->project->getId() === 'console') { + return; + } + + // Read-modify-write from the live schema to avoid overwriting unrelated changes. + $migration = $this->dbForProject->getCollection('migrations'); + $attributes = $migration->getAttribute('attributes', []); + $attrsArray = \array_map(fn (Document $doc) => $doc->getArrayCopy(), $attributes); + $errorsIdx = \array_search('errors', \array_column($attrsArray, '$id')); + + if ($errorsIdx === false) { + Console::warning("Skipping: 'errors' attribute not found in migrations collection for project {$this->project->getId()}"); + return; + } + + $desiredSize = 1_000_000; + $migrationAttributes = Config::getParam('collections', [])['projects']['migrations']['attributes'] ?? []; + $migrationIndex = \array_search('errors', \array_column($migrationAttributes, '$id')); + + if ($migrationIndex !== false && isset($migrationAttributes[$migrationIndex]['size'])) { + $desiredSize = (int) $migrationAttributes[$migrationIndex]['size']; + } + + $currentSize = (int) ($attributes[$errorsIdx]['size'] ?? 0); + + if ($currentSize === $desiredSize) { + Console::warning("Skipping: 'errors' attribute already of desired size {$desiredSize} in migrations collection for project {$this->project->getId()}"); + return; + } + $attributes[$errorsIdx]['size'] = $desiredSize; + $migration->setAttribute('attributes', $attributes); + $this->dbForProject->updateDocument($migration->getCollection(), $migration->getId(), $migration); + $this->dbForProject->purgeCachedCollection('migrations'); + } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Action.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Action.php index 884b9c5589..728e732cc5 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Action.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Action.php @@ -2,9 +2,13 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases; -use Utopia\Platform\Action as UtopiaAction; +use Appwrite\Extend\Exception; +use Appwrite\Platform\Action as AppwriteAction; +use Utopia\Database\Database; +use Utopia\Database\Document; +use Utopia\Database\Operator; -class Action extends UtopiaAction +class Action extends AppwriteAction { private string $context = 'legacy'; @@ -13,11 +17,81 @@ class Action extends UtopiaAction return $this->context; } - public function setHttpPath(string $path): UtopiaAction + public function setHttpPath(string $path): AppwriteAction { if (\str_contains($path, '/tablesdb')) { $this->context = 'tablesdb'; } return parent::setHttpPath($path); } + + /** + * Parse operator strings in data array and convert them to Operator objects. + * + * @param array $data The data array that may contain operator JSON strings or arrays + * @param Document $collection The collection document to check for relationship attributes + * @return array The data array with operators converted to Operator objects + * @throws Exception If an operator string is invalid + */ + protected function parseOperators(array $data, Document $collection): array + { + $relationshipKeys = []; + foreach ($collection->getAttribute('attributes', []) as $attribute) { + if ($attribute->getAttribute('type') === Database::VAR_RELATIONSHIP) { + $relationshipKeys[$attribute->getAttribute('key')] = true; + } + } + + foreach ($data as $key => $value) { + if (!\is_string($key)) { + if (\is_array($value)) { + $data[$key] = $this->parseOperators($value, $collection); + } + continue; + } + + if (\str_starts_with($key, '$')) { + continue; + } + + if (isset($relationshipKeys[$key])) { + continue; + } + + // Handle operator as JSON string (from API requests) + if (\is_string($value)) { + $decoded = \json_decode($value, true); + + if ( + \is_array($decoded) && + isset($decoded['method']) && + \is_string($decoded['method']) && + Operator::isMethod($decoded['method']) + ) { + try { + $data[$key] = Operator::parse($value); + } catch (\Exception $e) { + throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Invalid operator for attribute "' . $key . '": ' . $e->getMessage()); + } + } + } + // Handle operator as array (from transaction logs after serialization) + elseif ( + \is_array($value) && + isset($value['method']) && + \is_string($value['method']) && + Operator::isMethod($value['method']) + ) { + try { + $data[$key] = Operator::parseOperator($value); + } catch (\Exception $e) { + throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Invalid operator for attribute "' . $key . '": ' . $e->getMessage()); + } + } elseif (\is_array($value)) { + $data[$key] = $this->parseOperators($value, $collection); + } + } + + return $data; + } } diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Boolean/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Boolean/Create.php index 6c8e5dcf3d..8dfe80a390 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Boolean/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Boolean/Create.php @@ -16,6 +16,7 @@ use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\Boolean; +use Utopia\Validator\Nullable; class Create extends Action { @@ -62,7 +63,7 @@ class Create extends Action ->param('collectionId', '', new UID(), 'Collection ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') ->param('key', '', new Key(), 'Attribute Key.') ->param('required', null, new Boolean(), 'Is attribute required?') - ->param('default', null, new Boolean(), 'Default value for attribute when not provided. Cannot be set when attribute is required.', true) + ->param('default', null, new Nullable(new Boolean()), 'Default value for attribute when not provided. Cannot be set when attribute is required.', true) ->param('array', false, new Boolean(), 'Is attribute an array?', true) ->inject('response') ->inject('dbForProject') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Boolean/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Boolean/Update.php index d4724ea551..ddb01ff011 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Boolean/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Boolean/Update.php @@ -64,7 +64,7 @@ class Update extends Action ->param('key', '', new Key(), 'Attribute Key.') ->param('required', null, new Boolean(), 'Is attribute required?') ->param('default', null, new Nullable(new Boolean()), 'Default value for attribute when not provided. Cannot be set when attribute is required.') - ->param('newKey', null, new Key(), 'New attribute key.', true) + ->param('newKey', null, new Nullable(new Key()), 'New attribute key.', true) ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Datetime/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Datetime/Create.php index 1f2098e7af..d0f45dc664 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Datetime/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Datetime/Create.php @@ -17,6 +17,7 @@ use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\Boolean; +use Utopia\Validator\Nullable; class Create extends Action { @@ -63,7 +64,7 @@ class Create extends Action ->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#createCollection).') ->param('key', '', new Key(), 'Attribute Key.') ->param('required', null, new Boolean(), 'Is attribute required?') - ->param('default', null, fn (Database $dbForProject) => new DatetimeValidator($dbForProject->getAdapter()->getMinDateTime(), $dbForProject->getAdapter()->getMaxDateTime()), 'Default value for the attribute in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. Cannot be set when attribute is required.', true, ['dbForProject']) + ->param('default', null, fn (Database $dbForProject) => new Nullable(new DatetimeValidator($dbForProject->getAdapter()->getMinDateTime(), $dbForProject->getAdapter()->getMaxDateTime())), 'Default value for the attribute in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. Cannot be set when attribute is required.', true, ['dbForProject']) ->param('array', false, new Boolean(), 'Is attribute an array?', true) ->inject('response') ->inject('dbForProject') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Datetime/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Datetime/Update.php index cb4d0d924b..1a30a09867 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Datetime/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Datetime/Update.php @@ -65,7 +65,7 @@ class Update extends Action ->param('key', '', new Key(), 'Attribute Key.') ->param('required', null, new Boolean(), 'Is attribute required?') ->param('default', null, fn (Database $dbForProject) => new Nullable(new DatetimeValidator($dbForProject->getAdapter()->getMinDateTime(), $dbForProject->getAdapter()->getMaxDateTime())), 'Default value for attribute when not provided. Cannot be set when attribute is required.', injections: ['dbForProject']) - ->param('newKey', null, new Key(), 'New attribute key.', true) + ->param('newKey', null, new Nullable(new Key()), 'New attribute key.', true) ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Email/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Email/Create.php index cbfd66e4d9..9f4c38d490 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Email/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Email/Create.php @@ -17,6 +17,7 @@ use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\Boolean; +use Utopia\Validator\Nullable; class Create extends Action { @@ -63,7 +64,7 @@ class Create extends Action ->param('collectionId', '', new UID(), 'Collection ID.') ->param('key', '', new Key(), 'Attribute Key.') ->param('required', null, new Boolean(), 'Is attribute required?') - ->param('default', null, new Email(), 'Default value for attribute when not provided. Cannot be set when attribute is required.', true) + ->param('default', null, new Nullable(new Email()), 'Default value for attribute when not provided. Cannot be set when attribute is required.', true) ->param('array', false, new Boolean(), 'Is attribute an array?', true) ->inject('response') ->inject('dbForProject') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Email/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Email/Update.php index 2446722f7a..59a0490e6f 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Email/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Email/Update.php @@ -65,7 +65,7 @@ class Update extends Action ->param('key', '', new Key(), 'Attribute Key.') ->param('required', null, new Boolean(), 'Is attribute required?') ->param('default', null, new Nullable(new Email()), 'Default value for attribute when not provided. Cannot be set when attribute is required.') - ->param('newKey', null, new Key(), 'New Attribute Key.', true) + ->param('newKey', null, new Nullable(new Key()), 'New Attribute Key.', true) ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Enum/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Enum/Create.php index 98ed83861d..d2ccf9f972 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Enum/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Enum/Create.php @@ -18,6 +18,7 @@ use Utopia\Database\Validator\UID; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\ArrayList; use Utopia\Validator\Boolean; +use Utopia\Validator\Nullable; use Utopia\Validator\Text; class Create extends Action @@ -66,7 +67,7 @@ class Create extends Action ->param('key', '', new Key(), 'Attribute Key.') ->param('elements', [], new ArrayList(new Text(Database::LENGTH_KEY), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Array of enum values.') ->param('required', null, new Boolean(), 'Is attribute required?') - ->param('default', null, new Text(0), 'Default value for attribute when not provided. Cannot be set when attribute is required.', true) + ->param('default', null, new Nullable(new Text(0)), 'Default value for attribute when not provided. Cannot be set when attribute is required.', true) ->param('array', false, new Boolean(), 'Is attribute an array?', true) ->inject('response') ->inject('dbForProject') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Enum/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Enum/Update.php index 23dc807360..560107dd38 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Enum/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Enum/Update.php @@ -67,7 +67,7 @@ class Update extends Action ->param('elements', null, new ArrayList(new Text(Database::LENGTH_KEY), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Updated list of enum values.') ->param('required', null, new Boolean(), 'Is attribute required?') ->param('default', null, new Nullable(new Text(0)), 'Default value for attribute when not provided. Cannot be set when attribute is required.') - ->param('newKey', null, new Key(), 'New Attribute Key.', true) + ->param('newKey', null, new Nullable(new Key()), 'New Attribute Key.', true) ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Float/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Float/Create.php index 83deb92edc..f48348c192 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Float/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Float/Create.php @@ -18,6 +18,7 @@ use Utopia\Database\Validator\UID; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\Boolean; use Utopia\Validator\FloatValidator; +use Utopia\Validator\Nullable; use Utopia\Validator\Range; class Create extends Action @@ -65,9 +66,9 @@ class Create extends Action ->param('collectionId', '', new UID(), 'Collection ID.') ->param('key', '', new Key(), 'Attribute Key.') ->param('required', null, new Boolean(), 'Is attribute required?') - ->param('min', null, new FloatValidator(), 'Minimum value.', true) - ->param('max', null, new FloatValidator(), 'Maximum value.', true) - ->param('default', null, new FloatValidator(), 'Default value. Cannot be set when required.', true) + ->param('min', null, new Nullable(new FloatValidator()), 'Minimum value.', true) + ->param('max', null, new Nullable(new FloatValidator()), 'Maximum value.', true) + ->param('default', null, new Nullable(new FloatValidator()), 'Default value. Cannot be set when required.', true) ->param('array', false, new Boolean(), 'Is attribute an array?', true) ->inject('response') ->inject('dbForProject') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Float/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Float/Update.php index 7f295a1a94..99ac992b9e 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Float/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Float/Update.php @@ -64,10 +64,10 @@ class Update extends Action ->param('collectionId', '', new UID(), 'Collection ID.') ->param('key', '', new Key(), 'Attribute Key.') ->param('required', null, new Boolean(), 'Is attribute required?') - ->param('min', null, new FloatValidator(), 'Minimum value.', true) - ->param('max', null, new FloatValidator(), 'Maximum value.', true) + ->param('min', null, new Nullable(new FloatValidator()), 'Minimum value.', true) + ->param('max', null, new Nullable(new FloatValidator()), 'Maximum value.', true) ->param('default', null, new Nullable(new FloatValidator()), 'Default value. Cannot be set when required.') - ->param('newKey', null, new Key(), 'New Attribute Key.', true) + ->param('newKey', null, new Nullable(new Key()), 'New Attribute Key.', true) ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/IP/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/IP/Create.php index 6e6264466c..af3ed99bdf 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/IP/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/IP/Create.php @@ -17,6 +17,7 @@ use Utopia\Database\Validator\UID; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\Boolean; use Utopia\Validator\IP; +use Utopia\Validator\Nullable; class Create extends Action { @@ -63,7 +64,7 @@ class Create extends Action ->param('collectionId', '', new UID(), 'Collection ID.') ->param('key', '', new Key(), 'Attribute Key.') ->param('required', null, new Boolean(), 'Is attribute required?') - ->param('default', null, new IP(), 'Default value. Cannot be set when attribute is required.', true) + ->param('default', null, new Nullable(new IP()), 'Default value. Cannot be set when attribute is required.', true) ->param('array', false, new Boolean(), 'Is attribute an array?', true) ->inject('response') ->inject('dbForProject') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/IP/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/IP/Update.php index 6cedf10760..a757ed47d1 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/IP/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/IP/Update.php @@ -65,7 +65,7 @@ class Update extends Action ->param('key', '', new Key(), 'Attribute Key.') ->param('required', null, new Boolean(), 'Is attribute required?') ->param('default', null, new Nullable(new IP()), 'Default value. Cannot be set when attribute is required.') - ->param('newKey', null, new Key(), 'New Attribute Key.', true) + ->param('newKey', null, new Nullable(new Key()), 'New Attribute Key.', true) ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Integer/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Integer/Create.php index 090d63c403..5e147c771d 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Integer/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Integer/Create.php @@ -18,6 +18,7 @@ use Utopia\Database\Validator\UID; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\Boolean; use Utopia\Validator\Integer; +use Utopia\Validator\Nullable; use Utopia\Validator\Range; class Create extends Action @@ -65,9 +66,9 @@ class Create extends Action ->param('collectionId', '', new UID(), 'Collection ID.') ->param('key', '', new Key(), 'Attribute Key.') ->param('required', null, new Boolean(), 'Is attribute required?') - ->param('min', null, new Integer(), 'Minimum value', true) - ->param('max', null, new Integer(), 'Maximum value', true) - ->param('default', null, new Integer(), 'Default value. Cannot be set when attribute is required.', true) + ->param('min', null, new Nullable(new Integer()), 'Minimum value', true) + ->param('max', null, new Nullable(new Integer()), 'Maximum value', true) + ->param('default', null, new Nullable(new Integer()), 'Default value. Cannot be set when attribute is required.', true) ->param('array', false, new Boolean(), 'Is attribute an array?', true) ->inject('response') ->inject('dbForProject') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Integer/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Integer/Update.php index b6ae79bd8a..6d3858992b 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Integer/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Integer/Update.php @@ -64,10 +64,10 @@ class Update extends Action ->param('collectionId', '', new UID(), 'Collection ID.') ->param('key', '', new Key(), 'Attribute Key.') ->param('required', null, new Boolean(), 'Is attribute required?') - ->param('min', null, new Integer(), 'Minimum value', true) - ->param('max', null, new Integer(), 'Maximum value', true) + ->param('min', null, new Nullable(new Integer()), 'Minimum value', true) + ->param('max', null, new Nullable(new Integer()), 'Maximum value', true) ->param('default', null, new Nullable(new Integer()), 'Default value. Cannot be set when attribute is required.') - ->param('newKey', null, new Key(), 'New Attribute Key.', true) + ->param('newKey', null, new Nullable(new Key()), 'New Attribute Key.', true) ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Line/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Line/Update.php index 52f04ba5bc..8ef2f96ec2 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Line/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Line/Update.php @@ -65,7 +65,7 @@ class Update extends Action ->param('key', '', new Key(), 'Attribute Key.') ->param('required', null, new Boolean(), 'Is attribute required?') ->param('default', null, new Nullable(new Spatial(Database::VAR_LINESTRING)), 'Default value for attribute when not provided, two-dimensional array of coordinate pairs, [[longitude, latitude], [longitude, latitude], …], listing the vertices of the line in order. Cannot be set when attribute is required.', true) - ->param('newKey', null, new Key(), 'New attribute key.', true) + ->param('newKey', null, new Nullable(new Key()), 'New attribute key.', true) ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Point/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Point/Update.php index 73964ab461..62f35ad2a7 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Point/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Point/Update.php @@ -65,7 +65,7 @@ class Update extends Action ->param('key', '', new Key(), 'Attribute Key.') ->param('required', null, new Boolean(), 'Is attribute required?') ->param('default', null, new Nullable(new Spatial(Database::VAR_POINT)), 'Default value for attribute when not provided, array of two numbers [longitude, latitude], representing a single coordinate. Cannot be set when attribute is required.', true) - ->param('newKey', null, new Key(), 'New attribute key.', true) + ->param('newKey', null, new Nullable(new Key()), 'New attribute key.', true) ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Polygon/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Polygon/Update.php index 23eb06f45d..dba83d44d5 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Polygon/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Polygon/Update.php @@ -65,7 +65,7 @@ class Update extends Action ->param('key', '', new Key(), 'Attribute Key.') ->param('required', null, new Boolean(), 'Is attribute required?') ->param('default', null, new Nullable(new Spatial(Database::VAR_POLYGON)), 'Default value for attribute when not provided, three-dimensional array where the outer array holds one or more linear rings, [[[longitude, latitude], …], …], the first ring is the exterior boundary, any additional rings are interior holes, and each ring must start and end with the same coordinate pair. Cannot be set when attribute is required.', true) - ->param('newKey', null, new Key(), 'New attribute key.', true) + ->param('newKey', null, new Nullable(new Key()), 'New attribute key.', true) ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Relationship/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Relationship/Create.php index 75471b826c..6fc27a9836 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Relationship/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Relationship/Create.php @@ -18,6 +18,7 @@ use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\Boolean; +use Utopia\Validator\Nullable; use Utopia\Validator\WhiteList; class Create extends Action @@ -71,8 +72,8 @@ class Create extends Action Database::RELATION_ONE_TO_MANY ], true), 'Relation type') ->param('twoWay', false, new Boolean(), 'Is Two Way?', true) - ->param('key', null, new Key(), 'Attribute Key.', true) - ->param('twoWayKey', null, new Key(), 'Two Way Attribute Key.', true) + ->param('key', null, new Nullable(new Key()), 'Attribute Key.', true) + ->param('twoWayKey', null, new Nullable(new Key()), 'Two Way Attribute Key.', true) ->param('onDelete', Database::RELATION_MUTATE_RESTRICT, new WhiteList([ Database::RELATION_MUTATE_CASCADE, Database::RELATION_MUTATE_RESTRICT, diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Relationship/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Relationship/Update.php index 897cbd434f..f9f1d6f3ab 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Relationship/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/Relationship/Update.php @@ -14,6 +14,7 @@ use Utopia\Database\Database; use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; use Utopia\Swoole\Response as SwooleResponse; +use Utopia\Validator\Nullable; use Utopia\Validator\WhiteList; class Update extends Action @@ -61,12 +62,12 @@ class Update extends Action ->param('databaseId', '', new UID(), 'Database ID.') ->param('collectionId', '', new UID(), 'Collection ID.') ->param('key', '', new Key(), 'Attribute Key.') - ->param('onDelete', null, new WhiteList([ + ->param('onDelete', null, new Nullable(new WhiteList([ Database::RELATION_MUTATE_CASCADE, Database::RELATION_MUTATE_RESTRICT, Database::RELATION_MUTATE_SET_NULL - ], true), 'Constraints option', true) - ->param('newKey', null, new Key(), 'New Attribute Key.', true) + ], true)), 'Constraints option', true) + ->param('newKey', null, new Nullable(new Key()), 'New Attribute Key.', true) ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/String/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/String/Create.php index 1527c4d1d9..88cb161505 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/String/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/String/Create.php @@ -19,6 +19,7 @@ use Utopia\Database\Validator\UID; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator; use Utopia\Validator\Boolean; +use Utopia\Validator\Nullable; use Utopia\Validator\Range; use Utopia\Validator\Text; @@ -68,7 +69,7 @@ class Create extends Action ->param('key', '', new Key(), 'Attribute Key.') ->param('size', null, new Range(1, APP_DATABASE_ATTRIBUTE_STRING_MAX_LENGTH, Validator::TYPE_INTEGER), 'Attribute size for text attributes, in number of characters.') ->param('required', null, new Boolean(), 'Is attribute required?') - ->param('default', null, new Text(0, 0), 'Default value for attribute when not provided. Cannot be set when attribute is required.', true) + ->param('default', null, new Nullable(new Text(0, 0)), 'Default value for attribute when not provided. Cannot be set when attribute is required.', true) ->param('array', false, new Boolean(), 'Is attribute an array?', true) ->param('encrypt', false, new Boolean(), 'Toggle encryption for the attribute. Encryption enhances security by not storing any plain text values in the database. However, encrypted attributes cannot be queried.', true) ->inject('response') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/String/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/String/Update.php index 8614dfb202..6687178cb1 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/String/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/String/Update.php @@ -67,8 +67,8 @@ class Update extends Action ->param('key', '', new Key(), 'Attribute Key.') ->param('required', null, new Boolean(), 'Is attribute required?') ->param('default', null, new Nullable(new Text(0, 0)), 'Default value for attribute when not provided. Cannot be set when attribute is required.') - ->param('size', null, new Range(1, APP_DATABASE_ATTRIBUTE_STRING_MAX_LENGTH, Validator::TYPE_INTEGER), 'Maximum size of the string attribute.', true) - ->param('newKey', null, new Key(), 'New Attribute Key.', true) + ->param('size', null, new Nullable(new Range(1, APP_DATABASE_ATTRIBUTE_STRING_MAX_LENGTH, Validator::TYPE_INTEGER)), 'Maximum size of the string attribute.', true) + ->param('newKey', null, new Nullable(new Key()), 'New Attribute Key.', true) ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/URL/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/URL/Create.php index ce0175966b..2d3b0c6168 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/URL/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/URL/Create.php @@ -16,6 +16,7 @@ use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\Boolean; +use Utopia\Validator\Nullable; use Utopia\Validator\URL; class Create extends Action @@ -63,7 +64,7 @@ class Create extends Action ->param('collectionId', '', new UID(), 'Collection ID.') ->param('key', '', new Key(), 'Attribute Key.') ->param('required', null, new Boolean(), 'Is attribute required?') - ->param('default', null, new URL(), 'Default value for attribute when not provided. Cannot be set when attribute is required.', true) + ->param('default', null, new Nullable(new URL()), 'Default value for attribute when not provided. Cannot be set when attribute is required.', true) ->param('array', false, new Boolean(), 'Is attribute an array?', true) ->inject('response') ->inject('dbForProject') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/URL/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/URL/Update.php index 7ba12ad98a..ebaea9e61d 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/URL/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Attributes/URL/Update.php @@ -65,7 +65,7 @@ class Update extends Action ->param('key', '', new Key(), 'Attribute Key.') ->param('required', null, new Boolean(), 'Is attribute required?') ->param('default', null, new Nullable(new URL()), 'Default value for attribute when not provided. Cannot be set when attribute is required.') - ->param('newKey', null, new Key(), 'New Attribute Key.', true) + ->param('newKey', null, new Nullable(new Key()), 'New Attribute Key.', true) ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Create.php index b810ce602f..922cc45428 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Create.php @@ -24,6 +24,7 @@ use Utopia\Database\Validator\Permissions; use Utopia\Database\Validator\UID; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\Boolean; +use Utopia\Validator\Nullable; use Utopia\Validator\Text; class Create extends Action @@ -71,7 +72,7 @@ class Create extends Action ->param('databaseId', '', new UID(), 'Database ID.') ->param('collectionId', '', new CustomId(), 'Unique Id. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.') ->param('name', '', new Text(128), 'Collection name. Max length: 128 chars.') - ->param('permissions', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE), 'An array of permissions strings. By default, no user is granted with any permissions. [Learn more about permissions](https://appwrite.io/docs/permissions).', true) + ->param('permissions', null, new Nullable(new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE)), 'An array of permissions strings. By default, no user is granted with any permissions. [Learn more about permissions](https://appwrite.io/docs/permissions).', true) ->param('documentSecurity', false, new Boolean(true), 'Enables configuring permissions for individual documents. A user needs one of document or collection level permissions to access a document. [Learn more about permissions](https://appwrite.io/docs/permissions).', true) ->param('enabled', true, new Boolean(), 'Is collection enabled? When set to \'disabled\', users cannot access the collection but Server SDKs with and API key can still read and write to the collection. No data is lost when this is toggled.', true) ->inject('response') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Action.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Action.php index c4c169b77c..08eea88e19 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Action.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Action.php @@ -4,13 +4,12 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Documen use Appwrite\Event\Event; use Appwrite\Extend\Exception; -use Appwrite\Platform\Action as AppwriteAction; +use Appwrite\Platform\Modules\Databases\Http\Databases\Action as DatabasesAction; use Utopia\Database\Database; use Utopia\Database\Document; -use Utopia\Database\Operator; use Utopia\Database\Validator\Authorization; -abstract class Action extends AppwriteAction +abstract class Action extends DatabasesAction { /** * @var string|null The current context (either 'row' or 'document') @@ -22,7 +21,7 @@ abstract class Action extends AppwriteAction */ abstract protected function getResponseModel(): string; - public function setHttpPath(string $path): AppwriteAction + public function setHttpPath(string $path): DatabasesAction { if (str_contains($path, '/tablesdb/')) { $this->context = ROWS; @@ -339,53 +338,6 @@ abstract class Action extends AppwriteAction return true; } - /** - * Parse operator strings in data array and convert them to Operator objects. - * - * @param array $data The data array that may contain operator JSON strings - * @param Document $collection The collection document to check for relationship attributes - * @return array The data array with operators converted to Operator objects - * @throws Exception If an operator string is invalid - */ - protected function parseOperators(array $data, Document $collection): array - { - $relationshipKeys = []; - foreach ($collection->getAttribute('attributes', []) as $attribute) { - if ($attribute->getAttribute('type') === Database::VAR_RELATIONSHIP) { - $relationshipKeys[$attribute->getAttribute('key')] = true; - } - } - - foreach ($data as $key => $value) { - if (\str_starts_with($key, '$')) { - continue; - } - - if (isset($relationshipKeys[$key])) { - continue; - } - - if (\is_string($value)) { - $decoded = \json_decode($value, true); - - if ( - \is_array($decoded) && - isset($decoded['method']) && - \is_string($decoded['method']) && - Operator::isMethod($decoded['method']) - ) { - try { - $data[$key] = Operator::parse($value); - } catch (\Exception $e) { - throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Invalid operator for attribute "' . $key . '": ' . $e->getMessage()); - } - } - } - } - - return $data; - } - /** * For triggering different queues for each document for a bulk documents * @param string $event diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Decrement.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Decrement.php index 0b3d0e9fb4..fb5925c291 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Decrement.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Decrement.php @@ -25,6 +25,7 @@ use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; use Utopia\Swoole\Response as SwooleResponse; +use Utopia\Validator\Nullable; use Utopia\Validator\Numeric; class Decrement extends Action @@ -77,8 +78,8 @@ class Decrement extends Action ->param('documentId', '', new UID(), 'Document ID.') ->param('attribute', '', new Key(), 'Attribute key.') ->param('value', 1, new Numeric(), 'Value to increment the attribute by. The value must be a number.', true) - ->param('min', null, new Numeric(), 'Minimum value for the attribute. If the current value is lesser than this value, an exception will be thrown.', true) - ->param('transactionId', null, new UID(), 'Transaction ID for staging the operation.', true) + ->param('min', null, new Nullable(new Numeric()), 'Minimum value for the attribute. If the current value is lesser than this value, an exception will be thrown.', true) + ->param('transactionId', null, new Nullable(new UID()), 'Transaction ID for staging the operation.', true) ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') @@ -152,6 +153,8 @@ class Decrement extends Action ); }); + $queueForEvents->reset(); + // Return successful response without actually decrementing $groupId = $this->getGroupId(); $mockDocument = new Document([ diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Increment.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Increment.php index ef64099fa8..5172890046 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Increment.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Attribute/Increment.php @@ -25,6 +25,7 @@ use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; use Utopia\Swoole\Response as SwooleResponse; +use Utopia\Validator\Nullable; use Utopia\Validator\Numeric; class Increment extends Action @@ -77,8 +78,8 @@ class Increment extends Action ->param('documentId', '', new UID(), 'Document ID.') ->param('attribute', '', new Key(), 'Attribute key.') ->param('value', 1, new Numeric(), 'Value to increment the attribute by. The value must be a number.', true) - ->param('max', null, new Numeric(), 'Maximum value for the attribute. If the current value is greater than this value, an error will be thrown.', true) - ->param('transactionId', null, new UID(), 'Transaction ID for staging the operation.', true) + ->param('max', null, new Nullable(new Numeric()), 'Maximum value for the attribute. If the current value is greater than this value, an error will be thrown.', true) + ->param('transactionId', null, new Nullable(new UID()), 'Transaction ID for staging the operation.', true) ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') @@ -152,6 +153,8 @@ class Increment extends Action ); }); + $queueForEvents->reset(); + // Return successful response without actually incrementing $groupId = $this->getGroupId(); $mockDocument = new Document([ diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Delete.php index 4b1251e016..fdc4c96fe4 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Delete.php @@ -22,6 +22,7 @@ use Utopia\Database\Query; use Utopia\Database\Validator\UID; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\ArrayList; +use Utopia\Validator\Nullable; use Utopia\Validator\Text; class Delete extends Action @@ -71,7 +72,7 @@ class Delete extends Action ->param('databaseId', '', new UID(), 'Database ID.') ->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') ->param('queries', [], new ArrayList(new Text(APP_LIMIT_ARRAY_ELEMENT_SIZE), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long.', true) - ->param('transactionId', null, new UID(), 'Transaction ID for staging the operation.', true) + ->param('transactionId', null, new Nullable(new UID()), 'Transaction ID for staging the operation.', true) ->inject('response') ->inject('dbForProject') ->inject('queueForStatsUsage') @@ -150,6 +151,8 @@ class Delete extends Action ); }); + $queueForEvents->reset(); + // Return successful response without actually deleting documents $response->dynamic(new Document([ $this->getSDKGroup() => [], diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Update.php index 2152534efe..4adf11311e 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Update.php @@ -25,6 +25,7 @@ use Utopia\Database\Validator\UID; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\ArrayList; use Utopia\Validator\JSON; +use Utopia\Validator\Nullable; use Utopia\Validator\Text; class Update extends Action @@ -75,7 +76,7 @@ class Update extends Action ->param('collectionId', '', new UID(), 'Collection ID.') ->param('data', [], new JSON(), 'Document data as JSON object. Include only attribute and value pairs to be updated.', true) ->param('queries', [], new ArrayList(new Text(APP_LIMIT_ARRAY_ELEMENT_SIZE), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long.', true) - ->param('transactionId', null, new UID(), 'Transaction ID for staging the operation.', true) + ->param('transactionId', null, new Nullable(new UID()), 'Transaction ID for staging the operation.', true) ->inject('response') ->inject('dbForProject') ->inject('queueForStatsUsage') @@ -107,7 +108,9 @@ class Update extends Action throw new Exception($this->getParentNotFoundException()); } - $data = $this->parseOperators($data, $collection); + if ($transactionId === null) { + $data = $this->parseOperators($data, $collection); + } $hasRelationships = \array_filter( $collection->getAttribute('attributes', []), @@ -174,6 +177,8 @@ class Update extends Action ); }); + $queueForEvents->reset(); + // Return successful response without actually updating documents $response->dynamic(new Document([ $this->getSDKGroup() => [], diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Upsert.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Upsert.php index 6c869723a7..d30135de75 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Upsert.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Bulk/Upsert.php @@ -23,6 +23,7 @@ use Utopia\Database\Validator\UID; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\ArrayList; use Utopia\Validator\JSON; +use Utopia\Validator\Nullable; class Upsert extends Action { @@ -73,7 +74,7 @@ class Upsert extends Action ->param('databaseId', '', new UID(), 'Database ID.') ->param('collectionId', '', new UID(), 'Collection ID.') ->param('documents', [], fn (array $plan) => new ArrayList(new JSON(), $plan['databasesBatchSize'] ?? APP_LIMIT_DATABASE_BATCH), 'Array of document data as JSON objects. May contain partial documents.', false, ['plan']) - ->param('transactionId', null, new UID(), 'Transaction ID for staging the operation.', true) + ->param('transactionId', null, new Nullable(new UID()), 'Transaction ID for staging the operation.', true) ->inject('response') ->inject('dbForProject') ->inject('queueForStatsUsage') @@ -107,7 +108,9 @@ class Upsert extends Action } foreach ($documents as $key => $document) { - $document = $this->parseOperators($document, $collection); + if ($transactionId === null) { + $document = $this->parseOperators($document, $collection); + } $document = $this->removeReadonlyAttributes($document, privileged: true); $documents[$key] = new Document($document); } @@ -149,6 +152,8 @@ class Upsert extends Action ); }); + $queueForEvents->reset(); + // Return successful response without actually upserting documents $response->dynamic(new Document([ $this->getSDKGroup() => [], diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php index 521190d3dc..1320a0c9bc 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Create.php @@ -29,6 +29,7 @@ use Utopia\Database\Validator\UID; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\ArrayList; use Utopia\Validator\JSON; +use Utopia\Validator\Nullable; class Create extends Action { @@ -119,9 +120,9 @@ class Create extends Action ->param('documentId', '', new CustomId(), 'Document ID. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.', true) ->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection). Make sure to define attributes before creating documents.') ->param('data', [], new JSON(), 'Document data as JSON object.', true, example: '{"username":"walter.obrien","email":"walter.obrien@example.com","fullName":"Walter O\'Brien","age":30,"isAdmin":false}') - ->param('permissions', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE, [Database::PERMISSION_READ, Database::PERMISSION_UPDATE, Database::PERMISSION_DELETE, Database::PERMISSION_WRITE]), 'An array of permissions strings. By default, only the current user is granted all permissions. [Learn more about permissions](https://appwrite.io/docs/permissions).', true) + ->param('permissions', null, new Nullable(new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE, [Database::PERMISSION_READ, Database::PERMISSION_UPDATE, Database::PERMISSION_DELETE, Database::PERMISSION_WRITE])), 'An array of permissions strings. By default, only the current user is granted all permissions. [Learn more about permissions](https://appwrite.io/docs/permissions).', true) ->param('documents', [], fn (array $plan) => new ArrayList(new JSON(), $plan['databasesBatchSize'] ?? APP_LIMIT_DATABASE_BATCH), 'Array of documents data as JSON objects.', true, ['plan']) - ->param('transactionId', null, new UID(), 'Transaction ID for staging the operation.', true) + ->param('transactionId', null, new Nullable(new UID()), 'Transaction ID for staging the operation.', true) ->inject('response') ->inject('dbForProject') ->inject('user') @@ -412,6 +413,8 @@ class Create extends Action ); }); + $queueForEvents->reset(); + // Return successful response without actually creating documents if ($isBulk) { $response->dynamic(new Document([ diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Delete.php index a4cef59e5f..194bf27816 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Delete.php @@ -21,6 +21,7 @@ use Utopia\Database\Helpers\ID; use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\UID; use Utopia\Swoole\Response as SwooleResponse; +use Utopia\Validator\Nullable; class Delete extends Action { @@ -74,7 +75,7 @@ class Delete extends Action ->param('databaseId', '', new UID(), 'Database ID.') ->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') ->param('documentId', '', new UID(), 'Document ID.') - ->param('transactionId', null, new UID(), 'Transaction ID for staging the operation.', true) + ->param('transactionId', null, new Nullable(new UID()), 'Transaction ID for staging the operation.', true) ->inject('requestTimestamp') ->inject('response') ->inject('dbForProject') @@ -176,6 +177,8 @@ class Delete extends Action ); }); + $queueForEvents->reset(); + // Return successful response without actually deleting document $response->noContent(); return; diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Get.php index 3d8cd9198c..c92a487697 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Get.php @@ -19,6 +19,7 @@ use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\UID; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\ArrayList; +use Utopia\Validator\Nullable; use Utopia\Validator\Text; class Get extends Action @@ -64,7 +65,7 @@ class Get extends Action ->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') ->param('documentId', '', new UID(), 'Document ID.') ->param('queries', [], new ArrayList(new Text(APP_LIMIT_ARRAY_ELEMENT_SIZE), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long.', true) - ->param('transactionId', null, new UID(), 'Transaction ID to read uncommitted changes within the transaction.', true) + ->param('transactionId', null, new Nullable(new UID()), 'Transaction ID to read uncommitted changes within the transaction.', true) ->inject('response') ->inject('dbForProject') ->inject('queueForStatsUsage') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php index ed83f3fdd3..ecdbc738a1 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Update.php @@ -27,6 +27,7 @@ use Utopia\Database\Validator\Permissions; use Utopia\Database\Validator\UID; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\JSON; +use Utopia\Validator\Nullable; class Update extends Action { @@ -77,8 +78,8 @@ class Update extends Action ->param('collectionId', '', new UID(), 'Collection ID.') ->param('documentId', '', new UID(), 'Document ID.') ->param('data', [], new JSON(), 'Document data as JSON object. Include only attribute and value pairs to be updated.', true) - ->param('permissions', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE, [Database::PERMISSION_READ, Database::PERMISSION_UPDATE, Database::PERMISSION_DELETE, Database::PERMISSION_WRITE]), 'An array of permissions strings. By default, the current permissions are inherited. [Learn more about permissions](https://appwrite.io/docs/permissions).', true) - ->param('transactionId', null, new UID(), 'Transaction ID for staging the operation.', true) + ->param('permissions', null, new Nullable(new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE, [Database::PERMISSION_READ, Database::PERMISSION_UPDATE, Database::PERMISSION_DELETE, Database::PERMISSION_WRITE])), 'An array of permissions strings. By default, the current permissions are inherited. [Learn more about permissions](https://appwrite.io/docs/permissions).', true) + ->param('transactionId', null, new Nullable(new UID()), 'Transaction ID for staging the operation.', true) ->inject('requestTimestamp') ->inject('response') ->inject('dbForProject') @@ -112,7 +113,9 @@ class Update extends Action throw new Exception($this->getParentNotFoundException()); } - $data = $this->parseOperators($data, $collection); + if ($transactionId === null) { + $data = $this->parseOperators($data, $collection); + } // Read permission should not be required for update /** @var Document $document */ @@ -243,7 +246,6 @@ class Update extends Action ->addMetric(METRIC_DATABASES_OPERATIONS_WRITES, max($operations, 1)) ->addMetric(str_replace('{databaseInternalId}', $database->getSequence(), METRIC_DATABASE_ID_OPERATIONS_WRITES), $operations); - // Handle transaction staging if ($transactionId !== null) { $transaction = ($isAPIKey || $isPrivilegedUser) @@ -302,6 +304,9 @@ class Update extends Action ...$document->getArrayCopy(), ...$data ]); + + $queueForEvents->reset(); + $response ->setStatusCode(SwooleResponse::STATUS_CODE_OK) ->dynamic($mockDocument, $this->getResponseModel()); diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Upsert.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Upsert.php index 8d47410649..7901865b62 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Upsert.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/Upsert.php @@ -28,6 +28,7 @@ use Utopia\Database\Validator\Permissions; use Utopia\Database\Validator\UID; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\JSON; +use Utopia\Validator\Nullable; class Upsert extends Action { @@ -80,8 +81,8 @@ class Upsert extends Action ->param('collectionId', '', new UID(), 'Collection ID.') ->param('documentId', '', new CustomId(), 'Document ID.') ->param('data', [], new JSON(), 'Document data as JSON object. Include all required attributes of the document to be created or updated.') - ->param('permissions', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE, [Database::PERMISSION_READ, Database::PERMISSION_UPDATE, Database::PERMISSION_DELETE, Database::PERMISSION_WRITE]), 'An array of permissions strings. By default, the current permissions are inherited. [Learn more about permissions](https://appwrite.io/docs/permissions).', true) - ->param('transactionId', null, new UID(), 'Transaction ID for staging the operation.', true) + ->param('permissions', null, new Nullable(new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE, [Database::PERMISSION_READ, Database::PERMISSION_UPDATE, Database::PERMISSION_DELETE, Database::PERMISSION_WRITE])), 'An array of permissions strings. By default, the current permissions are inherited. [Learn more about permissions](https://appwrite.io/docs/permissions).', true) + ->param('transactionId', null, new Nullable(new UID()), 'Transaction ID for staging the operation.', true) ->inject('requestTimestamp') ->inject('response') ->inject('user') @@ -118,7 +119,9 @@ class Upsert extends Action throw new Exception($this->getParentNotFoundException()); } - $data = $this->parseOperators($data, $collection); + if ($transactionId === null) { + $data = $this->parseOperators($data, $collection); + } $allowedPermissions = [ Database::PERMISSION_READ, @@ -302,6 +305,8 @@ class Upsert extends Action ); }); + $queueForEvents->reset(); + // Return successful response without actually upserting document $groupId = $this->getGroupId(); $mockDocument = new Document([ diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/XList.php index f159043531..630170a031 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Documents/XList.php @@ -23,6 +23,7 @@ use Utopia\Database\Validator\UID; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\ArrayList; use Utopia\Validator\Boolean; +use Utopia\Validator\Nullable; use Utopia\Validator\Text; class XList extends Action @@ -67,7 +68,7 @@ class XList extends Action ->param('databaseId', '', new UID(), 'Database ID.') ->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection).') ->param('queries', [], new ArrayList(new Text(APP_LIMIT_ARRAY_ELEMENT_SIZE), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long.', true) - ->param('transactionId', null, new UID(), 'Transaction ID to read uncommitted changes within the transaction.', true) + ->param('transactionId', null, new Nullable(new UID()), 'Transaction ID to read uncommitted changes within the transaction.', true) ->param('total', true, new Boolean(true), 'When set to false, the total count returned will be 0 and will not be calculated.', true) ->inject('response') ->inject('dbForProject') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Update.php index 49870002ce..7575c9803b 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Collections/Update.php @@ -17,6 +17,7 @@ use Utopia\Database\Validator\Permissions; use Utopia\Database\Validator\UID; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\Boolean; +use Utopia\Validator\Nullable; use Utopia\Validator\Text; class Update extends Action @@ -64,7 +65,7 @@ class Update extends Action ->param('databaseId', '', new UID(), 'Database ID.') ->param('collectionId', '', new UID(), 'Collection ID.') ->param('name', null, new Text(128), 'Collection name. Max length: 128 chars.') - ->param('permissions', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE), 'An array of permission strings. By default, the current permissions are inherited. [Learn more about permissions](https://appwrite.io/docs/permissions).', true) + ->param('permissions', null, new Nullable(new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE)), 'An array of permission strings. By default, the current permissions are inherited. [Learn more about permissions](https://appwrite.io/docs/permissions).', true) ->param('documentSecurity', false, new Boolean(true), 'Enables configuring permissions for individual documents. A user needs one of document or collection level permissions to access a document. [Learn more about permissions](https://appwrite.io/docs/permissions).', true) ->param('enabled', true, new Boolean(), 'Is collection enabled? When set to \'disabled\', users cannot access the collection but Server SDKs with and API key can still read and write to the collection. No data is lost when this is toggled.', true) ->inject('response') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Action.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Action.php index 8915ae6141..e2a4491736 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Action.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Action.php @@ -2,16 +2,16 @@ namespace Appwrite\Platform\Modules\Databases\Http\Databases\Transactions; -use Utopia\Platform\Action as UtopiaAction; +use Appwrite\Platform\Modules\Databases\Http\Databases\Action as DatabasesAction; -abstract class Action extends UtopiaAction +abstract class Action extends DatabasesAction { /** * The current API context (either 'table' or 'collection'). */ private ?string $context = COLLECTIONS; - public function setHttpPath(string $path): UtopiaAction + public function setHttpPath(string $path): DatabasesAction { if (\str_contains($path, '/tablesdb')) { $this->context = TABLES; diff --git a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php index 6e2bd63827..899c2b3eaf 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/Databases/Transactions/Update.php @@ -149,6 +149,7 @@ class Update extends Action ])); $state = []; + $collections = []; foreach ($operations as $operation) { $databaseInternalId = $operation['databaseInternalId']; @@ -159,6 +160,21 @@ class Update extends Action $action = $operation['action']; $data = $operation['data']; + if ($data instanceof Document) { + $data = $data->getArrayCopy(); + } + + if (!isset($collections[$collectionId])) { + $collections[$collectionId] = Authorization::skip( + fn () => $dbForProject->getCollection($collectionId) + ); + } + $collection = $collections[$collectionId]; + + if (\is_array($data) && !empty($data)) { + $data = $this->parseOperators($data, $collection); + } + if ($action === 'delete' && $documentId && empty($data)) { $doc = $dbForProject->getDocument($collectionId, $documentId); if (!$doc->isEmpty()) { @@ -172,10 +188,6 @@ class Update extends Action $databaseOperations[$databaseInternalId] = ($databaseOperations[$databaseInternalId] ?? 0) + 1; } - if ($data instanceof Document) { - $data = $data->getArrayCopy(); - } - switch ($action) { case 'create': $this->handleCreateOperation($dbForProject, $collectionId, $documentId, $data, $createdAt, $state); diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Boolean/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Boolean/Create.php index 039964b2c6..d9dfc15ca8 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Boolean/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Boolean/Create.php @@ -11,6 +11,7 @@ use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\Boolean; +use Utopia\Validator\Nullable; class Create extends BooleanCreate { @@ -53,7 +54,7 @@ class Create extends BooleanCreate ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/references/cloud/server-dart/tablesDB#createTable).') ->param('key', '', new Key(), 'Column Key.') ->param('required', null, new Boolean(), 'Is column required?') - ->param('default', null, new Boolean(), 'Default value for column when not provided. Cannot be set when column is required.', true) + ->param('default', null, new Nullable(new Boolean()), 'Default value for column when not provided. Cannot be set when column is required.', true) ->param('array', false, new Boolean(), 'Is column an array?', true) ->inject('response') ->inject('dbForProject') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Boolean/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Boolean/Update.php index 7df901a1cc..3b83e71c12 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Boolean/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Boolean/Update.php @@ -57,7 +57,7 @@ class Update extends BooleanUpdate ->param('key', '', new Key(), 'Column Key.') ->param('required', null, new Boolean(), 'Is column required?') ->param('default', null, new Nullable(new Boolean()), 'Default value for column when not provided. Cannot be set when column is required.') - ->param('newKey', null, new Key(), 'New Column Key.', true) + ->param('newKey', null, new Nullable(new Key()), 'New Column Key.', true) ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Datetime/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Datetime/Create.php index db5a3059f1..69d6bd2b4b 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Datetime/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Datetime/Create.php @@ -13,6 +13,7 @@ use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\Boolean; +use Utopia\Validator\Nullable; class Create extends DatetimeCreate { @@ -55,7 +56,7 @@ class Create extends DatetimeCreate ->param('tableId', '', new UID(), 'Table ID.') ->param('key', '', new Key(), 'Column Key.') ->param('required', null, new Boolean(), 'Is column required?') - ->param('default', null, fn (Database $dbForProject) => new DatetimeValidator($dbForProject->getAdapter()->getMinDateTime(), $dbForProject->getAdapter()->getMaxDateTime()), 'Default value for the column in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. Cannot be set when column is required.', true, ['dbForProject']) + ->param('default', null, fn (Database $dbForProject) => new Nullable(new DatetimeValidator($dbForProject->getAdapter()->getMinDateTime(), $dbForProject->getAdapter()->getMaxDateTime())), 'Default value for the column in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. Cannot be set when column is required.', true, ['dbForProject']) ->param('array', false, new Boolean(), 'Is column an array?', true) ->inject('response') ->inject('dbForProject') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Datetime/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Datetime/Update.php index 151422af75..255abf00bf 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Datetime/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Datetime/Update.php @@ -59,7 +59,7 @@ class Update extends DatetimeUpdate ->param('key', '', new Key(), 'Column Key.') ->param('required', null, new Boolean(), 'Is column required?') ->param('default', null, fn (Database $dbForProject) => new Nullable(new DatetimeValidator($dbForProject->getAdapter()->getMinDateTime(), $dbForProject->getAdapter()->getMaxDateTime())), 'Default value for column when not provided. Cannot be set when column is required.', injections: ['dbForProject']) - ->param('newKey', null, new Key(), 'New Column Key.', true) + ->param('newKey', null, new Nullable(new Key()), 'New Column Key.', true) ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Email/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Email/Create.php index 3a8d492e4e..58ea459d0f 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Email/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Email/Create.php @@ -12,6 +12,7 @@ use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\Boolean; +use Utopia\Validator\Nullable; class Create extends EmailCreate { @@ -54,7 +55,7 @@ class Create extends EmailCreate ->param('tableId', '', new UID(), 'Table ID.') ->param('key', '', new Key(), 'Column Key.') ->param('required', null, new Boolean(), 'Is column required?') - ->param('default', null, new Email(), 'Default value for column when not provided. Cannot be set when column is required.', true) + ->param('default', null, new Nullable(new Email()), 'Default value for column when not provided. Cannot be set when column is required.', true) ->param('array', false, new Boolean(), 'Is column an array?', true) ->inject('response') ->inject('dbForProject') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Email/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Email/Update.php index 4d32489357..0105345555 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Email/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Email/Update.php @@ -58,7 +58,7 @@ class Update extends EmailUpdate ->param('key', '', new Key(), 'Column Key.') ->param('required', null, new Boolean(), 'Is column required?') ->param('default', null, new Nullable(new Email()), 'Default value for column when not provided. Cannot be set when column is required.') - ->param('newKey', null, new Key(), 'New Column Key.', true) + ->param('newKey', null, new Nullable(new Key()), 'New Column Key.', true) ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Enum/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Enum/Create.php index 68dc2f8e08..8ab8019626 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Enum/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Enum/Create.php @@ -13,6 +13,7 @@ use Utopia\Database\Validator\UID; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\ArrayList; use Utopia\Validator\Boolean; +use Utopia\Validator\Nullable; use Utopia\Validator\Text; class Create extends EnumCreate @@ -57,7 +58,7 @@ class Create extends EnumCreate ->param('key', '', new Key(), 'Column Key.') ->param('elements', [], new ArrayList(new Text(Database::LENGTH_KEY), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Array of enum values.') ->param('required', null, new Boolean(), 'Is column required?') - ->param('default', null, new Text(0), 'Default value for column when not provided. Cannot be set when column is required.', true) + ->param('default', null, new Nullable(new Text(0)), 'Default value for column when not provided. Cannot be set when column is required.', true) ->param('array', false, new Boolean(), 'Is column an array?', true) ->inject('response') ->inject('dbForProject') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Enum/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Enum/Update.php index 3b611a5fde..968c84c56b 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Enum/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Enum/Update.php @@ -61,7 +61,7 @@ class Update extends EnumUpdate ->param('elements', null, new ArrayList(new Text(Database::LENGTH_KEY), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Updated list of enum values.') ->param('required', null, new Boolean(), 'Is column required?') ->param('default', null, new Nullable(new Text(0)), 'Default value for column when not provided. Cannot be set when column is required.') - ->param('newKey', null, new Key(), 'New Column Key.', true) + ->param('newKey', null, new Nullable(new Key()), 'New Column Key.', true) ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Float/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Float/Create.php index 9fe6987cab..21e855d912 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Float/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Float/Create.php @@ -12,6 +12,7 @@ use Utopia\Database\Validator\UID; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\Boolean; use Utopia\Validator\FloatValidator; +use Utopia\Validator\Nullable; class Create extends FloatCreate { @@ -54,9 +55,9 @@ class Create extends FloatCreate ->param('tableId', '', new UID(), 'Table ID.') ->param('key', '', new Key(), 'Column Key.') ->param('required', null, new Boolean(), 'Is column required?') - ->param('min', null, new FloatValidator(), 'Minimum value', true) - ->param('max', null, new FloatValidator(), 'Maximum value', true) - ->param('default', null, new FloatValidator(), 'Default value. Cannot be set when required.', true) + ->param('min', null, new Nullable(new FloatValidator()), 'Minimum value', true) + ->param('max', null, new Nullable(new FloatValidator()), 'Maximum value', true) + ->param('default', null, new Nullable(new FloatValidator()), 'Default value. Cannot be set when required.', true) ->param('array', false, new Boolean(), 'Is column an array?', true) ->inject('response') ->inject('dbForProject') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Float/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Float/Update.php index 023e2e834e..6a479ea266 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Float/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Float/Update.php @@ -57,10 +57,10 @@ class Update extends FloatUpdate ->param('tableId', '', new UID(), 'Table ID.') ->param('key', '', new Key(), 'Column Key.') ->param('required', null, new Boolean(), 'Is column required?') - ->param('min', null, new FloatValidator(), 'Minimum value', true) - ->param('max', null, new FloatValidator(), 'Maximum value', true) + ->param('min', null, new Nullable(new FloatValidator()), 'Minimum value', true) + ->param('max', null, new Nullable(new FloatValidator()), 'Maximum value', true) ->param('default', null, new Nullable(new FloatValidator()), 'Default value. Cannot be set when required.') - ->param('newKey', null, new Key(), 'New Column Key.', true) + ->param('newKey', null, new Nullable(new Key()), 'New Column Key.', true) ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/IP/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/IP/Create.php index 81ca8da81f..08912ebb56 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/IP/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/IP/Create.php @@ -12,6 +12,7 @@ use Utopia\Database\Validator\UID; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\Boolean; use Utopia\Validator\IP; +use Utopia\Validator\Nullable; class Create extends IPCreate { @@ -54,7 +55,7 @@ class Create extends IPCreate ->param('tableId', '', new UID(), 'Table ID.') ->param('key', '', new Key(), 'Column Key.') ->param('required', null, new Boolean(), 'Is column required?') - ->param('default', null, new IP(), 'Default value. Cannot be set when column is required.', true) + ->param('default', null, new Nullable(new IP()), 'Default value. Cannot be set when column is required.', true) ->param('array', false, new Boolean(), 'Is column an array?', true) ->inject('response') ->inject('dbForProject') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/IP/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/IP/Update.php index 0db95b0206..9df9f573a2 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/IP/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/IP/Update.php @@ -58,7 +58,7 @@ class Update extends IPUpdate ->param('key', '', new Key(), 'Column Key.') ->param('required', null, new Boolean(), 'Is column required?') ->param('default', null, new Nullable(new IP()), 'Default value. Cannot be set when column is required.') - ->param('newKey', null, new Key(), 'New Column Key.', true) + ->param('newKey', null, new Nullable(new Key()), 'New Column Key.', true) ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Integer/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Integer/Create.php index dfca51a6c5..eb9230f48f 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Integer/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Integer/Create.php @@ -12,6 +12,7 @@ use Utopia\Database\Validator\UID; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\Boolean; use Utopia\Validator\Integer; +use Utopia\Validator\Nullable; class Create extends IntegerCreate { @@ -54,9 +55,9 @@ class Create extends IntegerCreate ->param('tableId', '', new UID(), 'Table ID.') ->param('key', '', new Key(), 'Column Key.') ->param('required', null, new Boolean(), 'Is column required?') - ->param('min', null, new Integer(), 'Minimum value', true) - ->param('max', null, new Integer(), 'Maximum value', true) - ->param('default', null, new Integer(), 'Default value. Cannot be set when column is required.', true) + ->param('min', null, new Nullable(new Integer()), 'Minimum value', true) + ->param('max', null, new Nullable(new Integer()), 'Maximum value', true) + ->param('default', null, new Nullable(new Integer()), 'Default value. Cannot be set when column is required.', true) ->param('array', false, new Boolean(), 'Is column an array?', true) ->inject('response') ->inject('dbForProject') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Integer/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Integer/Update.php index a1568d069b..6c707f1655 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Integer/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Integer/Update.php @@ -57,10 +57,10 @@ class Update extends IntegerUpdate ->param('tableId', '', new UID(), 'Table ID.') ->param('key', '', new Key(), 'Column Key.') ->param('required', null, new Boolean(), 'Is column required?') - ->param('min', null, new Integer(), 'Minimum value', true) - ->param('max', null, new Integer(), 'Maximum value', true) + ->param('min', null, new Nullable(new Integer()), 'Minimum value', true) + ->param('max', null, new Nullable(new Integer()), 'Maximum value', true) ->param('default', null, new Nullable(new Integer()), 'Default value. Cannot be set when column is required.') - ->param('newKey', null, new Key(), 'New Column Key.', true) + ->param('newKey', null, new Nullable(new Key()), 'New Column Key.', true) ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Line/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Line/Update.php index d3823445a2..fd7d200eb3 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Line/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Line/Update.php @@ -59,7 +59,7 @@ class Update extends LineUpdate ->param('key', '', new Key(), 'Column Key.') ->param('required', null, new Boolean(), 'Is column required?') ->param('default', null, new Nullable(new Spatial(Database::VAR_LINESTRING)), 'Default value for column when not provided, two-dimensional array of coordinate pairs, [[longitude, latitude], [longitude, latitude], …], listing the vertices of the line in order. Cannot be set when column is required.', true) - ->param('newKey', null, new Key(), 'New Column Key.', true) + ->param('newKey', null, new Nullable(new Key()), 'New Column Key.', true) ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Point/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Point/Update.php index 3c855e137c..8b8dd7b66c 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Point/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Point/Update.php @@ -59,7 +59,7 @@ class Update extends PointUpdate ->param('key', '', new Key(), 'Column Key.') ->param('required', null, new Boolean(), 'Is column required?') ->param('default', null, new Nullable(new Spatial(Database::VAR_POINT)), 'Default value for column when not provided, array of two numbers [longitude, latitude], representing a single coordinate. Cannot be set when column is required.', true) - ->param('newKey', null, new Key(), 'New Column Key.', true) + ->param('newKey', null, new Nullable(new Key()), 'New Column Key.', true) ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Polygon/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Polygon/Update.php index 866bbaf8b0..c49351fc59 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Polygon/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Polygon/Update.php @@ -59,7 +59,7 @@ class Update extends PolygonUpdate ->param('key', '', new Key(), 'Column Key.') ->param('required', null, new Boolean(), 'Is column required?') ->param('default', null, new Nullable(new Spatial(Database::VAR_POLYGON)), 'Default value for column when not provided, three-dimensional array where the outer array holds one or more linear rings, [[[longitude, latitude], …], …], the first ring is the exterior boundary, any additional rings are interior holes, and each ring must start and end with the same coordinate pair. Cannot be set when column is required.', true) - ->param('newKey', null, new Key(), 'New Column Key.', true) + ->param('newKey', null, new Nullable(new Key()), 'New Column Key.', true) ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Relationship/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Relationship/Create.php index 0a6c76d8c5..cccc61beaa 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Relationship/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Relationship/Create.php @@ -12,6 +12,7 @@ use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\Boolean; +use Utopia\Validator\Nullable; use Utopia\Validator\WhiteList; class Create extends RelationshipCreate @@ -61,8 +62,8 @@ class Create extends RelationshipCreate Database::RELATION_ONE_TO_MANY ], true), 'Relation type') ->param('twoWay', false, new Boolean(), 'Is Two Way?', true) - ->param('key', null, new Key(), 'Column Key.', true) - ->param('twoWayKey', null, new Key(), 'Two Way Column Key.', true) + ->param('key', null, new Nullable(new Key()), 'Column Key.', true) + ->param('twoWayKey', null, new Nullable(new Key()), 'Two Way Column Key.', true) ->param('onDelete', Database::RELATION_MUTATE_RESTRICT, new WhiteList([ Database::RELATION_MUTATE_CASCADE, Database::RELATION_MUTATE_RESTRICT, diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Relationship/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Relationship/Update.php index b645454be1..5953d600f8 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Relationship/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/Relationship/Update.php @@ -12,6 +12,7 @@ use Utopia\Database\Database; use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; use Utopia\Swoole\Response as SwooleResponse; +use Utopia\Validator\Nullable; use Utopia\Validator\WhiteList; class Update extends RelationshipUpdate @@ -55,12 +56,12 @@ class Update extends RelationshipUpdate ->param('databaseId', '', new UID(), 'Database ID.') ->param('tableId', '', new UID(), 'Table ID.') ->param('key', '', new Key(), 'Column Key.') - ->param('onDelete', null, new WhiteList([ + ->param('onDelete', null, new Nullable(new WhiteList([ Database::RELATION_MUTATE_CASCADE, Database::RELATION_MUTATE_RESTRICT, Database::RELATION_MUTATE_SET_NULL - ], true), 'Constraints option', true) - ->param('newKey', null, new Key(), 'New Column Key.', true) + ], true)), 'Constraints option', true) + ->param('newKey', null, new Nullable(new Key()), 'New Column Key.', true) ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/String/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/String/Create.php index 6fe9fd679c..8d37c9011b 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/String/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/String/Create.php @@ -12,6 +12,7 @@ use Utopia\Database\Validator\UID; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator; use Utopia\Validator\Boolean; +use Utopia\Validator\Nullable; use Utopia\Validator\Range; use Utopia\Validator\Text; @@ -57,7 +58,7 @@ class Create extends StringCreate ->param('key', '', new Key(), 'Column Key.') ->param('size', null, new Range(1, APP_DATABASE_ATTRIBUTE_STRING_MAX_LENGTH, Validator::TYPE_INTEGER), 'Column size for text columns, in number of characters.') ->param('required', null, new Boolean(), 'Is column required?') - ->param('default', null, new Text(0, 0), 'Default value for column when not provided. Cannot be set when column is required.', true) + ->param('default', null, new Nullable(new Text(0, 0)), 'Default value for column when not provided. Cannot be set when column is required.', true) ->param('array', false, new Boolean(), 'Is column an array?', true) ->param('encrypt', false, new Boolean(), 'Toggle encryption for the column. Encryption enhances security by not storing any plain text values in the database. However, encrypted columns cannot be queried.', true) ->inject('response') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/String/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/String/Update.php index 5ec9b78dda..43083616ba 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/String/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/String/Update.php @@ -60,8 +60,8 @@ class Update extends StringUpdate ->param('key', '', new Key(), 'Column Key.') ->param('required', null, new Boolean(), 'Is column required?') ->param('default', null, new Nullable(new Text(0, 0)), 'Default value for column when not provided. Cannot be set when column is required.') - ->param('size', null, new Range(1, APP_DATABASE_ATTRIBUTE_STRING_MAX_LENGTH, Validator::TYPE_INTEGER), 'Maximum size of the string column.', true) - ->param('newKey', null, new Key(), 'New Column Key.', true) + ->param('size', null, new Nullable(new Range(1, APP_DATABASE_ATTRIBUTE_STRING_MAX_LENGTH, Validator::TYPE_INTEGER)), 'Maximum size of the string column.', true) + ->param('newKey', null, new Nullable(new Key()), 'New Column Key.', true) ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/URL/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/URL/Create.php index 99ec36b721..3fd6f1e463 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/URL/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/URL/Create.php @@ -11,6 +11,7 @@ use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\Boolean; +use Utopia\Validator\Nullable; use Utopia\Validator\URL; class Create extends URLCreate @@ -54,7 +55,7 @@ class Create extends URLCreate ->param('tableId', '', new UID(), 'Table ID.') ->param('key', '', new Key(), 'Column Key.') ->param('required', null, new Boolean(), 'Is column required?') - ->param('default', null, new URL(), 'Default value for column when not provided. Cannot be set when column is required.', true) + ->param('default', null, new Nullable(new URL()), 'Default value for column when not provided. Cannot be set when column is required.', true) ->param('array', false, new Boolean(), 'Is column an array?', true) ->inject('response') ->inject('dbForProject') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/URL/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/URL/Update.php index 51168b0383..64dfdfbf69 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/URL/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Columns/URL/Update.php @@ -58,7 +58,7 @@ class Update extends URLUpdate ->param('key', '', new Key(), 'Column Key.') ->param('required', null, new Boolean(), 'Is column required?') ->param('default', null, new Nullable(new URL()), 'Default value for column when not provided. Cannot be set when column is required.') - ->param('newKey', null, new Key(), 'New Column Key.', true) + ->param('newKey', null, new Nullable(new Key()), 'New Column Key.', true) ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Create.php index 4f62200d7c..68d3e772ec 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Create.php @@ -13,6 +13,7 @@ use Utopia\Database\Validator\Permissions; use Utopia\Database\Validator\UID; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\Boolean; +use Utopia\Validator\Nullable; use Utopia\Validator\Text; class Create extends CollectionCreate @@ -56,7 +57,7 @@ class Create extends CollectionCreate ->param('databaseId', '', new UID(), 'Database ID.') ->param('tableId', '', new CustomId(), 'Unique Id. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.') ->param('name', '', new Text(128), 'Table name. Max length: 128 chars.') - ->param('permissions', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE), 'An array of permissions strings. By default, no user is granted with any permissions. [Learn more about permissions](https://appwrite.io/docs/permissions).', true) + ->param('permissions', null, new Nullable(new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE)), 'An array of permissions strings. By default, no user is granted with any permissions. [Learn more about permissions](https://appwrite.io/docs/permissions).', true) ->param('rowSecurity', false, new Boolean(true), 'Enables configuring permissions for individual rows. A user needs one of row or table level permissions to access a row. [Learn more about permissions](https://appwrite.io/docs/permissions).', true) ->param('enabled', true, new Boolean(), 'Is table enabled? When set to \'disabled\', users cannot access the table but Server SDKs with and API key can still read and write to the table. No data is lost when this is toggled.', true) ->inject('response') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Bulk/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Bulk/Delete.php index 86e9a48f63..accb0392fe 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Bulk/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Bulk/Delete.php @@ -11,6 +11,7 @@ use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Validator\UID; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\ArrayList; +use Utopia\Validator\Nullable; use Utopia\Validator\Text; class Delete extends DocumentsDelete @@ -56,7 +57,7 @@ class Delete extends DocumentsDelete ->param('databaseId', '', new UID(), 'Database ID.') ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/references/cloud/server-dart/tablesDB#createTable).') ->param('queries', [], new ArrayList(new Text(APP_LIMIT_ARRAY_ELEMENT_SIZE), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long.', true) - ->param('transactionId', null, new UID(), 'Transaction ID for staging the operation.', true) + ->param('transactionId', null, new Nullable(new UID()), 'Transaction ID for staging the operation.', true) ->inject('response') ->inject('dbForProject') ->inject('queueForStatsUsage') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Bulk/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Bulk/Update.php index 5005ab4f41..856f17ed10 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Bulk/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Bulk/Update.php @@ -12,6 +12,7 @@ use Utopia\Database\Validator\UID; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\ArrayList; use Utopia\Validator\JSON; +use Utopia\Validator\Nullable; use Utopia\Validator\Text; class Update extends DocumentsUpdate @@ -58,7 +59,7 @@ class Update extends DocumentsUpdate ->param('tableId', '', new UID(), 'Table ID.') ->param('data', [], new JSON(), 'Row data as JSON object. Include only column and value pairs to be updated.', true) ->param('queries', [], new ArrayList(new Text(APP_LIMIT_ARRAY_ELEMENT_SIZE), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long.', true) - ->param('transactionId', null, new UID(), 'Transaction ID for staging the operation.', true) + ->param('transactionId', null, new Nullable(new UID()), 'Transaction ID for staging the operation.', true) ->inject('response') ->inject('dbForProject') ->inject('queueForStatsUsage') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Bulk/Upsert.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Bulk/Upsert.php index d0a1f08003..492af25e9f 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Bulk/Upsert.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Bulk/Upsert.php @@ -12,6 +12,7 @@ use Utopia\Database\Validator\UID; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\ArrayList; use Utopia\Validator\JSON; +use Utopia\Validator\Nullable; class Upsert extends DocumentsUpsert { @@ -58,7 +59,7 @@ class Upsert extends DocumentsUpsert ->param('databaseId', '', new UID(), 'Database ID.') ->param('tableId', '', new UID(), 'Table ID.') ->param('rows', [], fn (array $plan) => new ArrayList(new JSON(), $plan['databasesBatchSize'] ?? APP_LIMIT_DATABASE_BATCH), 'Array of row data as JSON objects. May contain partial rows.', false, ['plan']) - ->param('transactionId', null, new UID(), 'Transaction ID for staging the operation.', true) + ->param('transactionId', null, new Nullable(new UID()), 'Transaction ID for staging the operation.', true) ->inject('response') ->inject('dbForProject') ->inject('queueForStatsUsage') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Column/Decrement.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Column/Decrement.php index d42cf5e9eb..42f2919ce1 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Column/Decrement.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Column/Decrement.php @@ -11,6 +11,7 @@ use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; use Utopia\Swoole\Response as SwooleResponse; +use Utopia\Validator\Nullable; use Utopia\Validator\Numeric; class Decrement extends DecrementDocumentAttribute @@ -59,8 +60,8 @@ class Decrement extends DecrementDocumentAttribute ->param('rowId', '', new UID(), 'Row ID.') ->param('column', '', new Key(), 'Column key.') ->param('value', 1, new Numeric(), 'Value to increment the column by. The value must be a number.', true) - ->param('min', null, new Numeric(), 'Minimum value for the column. If the current value is lesser than this value, an exception will be thrown.', true) - ->param('transactionId', null, new UID(), 'Transaction ID for staging the operation.', true) + ->param('min', null, new Nullable(new Numeric()), 'Minimum value for the column. If the current value is lesser than this value, an exception will be thrown.', true) + ->param('transactionId', null, new Nullable(new UID()), 'Transaction ID for staging the operation.', true) ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Column/Increment.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Column/Increment.php index c58e16c8e3..3d04d71c26 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Column/Increment.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Column/Increment.php @@ -11,6 +11,7 @@ use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; use Utopia\Swoole\Response as SwooleResponse; +use Utopia\Validator\Nullable; use Utopia\Validator\Numeric; class Increment extends IncrementDocumentAttribute @@ -59,8 +60,8 @@ class Increment extends IncrementDocumentAttribute ->param('rowId', '', new UID(), 'Row ID.') ->param('column', '', new Key(), 'Column key.') ->param('value', 1, new Numeric(), 'Value to increment the column by. The value must be a number.', true) - ->param('max', null, new Numeric(), 'Maximum value for the column. If the current value is greater than this value, an error will be thrown.', true) - ->param('transactionId', null, new UID(), 'Transaction ID for staging the operation.', true) + ->param('max', null, new Nullable(new Numeric()), 'Maximum value for the column. If the current value is greater than this value, an error will be thrown.', true) + ->param('transactionId', null, new Nullable(new UID()), 'Transaction ID for staging the operation.', true) ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Create.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Create.php index bfc832f2b5..d657e5596b 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Create.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Create.php @@ -16,6 +16,7 @@ use Utopia\Database\Validator\UID; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\ArrayList; use Utopia\Validator\JSON; +use Utopia\Validator\Nullable; class Create extends DocumentCreate { @@ -98,9 +99,9 @@ class Create extends DocumentCreate ->param('rowId', '', new CustomId(), 'Row ID. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.', true) ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/references/cloud/server-dart/tablesDB#createTable). Make sure to define columns before creating rows.') ->param('data', [], new JSON(), 'Row data as JSON object.', true, example: '{"username":"walter.obrien","email":"walter.obrien@example.com","fullName":"Walter O\'Brien","age":30,"isAdmin":false}') - ->param('permissions', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE, [Database::PERMISSION_READ, Database::PERMISSION_UPDATE, Database::PERMISSION_DELETE, Database::PERMISSION_WRITE]), 'An array of permissions strings. By default, only the current user is granted all permissions. [Learn more about permissions](https://appwrite.io/docs/permissions).', true) + ->param('permissions', null, new Nullable(new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE, [Database::PERMISSION_READ, Database::PERMISSION_UPDATE, Database::PERMISSION_DELETE, Database::PERMISSION_WRITE])), 'An array of permissions strings. By default, only the current user is granted all permissions. [Learn more about permissions](https://appwrite.io/docs/permissions).', true) ->param('rows', [], fn (array $plan) => new ArrayList(new JSON(), $plan['databasesBatchSize'] ?? APP_LIMIT_DATABASE_BATCH), 'Array of rows data as JSON objects.', true, ['plan']) - ->param('transactionId', null, new UID(), 'Transaction ID for staging the operation.', true) + ->param('transactionId', null, new Nullable(new UID()), 'Transaction ID for staging the operation.', true) ->inject('response') ->inject('dbForProject') ->inject('user') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Delete.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Delete.php index 687b1c8c11..4c8b599c8c 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Delete.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Delete.php @@ -10,6 +10,7 @@ use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Validator\UID; use Utopia\Swoole\Response as SwooleResponse; +use Utopia\Validator\Nullable; class Delete extends DocumentDelete { @@ -61,7 +62,7 @@ class Delete extends DocumentDelete ->param('databaseId', '', new UID(), 'Database ID.') ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/references/cloud/server-dart/tablesDB#createTable).') ->param('rowId', '', new UID(), 'Row ID.') - ->param('transactionId', null, new UID(), 'Transaction ID for staging the operation.', true) + ->param('transactionId', null, new Nullable(new UID()), 'Transaction ID for staging the operation.', true) ->inject('requestTimestamp') ->inject('response') ->inject('dbForProject') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Get.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Get.php index f8b70516b6..f355ebb9e6 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Get.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Get.php @@ -11,6 +11,7 @@ use Appwrite\Utopia\Response as UtopiaResponse; use Utopia\Database\Validator\UID; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\ArrayList; +use Utopia\Validator\Nullable; use Utopia\Validator\Text; class Get extends DocumentGet @@ -52,7 +53,7 @@ class Get extends DocumentGet ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the Database service [server integration](https://appwrite.io/docs/references/cloud/server-dart/tablesDB#createTable).') ->param('rowId', '', new UID(), 'Row ID.') ->param('queries', [], new ArrayList(new Text(APP_LIMIT_ARRAY_ELEMENT_SIZE), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long.', true) - ->param('transactionId', null, new UID(), 'Transaction ID to read uncommitted changes within the transaction.', true) + ->param('transactionId', null, new Nullable(new UID()), 'Transaction ID to read uncommitted changes within the transaction.', true) ->inject('response') ->inject('dbForProject') ->inject('queueForStatsUsage') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Update.php index cb1d5888cf..8f3786b8cf 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Update.php @@ -13,6 +13,7 @@ use Utopia\Database\Validator\Permissions; use Utopia\Database\Validator\UID; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\JSON; +use Utopia\Validator\Nullable; class Update extends DocumentUpdate { @@ -59,8 +60,8 @@ class Update extends DocumentUpdate ->param('tableId', '', new UID(), 'Table ID.') ->param('rowId', '', new UID(), 'Row ID.') ->param('data', [], new JSON(), 'Row data as JSON object. Include only columns and value pairs to be updated.', true) - ->param('permissions', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE, [Database::PERMISSION_READ, Database::PERMISSION_UPDATE, Database::PERMISSION_DELETE, Database::PERMISSION_WRITE]), 'An array of permissions strings. By default, the current permissions are inherited. [Learn more about permissions](https://appwrite.io/docs/permissions).', true) - ->param('transactionId', null, new UID(), 'Transaction ID for staging the operation.', true) + ->param('permissions', null, new Nullable(new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE, [Database::PERMISSION_READ, Database::PERMISSION_UPDATE, Database::PERMISSION_DELETE, Database::PERMISSION_WRITE])), 'An array of permissions strings. By default, the current permissions are inherited. [Learn more about permissions](https://appwrite.io/docs/permissions).', true) + ->param('transactionId', null, new Nullable(new UID()), 'Transaction ID for staging the operation.', true) ->inject('requestTimestamp') ->inject('response') ->inject('dbForProject') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Upsert.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Upsert.php index 0bc373cc93..d4cd61cfdd 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Upsert.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/Upsert.php @@ -13,6 +13,7 @@ use Utopia\Database\Validator\Permissions; use Utopia\Database\Validator\UID; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\JSON; +use Utopia\Validator\Nullable; class Upsert extends DocumentUpsert { @@ -61,8 +62,8 @@ class Upsert extends DocumentUpsert ->param('tableId', '', new UID(), 'Table ID.') ->param('rowId', '', new UID(), 'Row ID.') ->param('data', [], new JSON(), 'Row data as JSON object. Include all required columns of the row to be created or updated.', true) - ->param('permissions', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE, [Database::PERMISSION_READ, Database::PERMISSION_UPDATE, Database::PERMISSION_DELETE, Database::PERMISSION_WRITE]), 'An array of permissions strings. By default, the current permissions are inherited. [Learn more about permissions](https://appwrite.io/docs/permissions).', true) - ->param('transactionId', null, new UID(), 'Transaction ID for staging the operation.', true) + ->param('permissions', null, new Nullable(new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE, [Database::PERMISSION_READ, Database::PERMISSION_UPDATE, Database::PERMISSION_DELETE, Database::PERMISSION_WRITE])), 'An array of permissions strings. By default, the current permissions are inherited. [Learn more about permissions](https://appwrite.io/docs/permissions).', true) + ->param('transactionId', null, new Nullable(new UID()), 'Transaction ID for staging the operation.', true) ->inject('requestTimestamp') ->inject('response') ->inject('user') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/XList.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/XList.php index b96e84b722..cd6141a6b4 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/XList.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Rows/XList.php @@ -12,6 +12,7 @@ use Utopia\Database\Validator\UID; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\ArrayList; use Utopia\Validator\Boolean; +use Utopia\Validator\Nullable; use Utopia\Validator\Text; class XList extends DocumentXList @@ -52,7 +53,7 @@ class XList extends DocumentXList ->param('databaseId', '', new UID(), 'Database ID.') ->param('tableId', '', new UID(), 'Table ID. You can create a new table using the TablesDB service [server integration](https://appwrite.io/docs/products/databases/tables#create-table).') ->param('queries', [], new ArrayList(new Text(APP_LIMIT_ARRAY_ELEMENT_SIZE), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long.', true) - ->param('transactionId', null, new UID(), 'Transaction ID to read uncommitted changes within the transaction.', true) + ->param('transactionId', null, new Nullable(new UID()), 'Transaction ID to read uncommitted changes within the transaction.', true) ->param('total', true, new Boolean(true), 'When set to false, the total count returned will be 0 and will not be calculated.', true) ->inject('response') ->inject('dbForProject') diff --git a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Update.php b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Update.php index 8424b37a6d..a4bfb5bf23 100644 --- a/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Update.php +++ b/src/Appwrite/Platform/Modules/Databases/Http/TablesDB/Tables/Update.php @@ -12,6 +12,7 @@ use Utopia\Database\Validator\Permissions; use Utopia\Database\Validator\UID; use Utopia\Swoole\Response as SwooleResponse; use Utopia\Validator\Boolean; +use Utopia\Validator\Nullable; use Utopia\Validator\Text; class Update extends CollectionUpdate @@ -55,8 +56,8 @@ class Update extends CollectionUpdate ->param('databaseId', '', new UID(), 'Database ID.') ->param('tableId', '', new UID(), 'Table ID.') ->param('name', null, new Text(128), 'Table name. Max length: 128 chars.') - ->param('permissions', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE), 'An array of permission strings. By default, the current permissions are inherited. [Learn more about permissions](https://appwrite.io/docs/permissions).', true) - ->param('rowSecurity', false, new Boolean(true), 'Enables configuring permissions for individual rows. A user needs one of row or table level permissions to access a document. [Learn more about permissions](https://appwrite.io/docs/permissions).', true) + ->param('permissions', null, new Nullable(new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE)), 'An array of permission strings. By default, the current permissions are inherited. [Learn more about permissions](https://appwrite.io/docs/permissions).', true) + ->param('rowSecurity', false, new Boolean(true), 'Enables configuring permissions for individual rows. A user needs one of row or table-level permissions to access a row. [Learn more about permissions](https://appwrite.io/docs/permissions).', true) ->param('enabled', true, new Boolean(), 'Is table enabled? When set to \'disabled\', users cannot access the table but Server SDKs with and API key can still read and write to the table. No data is lost when this is toggled.', true) ->inject('response') ->inject('dbForProject') diff --git a/src/Appwrite/Platform/Modules/Functions/Http/Deployments/Create.php b/src/Appwrite/Platform/Modules/Functions/Http/Deployments/Create.php index f64a960507..d53324b27f 100644 --- a/src/Appwrite/Platform/Modules/Functions/Http/Deployments/Create.php +++ b/src/Appwrite/Platform/Modules/Functions/Http/Deployments/Create.php @@ -28,6 +28,7 @@ use Utopia\Storage\Validator\Upload; use Utopia\Swoole\Request; use Utopia\System\System; use Utopia\Validator\Boolean; +use Utopia\Validator\Nullable; use Utopia\Validator\Text; class Create extends Action @@ -74,8 +75,8 @@ class Create extends Action packaging: true, )) ->param('functionId', '', new UID(), 'Function ID.') - ->param('entrypoint', null, new Text(1028), 'Entrypoint File.', true) - ->param('commands', null, new Text(8192, 0), 'Build Commands.', true) + ->param('entrypoint', null, new Nullable(new Text(1028)), 'Entrypoint File.', true) + ->param('commands', null, new Nullable(new Text(8192, 0)), 'Build Commands.', true) ->param('code', [], new File(), 'Gzip file with your code package. When used with the Appwrite CLI, pass the path to your code directory, and the CLI will automatically package your code. Use a path that is within the current directory.', skipValidation: true) ->param('activate', false, new Boolean(true), 'Automatically activate the deployment when it is finished building.') ->inject('request') diff --git a/src/Appwrite/Platform/Modules/Functions/Http/Deployments/Template/Create.php b/src/Appwrite/Platform/Modules/Functions/Http/Deployments/Template/Create.php index bbe84c56ee..00c29d6bba 100644 --- a/src/Appwrite/Platform/Modules/Functions/Http/Deployments/Template/Create.php +++ b/src/Appwrite/Platform/Modules/Functions/Http/Deployments/Template/Create.php @@ -21,6 +21,7 @@ use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Request; use Utopia\Validator\Boolean; use Utopia\Validator\Text; +use Utopia\Validator\WhiteList; use Utopia\VCS\Adapter\Git\GitHub; class Create extends Base @@ -65,7 +66,8 @@ class Create extends Base ->param('repository', '', new Text(128, 0), 'Repository name of the template.') ->param('owner', '', new Text(128, 0), 'The name of the owner of the template.') ->param('rootDirectory', '', new Text(128, 0), 'Path to function code in the template repo.') - ->param('version', '', new Text(128, 0), 'Version (tag) for the repo linked to the function template.') + ->param('type', '', new WhiteList(['commit', 'branch', 'tag']), 'Type for the reference provided. Can be commit, branch, or tag') + ->param('reference', '', new Text(128, 0), 'Reference value, can be a commit hash, branch name, or release tag') ->param('activate', false, new Boolean(), 'Automatically activate the deployment when it is finished building.', true) ->inject('request') ->inject('response') @@ -83,7 +85,8 @@ class Create extends Base string $repository, string $owner, string $rootDirectory, - string $version, + string $type, + string $reference, bool $activate, Request $request, Response $response, @@ -100,11 +103,16 @@ class Create extends Base throw new Exception(Exception::FUNCTION_NOT_FOUND); } + $branchUrl = "https://github.com/$owner/$repository/blob/$reference"; + + $repositoryUrl = "https://github.com/$owner/$repository"; + $template = new Document([ 'repositoryName' => $repository, 'ownerName' => $owner, 'rootDirectory' => $rootDirectory, - 'version' => $version + 'referenceType' => $type, + 'referenceValue' => $reference, ]); if (!empty($function->getAttribute('providerRepositoryId'))) { @@ -146,7 +154,12 @@ class Create extends Base 'resourceType' => 'functions', 'entrypoint' => $function->getAttribute('entrypoint', ''), 'buildCommands' => $function->getAttribute('commands', ''), - 'type' => 'manual', + 'providerRepositoryName' => $repository, + 'providerRepositoryOwner' => $owner, + 'providerRepositoryUrl' => $repositoryUrl, + 'providerBranchUrl' => $branchUrl, + 'providerBranch' => $type == GitHub::CLONE_TYPE_BRANCH ? $reference : '', + 'type' => 'vcs', 'activate' => $activate, ])); diff --git a/src/Appwrite/Platform/Modules/Functions/Http/Executions/Create.php b/src/Appwrite/Platform/Modules/Functions/Http/Executions/Create.php index 69af3b7d04..a699c0f097 100644 --- a/src/Appwrite/Platform/Modules/Functions/Http/Executions/Create.php +++ b/src/Appwrite/Platform/Modules/Functions/Http/Executions/Create.php @@ -36,6 +36,7 @@ use Utopia\System\System; use Utopia\Validator\AnyOf; use Utopia\Validator\Assoc; use Utopia\Validator\Boolean; +use Utopia\Validator\Nullable; use Utopia\Validator\Text; use Utopia\Validator\WhiteList; @@ -81,7 +82,7 @@ class Create extends Base ->param('path', '/', new Text(2048), 'HTTP path of execution. Path can include query params. Default value is /', true) ->param('method', 'POST', new Whitelist(['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS', 'HEAD'], true), 'HTTP method of execution. Default value is POST.', true) ->param('headers', [], new AnyOf([new Assoc(), new Text(65535)], AnyOf::TYPE_MIXED), 'HTTP headers of execution. Defaults to empty.', true) - ->param('scheduledAt', null, new Text(100), 'Scheduled execution time in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. DateTime value must be in future with precision in minutes.', true) + ->param('scheduledAt', null, new Nullable(new Text(100)), 'Scheduled execution time in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. DateTime value must be in future with precision in minutes.', true) ->inject('response') ->inject('request') ->inject('project') diff --git a/src/Appwrite/Platform/Modules/Functions/Http/Variables/Update.php b/src/Appwrite/Platform/Modules/Functions/Http/Variables/Update.php index 639b1c74d5..95fd235e4b 100644 --- a/src/Appwrite/Platform/Modules/Functions/Http/Variables/Update.php +++ b/src/Appwrite/Platform/Modules/Functions/Http/Variables/Update.php @@ -16,6 +16,7 @@ use Utopia\Database\Validator\UID; use Utopia\Platform\Action; use Utopia\Platform\Scope\HTTP; use Utopia\Validator\Boolean; +use Utopia\Validator\Nullable; use Utopia\Validator\Text; class Update extends Base @@ -56,8 +57,8 @@ class Update extends Base ->param('functionId', '', new UID(), 'Function unique ID.', false) ->param('variableId', '', new UID(), 'Variable unique ID.', false) ->param('key', null, new Text(255), 'Variable key. Max length: 255 chars.', false) - ->param('value', null, new Text(8192, 0), 'Variable value. Max length: 8192 chars.', true) - ->param('secret', null, new Boolean(), 'Secret variables can be updated or deleted, but only functions can read them during build and runtime.', true) + ->param('value', null, new Nullable(new Text(8192, 0)), 'Variable value. Max length: 8192 chars.', true) + ->param('secret', null, new Nullable(new Boolean()), 'Secret variables can be updated or deleted, but only functions can read them during build and runtime.', true) ->inject('response') ->inject('dbForProject') ->inject('dbForPlatform') diff --git a/src/Appwrite/Platform/Modules/Functions/Workers/Builds.php b/src/Appwrite/Platform/Modules/Functions/Workers/Builds.php index f9aa60db5f..22b302f26e 100644 --- a/src/Appwrite/Platform/Modules/Functions/Workers/Builds.php +++ b/src/Appwrite/Platform/Modules/Functions/Workers/Builds.php @@ -310,20 +310,23 @@ class Builds extends Action // Non-VCS + Template $templateRepositoryName = $template->getAttribute('repositoryName', ''); $templateOwnerName = $template->getAttribute('ownerName', ''); - $templateVersion = $template->getAttribute('version', ''); + $templateReferenceType = $template->getAttribute('referenceType', ''); + $templateReferenceValue = $template->getAttribute('referenceValue', ''); $templateRootDirectory = $template->getAttribute('rootDirectory', ''); $templateRootDirectory = \rtrim($templateRootDirectory, '/'); $templateRootDirectory = \ltrim($templateRootDirectory, '.'); $templateRootDirectory = \ltrim($templateRootDirectory, '/'); - if (!empty($templateRepositoryName) && !empty($templateOwnerName) && !empty($templateVersion)) { + if (!empty($templateRepositoryName) && !empty($templateOwnerName) && !empty($templateReferenceType) && !empty($templateReferenceValue)) { $stdout = ''; $stderr = ''; // Clone template repo $tmpTemplateDirectory = '/tmp/builds/' . $deploymentId . '-template'; - $gitCloneCommandForTemplate = $github->generateCloneCommand($templateOwnerName, $templateRepositoryName, $templateVersion, GitHub::CLONE_TYPE_TAG, $tmpTemplateDirectory, $templateRootDirectory); + + $gitCloneCommandForTemplate = $github->generateCloneCommand($templateOwnerName, $templateRepositoryName, $templateReferenceValue, $templateReferenceType, $tmpTemplateDirectory, $templateRootDirectory); + $exit = Console::execute($gitCloneCommandForTemplate, '', $stdout, $stderr); if ($exit !== 0) { diff --git a/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Create.php b/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Create.php index 4bd3afa1f5..aa622d8d84 100644 --- a/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Create.php +++ b/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Create.php @@ -29,6 +29,7 @@ use Utopia\Storage\Validator\Upload; use Utopia\Swoole\Request; use Utopia\System\System; use Utopia\Validator\Boolean; +use Utopia\Validator\Nullable; use Utopia\Validator\Text; class Create extends Action @@ -71,9 +72,9 @@ class Create extends Action packaging: true, )) ->param('siteId', '', new UID(), 'Site ID.') - ->param('installCommand', null, new Text(8192, 0), 'Install Commands.', true) - ->param('buildCommand', null, new Text(8192, 0), 'Build Commands.', true) - ->param('outputDirectory', null, new Text(8192, 0), 'Output Directory.', true) + ->param('installCommand', null, new Nullable(new Text(8192, 0)), 'Install Commands.', true) + ->param('buildCommand', null, new Nullable(new Text(8192, 0)), 'Build Commands.', true) + ->param('outputDirectory', null, new Nullable(new Text(8192, 0)), 'Output Directory.', true) ->param('code', [], new File(), 'Gzip file with your code package. When used with the Appwrite CLI, pass the path to your code directory, and the CLI will automatically package your code. Use a path that is within the current directory.', skipValidation: true) ->param('activate', false, new Boolean(true), 'Automatically activate the deployment when it is finished building.') ->inject('request') diff --git a/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Template/Create.php b/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Template/Create.php index dc90045b0c..dc7d4c4ace 100644 --- a/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Template/Create.php +++ b/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Template/Create.php @@ -23,6 +23,7 @@ use Utopia\Swoole\Request; use Utopia\System\System; use Utopia\Validator\Boolean; use Utopia\Validator\Text; +use Utopia\Validator\WhiteList; use Utopia\VCS\Adapter\Git\GitHub; class Create extends Base @@ -67,7 +68,8 @@ class Create extends Base ->param('repository', '', new Text(128, 0), 'Repository name of the template.') ->param('owner', '', new Text(128, 0), 'The name of the owner of the template.') ->param('rootDirectory', '', new Text(128, 0), 'Path to site code in the template repo.') - ->param('version', '', new Text(128, 0), 'Version (tag) for the repo linked to the site template.') + ->param('type', '', new WhiteList(['branch', 'commit', 'tag']), 'Type for the reference provided. Can be commit, branch, or tag') + ->param('reference', '', new Text(128, 0), 'Reference value, can be a commit hash, branch name, or release tag') ->param('activate', false, new Boolean(), 'Automatically activate the deployment when it is finished building.', true) ->inject('request') ->inject('response') @@ -85,7 +87,8 @@ class Create extends Base string $repository, string $owner, string $rootDirectory, - string $version, + string $type, + string $reference, bool $activate, Request $request, Response $response, @@ -102,11 +105,15 @@ class Create extends Base throw new Exception(Exception::SITE_NOT_FOUND); } + $branchUrl = "https://github.com/$owner/$repository/blob/$reference"; + $repositoryUrl = "https://github.com/$owner/$repository"; + $template = new Document([ 'repositoryName' => $repository, 'ownerName' => $owner, 'rootDirectory' => $rootDirectory, - 'version' => $version + 'referenceType' => $type, + 'referenceValue' => $reference ]); if (!empty($site->getAttribute('providerRepositoryId'))) { @@ -157,9 +164,14 @@ class Create extends Base 'resourceType' => 'sites', 'buildCommands' => \implode(' && ', $commands), 'buildOutput' => $site->getAttribute('outputDirectory', ''), + 'providerRepositoryName' => $repository, + 'providerRepositoryOwner' => $owner, + 'providerRepositoryUrl' => $repositoryUrl, + 'providerBranchUrl' => $branchUrl, + 'providerBranch' => $type == GitHub::CLONE_TYPE_BRANCH ? $reference : '', 'adapter' => $site->getAttribute('adapter', ''), 'fallbackFile' => $site->getAttribute('fallbackFile', ''), - 'type' => 'manual', + 'type' => 'vcs', 'activate' => $activate, ])); diff --git a/src/Appwrite/Platform/Modules/Sites/Http/Variables/Update.php b/src/Appwrite/Platform/Modules/Sites/Http/Variables/Update.php index 3cf2e2f85f..6f4ea35eea 100644 --- a/src/Appwrite/Platform/Modules/Sites/Http/Variables/Update.php +++ b/src/Appwrite/Platform/Modules/Sites/Http/Variables/Update.php @@ -14,6 +14,7 @@ use Utopia\Database\Validator\UID; use Utopia\Platform\Action; use Utopia\Platform\Scope\HTTP; use Utopia\Validator\Boolean; +use Utopia\Validator\Nullable; use Utopia\Validator\Text; class Update extends Base @@ -54,8 +55,8 @@ class Update extends Base ->param('siteId', '', new UID(), 'Site unique ID.', false) ->param('variableId', '', new UID(), 'Variable unique ID.', false) ->param('key', null, new Text(255), 'Variable key. Max length: 255 chars.', false) - ->param('value', null, new Text(8192, 0), 'Variable value. Max length: 8192 chars.', true) - ->param('secret', null, new Boolean(), 'Secret variables can be updated or deleted, but only sites can read them during build and runtime.', true) + ->param('value', null, new Nullable(new Text(8192, 0)), 'Variable value. Max length: 8192 chars.', true) + ->param('secret', null, new Nullable(new Boolean()), 'Secret variables can be updated or deleted, but only sites can read them during build and runtime.', true) ->inject('response') ->inject('dbForProject') ->callback($this->action(...)); diff --git a/src/Appwrite/Platform/Modules/Tokens/Http/Tokens/Buckets/Files/Create.php b/src/Appwrite/Platform/Modules/Tokens/Http/Tokens/Buckets/Files/Create.php index fe7a0187e9..e4de4c1380 100644 --- a/src/Appwrite/Platform/Modules/Tokens/Http/Tokens/Buckets/Files/Create.php +++ b/src/Appwrite/Platform/Modules/Tokens/Http/Tokens/Buckets/Files/Create.php @@ -61,7 +61,7 @@ class Create extends Action )) ->param('bucketId', '', new UID(), 'Storage bucket unique ID. You can create a new storage bucket using the Storage service [server integration](https://appwrite.io/docs/server/storage#createBucket).') ->param('fileId', '', new UID(), 'File unique ID.') - ->param('expire', null, new Nullable(new DatetimeValidator()), 'Token expiry date', true) + ->param('expire', null, new Nullable(new DatetimeValidator(requireDateInFuture: true)), 'Token expiry date', true) ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') @@ -70,7 +70,6 @@ class Create extends Action public function action(string $bucketId, string $fileId, ?string $expire, Response $response, Database $dbForProject, Event $queueForEvents): void { - /** * @var Document $bucket * @var Document $file diff --git a/src/Appwrite/Platform/Modules/Tokens/Http/Tokens/Update.php b/src/Appwrite/Platform/Modules/Tokens/Http/Tokens/Update.php index 7a15708011..fef2c38a81 100644 --- a/src/Appwrite/Platform/Modules/Tokens/Http/Tokens/Update.php +++ b/src/Appwrite/Platform/Modules/Tokens/Http/Tokens/Update.php @@ -57,7 +57,7 @@ class Update extends Action contentType: ContentType::JSON )) ->param('tokenId', '', new UID(), 'Token unique ID.') - ->param('expire', null, new Nullable(new DatetimeValidator()), 'File token expiry date', true) + ->param('expire', null, new Nullable(new DatetimeValidator(requireDateInFuture: true)), 'File token expiry date', true) ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') diff --git a/src/Appwrite/Platform/Tasks/Maintenance.php b/src/Appwrite/Platform/Tasks/Maintenance.php index 036e8783d4..f5785d0bb4 100644 --- a/src/Appwrite/Platform/Tasks/Maintenance.php +++ b/src/Appwrite/Platform/Tasks/Maintenance.php @@ -95,6 +95,7 @@ class Maintenance extends Action $this->renewCertificates($dbForPlatform, $queueForCertificates); $this->notifyDeleteCache($cacheRetention, $queueForDeletes); $this->notifyDeleteSchedules($schedulesDeletionRetention, $queueForDeletes); + $this->notifyDeleteCSVExports($queueForDeletes); }, $interval, $delay); } @@ -106,6 +107,13 @@ class Maintenance extends Action ->trigger(); } + private function notifyDeleteCSVExports(Delete $queueForDeletes): void + { + $queueForDeletes + ->setType(DELETE_TYPE_CSV_EXPORTS) + ->trigger(); + } + private function renewCertificates(Database $dbForPlatform, Certificate $queueForCertificate): void { $time = DatabaseDateTime::now(); diff --git a/src/Appwrite/Platform/Tasks/SDKs.php b/src/Appwrite/Platform/Tasks/SDKs.php index cf4f107e8e..d3c605655f 100644 --- a/src/Appwrite/Platform/Tasks/SDKs.php +++ b/src/Appwrite/Platform/Tasks/SDKs.php @@ -259,8 +259,6 @@ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND } if ($createRelease) { - Console::execute('git config --global user.email "$GIT_EMAIL"', stdin: '', stdout: '', stderr: ''); - $releaseVersion = $language['version']; $repoName = $language['gitUserName'] . '/' . $language['gitRepoName']; @@ -429,16 +427,21 @@ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND mkdir -p ' . $target . ' && \ cd ' . $target . ' && \ git init && \ + git config core.ignorecase false && \ + git config pull.rebase false && \ git remote add origin ' . $gitUrl . ' && \ git fetch origin && \ - git checkout ' . $repoBranch . ' || git checkout -b ' . $repoBranch . ' && \ + (git checkout -f ' . $repoBranch . ' 2>/dev/null || git checkout -b ' . $repoBranch . ') && \ git pull origin ' . $repoBranch . ' && \ - git checkout ' . $gitBranch . ' || git checkout -b ' . $gitBranch . ' && \ - git fetch origin ' . $gitBranch . ' || git push -u origin ' . $gitBranch . ' && \ - git pull origin ' . $gitBranch . ' && \ - find . -mindepth 1 ! -path "./.git*" -delete && \ + (git checkout -f ' . $gitBranch . ' 2>/dev/null || git checkout -b ' . $gitBranch . ') && \ + (git fetch origin ' . $gitBranch . ' 2>/dev/null || git push -u origin ' . $gitBranch . ') && \ + git reset --hard origin/' . $gitBranch . ' 2>/dev/null || true && \ + (test -d .github && cp -r .github /tmp/.github-backup-$$ || true) && \ + git rm -rf --cached . && \ + git clean -fdx -e .git -e .github && \ cp -r ' . $result . '/. ' . $target . '/ && \ - git add . && \ + (test -d /tmp/.github-backup-$$ && cp -r /tmp/.github-backup-$$/.github . && rm -rf /tmp/.github-backup-$$ || true) && \ + git add -A && \ git commit -m "' . $message . '" && \ git push -u origin ' . $gitBranch . ' '); diff --git a/src/Appwrite/Platform/Workers/Deletes.php b/src/Appwrite/Platform/Workers/Deletes.php index 331a2668a3..7df2770ac6 100644 --- a/src/Appwrite/Platform/Workers/Deletes.php +++ b/src/Appwrite/Platform/Workers/Deletes.php @@ -179,6 +179,9 @@ class Deletes extends Action case DELETE_TYPE_SESSION_TARGETS: $this->deleteSessionTargets($project, $getProjectDB, $document); break; + case DELETE_TYPE_CSV_EXPORTS: + $this->deleteOldCSVExports($dbForPlatform, $deviceForFiles); + break; case DELETE_TYPE_MAINTENANCE: $this->deleteExpiredTargets($project, $getProjectDB); $this->deleteExecutionLogs($project, $getProjectDB, $executionRetention); @@ -720,6 +723,41 @@ class Deletes extends Action ], $dbForProject); } + /** + * @param Database $dbForPlatform + * @param Device $deviceForFiles + * @return void + * @throws Exception|Throwable + */ + private function deleteOldCSVExports(Database $dbForPlatform, Device $deviceForFiles): void + { + $bucket = $dbForPlatform->getDocument('buckets', 'default'); + + if ($bucket->isEmpty()) { + Console::warning('Default bucket not found, skipping CSV export cleanup'); + return; + } + + $oneWeekAgo = DateTime::addSeconds(new \DateTime(), -1 * 60 * 60 * 24 * 7); // 1 week + + Console::info("Deleting CSV export files older than " . $oneWeekAgo); + + $this->deleteByGroup('bucket_' . $bucket->getSequence(), [ + Query::select([...$this->selects, '$createdAt', 'name', 'path']), + Query::equal('bucketId', ['default']), + Query::createdBefore($oneWeekAgo), + Query::endsWith('name', ['.csv']), + Query::orderDesc('$createdAt'), + Query::orderDesc(), + ], $dbForPlatform, function (Document $file) use ($deviceForFiles) { + $path = $file->getAttribute('path'); + if ($deviceForFiles->exists($path)) { + $deviceForFiles->delete($path); + Console::success('Deleted CSV file: ' . $file->getAttribute('name')); + } + }); + } + /** * @param Database $dbForPlatform * @param string $datetime diff --git a/src/Appwrite/Platform/Workers/Migrations.php b/src/Appwrite/Platform/Workers/Migrations.php index fc7949e783..0bd5c50e04 100644 --- a/src/Appwrite/Platform/Workers/Migrations.php +++ b/src/Appwrite/Platform/Workers/Migrations.php @@ -11,12 +11,15 @@ use Utopia\CLI\Console; use Utopia\Config\Config; use Utopia\Database\Database; use Utopia\Database\Document; -use Utopia\Database\Exception\Authorization; +use Utopia\Database\Exception\Authorization as AuthorizationException; use Utopia\Database\Exception\Conflict; use Utopia\Database\Exception\Restricted; use Utopia\Database\Exception\Structure; use Utopia\Database\Helpers\ID; +use Utopia\Database\Helpers\Permission; +use Utopia\Database\Helpers\Role; use Utopia\Database\Query; +use Utopia\Database\Validator\Authorization; use Utopia\Locale\Locale; use Utopia\Migration\Destination; use Utopia\Migration\Destinations\Appwrite as DestinationAppwrite; @@ -223,7 +226,7 @@ class Migrations extends Action } /** - * @throws Authorization + * @throws AuthorizationException * @throws Structure * @throws Conflict * @throws \Utopia\Database\Exception @@ -282,7 +285,7 @@ class Migrations extends Action } /** - * @throws Authorization + * @throws AuthorizationException * @throws Conflict * @throws Restricted * @throws Structure @@ -421,7 +424,7 @@ class Migrations extends Action * @param Document $migration * @param Mail $queueForMails * @return void - * @throws Authorization + * @throws AuthorizationException * @throws Structure * @throws \Utopia\Database\Exception * @throws Exception @@ -432,13 +435,20 @@ class Migrations extends Action Mail $queueForMails ): void { $options = $migration->getAttribute('options', []); - $bucketId = $options['bucketId'] ?? null; + $bucketId = 'default'; // Always use platform default bucket $filename = $options['filename'] ?? 'export_' . \time(); $userInternalId = $options['userInternalId'] ?? ''; + $user = $this->dbForPlatform->findOne('users', [ + Query::equal('$sequence', [$userInternalId]) + ]); - $bucket = $this->dbForProject->getDocument('buckets', $bucketId); + if ($user->isEmpty()) { + throw new \Exception('User ' . $userInternalId . ' not found'); + } + + $bucket = Authorization::skip(fn () => $this->dbForPlatform->getDocument('buckets', $bucketId)); if ($bucket->isEmpty()) { - throw new \Exception("Bucket not found: $bucketId"); + throw new \Exception('Bucket not found'); } $path = $this->deviceForFiles->getPath($bucketId . '/' . $this->sanitizeFilename($filename) . '.csv'); @@ -469,7 +479,7 @@ class Migrations extends Action $this->sendCSVEmail( success: false, project: $project, - userInternalId: $userInternalId, + user: $user, options: $options, queueForMails: $queueForMails, sizeMB: $sizeMB @@ -479,9 +489,11 @@ class Migrations extends Action } } - $this->dbForProject->createDocument('bucket_' . $bucket->getSequence(), new Document([ + $this->dbForPlatform->createDocument('bucket_' . $bucket->getSequence(), new Document([ '$id' => $fileId, - '$permissions' => [], + '$permissions' => [ + Permission::read(Role::user($user->getId())), + ], 'bucketId' => $bucket->getId(), 'bucketInternalId' => $bucket->getSequence(), 'name' => $filename, @@ -511,6 +523,7 @@ class Migrations extends Action 'bucketId' => $bucketId, 'fileId' => $fileId, 'projectId' => $project->getId(), + 'internal' => true, ]); // Generate download URL with JWT @@ -521,7 +534,7 @@ class Migrations extends Action $this->sendCSVEmail( success: true, project: $project, - userInternalId: $userInternalId, + user: $user, options: $options, queueForMails: $queueForMails, downloadUrl: $downloadUrl @@ -533,7 +546,7 @@ class Migrations extends Action * * @param bool $success Whether the export was successful * @param Document $project - * @param string $userInternalId Internal ID of the user + * @param Document $user The user who triggered the operation * @param array $options Migration options * @param Mail $queueForMails * @param string $downloadUrl Download URL for successful exports @@ -544,7 +557,7 @@ class Migrations extends Action protected function sendCSVEmail( bool $success, Document $project, - string $userInternalId, + Document $user, array $options, Mail $queueForMails, string $downloadUrl = '', @@ -554,12 +567,8 @@ class Migrations extends Action return; } - $user = $this->dbForPlatform->findOne('users', [ - Query::equal('$sequence', [$userInternalId]) - ]); - if ($user->isEmpty()) { - Console::warning("User not found for CSV export notification: $userInternalId"); + Console::warning("User not found for CSV export notification: {$user->getInternalId()}"); return; } diff --git a/src/Appwrite/SDK/Specification/Format/OpenAPI3.php b/src/Appwrite/SDK/Specification/Format/OpenAPI3.php index 38613313db..0ab16d306b 100644 --- a/src/Appwrite/SDK/Specification/Format/OpenAPI3.php +++ b/src/Appwrite/SDK/Specification/Format/OpenAPI3.php @@ -255,10 +255,21 @@ class OpenAPI3 extends Format } foreach ($methodObj->getResponses() as $response) { - if (\is_array($response->getModel())) { + /** @var Response|array $response */ + $responseModel = $response->getModel(); + + if (\is_array($responseModel)) { + foreach ($responseModel as $modelName) { + foreach ($this->models as $value) { + if ($value->getType() === $modelName) { + $usedModels[] = $modelName; + break; + } + } + } $additionalMethod['responses'][] = [ 'code' => $response->getCode(), - 'model' => \array_map(fn ($m) => '#/components/schemas/' . $m, $response->getModel()) + 'model' => \array_map(fn ($m) => '#/components/schemas/' . $m, $responseModel) ]; } else { $responseData = [ @@ -267,7 +278,13 @@ class OpenAPI3 extends Format // lets not assume stuff here! if ($response->getCode() !== 204) { - $responseData['model'] = '#/components/schemas/' . $response->getModel(); + $responseData['model'] = '#/components/schemas/' . $responseModel; + foreach ($this->models as $value) { + if ($value->getType() === $responseModel) { + $usedModels[] = $responseModel; + break; + } + } } $additionalMethod['responses'][] = $responseData; diff --git a/src/Appwrite/SDK/Specification/Format/Swagger2.php b/src/Appwrite/SDK/Specification/Format/Swagger2.php index d497369b9a..7606c48750 100644 --- a/src/Appwrite/SDK/Specification/Format/Swagger2.php +++ b/src/Appwrite/SDK/Specification/Format/Swagger2.php @@ -264,11 +264,20 @@ class Swagger2 extends Format } foreach ($methodObj->getResponses() as $response) { - /** @var Response $response */ - if (\is_array($response->getModel())) { + /** @var Response|array $response */ + $responseModel = $response->getModel(); + if (\is_array($responseModel)) { + foreach ($responseModel as $modelName) { + foreach ($this->models as $value) { + if ($value->getType() === $modelName) { + $usedModels[] = $modelName; + break; + } + } + } $additionalMethod['responses'][] = [ 'code' => $response->getCode(), - 'model' => \array_map(fn ($m) => '#/definitions/' . $m, $response->getModel()) + 'model' => \array_map(fn ($m) => '#/definitions/' . $m, $responseModel) ]; } else { $responseData = [ @@ -277,7 +286,13 @@ class Swagger2 extends Format // lets not assume stuff here! if ($response->getCode() !== 204) { - $responseData['model'] = '#/definitions/' . $response->getModel(); + $responseData['model'] = '#/definitions/' . $responseModel; + foreach ($this->models as $value) { + if ($value->getType() === $responseModel) { + $usedModels[] = $responseModel; + break; + } + } } $additionalMethod['responses'][] = $responseData; diff --git a/src/Appwrite/Utopia/Request/Filters/V21.php b/src/Appwrite/Utopia/Request/Filters/V21.php new file mode 100644 index 0000000000..3ef0becf1d --- /dev/null +++ b/src/Appwrite/Utopia/Request/Filters/V21.php @@ -0,0 +1,34 @@ +<?php + +namespace Appwrite\Utopia\Request\Filters; + +use Appwrite\Utopia\Request\Filter; + +class V21 extends Filter +{ + // Convert 1.8.0 params to 1.8.1 + public function parse(array $content, string $model): array + { + switch ($model) { + case 'functions.createTemplateDeployment': + case 'sites.createTemplateDeployment': + $content = $this->convertVersionToTypeAndReference($content); + break; + } + return $content; + } + + /** + * Convert version parameter to type and reference for backwards compatibility + * with 1.8.0 template deployment endpoints + */ + protected function convertVersionToTypeAndReference(array $content): array + { + if (!empty($content['version'])) { + $content['type'] = 'tag'; + $content['reference'] = $content['version']; + unset($content['version']); + } + return $content; + } +} diff --git a/tests/e2e/General/UsageTest.php b/tests/e2e/General/UsageTest.php index 8f5477331a..dc49d27aea 100644 --- a/tests/e2e/General/UsageTest.php +++ b/tests/e2e/General/UsageTest.php @@ -28,6 +28,7 @@ class UsageTest extends Scope FunctionsBase::createVariable insteadof SitesBase; FunctionsBase::getVariable insteadof SitesBase; FunctionsBase::listVariables insteadof SitesBase; + FunctionsBase::helperGetLatestCommit insteadof SitesBase; FunctionsBase::updateVariable insteadof SitesBase; FunctionsBase::deleteVariable insteadof SitesBase; FunctionsBase::getDeployment insteadof SitesBase; diff --git a/tests/e2e/Services/Account/AccountBase.php b/tests/e2e/Services/Account/AccountBase.php index b2f85637a8..9f35932700 100644 --- a/tests/e2e/Services/Account/AccountBase.php +++ b/tests/e2e/Services/Account/AccountBase.php @@ -41,6 +41,11 @@ trait AccountBase $this->assertNotEmpty($response['body']['accessedAt']); $this->assertArrayHasKey('targets', $response['body']); $this->assertEquals($email, $response['body']['targets'][0]['identifier']); + $this->assertArrayNotHasKey('emailCanonical', $response['body']); + $this->assertArrayNotHasKey('emailIsFree', $response['body']); + $this->assertArrayNotHasKey('emailIsDisposable', $response['body']); + $this->assertArrayNotHasKey('emailIsCorporate', $response['body']); + $this->assertArrayNotHasKey('emailIsCanonical', $response['body']); /** * Test for FAILURE diff --git a/tests/e2e/Services/Databases/TablesDB/Transactions/TransactionsBase.php b/tests/e2e/Services/Databases/TablesDB/Transactions/TransactionsBase.php index efa3b52cef..488dc60239 100644 --- a/tests/e2e/Services/Databases/TablesDB/Transactions/TransactionsBase.php +++ b/tests/e2e/Services/Databases/TablesDB/Transactions/TransactionsBase.php @@ -6,6 +6,7 @@ use Tests\E2E\Client; use Utopia\Database\Helpers\ID; use Utopia\Database\Helpers\Permission; use Utopia\Database\Helpers\Role; +use Utopia\Database\Operator; use Utopia\Database\Query; trait TransactionsBase @@ -5561,4 +5562,395 @@ trait TransactionsBase $this->assertEquals('Updated after upsert', $response['body']['name']); $this->assertEquals(20, $response['body']['counter']); } + + /** + * Test array operators in transactions using updateRow with transactionId + * This tests the fix for operators not being parsed when stored in transaction logs + */ + public function testArrayOperatorsWithUpdateRow(): void + { + // Create database + $database = $this->client->call(Client::METHOD_POST, '/tablesdb', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'databaseId' => ID::unique(), + 'name' => 'ArrayOperatorsTestDB' + ]); + + $this->assertEquals(201, $database['headers']['status-code']); + $databaseId = $database['body']['$id']; + + // Create table with array column + $table = $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'tableId' => ID::unique(), + 'name' => 'Items', + 'permissions' => [ + Permission::create(Role::any()), + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + ]); + + $this->assertEquals(201, $table['headers']['status-code']); + $tableId = $table['body']['$id']; + + // Create array column + $column = $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$tableId}/columns/string", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'items', + 'size' => 255, + 'required' => false, + 'array' => true, + ]); + + $this->assertEquals(202, $column['headers']['status-code']); + sleep(2); // Wait for column to be created + + // Create initial row with some items + $row = $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$tableId}/rows", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'rowId' => 'test-row', + 'data' => [ + 'items' => ['item1', 'item2', 'item3', 'item4'] + ] + ]); + + $this->assertEquals(201, $row['headers']['status-code']); + $this->assertEquals(['item1', 'item2', 'item3', 'item4'], $row['body']['items']); + + // Create transaction + $transaction = $this->client->call(Client::METHOD_POST, '/tablesdb/transactions', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(201, $transaction['headers']['status-code']); + $transactionId = $transaction['body']['$id']; + + // Test arrayRemove operator + $updateResponse = $this->client->call(Client::METHOD_PATCH, "/tablesdb/{$databaseId}/tables/{$tableId}/rows/test-row", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'transactionId' => $transactionId, + 'data' => [ + 'items' => Operator::arrayRemove('item2')->toString() + ] + ]); + + $this->assertEquals(200, $updateResponse['headers']['status-code']); + + // Test arrayInsert operator + $updateResponse = $this->client->call(Client::METHOD_PATCH, "/tablesdb/{$databaseId}/tables/{$tableId}/rows/test-row", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'transactionId' => $transactionId, + 'data' => [ + 'items' => Operator::arrayInsert(2, 'newItem')->toString() + ] + ]); + + $this->assertEquals(200, $updateResponse['headers']['status-code']); + + // Commit transaction + $commitResponse = $this->client->call(Client::METHOD_PATCH, "/tablesdb/transactions/{$transactionId}", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'commit' => true + ]); + + $this->assertEquals(200, $commitResponse['headers']['status-code']); + + // Verify the operations were applied correctly + $row = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$tableId}/rows/test-row", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(200, $row['headers']['status-code']); + // After removing item2: ['item1', 'item3', 'item4'] + // After inserting 'newItem' at index 2: ['item1', 'item3', 'newItem', 'item4'] + $this->assertEquals(['item1', 'item3', 'newItem', 'item4'], $row['body']['items']); + } + + /** + * Test array operators in transactions using createOperations + * This tests the fix for operators not being parsed in bulk operation creation + */ + public function testArrayOperatorsWithCreateOperations(): void + { + // Create database + $database = $this->client->call(Client::METHOD_POST, '/tablesdb', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'databaseId' => ID::unique(), + 'name' => 'ArrayOperatorsBulkTestDB' + ]); + + $this->assertEquals(201, $database['headers']['status-code']); + $databaseId = $database['body']['$id']; + + // Create table with array column + $table = $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'tableId' => ID::unique(), + 'name' => 'Tags', + 'permissions' => [ + Permission::create(Role::any()), + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + ]); + + $this->assertEquals(201, $table['headers']['status-code']); + $tableId = $table['body']['$id']; + + // Create array column + $column = $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$tableId}/columns/string", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'tags', + 'size' => 255, + 'required' => false, + 'array' => true, + ]); + + $this->assertEquals(202, $column['headers']['status-code']); + sleep(2); // Wait for column to be created + + // Create initial row + $row = $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$tableId}/rows", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'rowId' => 'doc1', + 'data' => [ + 'tags' => ['php', 'javascript', 'python', 'ruby'] + ] + ]); + + $this->assertEquals(201, $row['headers']['status-code']); + + // Create transaction + $transaction = $this->client->call(Client::METHOD_POST, '/tablesdb/transactions', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(201, $transaction['headers']['status-code']); + $transactionId = $transaction['body']['$id']; + + // Create operations using bulk createOperations endpoint with array operators + $operations = $this->client->call(Client::METHOD_POST, "/tablesdb/transactions/{$transactionId}/operations", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'operations' => [ + [ + 'action' => 'update', + 'databaseId' => $databaseId, + 'tableId' => $tableId, + 'rowId' => 'doc1', + 'data' => [ + 'tags' => Operator::arrayRemove('javascript')->toString() + ] + ], + [ + 'action' => 'update', + 'databaseId' => $databaseId, + 'tableId' => $tableId, + 'rowId' => 'doc1', + 'data' => [ + 'tags' => Operator::arrayAppend(['go', 'rust'])->toString() + ] + ] + ] + ]); + + $this->assertEquals(201, $operations['headers']['status-code']); + $this->assertEquals(2, $operations['body']['operations']); + + // Commit transaction + $commitResponse = $this->client->call(Client::METHOD_PATCH, "/tablesdb/transactions/{$transactionId}", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'commit' => true + ]); + + $this->assertEquals(200, $commitResponse['headers']['status-code']); + + // Verify the operations were applied correctly + $row = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$tableId}/rows/doc1", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(200, $row['headers']['status-code']); + // After removing 'javascript': ['php', 'python', 'ruby'] + // After appending ['go', 'rust']: ['php', 'python', 'ruby', 'go', 'rust'] + $this->assertEquals(['php', 'python', 'ruby', 'go', 'rust'], $row['body']['tags']); + } + + /** + * Test multiple array operators in a single transaction + * This tests all common array operators to ensure comprehensive coverage + */ + public function testMultipleArrayOperators(): void + { + // Create database + $database = $this->client->call(Client::METHOD_POST, '/tablesdb', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'databaseId' => ID::unique(), + 'name' => 'MultipleOperatorsTestDB' + ]); + + $this->assertEquals(201, $database['headers']['status-code']); + $databaseId = $database['body']['$id']; + + // Create table + $table = $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'tableId' => ID::unique(), + 'name' => 'Arrays', + 'permissions' => [ + Permission::create(Role::any()), + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + ]); + + $this->assertEquals(201, $table['headers']['status-code']); + $tableId = $table['body']['$id']; + + // Create multiple array columns + $columns = [ + ['columnId' => 'list1', 'name' => 'List1'], + ['columnId' => 'list2', 'name' => 'List2'], + ['columnId' => 'list3', 'name' => 'List3'], + ]; + + foreach ($columns as $col) { + $column = $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$tableId}/columns/string", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => $col['columnId'], + 'size' => 255, + 'required' => false, + 'array' => true, + ]); + $this->assertEquals(202, $column['headers']['status-code']); + } + + sleep(2); // Wait for columns to be created + + // Create initial row + $row = $this->client->call(Client::METHOD_POST, "/tablesdb/{$databaseId}/tables/{$tableId}/rows", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'rowId' => 'multi-ops', + 'data' => [ + 'list1' => ['a', 'b', 'c'], + 'list2' => ['x', 'y', 'z'], + 'list3' => ['1', '2', '3', '4', '5'] + ] + ]); + + $this->assertEquals(201, $row['headers']['status-code']); + + // Create transaction + $transaction = $this->client->call(Client::METHOD_POST, '/tablesdb/transactions', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(201, $transaction['headers']['status-code']); + $transactionId = $transaction['body']['$id']; + + // Test arrayPrepend + $this->client->call(Client::METHOD_PATCH, "/tablesdb/{$databaseId}/tables/{$tableId}/rows/multi-ops", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'transactionId' => $transactionId, + 'data' => [ + 'list1' => Operator::arrayPrepend(['z'])->toString() + ] + ]); + + // Test arrayAppend + $this->client->call(Client::METHOD_PATCH, "/tablesdb/{$databaseId}/tables/{$tableId}/rows/multi-ops", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'transactionId' => $transactionId, + 'data' => [ + 'list2' => Operator::arrayAppend(['w'])->toString() + ] + ]); + + // Test arrayRemove + $this->client->call(Client::METHOD_PATCH, "/tablesdb/{$databaseId}/tables/{$tableId}/rows/multi-ops", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'transactionId' => $transactionId, + 'data' => [ + 'list3' => Operator::arrayRemove('3')->toString() + ] + ]); + + // Commit transaction + $commitResponse = $this->client->call(Client::METHOD_PATCH, "/tablesdb/transactions/{$transactionId}", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'commit' => true + ]); + + $this->assertEquals(200, $commitResponse['headers']['status-code']); + + // Verify all operations were applied correctly + $row = $this->client->call(Client::METHOD_GET, "/tablesdb/{$databaseId}/tables/{$tableId}/rows/multi-ops", array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(200, $row['headers']['status-code']); + $this->assertEquals(['z', 'a', 'b', 'c'], $row['body']['list1'], 'arrayPrepend should add element at the beginning'); + $this->assertEquals(['x', 'y', 'z', 'w'], $row['body']['list2'], 'arrayAppend should add element at the end'); + $this->assertEquals(['1', '2', '4', '5'], $row['body']['list3'], 'arrayRemove should remove the element'); + } } diff --git a/tests/e2e/Services/Functions/FunctionsBase.php b/tests/e2e/Services/Functions/FunctionsBase.php index 27b67d851d..7403b23a73 100644 --- a/tests/e2e/Services/Functions/FunctionsBase.php +++ b/tests/e2e/Services/Functions/FunctionsBase.php @@ -271,6 +271,29 @@ trait FunctionsBase return $template; } + protected function helperGetLatestCommit(string $owner, string $repository): ?string + { + $ch = curl_init("https://api.github.com/repos/{$owner}/{$repository}/commits/main"); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_HTTPHEADER, [ + 'User-Agent: Appwrite', + 'Accept: application/vnd.github.v3+json' + ]); + + $response = curl_exec($ch); + $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); + curl_close($ch); + + if ($httpCode === 200) { + $commitData = json_decode($response, true); + if (isset($commitData['sha'])) { + return $commitData['sha']; + } + } + + return null; + } + protected function createExecution(string $functionId, mixed $params = []): mixed { $execution = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/executions', array_merge([ diff --git a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php index f5846af959..8cc986b072 100644 --- a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php +++ b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php @@ -361,7 +361,7 @@ class FunctionsCustomServerTest extends Scope $starterTemplate = $this->getTemplate('starter'); $this->assertEquals(200, $starterTemplate['headers']['status-code']); - $phpRuntime = array_values(array_filter($starterTemplate['body']['runtimes'], function ($runtime) { + $runtime = array_values(array_filter($starterTemplate['body']['runtimes'], function ($runtime) { return $runtime['name'] === 'node-22'; }))[0]; @@ -374,15 +374,15 @@ class FunctionsCustomServerTest extends Scope 'name' => $starterTemplate['body']['name'], 'runtime' => 'node-22', 'execute' => $starterTemplate['body']['permissions'], - 'entrypoint' => $phpRuntime['entrypoint'], + 'entrypoint' => $runtime['entrypoint'], 'events' => $starterTemplate['body']['events'], 'schedule' => $starterTemplate['body']['cron'], 'timeout' => $starterTemplate['body']['timeout'], - 'commands' => $phpRuntime['commands'], + 'commands' => $runtime['commands'], 'scopes' => $starterTemplate['body']['scopes'], 'templateRepository' => $starterTemplate['body']['providerRepositoryId'], 'templateOwner' => $starterTemplate['body']['providerOwner'], - 'templateRootDirectory' => $phpRuntime['providerRootDirectory'], + 'templateRootDirectory' => $runtime['providerRootDirectory'], 'templateVersion' => $starterTemplate['body']['providerVersion'], ] ); @@ -399,19 +399,29 @@ class FunctionsCustomServerTest extends Scope 'activate' => true, 'repository' => $starterTemplate['body']['providerRepositoryId'], 'owner' => $starterTemplate['body']['providerOwner'], - 'rootDirectory' => $phpRuntime['providerRootDirectory'], - 'version' => $starterTemplate['body']['providerVersion'], + 'rootDirectory' => $runtime['providerRootDirectory'], + 'type' => 'tag', + 'reference' => $starterTemplate['body']['providerVersion'], ] ); $this->assertEquals(202, $deployment['headers']['status-code']); $this->assertNotEmpty($deployment['body']['$id']); - $deployment = $this->getDeployment($functionId, $deployment['body']['$id']); + // Wait for deployment to be ready + $deploymentId = $deployment['body']['$id']; + $this->assertEventually(function () use ($functionId, $deploymentId) { + $deployment = $this->getDeployment($functionId, $deploymentId); + $this->assertEquals('ready', $deployment['body']['status']); + }, 50000, 500); + + // Verify deployment sizes + $deployment = $this->getDeployment($functionId, $deploymentId); $this->assertEquals(200, $deployment['headers']['status-code']); - $this->assertEquals(0, $deployment['body']['sourceSize']); - $this->assertEquals(0, $deployment['body']['buildSize']); - $this->assertEquals(0, $deployment['body']['totalSize']); + $this->assertGreaterThan(0, $deployment['body']['sourceSize']); + $this->assertGreaterThan(0, $deployment['body']['buildSize']); + $totalSize = $deployment['body']['sourceSize'] + $deployment['body']['buildSize']; + $this->assertEquals($totalSize, $deployment['body']['totalSize']); $deployments = $this->listDeployments($functionId); @@ -433,16 +443,7 @@ class FunctionsCustomServerTest extends Scope $lastDeployment = $deployments['body']['deployments'][0]; $this->assertNotEmpty($lastDeployment['$id']); - $this->assertEquals(0, $lastDeployment['sourceSize']); - - $deploymentId = $lastDeployment['$id']; - - $this->assertEventually(function () use ($functionId, $deploymentId) { - $deployment = $this->getDeployment($functionId, $deploymentId); - - $this->assertEquals(200, $deployment['headers']['status-code']); - $this->assertEquals('ready', $deployment['body']['status']); - }, 50000, 1000); + $this->assertGreaterThan(0, $lastDeployment['sourceSize']); $function = $this->getFunction($functionId); @@ -511,7 +512,144 @@ class FunctionsCustomServerTest extends Scope $this->assertEquals($deployment['body']['$id'], $function['body']['deploymentId']); $this->assertEquals($deployment['body']['$createdAt'], $function['body']['deploymentCreatedAt']); - $function = $this->cleanupFunction($functionId); + $this->cleanupFunction($functionId); + } + + public function testCreateFunctionAndDeploymentFromTemplateBranch() + { + $starterTemplate = $this->getTemplate('starter'); + $this->assertEquals(200, $starterTemplate['headers']['status-code']); + + $runtime = array_values(array_filter($starterTemplate['body']['runtimes'], function ($runtime) { + return $runtime['name'] === 'node-22'; + }))[0]; + + // If this fails, the template has variables, and this test needs to be updated + $this->assertEmpty($starterTemplate['body']['variables']); + + $function = $this->createFunction( + [ + 'functionId' => ID::unique(), + 'name' => $starterTemplate['body']['name'] . ' - Branch Test', + 'runtime' => 'node-22', + 'execute' => $starterTemplate['body']['permissions'], + 'entrypoint' => $runtime['entrypoint'], + 'events' => $starterTemplate['body']['events'], + 'schedule' => $starterTemplate['body']['cron'], + 'timeout' => $starterTemplate['body']['timeout'], + 'commands' => $runtime['commands'], + 'scopes' => $starterTemplate['body']['scopes'], + ] + ); + + $this->assertEquals(201, $function['headers']['status-code']); + $this->assertNotEmpty($function['body']['$id']); + + $functionId = $function['body']['$id'] ?? ''; + + // Deploy using branch + $deployment = $this->createTemplateDeployment( + $functionId, + [ + 'resourceId' => ID::unique(), + 'activate' => true, + 'repository' => $starterTemplate['body']['providerRepositoryId'], + 'owner' => $starterTemplate['body']['providerOwner'], + 'rootDirectory' => $runtime['providerRootDirectory'], + 'type' => 'branch', + 'reference' => 'main', + ] + ); + + $this->assertEquals(202, $deployment['headers']['status-code']); + $this->assertNotEmpty($deployment['body']['$id']); + + $deploymentId = $deployment['body']['$id']; + $this->assertEventually(function () use ($functionId, $deploymentId) { + $deployment = $this->getDeployment($functionId, $deploymentId); + $this->assertEquals('ready', $deployment['body']['status']); + }, 50000, 500); + + $deployment = $this->getDeployment($functionId, $deploymentId); + $this->assertEquals(200, $deployment['headers']['status-code']); + $this->assertGreaterThan(0, $deployment['body']['sourceSize']); + $this->assertGreaterThan(0, $deployment['body']['buildSize']); + $totalSize = $deployment['body']['sourceSize'] + $deployment['body']['buildSize']; + $this->assertEquals($totalSize, $deployment['body']['totalSize']); + + $this->cleanupFunction($functionId); + } + + public function testCreateFunctionAndDeploymentFromTemplateCommit() + { + $starterTemplate = $this->getTemplate('starter'); + $this->assertEquals(200, $starterTemplate['headers']['status-code']); + + // Get latest commit using helper function + $latestCommit = $this->helperGetLatestCommit( + $starterTemplate['body']['providerOwner'], + $starterTemplate['body']['providerRepositoryId'] + ); + $this->assertNotNull($latestCommit); + + $runtime = array_values(array_filter($starterTemplate['body']['runtimes'], function ($runtime) { + return $runtime['name'] === 'node-22'; + }))[0]; + + // If this fails, the template has variables, and this test needs to be updated + $this->assertEmpty($starterTemplate['body']['variables']); + + $function = $this->createFunction( + [ + 'functionId' => ID::unique(), + 'name' => $starterTemplate['body']['name'] . ' - Commit Test', + 'runtime' => 'node-22', + 'execute' => $starterTemplate['body']['permissions'], + 'entrypoint' => $runtime['entrypoint'], + 'events' => $starterTemplate['body']['events'], + 'schedule' => $starterTemplate['body']['cron'], + 'timeout' => $starterTemplate['body']['timeout'], + 'commands' => $runtime['commands'], + 'scopes' => $starterTemplate['body']['scopes'], + ] + ); + + $this->assertEquals(201, $function['headers']['status-code']); + $this->assertNotEmpty($function['body']['$id']); + + $functionId = $function['body']['$id'] ?? ''; + + // Deploy using commit + $deployment = $this->createTemplateDeployment( + $functionId, + [ + 'resourceId' => ID::unique(), + 'activate' => true, + 'repository' => $starterTemplate['body']['providerRepositoryId'], + 'owner' => $starterTemplate['body']['providerOwner'], + 'rootDirectory' => $runtime['providerRootDirectory'], + 'type' => 'commit', + 'reference' => $latestCommit, + ] + ); + + $this->assertEquals(202, $deployment['headers']['status-code']); + $this->assertNotEmpty($deployment['body']['$id']); + + $deploymentId = $deployment['body']['$id']; + $this->assertEventually(function () use ($functionId, $deploymentId) { + $deployment = $this->getDeployment($functionId, $deploymentId); + $this->assertEquals('ready', $deployment['body']['status']); + }, 50000, 500); + + $deployment = $this->getDeployment($functionId, $deploymentId); + $this->assertEquals(200, $deployment['headers']['status-code']); + $this->assertGreaterThan(0, $deployment['body']['sourceSize']); + $this->assertGreaterThan(0, $deployment['body']['buildSize']); + $totalSize = $deployment['body']['sourceSize'] + $deployment['body']['buildSize']; + $this->assertEquals($totalSize, $deployment['body']['totalSize']); + + $this->cleanupFunction($functionId); } /** diff --git a/tests/e2e/Services/Migrations/MigrationsBase.php b/tests/e2e/Services/Migrations/MigrationsBase.php index 16e58c9c2c..f16864960e 100644 --- a/tests/e2e/Services/Migrations/MigrationsBase.php +++ b/tests/e2e/Services/Migrations/MigrationsBase.php @@ -1282,39 +1282,11 @@ trait MigrationsBase $this->assertEquals(200, $docs['headers']['status-code']); $this->assertEquals(10, $docs['body']['total'], 'Expected 10 documents but got ' . $docs['body']['total']); - // Create a storage bucket for the export - $bucketIdUnique = ID::unique(); - $bucket = $this->client->call(Client::METHOD_POST, '/storage/buckets', [ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'] - ], [ - 'bucketId' => $bucketIdUnique, - 'name' => 'Test Export Bucket', - 'permissions' => [ - Permission::read(Role::any()), - Permission::create(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()), - ], - 'fileSecurity' => false, - 'enabled' => true, - 'maximumFileSize' => 10485760, // 10MB - 'allowedFileExtensions' => ['csv'], - 'compression' => 'none', - 'encryption' => false, - 'antivirus' => false - ]); - - $this->assertEquals(201, $bucket['headers']['status-code']); - $bucketId = $bucket['body']['$id']; - - // Perform CSV export with notification enabled + // Perform CSV export with notification enabled (uses internal bucket) $migration = $this->client->call(Client::METHOD_POST, '/migrations/csv/exports', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'] ], $this->getHeaders()), [ - 'bucketId' => $bucketId, 'resourceId' => $databaseId . ':' . $collectionId, 'filename' => 'test-export', 'columns' => [], @@ -1329,7 +1301,7 @@ trait MigrationsBase $this->assertNotEmpty($migration['body']['$id']); $migrationId = $migration['body']['$id']; - $this->assertEventually(function () use ($bucketId, $migrationId) { + $this->assertEventually(function () use ($migrationId) { $response = $this->client->call(Client::METHOD_GET, '/migrations/' . $migrationId, [ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -1341,55 +1313,10 @@ trait MigrationsBase $this->assertEquals('completed', $response['body']['status']); $this->assertEquals('Appwrite', $response['body']['source']); $this->assertEquals('CSV', $response['body']['destination']); - $this->assertEquals($bucketId, $response['body']['options']['bucketId']); return true; }, 30000, 500); - // Check that the file was created in the bucket - // Query files by filename - $files = $this->client->call(Client::METHOD_GET, '/storage/buckets/' . $bucketId . '/files', [ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'] - ], [ - 'queries' => [ - Query::equal('name', ['test-export'])->toString() - ] - ]); - - $this->assertEquals(200, $files['headers']['status-code']); - $this->assertEquals(1, $files['body']['total'], 'Expected exactly one file with name "test-export"'); - - // Get the exported file - $file = $files['body']['files'][0]; - $fileId = $file['$id']; - - $this->assertEquals($bucketId, $file['bucketId']); - $this->assertEquals('test-export', $file['name']); - $this->assertEquals('text/csv', $file['mimeType']); - $this->assertGreaterThan(0, $file['sizeOriginal']); - - // Download and verify CSV content - $download = $this->client->call(Client::METHOD_GET, '/storage/buckets/' . $bucketId . '/files/' . $fileId . '/download', \array_merge([ - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders())); - - $this->assertEquals(200, $download['headers']['status-code']); - - $csvContent = $download['body']; - $lines = explode("\n", trim($csvContent)); - $this->assertCount(11, $lines); - $this->assertStringContainsString('$id', $lines[0]); - $this->assertStringContainsString('$permissions', $lines[0]); - $this->assertStringContainsString('$createdAt', $lines[0]); - $this->assertStringContainsString('$updatedAt', $lines[0]); - $this->assertStringContainsString('name', $lines[0]); - $this->assertStringContainsString('email', $lines[0]); - - $this->assertStringContainsString('Test User 1', $lines[1]); - $this->assertStringContainsString('user1@appwrite.io', $lines[1]); - // Check that email was sent with download link $lastEmail = $this->getLastEmail(); $this->assertNotEmpty($lastEmail); @@ -1407,28 +1334,25 @@ trait MigrationsBase \parse_str($components['query'] ?? '', $queryParams); $this->assertArrayHasKey('jwt', $queryParams, 'JWT not found in download URL'); $this->assertNotEmpty($queryParams['jwt']); + $this->assertArrayHasKey('project', $queryParams, 'Project not found in download URL'); + $this->assertStringContainsString('/storage/buckets/default/files/', $downloadUrl); // Test download with JWT $path = \str_replace('/v1', '', $components['path']); $downloadWithJwt = $this->client->call(Client::METHOD_GET, $path . '?project=' . $queryParams['project'] . '&jwt=' . $queryParams['jwt']); $this->assertEquals(200, $downloadWithJwt['headers']['status-code'], 'Failed to download file with JWT'); - $this->assertEquals($csvContent, $downloadWithJwt['body'], 'Downloaded content differs from original'); - // Test that download without JWT fails - $downloadWithoutJwt = $this->client->call(Client::METHOD_GET, '/storage/buckets/' . $bucketId . '/files/' . $fileId . '/download'); - $this->assertEquals(404, $downloadWithoutJwt['headers']['status-code'], 'File should not be downloadable without JWT'); + // Verify the downloaded content is valid CSV + $csvData = $downloadWithJwt['body']; + $this->assertNotEmpty($csvData, 'CSV export should not be empty'); + $this->assertStringContainsString('name', $csvData, 'CSV should contain the name column header'); + $this->assertStringContainsString('email', $csvData, 'CSV should contain the email column header'); + $this->assertStringContainsString('Test User 1', $csvData, 'CSV should contain test data'); - $this->client->call(Client::METHOD_DELETE, '/storage/buckets/' . $bucketId . '/files/' . $fileId, [ - 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'] - ]); + // Cleanup $this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId, [ 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'] ]); - $this->client->call(Client::METHOD_DELETE, '/storage/buckets/' . $bucketId, [ - 'x-appwrite-project' => $this->getProject()['$id'], - 'x-appwrite-key' => $this->getProject()['apiKey'] - ]); } } diff --git a/tests/e2e/Services/Sites/SitesBase.php b/tests/e2e/Services/Sites/SitesBase.php index 93c55b82b7..7eb5d9699c 100644 --- a/tests/e2e/Services/Sites/SitesBase.php +++ b/tests/e2e/Services/Sites/SitesBase.php @@ -329,9 +329,33 @@ trait SitesBase 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ]); + return $template; } + protected function helperGetLatestCommit(string $owner, string $repository): ?string + { + $ch = curl_init("https://api.github.com/repos/{$owner}/{$repository}/commits/main"); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_HTTPHEADER, [ + 'User-Agent: Appwrite', + 'Accept: application/vnd.github.v3+json' + ]); + + $response = curl_exec($ch); + $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); + curl_close($ch); + + if ($httpCode === 200) { + $commitData = json_decode($response, true); + if (isset($commitData['sha'])) { + return $commitData['sha']; + } + } + + return null; + } + protected function deleteSite(string $siteId): mixed { $site = $this->client->call(Client::METHOD_DELETE, '/sites/' . $siteId, array_merge([ diff --git a/tests/e2e/Services/Sites/SitesCustomServerTest.php b/tests/e2e/Services/Sites/SitesCustomServerTest.php index 8591514796..8c03ec7649 100644 --- a/tests/e2e/Services/Sites/SitesCustomServerTest.php +++ b/tests/e2e/Services/Sites/SitesCustomServerTest.php @@ -1567,7 +1567,157 @@ class SitesCustomServerTest extends Scope 'repository' => $template['providerRepositoryId'], 'owner' => $template['providerOwner'], 'rootDirectory' => $template['frameworks'][0]['providerRootDirectory'], - 'version' => $template['providerVersion'], + 'type' => 'tag', + 'reference' => $template['providerVersion'], + 'activate' => true + ]); + + $this->assertEquals(202, $deployment['headers']['status-code']); + $this->assertNotEmpty($deployment['body']['$id']); + + $deployment = $this->getDeployment($siteId, $deployment['body']['$id']); + $this->assertEquals(200, $deployment['headers']['status-code']); + $this->assertEquals(0, $deployment['body']['sourceSize']); + $this->assertEquals(0, $deployment['body']['buildSize']); + $this->assertEquals(0, $deployment['body']['totalSize']); + + $this->assertEventually(function () use ($siteId) { + $site = $this->getSite($siteId); + $this->assertNotEmpty($site['body']['deploymentId']); + }, 50000, 500); + + $domain = $this->setupSiteDomain($siteId); + $proxyClient = new Client(); + $proxyClient->setEndpoint('http://' . $domain); + + $response = $proxyClient->call(Client::METHOD_GET, '/'); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertStringContainsString("Astro Blog", $response['body']); + $this->assertStringContainsString("Hello, Astronaut!", $response['body']); + + $response = $proxyClient->call(Client::METHOD_GET, '/about'); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertStringContainsString("Astro Blog", $response['body']); + $this->assertStringContainsString("About Me", $response['body']); + + $deployment = $this->getDeployment($siteId, $deployment['body']['$id']); + $this->assertEquals(200, $deployment['headers']['status-code']); + $this->assertGreaterThan(0, $deployment['body']['sourceSize']); + $this->assertGreaterThan(0, $deployment['body']['buildSize']); + $totalSize = $deployment['body']['sourceSize'] + $deployment['body']['buildSize']; + $this->assertEquals($totalSize, $deployment['body']['totalSize']); + + $this->cleanupSite($siteId); + } + + public function testCreateSiteFromTemplateBranch() + { + $template = $this->getTemplate('playground-for-astro'); + $this->assertEquals(200, $template['headers']['status-code']); + + $template = $template['body']; + + $siteId = $this->setupSite([ + 'siteId' => ID::unique(), + 'name' => 'Astro Blog - Branch Test', + 'framework' => $template['frameworks'][0]['key'], + 'adapter' => $template['frameworks'][0]['adapter'], + 'buildRuntime' => $template['frameworks'][0]['buildRuntime'], + 'outputDirectory' => $template['frameworks'][0]['outputDirectory'], + 'buildCommand' => $template['frameworks'][0]['buildCommand'], + 'installCommand' => $template['frameworks'][0]['installCommand'], + 'fallbackFile' => $template['frameworks'][0]['fallbackFile'], + ]); + + $this->assertNotEmpty($siteId); + + // Deploy using branch + $deployment = $this->createTemplateDeployment($siteId, [ + 'repository' => $template['providerRepositoryId'], + 'owner' => $template['providerOwner'], + 'rootDirectory' => $template['frameworks'][0]['providerRootDirectory'], + 'type' => 'branch', + 'reference' => 'main', + 'activate' => true + ]); + + $this->assertEquals(202, $deployment['headers']['status-code']); + $this->assertNotEmpty($deployment['body']['$id']); + + $deployment = $this->getDeployment($siteId, $deployment['body']['$id']); + $this->assertEquals(200, $deployment['headers']['status-code']); + $this->assertEquals(0, $deployment['body']['sourceSize']); + $this->assertEquals(0, $deployment['body']['buildSize']); + $this->assertEquals(0, $deployment['body']['totalSize']); + + $this->assertEventually(function () use ($siteId) { + $site = $this->getSite($siteId); + $this->assertNotEmpty($site['body']['deploymentId']); + }, 50000, 500); + + $domain = $this->setupSiteDomain($siteId); + $proxyClient = new Client(); + $proxyClient->setEndpoint('http://' . $domain); + + $response = $proxyClient->call(Client::METHOD_GET, '/'); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertStringContainsString("Astro Blog", $response['body']); + $this->assertStringContainsString("Hello, Astronaut!", $response['body']); + + $response = $proxyClient->call(Client::METHOD_GET, '/about'); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertStringContainsString("Astro Blog", $response['body']); + $this->assertStringContainsString("About Me", $response['body']); + + $deployment = $this->getDeployment($siteId, $deployment['body']['$id']); + $this->assertEquals(200, $deployment['headers']['status-code']); + $this->assertGreaterThan(0, $deployment['body']['sourceSize']); + $this->assertGreaterThan(0, $deployment['body']['buildSize']); + $totalSize = $deployment['body']['sourceSize'] + $deployment['body']['buildSize']; + $this->assertEquals($totalSize, $deployment['body']['totalSize']); + + $this->cleanupSite($siteId); + } + + public function testCreateSiteFromTemplateCommit() + { + $template = $this->getTemplate('playground-for-astro'); + $this->assertEquals(200, $template['headers']['status-code']); + + // Get latest commit using helper function + $latestCommit = $this->helperGetLatestCommit( + $template['body']['providerOwner'], + $template['body']['providerRepositoryId'] + ); + $this->assertNotNull($latestCommit); + + $template = $template['body']; + + $siteId = $this->setupSite([ + 'siteId' => ID::unique(), + 'name' => 'Astro Blog - Commit Test', + 'framework' => $template['frameworks'][0]['key'], + 'adapter' => $template['frameworks'][0]['adapter'], + 'buildRuntime' => $template['frameworks'][0]['buildRuntime'], + 'outputDirectory' => $template['frameworks'][0]['outputDirectory'], + 'buildCommand' => $template['frameworks'][0]['buildCommand'], + 'installCommand' => $template['frameworks'][0]['installCommand'], + 'fallbackFile' => $template['frameworks'][0]['fallbackFile'], + ]); + + $this->assertNotEmpty($siteId); + + // Deploy using commit + $deployment = $this->createTemplateDeployment($siteId, [ + 'repository' => $template['providerRepositoryId'], + 'owner' => $template['providerOwner'], + 'rootDirectory' => $template['frameworks'][0]['providerRootDirectory'], + 'type' => 'commit', + 'reference' => $latestCommit, 'activate' => true ]); diff --git a/tests/e2e/Services/Storage/StorageBase.php b/tests/e2e/Services/Storage/StorageBase.php index 6879645a22..c67cfcc99a 100644 --- a/tests/e2e/Services/Storage/StorageBase.php +++ b/tests/e2e/Services/Storage/StorageBase.php @@ -30,7 +30,7 @@ trait StorageBase 'name' => 'Test Bucket', 'fileSecurity' => true, 'maximumFileSize' => 2000000, //2MB - 'allowedFileExtensions' => ['jpg', 'png', 'jfif'], + 'allowedFileExtensions' => ['jpg', 'png', 'jfif', 'webp'], 'permissions' => [ Permission::read(Role::any()), Permission::create(Role::any()), @@ -263,7 +263,39 @@ trait StorageBase $this->assertEquals(400, $res['headers']['status-code']); $this->assertEquals(Exception::STORAGE_INVALID_APPWRITE_ID, $res['body']['type']); - return ['bucketId' => $bucketId, 'fileId' => $file['body']['$id'], 'largeFileId' => $largeFile['body']['$id'], 'largeBucketId' => $bucket2['body']['$id']]; + /** + * Test for SUCCESS - Upload and view webp image + */ + $webpFile = $this->client->call(Client::METHOD_POST, '/storage/buckets/' . $bucketId . '/files', array_merge([ + 'content-type' => 'multipart/form-data', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'fileId' => ID::unique(), + 'file' => new CURLFile(realpath(__DIR__ . '/../../../resources/image.webp'), 'image/webp', 'image.webp'), + 'permissions' => [ + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + ]); + $this->assertEquals(201, $webpFile['headers']['status-code']); + $this->assertNotEmpty($webpFile['body']['$id']); + $this->assertEquals('image.webp', $webpFile['body']['name']); + $this->assertEquals('image/webp', $webpFile['body']['mimeType']); + + $webpFileId = $webpFile['body']['$id']; + + // View webp file + $webpView = $this->client->call(Client::METHOD_GET, '/storage/buckets/' . $bucketId . '/files/' . $webpFileId . '/view', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + + $this->assertEquals(200, $webpView['headers']['status-code']); + $this->assertEquals('image/webp', $webpView['headers']['content-type']); + $this->assertNotEmpty($webpView['body']); + + return ['bucketId' => $bucketId, 'fileId' => $file['body']['$id'], 'largeFileId' => $largeFile['body']['$id'], 'largeBucketId' => $bucket2['body']['$id'], 'webpFileId' => $webpFileId]; } public function testCreateBucketFileZstdCompression(): array @@ -416,7 +448,7 @@ trait StorageBase ], ]); $this->assertEquals(200, $files['headers']['status-code']); - $this->assertEquals(0, count($files['body']['files'])); + $this->assertEquals(1, count($files['body']['files'])); $files = $this->client->call(Client::METHOD_GET, '/storage/buckets/' . $data['bucketId'] . '/files', array_merge([ 'content-type' => 'application/json', @@ -869,6 +901,31 @@ trait StorageBase return $data; } + /** + * @depends testCreateBucketFile + */ + public function testFilePreview(array $data): array + { + $bucketId = $data['bucketId']; + $fileId = $data['fileId']; + + // Preview PNG as webp + $preview = $this->client->call(Client::METHOD_GET, '/storage/buckets/' . $bucketId . '/files/' . $fileId . '/preview', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'width' => 300, + 'height' => 300, + 'output' => 'webp', + ]); + + $this->assertEquals(200, $preview['headers']['status-code']); + $this->assertEquals('image/webp', $preview['headers']['content-type']); + $this->assertNotEmpty($preview['body']); + + return $data; + } + /** * @depends testUpdateBucketFile */ diff --git a/tests/e2e/Services/Tokens/TokensConsoleClientTest.php b/tests/e2e/Services/Tokens/TokensConsoleClientTest.php index c0f94a55bf..f1480faba0 100644 --- a/tests/e2e/Services/Tokens/TokensConsoleClientTest.php +++ b/tests/e2e/Services/Tokens/TokensConsoleClientTest.php @@ -9,7 +9,6 @@ use Tests\E2E\Client; use Tests\E2E\Scopes\ProjectCustom; use Tests\E2E\Scopes\Scope; use Tests\E2E\Scopes\SideServer; -use Utopia\Database\DateTime; use Utopia\Database\Helpers\ID; use Utopia\Database\Helpers\Permission; use Utopia\Database\Helpers\Role; @@ -63,10 +62,23 @@ class TokensConsoleClientTest extends Scope $fileId = $file['body']['$id']; + // Failure case: Expire date is in the past $token = $this->client->call(Client::METHOD_POST, '/tokens/buckets/' . $bucketId . '/files/' . $fileId, array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'] - ], $this->getHeaders())); + ], $this->getHeaders()), [ + 'expire' => '2022-11-02', + ]); + $this->assertEquals(400, $token['headers']['status-code']); + $this->assertStringContainsString('Value must be valid date in the future', $token['body']['message']); + + // Success case: No expire date + $token = $this->client->call(Client::METHOD_POST, '/tokens/buckets/' . $bucketId . '/files/' . $fileId, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'] + ], $this->getHeaders()), [ + 'expire' => null, + ]); $this->assertEquals(201, $token['headers']['status-code']); $this->assertEquals('files', $token['body']['resourceType']); @@ -107,8 +119,19 @@ class TokensConsoleClientTest extends Scope { $tokenId = $data['tokenId']; + // Failure case: Expire date is in the past + $token = $this->client->call(Client::METHOD_PATCH, '/tokens/' . $tokenId, [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ], [ + 'expire' => '2022-11-02', + ]); + $this->assertEquals(400, $token['headers']['status-code']); + $this->assertStringContainsString('Value must be valid date in the future', $token['body']['message']); + // Finite expiry - $expiry = DateTime::addSeconds(new \DateTime(), 3600); + $expiry = date('Y-m-d', strtotime("tomorrow")); $token = $this->client->call(Client::METHOD_PATCH, '/tokens/' . $tokenId, array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'] diff --git a/tests/e2e/Services/Tokens/TokensCustomServerTest.php b/tests/e2e/Services/Tokens/TokensCustomServerTest.php index fe8fa2bad9..779d5449b3 100644 --- a/tests/e2e/Services/Tokens/TokensCustomServerTest.php +++ b/tests/e2e/Services/Tokens/TokensCustomServerTest.php @@ -7,7 +7,6 @@ use Tests\E2E\Client; use Tests\E2E\Scopes\ProjectCustom; use Tests\E2E\Scopes\Scope; use Tests\E2E\Scopes\SideServer; -use Utopia\Database\DateTime; use Utopia\Database\Helpers\ID; use Utopia\Database\Helpers\Permission; use Utopia\Database\Helpers\Role; @@ -61,6 +60,17 @@ class TokensCustomServerTest extends Scope $fileId = $file['body']['$id']; + // Failure case: Expire date is in the past + $token = $this->client->call(Client::METHOD_POST, '/tokens/buckets/' . $bucketId . '/files/' . $fileId, array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'] + ], $this->getHeaders()), [ + 'expire' => '2022-11-02', + ]); + $this->assertEquals(400, $token['headers']['status-code']); + $this->assertStringContainsString('Value must be valid date in the future', $token['body']['message']); + + // Success case: No expire date $token = $this->client->call(Client::METHOD_POST, '/tokens/buckets/' . $bucketId . '/files/' . $fileId, array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'] @@ -83,8 +93,19 @@ class TokensCustomServerTest extends Scope { $tokenId = $data['tokenId']; - // Finite expiry - $expiry = DateTime::addSeconds(new \DateTime(), 3600); + // Failure case: Expire date is in the past + $token = $this->client->call(Client::METHOD_PATCH, '/tokens/' . $tokenId, [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ], [ + 'expire' => '2022-11-02', + ]); + $this->assertEquals(400, $token['headers']['status-code']); + $this->assertStringContainsString('Value must be valid date in the future', $token['body']['message']); + + // Success case: Finite expiry + $expiry = date('Y-m-d', strtotime("tomorrow")); $token = $this->client->call(Client::METHOD_PATCH, '/tokens/' . $tokenId, [ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -94,9 +115,10 @@ class TokensCustomServerTest extends Scope ]); $dateValidator = new DatetimeValidator(); + $this->assertEquals(200, $token['headers']['status-code']); $this->assertTrue($dateValidator->isValid($token['body']['expire'])); - // Infinite expiry + // Success case: Infinite expiry $token = $this->client->call(Client::METHOD_PATCH, '/tokens/' . $tokenId, array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], diff --git a/tests/resources/image.webp b/tests/resources/image.webp new file mode 100644 index 0000000000..4a8791784a Binary files /dev/null and b/tests/resources/image.webp differ